diff options
author | Jiri Kosina <jkosina@suse.cz> | 2014-11-20 16:42:02 +0300 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2014-11-20 16:42:02 +0300 |
commit | a02001086bbfb4da35d1228bebc2f1b442db455f (patch) | |
tree | 62ab47936cef06fd08657ca5b6cd1df98c19be57 /drivers/gpio/gpio-dwapb.c | |
parent | eff264efeeb0898408e8c9df72d8a32621035bed (diff) | |
parent | fc14f9c1272f62c3e8d01300f52467c0d9af50f9 (diff) | |
download | linux-a02001086bbfb4da35d1228bebc2f1b442db455f.tar.xz |
Merge Linus' tree to be be to apply submitted patches to newer code than
current trivial.git base
Diffstat (limited to 'drivers/gpio/gpio-dwapb.c')
-rw-r--r-- | drivers/gpio/gpio-dwapb.c | 412 |
1 files changed, 343 insertions, 69 deletions
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index cd3b81435274..b43cd84b61f1 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -21,6 +21,8 @@ #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/spinlock.h> +#include <linux/platform_data/gpio-dwapb.h> +#include <linux/slab.h> #define GPIO_SWPORTA_DR 0x00 #define GPIO_SWPORTA_DDR 0x04 @@ -35,6 +37,7 @@ #define GPIO_INTTYPE_LEVEL 0x38 #define GPIO_INT_POLARITY 0x3c #define GPIO_INTSTATUS 0x40 +#define GPIO_PORTA_DEBOUNCE 0x48 #define GPIO_PORTA_EOI 0x4c #define GPIO_EXT_PORTA 0x50 #define GPIO_EXT_PORTB 0x54 @@ -48,10 +51,28 @@ struct dwapb_gpio; +#ifdef CONFIG_PM_SLEEP +/* Store GPIO context across system-wide suspend/resume transitions */ +struct dwapb_context { + u32 data; + u32 dir; + u32 ext; + u32 int_en; + u32 int_mask; + u32 int_type; + u32 int_pol; + u32 int_deb; +}; +#endif + struct dwapb_gpio_port { struct bgpio_chip bgc; bool is_registered; struct dwapb_gpio *gpio; +#ifdef CONFIG_PM_SLEEP + struct dwapb_context *ctx; +#endif + unsigned int idx; }; struct dwapb_gpio { @@ -62,11 +83,33 @@ struct dwapb_gpio { struct irq_domain *domain; }; +static inline struct dwapb_gpio_port * +to_dwapb_gpio_port(struct bgpio_chip *bgc) +{ + return container_of(bgc, struct dwapb_gpio_port, bgc); +} + +static inline u32 dwapb_read(struct dwapb_gpio *gpio, unsigned int offset) +{ + struct bgpio_chip *bgc = &gpio->ports[0].bgc; + void __iomem *reg_base = gpio->regs; + + return bgc->read_reg(reg_base + offset); +} + +static inline void dwapb_write(struct dwapb_gpio *gpio, unsigned int offset, + u32 val) +{ + struct bgpio_chip *bgc = &gpio->ports[0].bgc; + void __iomem *reg_base = gpio->regs; + + bgc->write_reg(reg_base + offset, val); +} + static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - struct dwapb_gpio_port *port = container_of(bgc, struct - dwapb_gpio_port, bgc); + struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc); struct dwapb_gpio *gpio = port->gpio; return irq_find_mapping(gpio->domain, offset); @@ -74,21 +117,20 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs) { - u32 v = readl(gpio->regs + GPIO_INT_POLARITY); + u32 v = dwapb_read(gpio, GPIO_INT_POLARITY); if (gpio_get_value(gpio->ports[0].bgc.gc.base + offs)) v &= ~BIT(offs); else v |= BIT(offs); - writel(v, gpio->regs + GPIO_INT_POLARITY); + dwapb_write(gpio, GPIO_INT_POLARITY, v); } -static void dwapb_irq_handler(u32 irq, struct irq_desc *desc) +static u32 dwapb_do_irq(struct dwapb_gpio *gpio) { - struct dwapb_gpio *gpio = irq_get_handler_data(irq); - struct irq_chip *chip = irq_desc_get_chip(desc); u32 irq_status = readl_relaxed(gpio->regs + GPIO_INTSTATUS); + u32 ret = irq_status; while (irq_status) { int hwirq = fls(irq_status) - 1; @@ -102,6 +144,16 @@ static void dwapb_irq_handler(u32 irq, struct irq_desc *desc) dwapb_toggle_trigger(gpio, hwirq); } + return ret; +} + +static void dwapb_irq_handler(u32 irq, struct irq_desc *desc) +{ + struct dwapb_gpio *gpio = irq_get_handler_data(irq); + struct irq_chip *chip = irq_desc_get_chip(desc); + + dwapb_do_irq(gpio); + if (chip->irq_eoi) chip->irq_eoi(irq_desc_get_irq_data(desc)); } @@ -115,9 +167,9 @@ static void dwapb_irq_enable(struct irq_data *d) u32 val; spin_lock_irqsave(&bgc->lock, flags); - val = readl(gpio->regs + GPIO_INTEN); + val = dwapb_read(gpio, GPIO_INTEN); val |= BIT(d->hwirq); - writel(val, gpio->regs + GPIO_INTEN); + dwapb_write(gpio, GPIO_INTEN, val); spin_unlock_irqrestore(&bgc->lock, flags); } @@ -130,9 +182,9 @@ static void dwapb_irq_disable(struct irq_data *d) u32 val; spin_lock_irqsave(&bgc->lock, flags); - val = readl(gpio->regs + GPIO_INTEN); + val = dwapb_read(gpio, GPIO_INTEN); val &= ~BIT(d->hwirq); - writel(val, gpio->regs + GPIO_INTEN); + dwapb_write(gpio, GPIO_INTEN, val); spin_unlock_irqrestore(&bgc->lock, flags); } @@ -172,8 +224,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) return -EINVAL; spin_lock_irqsave(&bgc->lock, flags); - level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); - polarity = readl(gpio->regs + GPIO_INT_POLARITY); + level = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); + polarity = dwapb_read(gpio, GPIO_INT_POLARITY); switch (type) { case IRQ_TYPE_EDGE_BOTH: @@ -200,29 +252,55 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) irq_setup_alt_chip(d, type); - writel(level, gpio->regs + GPIO_INTTYPE_LEVEL); - writel(polarity, gpio->regs + GPIO_INT_POLARITY); + dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level); + dwapb_write(gpio, GPIO_INT_POLARITY, polarity); + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int dwapb_gpio_set_debounce(struct gpio_chip *gc, + unsigned offset, unsigned debounce) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + struct dwapb_gpio_port *port = to_dwapb_gpio_port(bgc); + struct dwapb_gpio *gpio = port->gpio; + unsigned long flags, val_deb; + unsigned long mask = bgc->pin2mask(bgc, offset); + + spin_lock_irqsave(&bgc->lock, flags); + + val_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); + if (debounce) + dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb | mask); + else + dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask); + spin_unlock_irqrestore(&bgc->lock, flags); return 0; } +static irqreturn_t dwapb_irq_handler_mfd(int irq, void *dev_id) +{ + u32 worked; + struct dwapb_gpio *gpio = dev_id; + + worked = dwapb_do_irq(gpio); + + return worked ? IRQ_HANDLED : IRQ_NONE; +} + static void dwapb_configure_irqs(struct dwapb_gpio *gpio, - struct dwapb_gpio_port *port) + struct dwapb_gpio_port *port, + struct dwapb_port_property *pp) { struct gpio_chip *gc = &port->bgc.gc; - struct device_node *node = gc->of_node; - struct irq_chip_generic *irq_gc; + struct device_node *node = pp->node; + struct irq_chip_generic *irq_gc = NULL; unsigned int hwirq, ngpio = gc->ngpio; struct irq_chip_type *ct; - int err, irq, i; - - irq = irq_of_parse_and_map(node, 0); - if (!irq) { - dev_warn(gpio->dev, "no irq for bank %s\n", - port->bgc.gc.of_node->full_name); - return; - } + int err, i; gpio->domain = irq_domain_add_linear(node, ngpio, &irq_generic_chip_ops, gpio); @@ -269,8 +347,24 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; irq_gc->chip_types[1].handler = handle_edge_irq; - irq_set_chained_handler(irq, dwapb_irq_handler); - irq_set_handler_data(irq, gpio); + if (!pp->irq_shared) { + irq_set_chained_handler(pp->irq, dwapb_irq_handler); + irq_set_handler_data(pp->irq, gpio); + } else { + /* + * Request a shared IRQ since where MFD would have devices + * using the same irq pin + */ + err = devm_request_irq(gpio->dev, pp->irq, + dwapb_irq_handler_mfd, + IRQF_SHARED, "gpio-dwapb-mfd", gpio); + if (err) { + dev_err(gpio->dev, "error requesting IRQ\n"); + irq_domain_remove(gpio->domain); + gpio->domain = NULL; + return; + } + } for (hwirq = 0 ; hwirq < ngpio ; hwirq++) irq_create_mapping(gpio->domain, hwirq); @@ -296,57 +390,53 @@ static void dwapb_irq_teardown(struct dwapb_gpio *gpio) } static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, - struct device_node *port_np, + struct dwapb_port_property *pp, unsigned int offs) { struct dwapb_gpio_port *port; - u32 port_idx, ngpio; void __iomem *dat, *set, *dirout; int err; - if (of_property_read_u32(port_np, "reg", &port_idx) || - port_idx >= DWAPB_MAX_PORTS) { - dev_err(gpio->dev, "missing/invalid port index for %s\n", - port_np->full_name); - return -EINVAL; - } - port = &gpio->ports[offs]; port->gpio = gpio; + port->idx = pp->idx; - if (of_property_read_u32(port_np, "snps,nr-gpios", &ngpio)) { - dev_info(gpio->dev, "failed to get number of gpios for %s\n", - port_np->full_name); - ngpio = 32; - } +#ifdef CONFIG_PM_SLEEP + port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL); + if (!port->ctx) + return -ENOMEM; +#endif - dat = gpio->regs + GPIO_EXT_PORTA + (port_idx * GPIO_EXT_PORT_SIZE); - set = gpio->regs + GPIO_SWPORTA_DR + (port_idx * GPIO_SWPORT_DR_SIZE); + dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE); + set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE); dirout = gpio->regs + GPIO_SWPORTA_DDR + - (port_idx * GPIO_SWPORT_DDR_SIZE); + (pp->idx * GPIO_SWPORT_DDR_SIZE); err = bgpio_init(&port->bgc, gpio->dev, 4, dat, set, NULL, dirout, NULL, false); if (err) { dev_err(gpio->dev, "failed to init gpio chip for %s\n", - port_np->full_name); + pp->name); return err; } - port->bgc.gc.ngpio = ngpio; - port->bgc.gc.of_node = port_np; +#ifdef CONFIG_OF_GPIO + port->bgc.gc.of_node = pp->node; +#endif + port->bgc.gc.ngpio = pp->ngpio; + port->bgc.gc.base = pp->gpio_base; - /* - * Only port A can provide interrupts in all configurations of the IP. - */ - if (port_idx == 0 && - of_property_read_bool(port_np, "interrupt-controller")) - dwapb_configure_irqs(gpio, port); + /* Only port A support debounce */ + if (pp->idx == 0) + port->bgc.gc.set_debounce = dwapb_gpio_set_debounce; + + if (pp->irq) + dwapb_configure_irqs(gpio, port, pp); err = gpiochip_add(&port->bgc.gc); if (err) dev_err(gpio->dev, "failed to register gpiochip for %s\n", - port_np->full_name); + pp->name); else port->is_registered = true; @@ -359,28 +449,119 @@ static void dwapb_gpio_unregister(struct dwapb_gpio *gpio) for (m = 0; m < gpio->nr_ports; ++m) if (gpio->ports[m].is_registered) - WARN_ON(gpiochip_remove(&gpio->ports[m].bgc.gc)); + gpiochip_remove(&gpio->ports[m].bgc.gc); +} + +static struct dwapb_platform_data * +dwapb_gpio_get_pdata_of(struct device *dev) +{ + struct device_node *node, *port_np; + struct dwapb_platform_data *pdata; + struct dwapb_port_property *pp; + int nports; + int i; + + node = dev->of_node; + if (!IS_ENABLED(CONFIG_OF_GPIO) || !node) + return ERR_PTR(-ENODEV); + + nports = of_get_child_count(node); + if (nports == 0) + return ERR_PTR(-ENODEV); + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL); + if (!pdata->properties) { + kfree(pdata); + return ERR_PTR(-ENOMEM); + } + + pdata->nports = nports; + + i = 0; + for_each_child_of_node(node, port_np) { + pp = &pdata->properties[i++]; + pp->node = port_np; + + if (of_property_read_u32(port_np, "reg", &pp->idx) || + pp->idx >= DWAPB_MAX_PORTS) { + dev_err(dev, "missing/invalid port index for %s\n", + port_np->full_name); + kfree(pdata->properties); + kfree(pdata); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(port_np, "snps,nr-gpios", + &pp->ngpio)) { + dev_info(dev, "failed to get number of gpios for %s\n", + port_np->full_name); + pp->ngpio = 32; + } + + /* + * Only port A can provide interrupts in all configurations of + * the IP. + */ + if (pp->idx == 0 && + of_property_read_bool(port_np, "interrupt-controller")) { + pp->irq = irq_of_parse_and_map(port_np, 0); + if (!pp->irq) { + dev_warn(dev, "no irq for bank %s\n", + port_np->full_name); + } + } + + pp->irq_shared = false; + pp->gpio_base = -1; + pp->name = port_np->full_name; + } + + return pdata; +} + +static inline void dwapb_free_pdata_of(struct dwapb_platform_data *pdata) +{ + if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata) + return; + + kfree(pdata->properties); + kfree(pdata); } static int dwapb_gpio_probe(struct platform_device *pdev) { + unsigned int i; struct resource *res; struct dwapb_gpio *gpio; - struct device_node *np; int err; - unsigned int offs = 0; + struct device *dev = &pdev->dev; + struct dwapb_platform_data *pdata = dev_get_platdata(dev); + bool is_pdata_alloc = !pdata; + + if (is_pdata_alloc) { + pdata = dwapb_gpio_get_pdata_of(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) - return -ENOMEM; - gpio->dev = &pdev->dev; + if (!pdata->nports) { + err = -ENODEV; + goto out_err; + } - gpio->nr_ports = of_get_child_count(pdev->dev.of_node); - if (!gpio->nr_ports) { - err = -EINVAL; + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + err = -ENOMEM; goto out_err; } - gpio->ports = devm_kzalloc(&pdev->dev, gpio->nr_ports * + gpio->dev = &pdev->dev; + gpio->nr_ports = pdata->nports; + + gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports, sizeof(*gpio->ports), GFP_KERNEL); if (!gpio->ports) { err = -ENOMEM; @@ -394,20 +575,23 @@ static int dwapb_gpio_probe(struct platform_device *pdev) goto out_err; } - for_each_child_of_node(pdev->dev.of_node, np) { - err = dwapb_gpio_add_port(gpio, np, offs++); + for (i = 0; i < gpio->nr_ports; i++) { + err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i); if (err) goto out_unregister; } platform_set_drvdata(pdev, gpio); - return 0; + goto out_err; out_unregister: dwapb_gpio_unregister(gpio); dwapb_irq_teardown(gpio); out_err: + if (is_pdata_alloc) + dwapb_free_pdata_of(pdata); + return err; } @@ -427,10 +611,100 @@ static const struct of_device_id dwapb_of_match[] = { }; MODULE_DEVICE_TABLE(of, dwapb_of_match); +#ifdef CONFIG_PM_SLEEP +static int dwapb_gpio_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dwapb_gpio *gpio = platform_get_drvdata(pdev); + struct bgpio_chip *bgc = &gpio->ports[0].bgc; + unsigned long flags; + int i; + + spin_lock_irqsave(&bgc->lock, flags); + for (i = 0; i < gpio->nr_ports; i++) { + unsigned int offset; + unsigned int idx = gpio->ports[i].idx; + struct dwapb_context *ctx = gpio->ports[i].ctx; + + BUG_ON(!ctx); + + offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE; + ctx->dir = dwapb_read(gpio, offset); + + offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE; + ctx->data = dwapb_read(gpio, offset); + + offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE; + ctx->ext = dwapb_read(gpio, offset); + + /* Only port A can provide interrupts */ + if (idx == 0) { + ctx->int_mask = dwapb_read(gpio, GPIO_INTMASK); + ctx->int_en = dwapb_read(gpio, GPIO_INTEN); + ctx->int_pol = dwapb_read(gpio, GPIO_INT_POLARITY); + ctx->int_type = dwapb_read(gpio, GPIO_INTTYPE_LEVEL); + ctx->int_deb = dwapb_read(gpio, GPIO_PORTA_DEBOUNCE); + + /* Mask out interrupts */ + dwapb_write(gpio, GPIO_INTMASK, 0xffffffff); + } + } + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static int dwapb_gpio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dwapb_gpio *gpio = platform_get_drvdata(pdev); + struct bgpio_chip *bgc = &gpio->ports[0].bgc; + unsigned long flags; + int i; + + spin_lock_irqsave(&bgc->lock, flags); + for (i = 0; i < gpio->nr_ports; i++) { + unsigned int offset; + unsigned int idx = gpio->ports[i].idx; + struct dwapb_context *ctx = gpio->ports[i].ctx; + + BUG_ON(!ctx); + + offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE; + dwapb_write(gpio, offset, ctx->data); + + offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE; + dwapb_write(gpio, offset, ctx->dir); + + offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE; + dwapb_write(gpio, offset, ctx->ext); + + /* Only port A can provide interrupts */ + if (idx == 0) { + dwapb_write(gpio, GPIO_INTTYPE_LEVEL, ctx->int_type); + dwapb_write(gpio, GPIO_INT_POLARITY, ctx->int_pol); + dwapb_write(gpio, GPIO_PORTA_DEBOUNCE, ctx->int_deb); + dwapb_write(gpio, GPIO_INTEN, ctx->int_en); + dwapb_write(gpio, GPIO_INTMASK, ctx->int_mask); + + /* Clear out spurious interrupts */ + dwapb_write(gpio, GPIO_PORTA_EOI, 0xffffffff); + } + } + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend, + dwapb_gpio_resume); + static struct platform_driver dwapb_gpio_driver = { .driver = { .name = "gpio-dwapb", .owner = THIS_MODULE, + .pm = &dwapb_gpio_pm_ops, .of_match_table = of_match_ptr(dwapb_of_match), }, .probe = dwapb_gpio_probe, |