[PATCH 3/6] U6715 gpio platform driver This driver is U6 platform generic (V2)
From: Russell King - ARM Linux <hidden>
Date: 2010-06-24 14:26:44
On Thu, May 27, 2010 at 10:27:29AM +0200, Philippe Langlais wrote:
quoted hunk ↗ jump to hunk
Signed-off-by: Philippe Langlais <redacted> --- arch/arm/mach-u67xx/board_u67xx_wavex.c | 469 ++++++++++++++++++++ arch/arm/mach-u67xx/devices.c | 12 +- arch/arm/plat-u6xxx/Makefile | 2 +- arch/arm/plat-u6xxx/gpio.c | 716 +++++++++++++++++++++++++++++++ arch/arm/plat-u6xxx/include/mach/gpio.h | 396 +++++++++++++++++ 5 files changed, 1588 insertions(+), 7 deletions(-) create mode 100644 arch/arm/plat-u6xxx/gpio.c create mode 100644 arch/arm/plat-u6xxx/include/mach/gpio.hdiff --git a/arch/arm/mach-u67xx/board_u67xx_wavex.c b/arch/arm/mach-u67xx/board_u67xx_wavex.c index 2a806e7..a21e465 100644 --- a/arch/arm/mach-u67xx/board_u67xx_wavex.c +++ b/arch/arm/mach-u67xx/board_u67xx_wavex.c@@ -24,6 +24,474 @@ #include <asm/mach/arch.h> #include <mach/irqs.h> #include <mach/timer.h> +#include <mach/gpio.h>
linux/gpio.h
+
+#include <mach/scon.h>
+
+/**
+ * SCON initial settings
+ * Allows to define the PIN multiplexing for all the platform (Linux and Modem)
+ */
+struct u6_scon_config u6_scon_init_config[SCON_REGISTER_NB] = {
+ {
+ (void __iomem *) SCON_SYSMUX0_REG,IOMEM() to eliminate the cast?
+/* GPIO def settings to avoid HW issue */
+struct u6_gpio_config u6_gpio_init_config[] = {
+ /* GPIO A bank */
+ {
+ .gpio = GPIO_A5,
+ .dir = GPIO_DIR_OUTPUT ,Unnecessary spaces between 'OUTPUT' and ','
quoted hunk ↗ jump to hunk
+ .value = 1, + }, + { + .gpio = GPIO_A6, + .dir = GPIO_DIR_OUTPUT , + .value = 1, + }, + { + .gpio = GPIO_A7, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A8, + .dir = GPIO_DIR_OUTPUT , + .value = 1, + }, + { + .gpio = GPIO_A9, + .dir = GPIO_DIR_OUTPUT , + .value = 1, + }, + { + .gpio = GPIO_A13, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A17, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A21, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A23, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A24, + .dir = GPIO_DIR_OUTPUT , + .value = 1, + }, + { + .gpio = GPIO_A25, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_A30, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + /* GPIO B bank */ + /* GPIO C bank */ + /* GPIO D bank */ + { + .gpio = GPIO_D0, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_D24, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_D29, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + /* GPIO E bank */ + { + .gpio = GPIO_E31, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + /* GPIO F bank */ + { + .gpio = GPIO_F0, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_F1, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_F2, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + }, + { + .gpio = GPIO_F9, + .dir = GPIO_DIR_OUTPUT , + .value = 0, + } +}; + +u32 gpio_to_configure = ARRAY_SIZE(u6_gpio_init_config); /* List of board specific devices */ static struct platform_device *devices[] __initdata = {@@ -31,6 +499,7 @@ static struct platform_device *devices[] __initdata = { void __init u67xx_init(void) { + u6_gpio_init(); /* Add specific board devices */ platform_add_devices(devices, ARRAY_SIZE(devices)); }diff --git a/arch/arm/mach-u67xx/devices.c b/arch/arm/mach-u67xx/devices.c index 0ead380..8e812fb 100644 --- a/arch/arm/mach-u67xx/devices.c +++ b/arch/arm/mach-u67xx/devices.c@@ -48,12 +48,12 @@ unsigned char extint_to_gpio[NR_EXTINT] = { EXPORT_SYMBOL(extint_to_gpio); struct gpio_bank u6_gpio_bank[6] = { - {(void __iomem *)GPIOA_PINS_REG, (void __iomem *)SCON_SYSMUX0_REG}, - {(void __iomem *)GPIOB_PINS_REG, (void __iomem *)SCON_SYSMUX2_REG}, - {(void __iomem *)GPIOC_PINS_REG, (void __iomem *)SCON_SYSMUX4_REG}, - {(void __iomem *)GPIOD_PINS_REG, (void __iomem *)SCON_SYSMUX6_REG}, - {(void __iomem *)GPIOE_PINS_REG, (void __iomem *)SCON_SYSMUX8_REG}, - {(void __iomem *)GPIOF_PINS_REG, (void __iomem *)SCON_SYSMUX10_REG}, + {GPIOA_PINS_REG, SCON_SYSMUX0_REG}, + {GPIOB_PINS_REG, SCON_SYSMUX2_REG}, + {GPIOC_PINS_REG, SCON_SYSMUX4_REG}, + {GPIOD_PINS_REG, SCON_SYSMUX6_REG}, + {GPIOE_PINS_REG, SCON_SYSMUX8_REG}, + {GPIOF_PINS_REG, SCON_SYSMUX10_REG}, }; static struct gpio_data u6_gpio_data = {diff --git a/arch/arm/plat-u6xxx/Makefile b/arch/arm/plat-u6xxx/Makefile index afdf82b..3d6898e 100644 --- a/arch/arm/plat-u6xxx/Makefile +++ b/arch/arm/plat-u6xxx/Makefile@@ -3,6 +3,6 @@ # # Common support -obj-y := io.o irq.o clock.o +obj-y := io.o irq.o clock.o gpio.o obj-$(CONFIG_U6_MTU_TIMER) += timer.odiff --git a/arch/arm/plat-u6xxx/gpio.c b/arch/arm/plat-u6xxx/gpio.c new file mode 100644 index 0000000..40e25fc --- /dev/null +++ b/arch/arm/plat-u6xxx/gpio.c@@ -0,0 +1,716 @@ +/* + * linux/arch/arm/plat-u6xxx/gpio.c + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Loic Pallardy <loic.pallardy@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + * Support functions for GPIO + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/sysdev.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/gpio.h> +#include <asm/mach/irq.h> +#include <mach/scon.h>
asm/ before mach/.
+
+/*
+ * PN5220 GPIO/MUX registers
+ * defined in asm/arch/registers.h
+ */
+
+#define U6_GPIO_PINS_OFFSET 0
+#define U6_GPIO_OUTPUT_OFFSET 4
+#define U6_GPIO_DIR_OFFSET 8
+
+#define U6_MUX2_OFFSET 4
+
+static struct gpio_bank *gpio_bank_desc;
+static int gpio_bank_count;
+
+static inline struct gpio_bank *get_gpio_bank(int gpio)
+{
+ /* 32 GPIOs per bank */
+ return &(gpio_bank_desc[gpio >> 5]);
+}
+
+static inline int get_gpio_index(int gpio)
+{
+ return gpio & 0x1f;
+}
+
+static int check_gpio(int gpio)
+{
+ int retval = ((unsigned int)gpio) < GPIO_COUNT;
+ if (unlikely(!retval)) {
+ printk(KERN_ERR "u6-gpio: invalid GPIO %d\n", gpio);
+ dump_stack();
+ }return WARN_ON((unsigned int)gpio < GPIO_COUNT); or use WARN(condition, format) if you want a custom message.
+static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
+{
+ u32 reg = bank->gpio_base;
+ u32 l;
+
+ /* select direction register */
+ reg += U6_GPIO_DIR_OFFSET;
+
+ /* in register 0 = input, 1 = output */
+ l = inl(reg);inl/outl are for PCI/ISA based IO accesses/emulation. I don't think your GPIO block is a PCI/ISA peripheral, so it should not be using these macros.
+static int initialized;
+
+static int __init u6_gpio_probe(struct platform_device *pdev)
+{
+ int i, j;
+ int gpio = 0;
+ struct gpio_bank *bank;
+ struct gpio_data *data = pdev->dev.platform_data;
+ unsigned long flags;
+
+ initialized = 1;Why is this here when you're really wanting to prevent the platform driver structure being registered more than once?
+
+ printk(KERN_INFO "U6 GPIO\n");
+ gpio_bank_desc = data->gpio_bank_desc;
+ gpio_bank_count = data->nb_banks;
+
+ for (i = 0; i < gpio_bank_count; i++) {
+ int gpio_count = 32; /* 32 GPIO per bank */
+ bank = &gpio_bank_desc[i];
+ bank->reserved_map = 0;
+ spin_lock_init(&bank->lock);
+
+ bank->chip.request = u6_gpio_acquire;
+ bank->chip.free = u6_gpio_release;
+ bank->chip.direction_input = gpio_input;
+ bank->chip.get = gpio_get;
+ bank->chip.direction_output = gpio_output;
+ bank->chip.set = gpio_set;
+ bank->chip.to_irq = gpio_2irq;
+ bank->chip.label = "gpio";
+ bank->chip.base = gpio;
+ gpio += gpio_count;
+
+ bank->chip.ngpio = gpio_count;
+
+ gpiochip_add(&bank->chip);
+
+ }
+
+ /* for extint */
+ for (j = IRQ_COUNT; j < IRQ_COUNT + NR_EXTINT; j++) {
+ set_irq_chip(j, &gpio_irq_chip);
+ set_irq_handler(j, handle_simple_irq);
+ set_irq_flags(j, IRQF_VALID);
+ }
+
+ hw_raw_local_irq_save(flags);
+ /* mask all EXT IRQ sources before registring handler */
+ /* read status */
+ j = inl(EXTINT_STATUS_REG) & inl(EXTINT_ENABLE3_REG);
+ /* clear IRQ source(s) */
+ outl(j, EXTINT_STATUS_REG);
+
+ outl(0, EXTINT_ENABLE3_REG);
+
+ /* set irq in low level */
+ set_irq_type(IRQ_EXTINT3, IRQF_TRIGGER_LOW);
+
+ /* chained GPIO-IRQ on EXTINT3 */
+ set_irq_chained_handler(IRQ_EXTINT3, gpio_irq_handler);
+ hw_raw_local_irq_restore(flags);
+
+ return 0;
+}
+
+static struct platform_driver u6_gpio_driver = {
+ .probe = u6_gpio_probe,
+ .remove = NULL,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = "u6-gpio",
+ },
+};
+
+static struct sysdev_class u6_gpio_sysclass = {
+ .name = "gpio",
+ .suspend = 0, /*u6_gpio_suspend, */
+ .resume = 0, /*u6_gpio_resume, */
+};
+
+static struct sys_device u6_gpio_device = {
+ .id = 0,
+ .cls = &u6_gpio_sysclass,
+};
+
+/*
+ * This may get called early from board specific init
+ * for boards that have interrupts routed via FPGA.
+ */
+int u6_gpio_init(void)
+{
+ if (!initialized)
+ return platform_driver_register(&u6_gpio_driver);Shouldn't this set 'initialized' once the platform driver has been registered to prevent the platform driver being registered again?
+ else
+ return 0;
+}
+
+static int __init u6_gpio_sysinit(void)
+{
+ int ret = 0;
+
+ if (!initialized)
+ ret = u6_gpio_init();Why not just call u6_gpio_init(), which handles the special case anyway?
quoted hunk ↗ jump to hunk
+ + if (ret == 0) { + ret = sysdev_class_register(&u6_gpio_sysclass); + if (ret == 0) + ret = sysdev_register(&u6_gpio_device); + } + + return ret; +} + +arch_initcall(u6_gpio_sysinit);diff --git a/arch/arm/plat-u6xxx/include/mach/gpio.h b/arch/arm/plat-u6xxx/include/mach/gpio.h new file mode 100644 index 0000000..a205f1c --- /dev/null +++ b/arch/arm/plat-u6xxx/include/mach/gpio.h@@ -0,0 +1,396 @@ +/* + * linux/arch/arm/plat-u6xxx/include/mach/gpio.h + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Loic Pallardy <loic.pallardy@stericsson.com> for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + * GPIO handling defines and functions + */ + +#ifndef __ASM_PLAT_U6_GPIO_H +#define __ASM_PLAT_U6_GPIO_H + +#include <linux/io.h> +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/gpio.h>
Recursive include of mach/gpio.h
+ +#include <linux/errno.h> +#include <asm-generic/gpio.h>
linux/ includes before asm and mach includes.