From abdc08a3a263a20e49534a36291d657bf53dda5b Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 19 Aug 2014 10:06:09 -0700 Subject: gpio: change gpiochip_request_own_desc() prototype The current prototype of gpiochip_request_own_desc() requires to obtain a pointer to a descriptor. This is in contradiction to all other GPIO request schemes, and imposes an extra step of obtaining a descriptor to drivers. Most drivers actually cannot even perform that step since the function that does it (gpichip_get_desc()) is gpiolib-private. Change gpiochip_request_own_desc() to return a descriptor from a (chip, hwnum) tuple and update users of this function (currently gpiolib-acpi only). Signed-off-by: Alexandre Courbot Tested-by: Mika Westerberg Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e78a2373e374..a2de58fffd19 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -166,7 +166,8 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip, #endif /* CONFIG_GPIO_IRQCHIP */ -int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label); +struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, + const char *label); void gpiochip_free_own_desc(struct gpio_desc *desc); #else /* CONFIG_GPIOLIB */ -- cgit v1.2.3 From 3af0dbd592fe0a92002f16e341519ba03e92adf7 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Mon, 1 Sep 2014 11:19:52 +0800 Subject: gpio: mcp23s08 to support both device tree and platform data Device tree is not enabled in some architecture where gpio driver mcp23s08 is still required. v2-changes: - Parse device tree properties into platform data other than individual variables. v3-changes: - Use of_node in gpio_chip device structure, because the struct device * always has an of_node which is NULL when OF is not used. Signed-off-by: Sonic Zhang Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 - drivers/gpio/gpio-mcp23s08.c | 64 +++++++++++++++++++++++--------------------- include/linux/spi/mcp23s08.h | 18 +++++++++++++ 3 files changed, 52 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ec27ec0be8c2..ec398bee7c60 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -806,7 +806,6 @@ config GPIO_MAX7301 config GPIO_MCP23S08 tristate "Microchip MCP23xxx I/O expander" - depends on OF_GPIO depends on (SPI_MASTER && !I2C) || I2C help SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 6f183d9b487e..8488e2fd307c 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -479,7 +479,7 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp) mutex_init(&mcp->irq_lock); - mcp->irq_domain = irq_domain_add_linear(chip->of_node, chip->ngpio, + mcp->irq_domain = irq_domain_add_linear(chip->dev->of_node, chip->ngpio, &irq_domain_simple_ops, mcp); if (!mcp->irq_domain) return -ENODEV; @@ -581,7 +581,7 @@ done: static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, void *data, unsigned addr, unsigned type, - unsigned base, unsigned pullups) + struct mcp23s08_platform_data *pdata, int cs) { int status; bool mirror = false; @@ -635,7 +635,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, return -EINVAL; } - mcp->chip.base = base; + mcp->chip.base = pdata->base; mcp->chip.can_sleep = true; mcp->chip.dev = dev; mcp->chip.owner = THIS_MODULE; @@ -648,11 +648,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, if (status < 0) goto fail; - mcp->irq_controller = of_property_read_bool(mcp->chip.of_node, - "interrupt-controller"); + mcp->irq_controller = pdata->irq_controller; if (mcp->irq && mcp->irq_controller && (type == MCP_TYPE_017)) - mirror = of_property_read_bool(mcp->chip.of_node, - "microchip,irq-mirror"); + mirror = pdata->mirror; if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN) || mirror) { /* mcp23s17 has IOCON twice, make sure they are in sync */ @@ -668,7 +666,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, } /* configure ~100K pullups */ - status = mcp->ops->write(mcp, MCP_GPPU, pullups); + status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups); if (status < 0) goto fail; @@ -768,25 +766,29 @@ MODULE_DEVICE_TABLE(of, mcp23s08_i2c_of_match); static int mcp230xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct mcp23s08_platform_data *pdata; + struct mcp23s08_platform_data *pdata, local_pdata; struct mcp23s08 *mcp; - int status, base, pullups; + int status; const struct of_device_id *match; match = of_match_device(of_match_ptr(mcp23s08_i2c_of_match), &client->dev); - pdata = dev_get_platdata(&client->dev); - if (match || !pdata) { - base = -1; - pullups = 0; + if (match) { + pdata = &local_pdata; + pdata->base = -1; + pdata->chip[0].pullups = 0; + pdata->irq_controller = of_property_read_bool( + client->dev.of_node, + "interrupt-controller"); + pdata->mirror = of_property_read_bool(client->dev.of_node, + "microchip,irq-mirror"); client->irq = irq_of_parse_and_map(client->dev.of_node, 0); } else { - if (!gpio_is_valid(pdata->base)) { + pdata = dev_get_platdata(&client->dev); + if (!pdata || !gpio_is_valid(pdata->base)) { dev_dbg(&client->dev, "invalid platform data\n"); return -EINVAL; } - base = pdata->base; - pullups = pdata->chip[0].pullups; } mcp = kzalloc(sizeof(*mcp), GFP_KERNEL); @@ -795,7 +797,7 @@ static int mcp230xx_probe(struct i2c_client *client, mcp->irq = client->irq; status = mcp23s08_probe_one(mcp, &client->dev, client, client->addr, - id->driver_data, base, pullups); + id->driver_data, pdata, 0); if (status) goto fail; @@ -863,14 +865,12 @@ static void mcp23s08_i2c_exit(void) { } static int mcp23s08_probe(struct spi_device *spi) { - struct mcp23s08_platform_data *pdata; + struct mcp23s08_platform_data *pdata, local_pdata; unsigned addr; int chips = 0; struct mcp23s08_driver_data *data; int status, type; - unsigned base = -1, - ngpio = 0, - pullups[ARRAY_SIZE(pdata->chip)]; + unsigned ngpio = 0; const struct of_device_id *match; u32 spi_present_mask = 0; @@ -893,11 +893,18 @@ static int mcp23s08_probe(struct spi_device *spi) return -ENODEV; } + pdata = &local_pdata; + pdata->base = -1; for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { - pullups[addr] = 0; + pdata->chip[addr].pullups = 0; if (spi_present_mask & (1 << addr)) chips++; } + pdata->irq_controller = of_property_read_bool( + spi->dev.of_node, + "interrupt-controller"); + pdata->mirror = of_property_read_bool(spi->dev.of_node, + "microchip,irq-mirror"); } else { type = spi_get_device_id(spi)->driver_data; pdata = dev_get_platdata(&spi->dev); @@ -917,10 +924,7 @@ static int mcp23s08_probe(struct spi_device *spi) return -EINVAL; } spi_present_mask |= 1 << addr; - pullups[addr] = pdata->chip[addr].pullups; } - - base = pdata->base; } if (!chips) @@ -938,13 +942,13 @@ static int mcp23s08_probe(struct spi_device *spi) chips--; data->mcp[addr] = &data->chip[chips]; status = mcp23s08_probe_one(data->mcp[addr], &spi->dev, spi, - 0x40 | (addr << 1), type, base, - pullups[addr]); + 0x40 | (addr << 1), type, pdata, + addr); if (status < 0) goto fail; - if (base != -1) - base += (type == MCP_TYPE_S17) ? 16 : 8; + if (pdata->base != -1) + pdata->base += (type == MCP_TYPE_S17) ? 16 : 8; ngpio += (type == MCP_TYPE_S17) ? 16 : 8; } data->ngpio = ngpio; diff --git a/include/linux/spi/mcp23s08.h b/include/linux/spi/mcp23s08.h index 2d676d5aaa89..aa07d7b32568 100644 --- a/include/linux/spi/mcp23s08.h +++ b/include/linux/spi/mcp23s08.h @@ -22,4 +22,22 @@ struct mcp23s08_platform_data { * base to base+15 (or base+31 for s17 variant). */ unsigned base; + /* Marks the device as a interrupt controller. + * NOTE: The interrupt functionality is only supported for i2c + * versions of the chips. The spi chips can also do the interrupts, + * but this is not supported by the linux driver yet. + */ + bool irq_controller; + + /* Sets the mirror flag in the IOCON register. Devices + * with two interrupt outputs (these are the devices ending with 17 and + * those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and + * IO 8-15 are bank 2. These chips have two different interrupt outputs: + * One for bank 1 and another for bank 2. If irq-mirror is set, both + * interrupts are generated regardless of the bank that an input change + * occurred on. If it is not set, the interrupt are only generated for + * the bank they belong to. + * On devices with only one interrupt output this property is useless. + */ + bool mirror; }; -- cgit v1.2.3 From e1db1706c86ee455f25eeaeadeda827e1e02310f Mon Sep 17 00:00:00 2001 From: abdoulaye berthe Date: Sat, 5 Jul 2014 18:28:50 +0200 Subject: gpio: gpiolib: set gpiochip_remove retval to void This avoids handling gpiochip remove error in device remove handler. Signed-off-by: Abdoulaye Berthe Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 24 +++++++----------------- include/linux/gpio/driver.h | 2 +- 2 files changed, 8 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a5831d6a9b91..bf1bb795f100 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -308,10 +308,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); * * A gpio_chip with any GPIOs still requested may not be removed. */ -int gpiochip_remove(struct gpio_chip *chip) +void gpiochip_remove(struct gpio_chip *chip) { unsigned long flags; - int status = 0; unsigned id; acpi_gpiochip_remove(chip); @@ -323,24 +322,15 @@ int gpiochip_remove(struct gpio_chip *chip) of_gpiochip_remove(chip); for (id = 0; id < chip->ngpio; id++) { - if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) { - status = -EBUSY; - break; - } - } - if (status == 0) { - for (id = 0; id < chip->ngpio; id++) - chip->desc[id].chip = NULL; - - list_del(&chip->list); + if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) + dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); } + for (id = 0; id < chip->ngpio; id++) + chip->desc[id].chip = NULL; + list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); - - if (status == 0) - gpiochip_unexport(chip); - - return status; + gpiochip_unexport(chip); } EXPORT_SYMBOL_GPL(gpiochip_remove); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a2de58fffd19..560bf7fa614f 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -141,7 +141,7 @@ extern const char *gpiochip_is_requested(struct gpio_chip *chip, /* add/remove chips */ extern int gpiochip_add(struct gpio_chip *chip); -extern int gpiochip_remove(struct gpio_chip *chip); +extern void gpiochip_remove(struct gpio_chip *chip); extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)); -- cgit v1.2.3 From 3d2613c4289ff22de3aa24d2d0a29e33937f023a Mon Sep 17 00:00:00 2001 From: Weike Chen Date: Wed, 17 Sep 2014 09:18:39 -0700 Subject: GPIO: gpio-dwapb: Enable platform driver binding to MFD driver The Synopsys DesignWare APB GPIO driver only supports open firmware devices. But, like Intel Quark X1000 SOC, which has a single PCI function exporting a GPIO and an I2C controller, it is a Multifunction device. This patch is to enable the current Synopsys DesignWare APB GPIO driver to support the Multifunction device which exports the designware GPIO controller. Reviewed-by: Hock Leong Kweh Signed-off-by: Weike Chen Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 - drivers/gpio/gpio-dwapb.c | 224 +++++++++++++++++++++++-------- include/linux/platform_data/gpio-dwapb.h | 32 +++++ 3 files changed, 199 insertions(+), 58 deletions(-) create mode 100644 include/linux/platform_data/gpio-dwapb.h (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ec398bee7c60..d9ffb6dfa54b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -136,7 +136,6 @@ config GPIO_DWAPB tristate "Synopsys DesignWare APB GPIO driver" select GPIO_GENERIC select GENERIC_IRQ_CHIP - depends on OF_GPIO help Say Y or M here to build support for the Synopsys DesignWare APB GPIO block. diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index d6618a6e2399..27466b5af5e8 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #define GPIO_SWPORTA_DR 0x00 #define GPIO_SWPORTA_DDR 0x04 @@ -84,11 +86,10 @@ static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs) writel(v, gpio->regs + GPIO_INT_POLARITY); } -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 +103,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)); } @@ -207,22 +218,26 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) 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 +284,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 +327,42 @@ 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; - 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; - } - - 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); + 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; @@ -362,25 +378,116 @@ static void dwapb_gpio_unregister(struct dwapb_gpio *gpio) 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 +501,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; } diff --git a/include/linux/platform_data/gpio-dwapb.h b/include/linux/platform_data/gpio-dwapb.h new file mode 100644 index 000000000000..28702c849af1 --- /dev/null +++ b/include/linux/platform_data/gpio-dwapb.h @@ -0,0 +1,32 @@ +/* + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef GPIO_DW_APB_H +#define GPIO_DW_APB_H + +struct dwapb_port_property { + struct device_node *node; + const char *name; + unsigned int idx; + unsigned int ngpio; + unsigned int gpio_base; + unsigned int irq; + bool irq_shared; +}; + +struct dwapb_platform_data { + struct dwapb_port_property *properties; + unsigned int nports; +}; + +#endif -- cgit v1.2.3 From 295494af0695bc190e6b939df1036af898c2856f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 19 Sep 2014 23:22:44 +0300 Subject: gpiolib: add irq_not_threaded flag to gpio_chip Some GPIO chips (e.g. the DLN2 USB adapter) have blocking get/set operation but do not need a threaded irq handler. Signed-off-by: Octavian Purdila Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- include/linux/gpio/driver.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4acf8b2e9226..6fdae789ccc9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -437,7 +437,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_lockdep_class(irq, &gpiochip_irq_lock_class); irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler); /* Chips that can sleep need nested thread handlers */ - if (chip->can_sleep) + if (chip->can_sleep && !chip->irq_not_threaded) irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 560bf7fa614f..719fab209158 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -56,6 +56,8 @@ struct seq_file; * as the chip access may sleep when e.g. reading out the IRQ status * registers. * @exported: flags if the gpiochip is exported for use from sysfs. Private. + * @irq_not_threaded: flag must be set if @can_sleep is set but the + * IRQs don't need to be threaded * * A gpio_chip can help platforms abstract various sources of GPIOs so * they can all be accessed through a common programing interface. @@ -101,6 +103,7 @@ struct gpio_chip { struct gpio_desc *desc; const char *const *names; bool can_sleep; + bool irq_not_threaded; bool exported; #ifdef CONFIG_GPIOLIB_IRQCHIP -- cgit v1.2.3