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.spran...@linutronix.de>
Signed-off-by: Holger Dengler <deng...@linutronix.de>
cc: Lee Jones <lee.jo...@linaro.org>
---
 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.spran...@linutronix.de>
+ *         Holger Dengler <deng...@linutronix.de>
+ *
+ * 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 <deng...@linutronix.de>");
+MODULE_AUTHOR("Benedikt Spranger <b.spran...@linutronix.de>");
+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.spran...@linutronix.de>
+ *         Holger Dengler <deng...@linutronix.de>
+ *
+ * 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.spran...@linutronix.de>
+ *         Holger Dengler <deng...@linutronix.de>
+ *
+ * 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

Reply via email to