[PATCH] Ported Xilinx GPIO driver to OpenFirmware.
Magnus Hjorth
mh at omnisys.se
Wed Mar 12 03:12:54 EST 2008
From: Magnus Hjorth <mh at omnisys.se>
Added of_device structure and init code.
Basic functionality tested (LED:s on/off).
Added code to validate channel number in ioctl.
Signed-off-by: Magnus Hjorth <mh at omnisys.se>
---
Patch against Xilinx GIT tree (commit 7fb2b...)
Did this as a learning exercise. Hope it gets through Outlook in one piece :)
Cheers /Magnus
diff --git a/drivers/char/xilinx_gpio/adapter.c b/drivers/char/xilinx_gpio/adapter.c
index ecd2897..6fdf7aa 100644
--- a/drivers/char/xilinx_gpio/adapter.c
+++ b/drivers/char/xilinx_gpio/adapter.c
@@ -43,15 +43,22 @@
#include <asm/irq.h>
#include <linux/interrupt.h>
+#ifdef CONFIG_OF
+// For open firmware.
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#endif
+
#define BUFSIZE 200
#define MIN(x,y) (x < y ? x : y)
-
struct xgpio_instance {
struct list_head link;
unsigned long base_phys; /* GPIO base address - physical */
unsigned long remap_size;
- u32 device_id;
+ u32 device_id; /* Dev ID for platform devices, 0 for OF devices */
+ void *of_id; /* of_dev pointer for OF devices, NULL for plat devices */
+ int is_dual;
wait_queue_head_t wait;
unsigned int head, tail, count;
__u64 buf[BUFSIZE]; /* 32xChan1, 32xChan2 */
@@ -125,8 +132,7 @@ static struct xgpio_instance *xgpio_getinst(unsigned int minor)
up_read(&inst_list_sem);
if (XGpio_IsReady(&inst->gpio)) {
return inst;
- }
- else {
+ } else {
return NULL;
}
}
@@ -147,6 +153,10 @@ static int xgpio_ioctl(struct inode *inode, struct file *file,
if (copy_from_user(&ioctl_data, (void *) arg, sizeof(ioctl_data)))
return -EFAULT;
+ /* Validate channel number */
+ if (ioctl_data.chan != 1 && (ioctl_data.chan != 2 || !inst->is_dual))
+ return -EINVAL;
+
switch (cmd) {
case XGPIO_IN:
/*
@@ -216,6 +226,7 @@ static int xgpio_ioctl(struct inode *inode, struct file *file,
return -ENOIOCTLCMD;
}
+
return 0;
}
@@ -237,7 +248,6 @@ static unsigned int prev(unsigned int ptr)
return ptr;
}
-
static ssize_t xgpio_read(struct file *file, char *buf,
size_t count, loff_t * ppos)
{
@@ -278,7 +288,6 @@ static ssize_t xgpio_read(struct file *file, char *buf,
return 0;
}
-
static irqreturn_t xgpio_interrupt(int irq, void *dev_id)
{
struct xgpio_instance *inst = dev_id;
@@ -287,7 +296,6 @@ static irqreturn_t xgpio_interrupt(int irq, void *dev_id)
inst->gpio.IsDual ? XIo_In32(inst->gpio.BaseAddress + 0x08) : 0;
__u32 int_status = XIo_In32(inst->gpio.BaseAddress + 0x120);
-
if (inst->buf[prev(inst->tail)] !=
(((__u64) val_1) | ((__u64) (val_2) << 32)))
if (next(inst->tail) != inst->head) {
@@ -303,7 +311,6 @@ static irqreturn_t xgpio_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
/*
* We get to all of the GPIOs through one minor number. Here's the
* miscdevice that gets registered for that minor number.
@@ -335,45 +342,33 @@ char *names[] = {
static int minor = XGPIO_MINOR;
-static int xgpio_probe(struct device *dev)
+static int xgpio_probe_main(struct device *dev, int dev_id, void *of_dev_id,
+ int is_dual,
+ struct resource *irq_res, struct resource *regs_res)
{
XGpio_Config xgpio_config;
struct xgpio_instance *xgpio_inst;
struct miscdevice *miscdev = 0;
- struct platform_device *pdev = to_platform_device(dev);
- struct resource *irq_res, *regs_res;
void *v_addr;
int retval;
- if (!dev)
- return -EINVAL;
-
memset(&xgpio_config, 0, sizeof(XGpio_Config));
xgpio_inst = kmalloc(sizeof(struct xgpio_instance), GFP_KERNEL);
if (!xgpio_inst) {
printk(KERN_ERR
"%s #%d: Couldn't allocate device private record\n",
- miscdev->name, pdev->id);
+ miscdev->name, dev_id);
return -ENOMEM;
}
memset(xgpio_inst, 0, sizeof(struct xgpio_instance));
- /* Map the control registers in */
- regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs_res || (regs_res->end - regs_res->start + 1 < 8)) {
- printk(KERN_ERR "%s #%d: Couldn't get registers resource\n",
- miscdev->name, pdev->id);
- retval = -EFAULT;
- goto failed1;
- }
-
xgpio_inst->remap_size = regs_res->end - regs_res->start + 1;
if (!request_mem_region(regs_res->start, xgpio_inst->remap_size,
DRIVER_NAME)) {
printk(KERN_ERR "Couldn't lock memory region at 0x%08lX\n",
(unsigned long) regs_res->start);
retval = -EBUSY;
- goto failed2;
+ goto failed1;
}
v_addr = ioremap(regs_res->start, xgpio_inst->remap_size);
@@ -381,21 +376,22 @@ static int xgpio_probe(struct device *dev)
printk(KERN_ERR "Couldn't ioremap memory at 0x%08lX\n",
(unsigned long) regs_res->start);
retval = -EFAULT;
- goto failed3;
+ goto failed2;
}
xgpio_inst->base_phys = regs_res->start;
/* The 1st GPIO channel uses */
- xgpio_inst->device_id = pdev->id;
- xgpio_config.DeviceId = pdev->id;
- xgpio_config.IsDual =
- ((unsigned) (dev->platform_data) & XGPIO_IS_DUAL) ? 1 : 0;
+ xgpio_inst->device_id = dev_id;
+ xgpio_inst->of_id = of_dev_id;
+ xgpio_inst->is_dual = is_dual ? 1 : 0;
+ xgpio_config.DeviceId = dev_id;
+ xgpio_config.IsDual = is_dual ? 1 : 0;
/* Tell the Xilinx code to bring this GPIO interface up. */
if (XGpio_CfgInitialize(&xgpio_inst->gpio, &xgpio_config,
(u32) v_addr) != XST_SUCCESS) {
printk(KERN_ERR "%s #%d: Could not initialize instance.\n",
- miscdev->name, pdev->id);
+ miscdev->name, dev_id);
retval = -ENODEV;
goto failed3;
}
@@ -407,7 +403,7 @@ static int xgpio_probe(struct device *dev)
if (!miscdev) {
printk(KERN_ERR
"%s #%d: Couldn't allocate device private record\n",
- "xgpio", pdev->id);
+ "xgpio", dev_id);
return -ENOMEM;
}
@@ -419,7 +415,7 @@ static int xgpio_probe(struct device *dev)
if (retval != 0) {
up_write(&inst_list_sem);
printk(KERN_ERR "%s #%d: Could not register miscdev.\n",
- miscdev->name, pdev->id);
+ miscdev->name, dev_id);
goto failed3;
}
@@ -427,7 +423,6 @@ static int xgpio_probe(struct device *dev)
minor++;
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq_res) {
if (request_irq(irq_res->start,
xgpio_interrupt, 0, "XGPIO", xgpio_inst))
@@ -471,24 +466,45 @@ static int xgpio_probe(struct device *dev)
return retval;
}
-static int xgpio_remove(struct device *dev)
+static int xgpio_probe(struct device *dev)
{
- struct list_head *entry;
- struct xgpio_instance *xgpio_inst = NULL;
struct platform_device *pdev = to_platform_device(dev);
+ struct resource *irq_res, *regs_res;
+ int is_dual;
if (!dev)
return -EINVAL;
+ /* Map the control registers in */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs_res || (regs_res->end - regs_res->start + 1 < 8)) {
+ dev_err(dev, "couldn't get registers resource\n");
+ return -EFAULT;
+ }
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ is_dual = ((unsigned)(dev->platform_data) & XGPIO_IS_DUAL);
+
+ return xgpio_probe_main(dev, pdev->id, NULL, is_dual, irq_res,
+ regs_res);
+}
+
+static int xgpio_remove_main(struct device *dev, int dev_id, void *of_dev_id)
+{
+ struct list_head *entry;
+ struct xgpio_instance *xgpio_inst = NULL;
+ struct platform_device *pdev = to_platform_device(dev);
+
/* Set xgpio_inst based on pdev->id match */
down_read(&inst_list_sem);
list_for_each(entry, &inst_list) {
xgpio_inst = list_entry(entry, struct xgpio_instance, link);
- if (pdev->id == xgpio_inst->device_id) {
+ if (pdev->id == xgpio_inst->device_id &&
+ of_dev_id == xgpio_inst->of_id) {
break;
- }
- else {
+ } else {
xgpio_inst = NULL;
}
}
@@ -515,6 +531,16 @@ static int xgpio_remove(struct device *dev)
return 0; /* success */
}
+static int xgpio_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ return xgpio_remove_main(dev, pdev->id, NULL);
+}
+
static struct device_driver xgpio_driver = {
.name = DRIVER_NAME,
.bus = &platform_bus_type,
@@ -523,13 +549,77 @@ static struct device_driver xgpio_driver = {
.remove = xgpio_remove
};
+#ifdef CONFIG_OF
+
+static int __devinit xgpio_of_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct resource r_irq_struct, r_mem_struct;
+ struct resource *r_irq;
+ int rc;
+ int l;
+ const u32 *p;
+ int is_dual;
+
+ rc = of_address_to_resource(ofdev->node, 0, &r_mem_struct);
+ if (rc) {
+ dev_warn(&ofdev->dev, "invalid address\n");
+ return rc;
+ }
+
+ rc = of_irq_to_resource(ofdev->node, 0, &r_irq_struct);
+ if (rc == NO_IRQ)
+ r_irq = NULL;
+ else
+ r_irq = &r_irq_struct;
+
+ p = of_get_property(ofdev->node, "xlnx,is-dual", &l);
+ if (p == NULL || l != sizeof(*p)) {
+ dev_warn(&ofdev->dev, "Property not found: xlnx,is-dual\n");
+ is_dual = 0;
+ } else
+ is_dual = *p;
+
+ return xgpio_probe_main(&ofdev->dev, 0, ofdev, is_dual, r_irq,
+ &r_mem_struct);
+}
+
+static int __devexit xgpio_of_remove(struct of_device *ofdev)
+{
+ if (!ofdev)
+ return -EINVAL;
+
+ return xgpio_remove_main(&ofdev->dev, 0, ofdev);
+}
+
+static struct of_device_id xgpio_of_match[] = {
+ {.compatible = "xlnx,xps-gpio-1.00.a"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, xgpio_of_match);
+
+static struct of_platform_driver xgpio_of_driver = {
+ .name = DRIVER_NAME,
+ .match_table = xgpio_of_match,
+ .probe = xgpio_of_probe,
+ .remove = __devexit_p(xgpio_of_remove),
+};
+
+#endif
+
static int __init xgpio_init(void)
{
+ int s;
/*
* No kernel boot options used,
* so we just need to register the driver
*/
- return driver_register(&xgpio_driver);
+ s = driver_register(&xgpio_driver);
+#ifdef CONFIG_OF
+ s |= of_register_platform_driver(&xgpio_of_driver);
+#endif
+ return s;
}
static void __exit xgpio_cleanup(void)
--
1.5.4.4
--
Magnus Hjorth, M.Sc.
Omnisys Instruments AB
Gruvgatan 8
SE-421 30 Västra Frölunda, SWEDEN
Phone: +46 31 734 34 09
Fax: +46 31 734 34 29
http://www.omnisys.se
More information about the Linuxppc-embedded
mailing list