mirror of https://gitee.com/openkylin/linux.git
gpio: pcf857x: Switch to use gpiolib irqchip helpers
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 21fd3cd187
("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>
This commit is contained in:
parent
6b516a1093
commit
a39294bdf4
|
@ -604,6 +604,7 @@ config GPIO_PCA953X_IRQ
|
||||||
config GPIO_PCF857X
|
config GPIO_PCF857X
|
||||||
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
|
tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
select IRQ_DOMAIN
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say yes here to provide access to most "quasi-bidirectional" I2C
|
Say yes here to provide access to most "quasi-bidirectional" I2C
|
||||||
|
|
|
@ -88,11 +88,9 @@ struct pcf857x {
|
||||||
struct gpio_chip chip;
|
struct gpio_chip chip;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock; /* protect 'out' */
|
struct mutex lock; /* protect 'out' */
|
||||||
struct irq_domain *irq_domain; /* for irq demux */
|
|
||||||
spinlock_t slock; /* protect irq demux */
|
spinlock_t slock; /* protect irq demux */
|
||||||
unsigned out; /* software latch */
|
unsigned out; /* software latch */
|
||||||
unsigned status; /* current status */
|
unsigned status; /* current status */
|
||||||
unsigned irq_mapped; /* mapped gpio irqs */
|
|
||||||
|
|
||||||
int (*write)(struct i2c_client *client, unsigned data);
|
int (*write)(struct i2c_client *client, unsigned data);
|
||||||
int (*read)(struct i2c_client *client);
|
int (*read)(struct i2c_client *client);
|
||||||
|
@ -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)
|
static irqreturn_t pcf857x_irq(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct pcf857x *gpio = data;
|
struct pcf857x *gpio = data;
|
||||||
|
@ -208,9 +194,9 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
|
||||||
* interrupt source, just to avoid bad irqs
|
* 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)
|
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;
|
gpio->status = status;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&gpio->slock, flags);
|
spin_unlock_irqrestore(&gpio->slock, flags);
|
||||||
|
@ -218,66 +204,6 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
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,
|
static int pcf857x_probe(struct i2c_client *client,
|
||||||
|
@ -314,15 +240,6 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
gpio->chip.direction_output = pcf857x_output;
|
gpio->chip.direction_output = pcf857x_output;
|
||||||
gpio->chip.ngpio = id->driver_data;
|
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
|
/* NOTE: the OnSemi jlc1562b is also largely compatible with
|
||||||
* these parts, notably for output. It has a low-resolution
|
* these parts, notably for output. It has a low-resolution
|
||||||
* DAC instead of pin change IRQs; and its inputs can be the
|
* DAC instead of pin change IRQs; and its inputs can be the
|
||||||
|
@ -398,6 +315,26 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto fail;
|
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.
|
/* Let platform code set up the GPIOs and their users.
|
||||||
* Now is the first time anyone could use them.
|
* Now is the first time anyone could use them.
|
||||||
*/
|
*/
|
||||||
|
@ -413,13 +350,12 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail_irq:
|
||||||
if (client->irq)
|
gpiochip_remove(&gpio->chip);
|
||||||
pcf857x_irq_domain_cleanup(gpio);
|
|
||||||
|
|
||||||
fail_irq_domain:
|
fail:
|
||||||
dev_dbg(&client->dev, "probe error %d for '%s'\n",
|
dev_dbg(&client->dev, "probe error %d for '%s'\n", status,
|
||||||
status, client->name);
|
client->name);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -441,9 +377,6 @@ static int pcf857x_remove(struct i2c_client *client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->irq)
|
|
||||||
pcf857x_irq_domain_cleanup(gpio);
|
|
||||||
|
|
||||||
gpiochip_remove(&gpio->chip);
|
gpiochip_remove(&gpio->chip);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue