[RESEND][PATCH] uio: Add of_platform_driver to uio_pdrv_genirq
From: Wolfram Sang
Date: Thu Dec 11 2008 - 10:05:49 EST
Make the generic uio-driver also accessible for of devices. It utilizes the
standard 'reg' and 'interrupt' properties. A typical usage would look like
this:
fpga-io@00003000 {
compatible = "generic-uio";
reg = <0x00003000 0x20>;
interrupts = <0xa>;
interrupt-parent = <&fpga_irq_mux>;
};
To achieve this, the probe function has been refactored, so it can be used by
platform and of code. Then, the of driver has been added.
Signed-off-by: Wolfram Sang <w.sang@xxxxxxxxxxxxxx>
---
drivers/uio/uio_pdrv_genirq.c | 178 ++++++++++++++++++++++++++++++++++++------
1 file changed, 154 insertions(+), 24 deletions(-)
Index: playground/drivers/uio/uio_pdrv_genirq.c
===================================================================
--- playground.orig/drivers/uio/uio_pdrv_genirq.c
+++ playground/drivers/uio/uio_pdrv_genirq.c
@@ -1,13 +1,15 @@
/*
* drivers/uio/uio_pdrv_genirq.c
*
- * Userspace I/O platform driver with generic IRQ handling code.
+ * Userspace I/O platform & of driver with generic IRQ handling code.
*
* Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008 Wolfram Sang, Pengutronix e.K.
*
* Based on uio_pdrv.c by Uwe Kleine-Koenig,
* Copyright (C) 2008 by Digi International Inc.
* All rights reserved.
+ * Adding of_platform_driver based on xilinxfb.c by Grant Likely.
*
* 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
@@ -20,6 +22,10 @@
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/stringify.h>
+#if defined(CONFIG_OF)
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#endif
#define DRIVER_NAME "uio_pdrv_genirq"
@@ -68,28 +74,18 @@
return 0;
}
-static int uio_pdrv_genirq_probe(struct platform_device *pdev)
+static int uio_pdrv_genirq_setup(struct device *dev, struct uio_info *uioinfo,
+ struct resource *resources, unsigned int num_resources)
{
- struct uio_info *uioinfo = pdev->dev.platform_data;
struct uio_pdrv_genirq_platdata *priv;
struct uio_mem *uiomem;
- int ret = -EINVAL;
- int i;
-
- if (!uioinfo || !uioinfo->name || !uioinfo->version) {
- dev_err(&pdev->dev, "missing platform_data\n");
- goto bad0;
- }
-
- if (uioinfo->handler || uioinfo->irqcontrol || uioinfo->irq_flags) {
- dev_err(&pdev->dev, "interrupt configuration error\n");
- goto bad0;
- }
+ unsigned int i;
+ int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
- dev_err(&pdev->dev, "unable to kmalloc\n");
+ dev_err(dev, "unable to kmalloc\n");
goto bad0;
}
@@ -99,14 +95,15 @@
uiomem = &uioinfo->mem[0];
- for (i = 0; i < pdev->num_resources; ++i) {
- struct resource *r = &pdev->resource[i];
+ for (i = 0; i < num_resources; ++i) {
+
+ struct resource *r = resources + i;
if (r->flags != IORESOURCE_MEM)
continue;
if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
- dev_warn(&pdev->dev, "device has more than "
+ dev_warn(dev, "device has more than "
__stringify(MAX_UIO_MAPS)
" I/O memory resources.\n");
break;
@@ -137,13 +134,13 @@
uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
uioinfo->priv = priv;
- ret = uio_register_device(&pdev->dev, priv->uioinfo);
+ ret = uio_register_device(dev, priv->uioinfo);
if (ret) {
- dev_err(&pdev->dev, "unable to register uio device\n");
+ dev_err(dev, "unable to register uio device\n");
goto bad1;
}
- platform_set_drvdata(pdev, priv);
+ dev_set_drvdata(dev, priv);
return 0;
bad1:
kfree(priv);
@@ -151,6 +148,24 @@
return ret;
}
+static int uio_pdrv_genirq_probe(struct platform_device *pdev)
+{
+ struct uio_info *uioinfo = pdev->dev.platform_data;
+
+ if (!uioinfo || !uioinfo->name || !uioinfo->version) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -EINVAL;
+ }
+
+ if (uioinfo->handler || uioinfo->irqcontrol || uioinfo->irq_flags) {
+ dev_err(&pdev->dev, "interrupt configuration error\n");
+ return -EINVAL;
+ }
+
+ return uio_pdrv_genirq_setup(&pdev->dev, uioinfo, pdev->resource,
+ pdev->num_resources);
+}
+
static int uio_pdrv_genirq_remove(struct platform_device *pdev)
{
struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
@@ -169,20 +184,135 @@
},
};
+/* ---------------------------------------------------------------------
+ * OF bus binding
+ */
+
+#if defined(CONFIG_OF)
+
+#define OF_DRIVER_NAME "uio_of_genirq"
+#define OF_DRIVER_VERSION "1"
+
+static int uio_of_genirq_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct uio_info *uioinfo;
+ struct resource *resources;
+ int i, ret = -ENOMEM;
+
+ uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+ if (!uioinfo)
+ goto bad0;
+
+ uioinfo->name = (char *)of_get_property(op->node, "name", NULL);
+ if (!uioinfo->name) {
+ ret = -ENODEV;
+ dev_err(&op->dev, "could not get node name\n");
+ goto bad1;
+ }
+
+ uioinfo->version = OF_DRIVER_VERSION;
+ uioinfo->irq = irq_of_parse_and_map(op->node, 0);
+ if (!uioinfo->irq)
+ uioinfo->irq = UIO_IRQ_NONE;
+
+ resources = kzalloc(MAX_UIO_MAPS * sizeof(struct resource), GFP_KERNEL);
+ if (!resources)
+ goto bad2;
+
+ for (i = 0; i < MAX_UIO_MAPS; ++i)
+ if (of_address_to_resource(op->node, i, resources + i))
+ break;
+
+ ret = uio_pdrv_genirq_setup(&op->dev, uioinfo, resources, i);
+ kfree(resources);
+ if (ret)
+ goto bad2;
+
+ return 0;
+
+ bad2:
+ if (uioinfo->irq != UIO_IRQ_NONE)
+ irq_dispose_mapping(uioinfo->irq);
+ bad1:
+ kfree(uioinfo);
+ bad0:
+ return ret;
+}
+
+static int uio_of_genirq_remove(struct of_device *op)
+{
+ struct uio_pdrv_genirq_platdata *priv = dev_get_drvdata(&op->dev);
+
+ uio_unregister_device(priv->uioinfo);
+
+ if (priv->uioinfo->irq != UIO_IRQ_NONE)
+ irq_dispose_mapping(priv->uioinfo->irq);
+
+ kfree(priv->uioinfo);
+ kfree(priv);
+ return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id uio_of_genirq_match[] = {
+ { .compatible = "generic-uio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
+
+static struct of_platform_driver uio_of_genirq_driver = {
+ .owner = THIS_MODULE,
+ .name = OF_DRIVER_NAME,
+ .match_table = uio_of_genirq_match,
+ .probe = uio_of_genirq_probe,
+ .remove = uio_of_genirq_remove,
+};
+
+/* Registration helpers to keep the number of #ifdefs to a minimum */
+static inline int __init uio_of_genirq_register(void)
+{
+ return of_register_platform_driver(&uio_of_genirq_driver);
+}
+
+static inline void __exit uio_of_genirq_unregister(void)
+{
+ of_unregister_platform_driver(&uio_of_genirq_driver);
+}
+#else /* CONFIG_OF */
+/* CONFIG_OF not enabled; do nothing helpers */
+static inline int __init uio_of_genirq_register(void) { return 0; }
+static inline void __exit uio_of_genirq_unregister(void) { }
+#endif /* CONFIG_OF */
+
+/* --------------------------------------------------------------------- */
+
static int __init uio_pdrv_genirq_init(void)
{
- return platform_driver_register(&uio_pdrv_genirq);
+ int retval;
+
+ retval = uio_of_genirq_register();
+ if (retval)
+ return retval;
+
+ retval = platform_driver_register(&uio_pdrv_genirq);
+ if (retval)
+ uio_of_genirq_unregister();
+
+ return retval;
}
static void __exit uio_pdrv_genirq_exit(void)
{
platform_driver_unregister(&uio_pdrv_genirq);
+ uio_of_genirq_unregister();
}
module_init(uio_pdrv_genirq_init);
module_exit(uio_pdrv_genirq_exit);
MODULE_AUTHOR("Magnus Damm");
-MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
+MODULE_DESCRIPTION("Userspace I/O platform & of driver with generic"
+ "IRQ handling");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/