UIO not working on ppc405 onchip registers
Markus Brunner
super.firetwister at googlemail.com
Tue Jul 22 05:52:14 EST 2008
Hi,
I'm unable to get UIO working on the ppc405ep onchip registers (e.g. gpio/iic)
however it's working fine on peripherals.
It seems to me to be a problem with UIO on powerpc, because if I change the
address (and nothing more) to point to a external FPGA it's working fine.
I also tried the generic uio_pdrv which had the same problems.
Sometimes I get a "bus error" sometimes it only produces wrong results.
The "bus error" occurred when not a full 32 bit register was read (e.g. only a
byte of it), but I'm not sure if it doesn't occur for other reasons as well.
Here is a simple example against 2.6.26. It should toggle the GPIO pin 0 on
ppc405ep, but can be changed easily to work on other ppc variants.
Can anyone reproduce this problem, did anyone already succeed in writing a UIO
driver for the onchip registers? How can I fix this?
Might this be something like commit c9698d6b1a90929e427a165bd8283f803f57d9bd which
added pgprot_noncached() to UIO mmap code to get it work on ppc?
Regards
Markus
diff -upNr linux-2.6.26/drivers/uio-orig/Kconfig linux-2.6.26/drivers/uio/Kconfig
--- linux-2.6.26/drivers/uio-orig/Kconfig 2008-07-18 09:15:51.000000000 +0200
+++ linux-2.6.26/drivers/uio/Kconfig 2008-07-18 09:16:18.000000000 +0200
@@ -39,4 +39,12 @@ config UIO_SMX
If you compile this as a module, it will be called uio_smx.
+config UIO_GPIO
+ tristate "Driver for PPC_4xx GPIO"
+ depends on UIO
+ default n
+ help
+ Driver for PPC_4xx GPIO Registers
+
endif
+
diff -upNr linux-2.6.26/drivers/uio-orig/Makefile linux-2.6.26/drivers/uio/Makefile
--- linux-2.6.26/drivers/uio-orig/Makefile 2008-07-18 09:27:18.000000000 +0200
+++ linux-2.6.26/drivers/uio/Makefile 2008-07-18 09:16:50.000000000 +0200
@@ -1,3 +1,4 @@
obj-$(CONFIG_UIO) += uio.o
obj-$(CONFIG_UIO_CIF) += uio_cif.o
obj-$(CONFIG_UIO_SMX) += uio_smx.o
+obj-$(CONFIG_UIO_GPIO) += uio_ppc_4xx-gpio.o
diff -upNr linux-2.6.26/drivers/uio-orig/uio-gpio.c linux-2.6.26/drivers/uio/uio-gpio.c
--- linux-2.6.26/drivers/uio-orig/uio-gpio.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.26/drivers/uio/uio-gpio.c 2008-07-18 09:18:56.000000000 +0200
@@ -0,0 +1,59 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const unsigned long pin_mask( unsigned int pin) { return (0x80000000 >> (pin));}
+
+const char UIO_DEV[] = "/dev/uio0";
+const unsigned int UIO_SIZE = 0x1000;
+const unsigned int UIO_ADDR = 0xef600700;
+
+const int or = 0;
+const int tcr = 1;
+
+const unsigned int gpio_pin = 0; /* What gpio pin do you want to toggle? */
+
+volatile unsigned long *gpio_regs;
+
+int main(int argc, char *argv[])
+{
+ int uiofd = open(UIO_DEV,O_RDWR);
+ if (uiofd < 0)
+ return uiofd;
+
+ unsigned long* map_addr = mmap(NULL,
+ UIO_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ uiofd,
+ 0);
+ if (map_addr == ((unsigned long*) -1))
+ return -1;
+ gpio_regs = (volatile unsigned long*) map_addr;
+ printf("Mapped %0lx bytes from %08lx to %08lx\n", UIO_SIZE, UIO_ADDR, (unsigned long)map_addr);
+
+ printf("TCR = %08lx\n", gpio_regs[tcr]);
+ printf("TCR = %08lx\n", gpio_regs[tcr]);
+ printf("setting TCR\n");
+ gpio_regs[tcr] = gpio_regs[tcr] | pin_mask( gpio_pin ); // set tcr for pin to 1
+ printf("TCR = %08lx\n", gpio_regs[tcr]);
+ printf("TCR = %08lx\n", gpio_regs[tcr]);
+
+ printf("OR = %08lx\n", gpio_regs[or]);
+ printf("OR = %08lx\n", gpio_regs[or]);
+ printf("setting OR\n");
+ gpio_regs[or] = gpio_regs[or] | pin_mask( gpio_pin ); // set tcr for pin to 1
+ printf("OR = %08lx\n", gpio_regs[or]);
+ printf("OR = %08lx\n", gpio_regs[or]);
+ sleep( 3 );
+ printf("setting OR\n");
+ gpio_regs[or] = gpio_regs[or] & ~pin_mask( gpio_pin ); // set tcr for pin to 0
+ printf("OR = %08lx\n", gpio_regs[or]);
+ printf("OR = %08lx\n", gpio_regs[or]);
+
+}
diff -upNr linux-2.6.26/drivers/uio-orig/uio_ppc_4xx-gpio.c linux-2.6.26/drivers/uio/uio_ppc_4xx-gpio.c
--- linux-2.6.26/drivers/uio-orig/uio_ppc_4xx-gpio.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.26/drivers/uio/uio_ppc_4xx-gpio.c 2008-07-18 09:23:32.000000000 +0200
@@ -0,0 +1,74 @@
+/*
+ * simple UIO GPIO driver.
+ *
+ * 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/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+
+#include <asm/io.h>
+
+static struct uio_info info = {
+ .name = "uio_gpio",
+ .version = "0.0.0",
+ .irq = UIO_IRQ_NONE,
+ .irq_flags = 0,
+ .mem[0].addr = 0xef600700,
+ .mem[0].size = 0x1000,
+ .mem[0].memtype = UIO_MEM_PHYS,
+};
+
+static int __devinit uio_gpio_probe(struct device *dev)
+{
+ if (uio_register_device(dev, &info)){
+ printk(KERN_ERR "uio_gpio: uio_register_device failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int uio_gpio_remove(struct device *dev)
+{
+ uio_unregister_device(&info);
+ info.mem[0].addr = 0;
+ info.mem[0].size = 0;
+ return 0;
+}
+
+static struct platform_device *uio_gpio_device;
+
+static struct device_driver uio_gpio_driver = {
+ .name = "uio_gpio",
+ .bus = &platform_bus_type,
+ .probe = uio_gpio_probe,
+ .remove = uio_gpio_remove,
+};
+
+
+static int __init uio_gpio_init(void)
+{
+ uio_gpio_device = platform_device_register_simple("uio_gpio", -1,
+ NULL, 0);
+ if (IS_ERR(uio_gpio_device))
+ return PTR_ERR(uio_gpio_device);
+
+ return driver_register(&uio_gpio_driver);
+}
+
+static void __exit uio_gpio_exit(void)
+{
+ platform_device_unregister(uio_gpio_device);
+ driver_unregister(&uio_gpio_driver);
+}
+
+module_init(uio_gpio_init);
+module_exit(uio_gpio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Brunner");
More information about the Linuxppc-dev
mailing list