[PATCH 08/12] misc: Flexcard misc device support

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


The Flexcard PCI BAR0 contain registers for configuration but also
for informational purpose like error counter, statistical information
and some timestamps. The read-only mmap of the misc device offers the
userspace a fast access to these registers.

Signed-off-by: Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>
Signed-off-by: Holger Dengler <dengler@xxxxxxxxxxxxx>
cc: Arnd Bergmann <arnd@xxxxxxxx>
cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
drivers/mfd/Kconfig | 1 +
drivers/misc/Kconfig | 6 ++
drivers/misc/Makefile | 1 +
drivers/misc/flexcard_misc.c | 165 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 173 insertions(+)
create mode 100644 drivers/misc/flexcard_misc.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 85fedf6..580f521 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -303,6 +303,7 @@ config MFD_FLEXCARD
tristate "Eberspaecher Flexcard PMC II Carrier Board"
select MFD_CORE
select IRQ_DOMAIN
+ select FLEXCARD_MISC
depends on PCI
help
This is the core driver for the Eberspaecher Flexcard
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971ba..3f54b58 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,12 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.

+config FLEXCARD_MISC
+ tristate "Flexcard Misc Device driver"
+ depends on MFD_FLEXCARD
+ help
+ Misc driver for Flexcard PMC II.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3198336..08a1729 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_FLEXCARD_MISC) += flexcard_misc.o

lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/flexcard_misc.c b/drivers/misc/flexcard_misc.c
new file mode 100644
index 0000000..93c951c
--- /dev/null
+++ b/drivers/misc/flexcard_misc.c
@@ -0,0 +1,165 @@
+/*
+ * EberspÃcher Flexcard PMC II Misc Device
+ *
+ * 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/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/flexcard.h>
+
+#define FLEXCARD_MAX_NAME 16
+
+struct flexcard_misc {
+ char name[FLEXCARD_MAX_NAME];
+ struct miscdevice dev;
+ struct platform_device *pdev;
+ struct fc_bar0_conf __iomem *conf;
+ struct fc_bar0_nf __iomem *nf;
+};
+
+static int flexcard_misc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long offset, vsize, psize, addr;
+ struct flexcard_misc *priv;
+ struct resource *res;
+
+ priv = container_of(filp->private_data, struct flexcard_misc, dev);
+ if (!priv)
+ return -EINVAL;
+
+ if (vma->vm_flags & (VM_WRITE | VM_EXEC))
+ return -EPERM;
+
+ res = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ if (offset > resource_size(res)) {
+ dev_err(&priv->pdev->dev,
+ "mmap offset out of resource range\n");
+ return -EINVAL;
+ }
+
+ vsize = vma->vm_end - vma->vm_start;
+ psize = round_up(resource_size(res) - offset, PAGE_SIZE);
+ addr = (res->start + offset) >> PAGE_SHIFT;
+ if (vsize > psize) {
+ dev_err(&priv->pdev->dev,
+ "requested mmap mapping too large\n");
+ return -EINVAL;
+ }
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ return io_remap_pfn_range(vma, vma->vm_start, addr, vsize,
+ vma->vm_page_prot);
+}
+
+static const struct file_operations flexcard_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = nonseekable_open,
+ .mmap = flexcard_misc_mmap,
+ .llseek = no_llseek,
+};
+
+static int flexcard_misc_iomap(struct platform_device *pdev)
+{
+ struct flexcard_misc *priv = platform_get_drvdata(pdev);
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ priv->conf = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->conf)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ priv->nf = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->nf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ return 0;
+out:
+ iounmap(priv->conf);
+ return ret;
+}
+
+static int flexcard_misc_probe(struct platform_device *pdev)
+{
+ struct flexcard_misc *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = flexcard_misc_iomap(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to map resource: %d\n", ret);
+ return ret;
+ }
+
+ snprintf(priv->name, sizeof(priv->name),
+ "flexcard%d", pdev->id);
+ priv->dev.name = priv->name;
+ priv->dev.minor = MISC_DYNAMIC_MINOR;
+ priv->dev.fops = &flexcard_misc_fops;
+ priv->dev.parent = &pdev->dev;
+ priv->pdev = pdev;
+
+ ret = misc_register(&priv->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register miscdevice: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int flexcard_misc_remove(struct platform_device *pdev)
+{
+ struct flexcard_misc *priv = platform_get_drvdata(pdev);
+
+ misc_deregister(&priv->dev);
+
+ return 0;
+}
+
+static struct platform_driver flexcard_misc_driver = {
+ .probe = flexcard_misc_probe,
+ .remove = flexcard_misc_remove,
+ .driver = {
+ .name = "flexcard-misc",
+ },
+};
+
+module_platform_driver(flexcard_misc_driver);
+
+MODULE_AUTHOR("Holger Dengler <dengler@xxxxxxxxxxxxx>");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Eberspaecher Flexcard PMC II Misc Driver");
+MODULE_LICENSE("GPL v2");
--
2.1.4