Skip to content

Commit

Permalink
gpio: pcf857x: Switch to use gpiolib irqchip helpers
Browse files Browse the repository at this point in the history
Switch the PCF857x GPIO driver to use the gpiolib irqchip helpers.
This driver uses a nested threaded interrupt, hence handle_nested_irq()
and gpiochip_set_chained_irqchip() must be used.

Note that this removes the checks added in commit 21fd3cd
("gpio: pcf857x: call the gpio user handler iff gpio_to_irq is done"),
as the interrupt mappings are no longer created on-demand by the driver,
but by gpiochip_irqchip_add() during initialization.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
  • Loading branch information
geertu authored and linusw committed Mar 4, 2015
1 parent 6b516a1 commit a39294b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 94 deletions.
1 change: 1 addition & 0 deletions drivers/gpio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ config GPIO_PCA953X_IRQ
config GPIO_PCF857X
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
depends on I2C
select GPIOLIB_IRQCHIP
select IRQ_DOMAIN
help
Say yes here to provide access to most "quasi-bidirectional" I2C
Expand Down
121 changes: 27 additions & 94 deletions drivers/gpio/gpio-pcf857x.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,9 @@ struct pcf857x {
struct gpio_chip chip;
struct i2c_client *client;
struct mutex lock; /* protect 'out' */
struct irq_domain *irq_domain; /* for irq demux */
spinlock_t slock; /* protect irq demux */
unsigned out; /* software latch */
unsigned status; /* current status */
unsigned irq_mapped; /* mapped gpio irqs */

int (*write)(struct i2c_client *client, unsigned data);
int (*read)(struct i2c_client *client);
Expand Down Expand Up @@ -182,18 +180,6 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value)

/*-------------------------------------------------------------------------*/

static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct pcf857x *gpio = container_of(chip, struct pcf857x, chip);
int ret;

ret = irq_create_mapping(gpio->irq_domain, offset);
if (ret > 0)
gpio->irq_mapped |= (1 << offset);

return ret;
}

static irqreturn_t pcf857x_irq(int irq, void *data)
{
struct pcf857x *gpio = data;
Expand All @@ -208,76 +194,16 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
* interrupt source, just to avoid bad irqs
*/

change = ((gpio->status ^ status) & gpio->irq_mapped);
change = (gpio->status ^ status);
for_each_set_bit(i, &change, gpio->chip.ngpio)
generic_handle_irq(irq_find_mapping(gpio->irq_domain, i));
handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i));
gpio->status = status;

spin_unlock_irqrestore(&gpio->slock, flags);

return IRQ_HANDLED;
}

static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hw)
{
struct pcf857x *gpio = domain->host_data;

irq_set_chip_and_handler(irq,
&dummy_irq_chip,
handle_level_irq);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
irq_set_noprobe(irq);
#endif
gpio->irq_mapped |= (1 << hw);

return 0;
}

static struct irq_domain_ops pcf857x_irq_domain_ops = {
.map = pcf857x_irq_domain_map,
};

static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio)
{
if (gpio->irq_domain)
irq_domain_remove(gpio->irq_domain);

}

static int pcf857x_irq_domain_init(struct pcf857x *gpio,
struct i2c_client *client)
{
int status;

gpio->irq_domain = irq_domain_add_linear(client->dev.of_node,
gpio->chip.ngpio,
&pcf857x_irq_domain_ops,
gpio);
if (!gpio->irq_domain)
goto fail;

/* enable real irq */
status = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf857x_irq, IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_SHARED,
dev_name(&client->dev), gpio);

if (status)
goto fail;

/* enable gpio_to_irq() */
gpio->chip.to_irq = pcf857x_to_irq;

return 0;

fail:
pcf857x_irq_domain_cleanup(gpio);
return -EINVAL;
}

/*-------------------------------------------------------------------------*/

static int pcf857x_probe(struct i2c_client *client,
Expand Down Expand Up @@ -314,15 +240,6 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->chip.direction_output = pcf857x_output;
gpio->chip.ngpio = id->driver_data;

/* enable gpio_to_irq() if platform has settings */
if (client->irq) {
status = pcf857x_irq_domain_init(gpio, client);
if (status < 0) {
dev_err(&client->dev, "irq_domain init failed\n");
goto fail_irq_domain;
}
}

/* NOTE: the OnSemi jlc1562b is also largely compatible with
* these parts, notably for output. It has a low-resolution
* DAC instead of pin change IRQs; and its inputs can be the
Expand Down Expand Up @@ -398,6 +315,26 @@ static int pcf857x_probe(struct i2c_client *client,
if (status < 0)
goto fail;

/* Enable irqchip if we have an interrupt */
if (client->irq) {
status = gpiochip_irqchip_add(&gpio->chip, &dummy_irq_chip, 0,
handle_level_irq, IRQ_TYPE_NONE);
if (status) {
dev_err(&client->dev, "cannot add irqchip\n");
goto fail_irq;
}

status = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf857x_irq, IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_SHARED,
dev_name(&client->dev), gpio);
if (status)
goto fail_irq;

gpiochip_set_chained_irqchip(&gpio->chip, &dummy_irq_chip,
client->irq, NULL);
}

/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
Expand All @@ -413,13 +350,12 @@ static int pcf857x_probe(struct i2c_client *client,

return 0;

fail:
if (client->irq)
pcf857x_irq_domain_cleanup(gpio);
fail_irq:
gpiochip_remove(&gpio->chip);

fail_irq_domain:
dev_dbg(&client->dev, "probe error %d for '%s'\n",
status, client->name);
fail:
dev_dbg(&client->dev, "probe error %d for '%s'\n", status,
client->name);

return status;
}
Expand All @@ -441,9 +377,6 @@ static int pcf857x_remove(struct i2c_client *client)
}
}

if (client->irq)
pcf857x_irq_domain_cleanup(gpio);

gpiochip_remove(&gpio->chip);
return status;
}
Expand Down

0 comments on commit a39294b

Please sign in to comment.