[PATCH 01/12] mfd: Eberspaecher Flexcard PMC II Carrier Board support

From: Holger Dengler
Date: Tue Dec 13 2016 - 19:13:56 EST


The Eberspaecher Flexcard PMC II is a PMC (PCI Mezzanine Card) II
carrier board. The carrier board can take up to 4 exchangeable physical
layer boards for e.g. CAN, FlexRay or Ethernet.

Signed-off-by: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
Signed-off-by: Holger Dengler <dengler@xxxxxxxxxxxxx>
cc: Lee Jones <lee.jones@xxxxxxxxxx>
---
drivers/mfd/Kconfig | 10 ++
drivers/mfd/Makefile | 2 +
drivers/mfd/flexcard_core.c | 218 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/flexcard.h | 103 ++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/flexcard.h | 64 +++++++++++++
6 files changed, 398 insertions(+)
create mode 100644 drivers/mfd/flexcard_core.c
create mode 100644 include/linux/mfd/flexcard.h
create mode 100644 include/uapi/linux/flexcard.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..a5a12da 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -299,6 +299,16 @@ config MFD_EXYNOS_LPASS
Select this option to enable support for Samsung Exynos Low Power
Audio Subsystem.

+config MFD_FLEXCARD
+ tristate "Eberspaecher Flexcard PMC II Carrier Board"
+ select MFD_CORE
+ depends on PCI
+ help
+ This is the core driver for the Eberspaecher Flexcard
+ PMC (PCI Mezzanine Card) II carrier board. This carrier board
+ can take up to 4 exchangeable physical layer boards for
+ CAN, FlexRay or Ethernet.
+
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..843e57c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
obj-$(CONFIG_MFD_MT6397) += mt6397-core.o

obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+flexcard-objs := flexcard_core.o
+obj-$(CONFIG_MFD_FLEXCARD) += flexcard.o
diff --git a/drivers/mfd/flexcard_core.c b/drivers/mfd/flexcard_core.c
new file mode 100644
index 0000000..e580971
--- /dev/null
+++ b/drivers/mfd/flexcard_core.c
@@ -0,0 +1,218 @@
+/*
+ * EberspÃcher Flexcard PMC II Carrier Board PCI Driver
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
+ * Holger Dengler <dengler@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/flexcard.h>
+
+#define FLEXCARD_CAN_OFFSET 0x2000
+#define FLEXCARD_CAN_SIZE 0x2000
+
+#define FLEXCARD_FR_OFFSET 0x4000
+#define FLEXCARD_FR_SIZE 0x2000
+
+enum flexcard_cell_id {
+ FLEXCARD_CELL_CAN,
+ FLEXCARD_CELL_FLEXRAY,
+};
+
+static int flexcard_tiny_can(struct flexcard_device *priv,
+ int idx, int id, u32 offset)
+{
+ struct mfd_cell *cell = &priv->cells[idx];
+ struct resource *res = &priv->res[idx];
+ struct pci_dev *pci = priv->pdev;
+
+ cell->name = "flexcard-dcan";
+ cell->resources = res;
+ cell->num_resources = 1;
+ cell->id = id;
+
+ res->name = "flexcard-dcan";
+ res->flags = IORESOURCE_MEM;
+ res->parent = &pci->resource[1];
+ res->start = pci->resource[1].start + offset;
+ res->end = res->start + FLEXCARD_CAN_SIZE - 1;
+
+ if (res->end > pci->resource[1].end)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int flexcard_tiny_flexray(struct flexcard_device *priv,
+ int idx, int id, u32 offset)
+{
+ struct mfd_cell *cell = &priv->cells[idx];
+ struct resource *res = &priv->res[idx];
+ struct pci_dev *pci = priv->pdev;
+
+ cell->name = "flexcard-eray";
+ cell->resources = res;
+ cell->num_resources = 1;
+ cell->id = id;
+
+ res->name = "flexcard-eray";
+ res->flags = IORESOURCE_MEM;
+ res->parent = &pci->resource[1];
+ res->start = pci->resource[1].start + offset;
+ res->end = res->start + FLEXCARD_FR_SIZE - 1;
+
+ if (res->end > pci->resource[1].end)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int flexcard_tiny_probe(struct flexcard_device *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ u32 fc_slic0, offset = 0;
+ u8 nr_can, nr_fr, nr;
+ int i, ret;
+
+ /*
+ * Reading FC_LIC[0] register to determine the number of CAN and
+ * FlexRay Devices
+ */
+ fc_slic0 = readl(&priv->bar0->conf.fc_slic[0]);
+ nr_can = (fc_slic0 >> 4) & 0xf;
+ nr_fr = fc_slic0 & 0xf;
+ nr = nr_can + nr_fr;
+
+ dev_info(&pdev->dev, "tinys: CAN: %d FR: %d", nr_can, nr_fr);
+
+ priv->cells = devm_kzalloc(&pdev->dev, nr * sizeof(struct mfd_cell),
+ GFP_KERNEL);
+ if (!priv->cells)
+ return -ENOMEM;
+
+ priv->res = devm_kzalloc(&pdev->dev, nr * sizeof(struct resource),
+ GFP_KERNEL);
+ if (!priv->res)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_fr; i++) {
+ ret = flexcard_tiny_flexray(priv, i, i, offset);
+ if (ret)
+ return ret;
+ offset += FLEXCARD_FR_OFFSET;
+ }
+
+ for (i = 0; i < nr_can; i++) {
+ ret = flexcard_tiny_can(priv, nr_fr + i, i, offset);
+ if (ret)
+ return ret;
+ offset += FLEXCARD_CAN_OFFSET;
+ }
+
+ return mfd_add_devices(&pdev->dev, 0, priv->cells, nr, NULL, 0, NULL);
+}
+
+static int flexcard_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct flexcard_device *priv;
+ union {
+ struct fc_version ver;
+ u32 reg;
+ } fw_ver, hw_ver;
+
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, priv);
+ priv->pdev = pdev;
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable device: %d\n", ret);
+ return ret;
+ }
+
+ pci_set_master(pdev);
+ ret = pci_request_regions(pdev, "flexcard");
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request regions: %d\n", ret);
+ goto out_disable;
+ }
+
+ priv->bar0 = pci_ioremap_bar(pdev, 0);
+ if (!priv->bar0) {
+ dev_err(&pdev->dev, "unable to remap bar0 regs\n");
+ ret = -ENOMEM;
+ goto out_release;
+ }
+ fw_ver.reg = readl(&priv->bar0->conf.fc_fw_ver);
+ hw_ver.reg = readl(&priv->bar0->conf.fc_hw_ver);
+
+ ret = flexcard_tiny_probe(priv);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to probe tinys: %d", ret);
+ goto out_unmap;
+ }
+
+ dev_info(&pdev->dev, "HW %02x.%02x.%02x FW %02x.%02x.%02x\n",
+ hw_ver.ver.maj, hw_ver.ver.min, hw_ver.ver.dev,
+ fw_ver.ver.maj, fw_ver.ver.min, fw_ver.ver.dev);
+
+ return 0;
+
+out_unmap:
+ iounmap(priv->bar0);
+out_release:
+ pci_release_regions(pdev);
+out_disable:
+ pci_disable_device(pdev);
+
+ return ret;
+}
+
+static void flexcard_remove(struct pci_dev *pdev)
+{
+ struct flexcard_device *priv = pci_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ iounmap(priv->bar0);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+#define PCI_VENDOR_ID_EBEL 0x1974
+
+static const struct pci_device_id flexcard_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_EBEL, 0x0009), },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, flexcard_pci_ids);
+
+static struct pci_driver flexcard_driver = {
+ .name = "flexcard",
+ .id_table = flexcard_pci_ids,
+ .probe = flexcard_probe,
+ .remove = flexcard_remove,
+};
+
+module_pci_driver(flexcard_driver);
+
+MODULE_AUTHOR("Holger Dengler <dengler@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Eberspaecher Flexcard PMC II Carrier Board Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
new file mode 100644
index 0000000..362b909
--- /dev/null
+++ b/include/linux/mfd/flexcard.h
@@ -0,0 +1,103 @@
+/*
+ * EberspÃcher Flexcard PMC II Carrier Board PCI Driver - device attributes
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
+ * Holger Dengler <dengler@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_MFD_FLEXCARD_H
+#define _LINUX_MFD_FLEXCARD_H
+
+#include <uapi/linux/flexcard.h>
+
+/* PCI BAR 0: Flexcard DMA register */
+struct fc_bar0_dma {
+ __u32 dma_ctrl; /* 500 */
+ __u32 dma_stat; /* 504 */
+ __u32 r16[2]; /* 508 */
+ __u64 dma_cba; /* 510 */
+ __u32 dma_cbs; /* 518 */
+ __u32 dma_txr; /* 51c */
+ __u32 dma_irer; /* 520 */
+ __u32 dma_irsr; /* 524 */
+ __u32 r17[10]; /* 528 */
+ __u32 dma_cbcr; /* 550 */
+ __u32 dma_cblr; /* 554 */
+ __u32 r18[2]; /* 558 */
+ __u32 dma_itcr; /* 560 */
+ __u32 dma_itr; /* 564 */
+ __u32 r19[2]; /* 568 */
+ __u32 dma_wptr; /* 570 */
+ __u32 dma_rptr; /* 574 */
+} __packed;
+
+/* PCI BAR 0: Flexcard clock register */
+struct fc_bar0_time {
+ __u32 ts_high; /* 700 */
+ __u32 ts_low; /* 704 */
+ __u32 r21[2]; /* 708 */
+ __u32 clk_src; /* 710 */
+} __packed;
+
+struct fc_bar0_nf {
+ __u32 fc_nfctrl; /* 170 */
+ __u32 nf_cnt; /* 174 */
+} __packed;
+
+/* PCI BAR 0: Flexcard register */
+struct fc_bar0 {
+ struct fc_bar0_conf conf; /* 000-13c */
+ __u32 fc_ts; /* 140 */
+ __u32 fc_reset; /* 144 */
+ __u32 trig_sc_ctrl; /* 148 */
+ __u32 trig_ctrl; /* 14c */
+ __u32 r12; /* 150 */
+ __u32 tirqir; /* 154 */
+ __u32 pccr1; /* 158 */
+ __u32 pccr2; /* 15c */
+ __u32 r13[4]; /* 160 */
+ struct fc_bar0_nf nf; /* 170-174 */
+ __u32 r14; /* 178 */
+ struct fc_bar0_dma dma; /* 500-574 */
+ __u32 r20[0x62]; /* 578 */
+ struct fc_bar0_time time; /* 700-710 */
+ __u32 r22[0x7b]; /* 714 */
+ __u32 faddr; /* 900 */
+ __u32 fwdat; /* 904 */
+ __u32 fctrl; /* 908 */
+ __u32 frdat; /* 90c */
+ __u32 bwdat[16]; /* 910 */
+ __u32 brdat[16]; /* 950 */
+ __u32 r23[28]; /* 990 */
+ __u32 fwmode; /* a00 */
+ __u32 recond; /* a04 */
+ __u32 wdtctrl; /* a08 */
+ __u32 imgsel; /* a0c */
+ __u32 actimg; /* a10 */
+ __u32 updimginf; /* a14 */
+ __u32 r24[0x32]; /* a18 */
+ __u32 factory_image_info[8]; /* ae0 */
+ __u32 app_image0_info[8]; /* b00 */
+ __u32 app_image1_info[8]; /* b20 */
+ __u32 app_image2_info[8]; /* b40 */
+ __u32 app_image3_info[8]; /* b60 */
+ __u32 app_image4_info[8]; /* b80 */
+ __u32 app_image5_info[8]; /* ba0 */
+ __u32 app_image6_info[8]; /* bc0 */
+ __u32 app_image7_info[8]; /* be0 */
+ __u32 r25[0x100]; /* c00 */
+} __packed;
+
+struct flexcard_device {
+ struct pci_dev *pdev;
+ struct fc_bar0 __iomem *bar0;
+ struct mfd_cell *cells;
+ struct resource *res;
+};
+
+#endif /* _LINUX_FLEXCARD_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index cd2be1c..46dc3c1 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -131,6 +131,7 @@ header-y += filter.h
header-y += firewire-cdev.h
header-y += firewire-constants.h
header-y += flat.h
+header-y += flexcard.h
header-y += fou.h
header-y += fs.h
header-y += fsl_hypervisor.h
diff --git a/include/uapi/linux/flexcard.h b/include/uapi/linux/flexcard.h
new file mode 100644
index 0000000..4e9f07b4
--- /dev/null
+++ b/include/uapi/linux/flexcard.h
@@ -0,0 +1,64 @@
+/*
+ * EberspÃcher Flexcard PMC II Carrier Board PCI Driver - device attributes
+ *
+ * Copyright (c) 2014,2016 Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
+ * Holger Dengler <dengler@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _UAPI_LINUX_FLEXCARD_H
+#define _UAPI_LINUX_FLEXCARD_H
+
+#include <linux/types.h>
+
+struct fc_version {
+ __u8 dev;
+ __u8 min;
+ __u8 maj;
+ __u8 reserved;
+} __packed;
+
+/* PCI BAR 0: Flexcard configuration */
+struct fc_bar0_conf {
+ __u32 r1; /* 000 */
+ struct fc_version fc_fw_ver; /* 004 */
+ struct fc_version fc_hw_ver; /* 008 */
+ __u32 r2[3]; /* 00c */
+ __u64 fc_sn; /* 018 */
+ __u32 fc_uid; /* 020 */
+ __u32 r3[7]; /* 024 */
+ __u32 fc_lic[6]; /* 040 */
+ __u32 fc_slic[6]; /* 058 */
+ __u32 trig_ctrl1; /* 070 */
+ __u32 r4; /* 074 */
+ __u32 trig_ctrl2; /* 078 */
+ __u32 r5[22]; /* 07c */
+ __u32 amreg; /* 0d4 */
+ __u32 tiny_stat; /* 0d8 */
+ __u32 r6[5]; /* 0dc */
+ __u32 can_dat_cnt; /* 0f0 */
+ __u32 can_err_cnt; /* 0f4 */
+ __u32 fc_data_cnt; /* 0f8 */
+ __u32 r7; /* 0fc */
+ __u32 fc_rocr; /* 100 */
+ __u32 r8; /* 104 */
+ __u32 pg_ctrl; /* 108 */
+ __u32 pg_term; /* 10c */
+ __u32 r9; /* 110 */
+ __u32 irs; /* 114 */
+ __u32 fr_tx_cnt; /* 118 */
+ __u32 irc; /* 11c */
+ __u64 pcnt; /* 120 */
+ __u32 r10; /* 128 */
+ __u32 nmv_cnt; /* 12c */
+ __u32 info_cnt; /* 130 */
+ __u32 stat_trg_cnt; /* 134 */
+ __u32 r11; /* 138 */
+ __u32 fr_rx_cnt; /* 13c */
+} __packed;
+
+#endif /* _UAPI_LINUX_FLEXCARD_H */
--
2.1.4