diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-em.c | 4 | ||||
-rw-r--r-- | drivers/gpio/gpio-lpc32xx.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 2 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxs.c | 48 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 23 | ||||
-rw-r--r-- | drivers/gpio/gpio-pxa.c | 77 | ||||
-rw-r--r-- | drivers/gpio/gpio-rdc321x.c | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 103 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra.c | 3 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl4030.c | 90 | ||||
-rw-r--r-- | drivers/gpio/gpio-vt8500.c | 316 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 2 |
14 files changed, 523 insertions, 160 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b16c8a72a2e2..a00b828b1643 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -183,6 +183,12 @@ config GPIO_STA2X11 Say yes here to support the STA2x11/ConneXt GPIO device. The GPIO module has 128 GPIO pins with alternate functions. +config GPIO_VT8500 + bool "VIA/Wondermedia SoC GPIO Support" + depends on ARCH_VT8500 + help + Say yes here to support the VT8500/WM8505/WM8650 GPIO controller. + config GPIO_XILINX bool "Xilinx GPIO support" depends on PPC_OF || MICROBLAZE @@ -294,7 +300,7 @@ config GPIO_MAX732X_IRQ config GPIO_MC9S08DZ60 bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions" - depends on I2C && MACH_MX35_3DS + depends on I2C=y && MACH_MX35_3DS help Select this to enable the MC9S08DZ60 GPIO driver diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 153caceeb053..a288142ad998 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o +obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index ae37181798b3..ec48ed512628 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -247,9 +247,9 @@ static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p) p->irq_base = irq_alloc_descs(pdata->irq_base, 0, pdata->number_of_pins, numa_node_id()); - if (IS_ERR_VALUE(p->irq_base)) { + if (p->irq_base < 0) { dev_err(&pdev->dev, "cannot get irq_desc\n"); - return -ENXIO; + return p->irq_base; } pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n", pdata->gpio_base, pdata->number_of_pins, p->irq_base); diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 8a420f13905e..ed94b4ea72e9 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -308,6 +308,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin, { struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + __set_gpio_level_p012(group, pin, value); __set_gpio_dir_p012(group, pin, 0); return 0; @@ -318,6 +319,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin, { struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + __set_gpio_level_p3(group, pin, value); __set_gpio_dir_p3(group, pin, 0); return 0; @@ -326,6 +328,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin, static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin, int value) { + struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip); + + __set_gpo_level_p3(group, pin, value); return 0; } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 5a1817eedd1b..9ae29cc0d17f 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -38,7 +38,7 @@ struct mpc8xxx_gpio_chip { */ u32 data; struct irq_domain *irq; - void *of_dev_id_data; + const void *of_dev_id_data; }; static inline u32 mpc8xxx_gpio2mask(unsigned int gpio) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 39e495669961..796fb13e4815 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_address.h> @@ -52,8 +53,6 @@ #define GPIO_INT_LEV_MASK (1 << 0) #define GPIO_INT_POL_MASK (1 << 1) -#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START) - enum mxs_gpio_id { IMX23_GPIO, IMX28_GPIO, @@ -63,7 +62,7 @@ struct mxs_gpio_port { void __iomem *base; int id; int irq; - int virtual_irq_start; + struct irq_domain *domain; struct bgpio_chip bgc; enum mxs_gpio_id devid; }; @@ -82,8 +81,7 @@ static inline int is_imx28_gpio(struct mxs_gpio_port *port) static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) { - u32 gpio = irq_to_gpio(d->irq); - u32 pin_mask = 1 << (gpio & 31); + u32 pin_mask = 1 << d->hwirq; struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxs_gpio_port *port = gc->private; void __iomem *pin_addr; @@ -120,7 +118,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) else writel(pin_mask, pin_addr + MXS_CLR); - writel(1 << (gpio & 0x1f), + writel(pin_mask, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); return 0; @@ -131,7 +129,6 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) { u32 irq_stat; struct mxs_gpio_port *port = irq_get_handler_data(irq); - u32 gpio_irq_no_base = port->virtual_irq_start; desc->irq_data.chip->irq_ack(&desc->irq_data); @@ -140,7 +137,7 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) while (irq_stat != 0) { int irqoffset = fls(irq_stat) - 1; - generic_handle_irq(gpio_irq_no_base + irqoffset); + generic_handle_irq(irq_find_mapping(port->domain, irqoffset)); irq_stat &= ~(1 << irqoffset); } } @@ -167,12 +164,12 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable) return 0; } -static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port) +static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base) { struct irq_chip_generic *gc; struct irq_chip_type *ct; - gc = irq_alloc_generic_chip("gpio-mxs", 1, port->virtual_irq_start, + gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base, port->base, handle_level_irq); gc->private = port; @@ -194,7 +191,7 @@ static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) struct mxs_gpio_port *port = container_of(bgc, struct mxs_gpio_port, bgc); - return port->virtual_irq_start + offset; + return irq_find_mapping(port->domain, offset); } static struct platform_device_id mxs_gpio_ids[] = { @@ -226,6 +223,7 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) static void __iomem *base; struct mxs_gpio_port *port; struct resource *iores = NULL; + int irq_base; int err; port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); @@ -241,7 +239,6 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) port->id = pdev->id; port->devid = pdev->id_entry->driver_data; } - port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32; port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) @@ -275,8 +272,19 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) /* clear address has to be used to clear IRQSTAT bits */ writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); + irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); + if (irq_base < 0) + return irq_base; + + port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (!port->domain) { + err = -ENODEV; + goto out_irqdesc_free; + } + /* gpio-mxs can be a generic irq chip */ - mxs_gpio_init_gc(port); + mxs_gpio_init_gc(port, irq_base); /* setup one handler for each entry */ irq_set_chained_handler(port->irq, mxs_gpio_irq_handler); @@ -287,18 +295,22 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) port->base + PINCTRL_DOUT(port), NULL, port->base + PINCTRL_DOE(port), NULL, 0); if (err) - return err; + goto out_irqdesc_free; port->bgc.gc.to_irq = mxs_gpio_to_irq; port->bgc.gc.base = port->id * 32; err = gpiochip_add(&port->bgc.gc); - if (err) { - bgpio_remove(&port->bgc); - return err; - } + if (err) + goto out_bgpio_remove; return 0; + +out_bgpio_remove: + bgpio_remove(&port->bgc); +out_irqdesc_free: + irq_free_descs(irq_base, 32); + return err; } static struct platform_driver mxs_gpio_driver = { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e6efd77668f0..94cbc842fbc3 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -25,11 +25,9 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/irqdomain.h> +#include <linux/gpio.h> +#include <linux/platform_data/gpio-omap.h> -#include <mach/hardware.h> -#include <asm/irq.h> -#include <mach/irqs.h> -#include <asm/gpio.h> #include <asm/mach/irq.h> #define OFF_MODE 1 @@ -385,13 +383,16 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, static int gpio_irq_type(struct irq_data *d, unsigned type) { struct gpio_bank *bank = irq_data_get_irq_chip_data(d); - unsigned gpio; + unsigned gpio = 0; int retval; unsigned long flags; - if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE) +#ifdef CONFIG_ARCH_OMAP1 + if (d->irq > IH_MPUIO_BASE) gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); - else +#endif + + if (!gpio) gpio = irq_to_gpio(bank, d->irq); if (type & ~IRQ_TYPE_SENSE_MASK) @@ -1058,7 +1059,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; const struct of_device_id *match; - struct omap_gpio_platform_data *pdata; + const struct omap_gpio_platform_data *pdata; struct resource *res; struct gpio_bank *bank; int ret = 0; @@ -1440,19 +1441,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { .fallingdetect = OMAP4_GPIO_FALLINGDETECT, }; -static struct omap_gpio_platform_data omap2_pdata = { +const static struct omap_gpio_platform_data omap2_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = false, }; -static struct omap_gpio_platform_data omap3_pdata = { +const static struct omap_gpio_platform_data omap3_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = true, }; -static struct omap_gpio_platform_data omap4_pdata = { +const static struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 9cac88a65f78..9528779ca463 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -26,6 +26,8 @@ #include <linux/syscore_ops.h> #include <linux/slab.h> +#include <asm/mach/irq.h> + #include <mach/irqs.h> /* @@ -59,6 +61,7 @@ #define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) int pxa_last_gpio; +static int irq_base; #ifdef CONFIG_OF static struct irq_domain *domain; @@ -167,63 +170,14 @@ static inline int __gpio_is_occupied(unsigned gpio) return ret; } -#ifdef CONFIG_ARCH_PXA -static inline int __pxa_gpio_to_irq(int gpio) -{ - if (gpio_is_pxa_type(gpio_type)) - return PXA_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __pxa_irq_to_gpio(int irq) -{ - if (gpio_is_pxa_type(gpio_type)) - return irq - PXA_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __pxa_gpio_to_irq(int gpio) { return -1; } -static inline int __pxa_irq_to_gpio(int irq) { return -1; } -#endif - -#ifdef CONFIG_ARCH_MMP -static inline int __mmp_gpio_to_irq(int gpio) -{ - if (gpio_is_mmp_type(gpio_type)) - return MMP_GPIO_TO_IRQ(gpio); - return -1; -} - -static inline int __mmp_irq_to_gpio(int irq) -{ - if (gpio_is_mmp_type(gpio_type)) - return irq - MMP_GPIO_TO_IRQ(0); - return -1; -} -#else -static inline int __mmp_gpio_to_irq(int gpio) { return -1; } -static inline int __mmp_irq_to_gpio(int irq) { return -1; } -#endif - static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - int gpio, ret; - - gpio = chip->base + offset; - ret = __pxa_gpio_to_irq(gpio); - if (ret >= 0) - return ret; - return __mmp_gpio_to_irq(gpio); + return chip->base + offset + irq_base; } int pxa_irq_to_gpio(int irq) { - int ret; - - ret = __pxa_irq_to_gpio(irq); - if (ret >= 0) - return ret; - return __mmp_irq_to_gpio(irq); + return irq - irq_base; } static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -403,6 +357,9 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) struct pxa_gpio_chip *c; int loop, gpio, gpio_base, n; unsigned long gedr; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); do { loop = 0; @@ -422,6 +379,8 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc) } } } while (loop); + + chained_irq_exit(chip, desc); } static void pxa_ack_muxed_gpio(struct irq_data *d) @@ -535,7 +494,7 @@ const struct irq_domain_ops pxa_irq_domain_ops = { static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev) { - int ret, nr_banks, nr_gpios, irq_base; + int ret, nr_banks, nr_gpios; struct device_node *prev, *next, *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(pxa_gpio_dt_ids, &pdev->dev); @@ -590,10 +549,20 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev) int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0; ret = pxa_gpio_probe_dt(pdev); - if (ret < 0) + if (ret < 0) { pxa_last_gpio = pxa_gpio_nums(); - else +#ifdef CONFIG_ARCH_PXA + if (gpio_is_pxa_type(gpio_type)) + irq_base = PXA_GPIO_TO_IRQ(0); +#endif +#ifdef CONFIG_ARCH_MMP + if (gpio_is_mmp_type(gpio_type)) + irq_base = MMP_GPIO_TO_IRQ(0); +#endif + } else { use_of = 1; + } + if (!pxa_last_gpio) return -EINVAL; diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index e97016af6443..b62d443e9a59 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -170,6 +170,7 @@ static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; + rdc321x_gpio_dev->chip.owner = THIS_MODULE; rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 41ab7f66cdf9..a006f0db15af 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -938,6 +938,67 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) s3c_gpiolib_track(chip); } +#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) +static int s3c24xx_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + unsigned int pin; + + if (WARN_ON(gc->of_gpio_n_cells < 3)) + return -EINVAL; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] > gc->ngpio) + return -EINVAL; + + pin = gc->base + gpiospec->args[0]; + + if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1]))) + pr_warn("gpio_xlate: failed to set pin function\n"); + if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff)) + pr_warn("gpio_xlate: failed to set pin pull up/down\n"); + + if (flags) + *flags = gpiospec->args[2] >> 16; + + return gpiospec->args[0]; +} + +static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = { + { .compatible = "samsung,s3c24xx-gpio", }, + {} +}; + +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + struct gpio_chip *gc = &chip->chip; + u64 address; + + if (!of_have_populated_dt()) + return; + + address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset; + gc->of_node = of_find_matching_node_by_address(NULL, + s3c24xx_gpio_dt_match, address); + if (!gc->of_node) { + pr_info("gpio: device tree node not found for gpio controller" + " with base address %08llx\n", address); + return; + } + gc->of_gpio_n_cells = 3; + gc->of_xlate = s3c24xx_gpio_xlate; +} +#else +static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, + u64 base, u64 offset) +{ + return; +} +#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */ + static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, int nr_chips, void __iomem *base) { @@ -962,6 +1023,8 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, gc->direction_output = samsung_gpiolib_2bit_output; samsung_gpiolib_add(chip); + + s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10); } } @@ -3152,46 +3215,6 @@ samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin) } EXPORT_SYMBOL(s3c_gpio_getpull); -/* gpiolib wrappers until these are totally eliminated */ - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) -{ - int ret; - - WARN_ON(to); /* should be none of these left */ - - if (!to) { - /* if pull is enabled, try first with up, and if that - * fails, try using down */ - - ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); - if (ret) - s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); - } else { - s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); - } -} -EXPORT_SYMBOL(s3c2410_gpio_pullup); - -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - /* do this via gpiolib until all users removed */ - - gpio_request(pin, "temporary"); - gpio_set_value(pin, to); - gpio_free(pin); -} -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); - unsigned long offs = pin - chip->chip.base; - - return __raw_readl(chip->base + 0x04) & (1 << offs); -} -EXPORT_SYMBOL(s3c2410_gpio_getpin); - #ifdef CONFIG_S5P_GPIO_DRVSTR s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) { diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index dc5184d57892..d982593d7563 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -30,9 +30,6 @@ #include <asm/mach/irq.h> -#include <mach/iomap.h> -#include <mach/suspend.h> - #define GPIO_BANK(x) ((x) >> 5) #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 94256fe7bf36..c5f8ca233e1f 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -51,6 +51,7 @@ static struct gpio_chip twl_gpiochip; +static int twl4030_gpio_base; static int twl4030_gpio_irq_base; /* genirq interfaces are not available to modules */ @@ -395,6 +396,29 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) static int gpio_twl4030_remove(struct platform_device *pdev); +static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev) +{ + struct twl4030_gpio_platform_data *omap_twl_info; + + omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL); + if (!omap_twl_info) + return NULL; + + omap_twl_info->use_leds = of_property_read_bool(dev->of_node, + "ti,use-leds"); + + of_property_read_u32(dev->of_node, "ti,debounce", + &omap_twl_info->debounce); + of_property_read_u32(dev->of_node, "ti,mmc-cd", + (u32 *)&omap_twl_info->mmc_cd); + of_property_read_u32(dev->of_node, "ti,pullups", + &omap_twl_info->pullups); + of_property_read_u32(dev->of_node, "ti,pulldowns", + &omap_twl_info->pulldowns); + + return omap_twl_info; +} + static int __devinit gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; @@ -427,49 +451,57 @@ no_irqs: twl_gpiochip.ngpio = TWL4030_GPIO_MAX; twl_gpiochip.dev = &pdev->dev; - if (pdata) { - twl_gpiochip.base = pdata->gpio_base; + if (node) + pdata = of_gpio_twl4030(&pdev->dev); - /* - * NOTE: boards may waste power if they don't set pullups - * and pulldowns correctly ... default for non-ULPI pins is - * pulldown, and some other pins may have external pullups - * or pulldowns. Careful! - */ - ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); - if (ret) - dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", - pdata->pullups, pdata->pulldowns, - ret); - - ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); - if (ret) - dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", - pdata->debounce, pdata->mmc_cd, - ret); - - /* - * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, - * is (still) clear if use_leds is set. - */ - if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -ENXIO; } + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, ret); + + /* + * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + ret = gpiochip_add(&twl_gpiochip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); twl_gpiochip.ngpio = 0; gpio_twl4030_remove(pdev); - } else if (pdata && pdata->setup) { + goto out; + } + + twl4030_gpio_base = twl_gpiochip.base; + + if (pdata && pdata->setup) { int status; status = pdata->setup(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) dev_dbg(&pdev->dev, "setup --> %d\n", status); } +out: return ret; } @@ -481,7 +513,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev) if (pdata && pdata->teardown) { status = pdata->teardown(&pdev->dev, - pdata->gpio_base, TWL4030_GPIO_MAX); + twl4030_gpio_base, TWL4030_GPIO_MAX); if (status) { dev_dbg(&pdev->dev, "teardown --> %d\n", status); return status; diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c new file mode 100644 index 000000000000..bcd8e4aa7c7d --- /dev/null +++ b/drivers/gpio/gpio-vt8500.c @@ -0,0 +1,316 @@ +/* drivers/gpio/gpio-vt8500.c + * + * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> + * Based on arch/arm/mach-vt8500/gpio.c: + * - Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_device.h> + +/* + We handle GPIOs by bank, each bank containing up to 32 GPIOs covered + by one set of registers (although not all may be valid). + + Because different SoC's have different register offsets, we pass the + register offsets as data in vt8500_gpio_dt_ids[]. + + A value of NO_REG is used to indicate that this register is not + supported. Only used for ->en at the moment. +*/ + +#define NO_REG 0xFFFF + +/* + * struct vt8500_gpio_bank_regoffsets + * @en: offset to enable register of the bank + * @dir: offset to direction register of the bank + * @data_out: offset to the data out register of the bank + * @data_in: offset to the data in register of the bank + * @ngpio: highest valid pin in this bank + */ + +struct vt8500_gpio_bank_regoffsets { + unsigned int en; + unsigned int dir; + unsigned int data_out; + unsigned int data_in; + unsigned char ngpio; +}; + +struct vt8500_gpio_data { + unsigned int num_banks; + struct vt8500_gpio_bank_regoffsets banks[]; +}; + +#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \ +{ \ + .en = __en, \ + .dir = __dir, \ + .data_out = __out, \ + .data_in = __in, \ + .ngpio = __ngpio, \ +} + +static struct vt8500_gpio_data vt8500_data = { + .num_banks = 7, + .banks = { + VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), + VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), + VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), + VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), + VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), + VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), + VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), + }, +}; + +static struct vt8500_gpio_data wm8505_data = { + .num_banks = 10, + .banks = { + VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), + VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), + VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), + VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16), + VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25), + VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5), + VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), + VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), + VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), + VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), + }, +}; + +/* + * No information about which bits are valid so we just make + * them all available until its figured out. + */ +static struct vt8500_gpio_data wm8650_data = { + .num_banks = 9, + .banks = { + VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32), + VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32), + VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32), + VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32), + VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32), + VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32), + VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32), + VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32), + VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32), + }, +}; + +struct vt8500_gpio_chip { + struct gpio_chip chip; + + const struct vt8500_gpio_bank_regoffsets *regs; + void __iomem *base; +}; + + +#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) + +static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + u32 val; + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + if (vt8500_chip->regs->en == NO_REG) + return 0; + + val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); + + return 0; +} + +static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + u32 val; + + if (vt8500_chip->regs->en == NO_REG) + return; + + val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en); + val &= ~BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en); +} + +static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); + val &= ~BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); + + return 0; +} + +static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir); + + if (value) { + val = readl_relaxed(vt8500_chip->base + + vt8500_chip->regs->data_out); + val |= BIT(offset); + writel_relaxed(val, vt8500_chip->base + + vt8500_chip->regs->data_out); + } + return 0; +} + +static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >> + offset) & 1; +} + +static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip); + + u32 val = readl_relaxed(vt8500_chip->base + + vt8500_chip->regs->data_out); + if (value) + val |= BIT(offset); + else + val &= ~BIT(offset); + + writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out); +} + +static int vt8500_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + /* bank if specificed in gpiospec->args[0] */ + if (flags) + *flags = gpiospec->args[2]; + + return gpiospec->args[1]; +} + +static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, + const struct vt8500_gpio_data *data) +{ + struct vt8500_gpio_chip *vtchip; + struct gpio_chip *chip; + int i; + int pin_cnt = 0; + + vtchip = devm_kzalloc(&pdev->dev, + sizeof(struct vt8500_gpio_chip) * data->num_banks, + GFP_KERNEL); + if (!vtchip) { + pr_err("%s: failed to allocate chip memory\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < data->num_banks; i++) { + vtchip[i].base = base; + vtchip[i].regs = &data->banks[i]; + + chip = &vtchip[i].chip; + + chip->of_xlate = vt8500_of_xlate; + chip->of_gpio_n_cells = 3; + chip->of_node = pdev->dev.of_node; + + chip->request = vt8500_gpio_request; + chip->free = vt8500_gpio_free; + chip->direction_input = vt8500_gpio_direction_input; + chip->direction_output = vt8500_gpio_direction_output; + chip->get = vt8500_gpio_get_value; + chip->set = vt8500_gpio_set_value; + chip->can_sleep = 0; + chip->base = pin_cnt; + chip->ngpio = data->banks[i].ngpio; + + pin_cnt += data->banks[i].ngpio; + + gpiochip_add(chip); + } + return 0; +} + +static struct of_device_id vt8500_gpio_dt_ids[] = { + { .compatible = "via,vt8500-gpio", .data = &vt8500_data, }, + { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, }, + { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, }, + { /* Sentinel */ }, +}; + +static int __devinit vt8500_gpio_probe(struct platform_device *pdev) +{ + void __iomem *gpio_base; + struct device_node *np; + const struct of_device_id *of_id = + of_match_device(vt8500_gpio_dt_ids, &pdev->dev); + + if (!of_id) { + dev_err(&pdev->dev, "Failed to find gpio controller\n"); + return -ENODEV; + } + + np = pdev->dev.of_node; + if (!np) { + dev_err(&pdev->dev, "Missing GPIO description in devicetree\n"); + return -EFAULT; + } + + gpio_base = of_iomap(np, 0); + if (!gpio_base) { + dev_err(&pdev->dev, "Unable to map GPIO registers\n"); + of_node_put(np); + return -ENOMEM; + } + + vt8500_add_chips(pdev, gpio_base, of_id->data); + + return 0; +} + +static struct platform_driver vt8500_gpio_driver = { + .probe = vt8500_gpio_probe, + .driver = { + .name = "vt8500-gpio", + .owner = THIS_MODULE, + .of_match_table = vt8500_gpio_dt_ids, + }, +}; + +module_platform_driver(vt8500_gpio_driver); + +MODULE_DESCRIPTION("VT8500 GPIO Driver"); +MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a18c4aa68b1e..f1a45997aea8 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -82,7 +82,7 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname, gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); of_node_put(gg_data.gpiospec.np); - pr_debug("%s exited with status %d\n", __func__, ret); + pr_debug("%s exited with status %d\n", __func__, gg_data.out_gpio); return gg_data.out_gpio; } EXPORT_SYMBOL(of_get_named_gpio_flags); |