gpio: simplify adding threaded interrupts
This tries to simplify the use of CONFIG_GPIOLIB_IRQCHIP when using threaded interrupts: add a new call gpiochip_irqchip_add_nested() to indicate that we're dealing with a nested rather than a chained irqchip, then create a separate gpiochip_set_nested_irqchip() to mirror the gpiochip_set_chained_irqchip() call to connect the parent and child interrupts. In the nested case gpiochip_set_nested_irqchip() does nothing more than call irq_set_parent() on each valid child interrupt, which has little semantic effect in the kernel, but this is probably still formally correct. Update all drivers using nested interrupts to use gpiochip_irqchip_add_nested() so we can now see clearly which these users are. The DLN2 driver can drop its specific hack with .irq_not_threaded as we now recognize whether a chip is threaded or not from its use of gpiochip_irqchip_add_nested() signature rather than from inspecting .can_sleep. We rename the .irq_parent to .irq_chained_parent since this parent IRQ is only really kept around for the chained interrupt handlers. Cc: Lars Poeschel <poeschel@lemonage.de> Cc: Octavian Purdila <octavian.purdila@intel.com> Cc: Daniel Baluta <daniel.baluta@intel.com> Cc: Bin Gao <bin.gao@linux.intel.com> Cc: Mika Westerberg <mika.westerberg@linux.intel.com> Cc: Ajay Thomas <ajay.thomas.david.rajamanickam@intel.com> Cc: Semen Protsenko <semen.protsenko@globallogic.com> Cc: Alexander Stein <alexander.stein@systec-electronic.com> Cc: Phil Reid <preid@electromag.com.au> Cc: Bartosz Golaszewski <bgolaszewski@baylibre.com> Cc: Patrice Chotard <patrice.chotard@st.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
07d9a38068
commit
d245b3f9bd
|
@ -175,8 +175,8 @@ The IRQ portions of the GPIO block are implemented using an irqchip, using
|
||||||
the header <linux/irq.h>. So basically such a driver is utilizing two sub-
|
the header <linux/irq.h>. So basically such a driver is utilizing two sub-
|
||||||
systems simultaneously: gpio and irq.
|
systems simultaneously: gpio and irq.
|
||||||
|
|
||||||
RT_FULL: GPIO driver should not use spinlock_t or any sleepable APIs
|
RT_FULL: a realtime compliant GPIO driver should not use spinlock_t or any
|
||||||
(like PM runtime) as part of its irq_chip implementation on -RT.
|
sleepable APIs (like PM runtime) as part of its irq_chip implementation.
|
||||||
- spinlock_t should be replaced with raw_spinlock_t [1].
|
- spinlock_t should be replaced with raw_spinlock_t [1].
|
||||||
- If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
|
- If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
|
||||||
and .irq_bus_unlock() callbacks, as these are the only slowpath callbacks
|
and .irq_bus_unlock() callbacks, as these are the only slowpath callbacks
|
||||||
|
@ -185,33 +185,32 @@ RT_FULL: GPIO driver should not use spinlock_t or any sleepable APIs
|
||||||
GPIO irqchips usually fall in one of two categories:
|
GPIO irqchips usually fall in one of two categories:
|
||||||
|
|
||||||
* CHAINED GPIO irqchips: these are usually the type that is embedded on
|
* CHAINED GPIO irqchips: these are usually the type that is embedded on
|
||||||
an SoC. This means that there is a fast IRQ handler for the GPIOs that
|
an SoC. This means that there is a fast IRQ flow handler for the GPIOs that
|
||||||
gets called in a chain from the parent IRQ handler, most typically the
|
gets called in a chain from the parent IRQ handler, most typically the
|
||||||
system interrupt controller. This means the GPIO irqchip is registered
|
system interrupt controller. This means that the GPIO irqchip handler will
|
||||||
using irq_set_chained_handler() or the corresponding
|
be called immediately from the parent irqchip, while holding the IRQs
|
||||||
gpiochip_set_chained_irqchip() helper function, and the GPIO irqchip
|
disabled. The GPIO irqchip will then end up calling something like this
|
||||||
handler will be called immediately from the parent irqchip, while
|
sequence in its interrupt handler:
|
||||||
holding the IRQs disabled. The GPIO irqchip will then end up calling
|
|
||||||
something like this sequence in its interrupt handler:
|
|
||||||
|
|
||||||
static irqreturn_t tc3589x_gpio_irq(int irq, void *data)
|
static irqreturn_t foo_gpio_irq(int irq, void *data)
|
||||||
chained_irq_enter(...);
|
chained_irq_enter(...);
|
||||||
generic_handle_irq(...);
|
generic_handle_irq(...);
|
||||||
chained_irq_exit(...);
|
chained_irq_exit(...);
|
||||||
|
|
||||||
Chained GPIO irqchips typically can NOT set the .can_sleep flag on
|
Chained GPIO irqchips typically can NOT set the .can_sleep flag on
|
||||||
struct gpio_chip, as everything happens directly in the callbacks.
|
struct gpio_chip, as everything happens directly in the callbacks: no
|
||||||
|
slow bus traffic like I2C can be used.
|
||||||
|
|
||||||
RT_FULL: Note, chained IRQ handlers will not be forced threaded on -RT.
|
RT_FULL: Note, chained IRQ handlers will not be forced threaded on -RT.
|
||||||
As result, spinlock_t or any sleepable APIs (like PM runtime) can't be used
|
As result, spinlock_t or any sleepable APIs (like PM runtime) can't be used
|
||||||
in chained IRQ handler.
|
in chained IRQ handler.
|
||||||
if required (and if it can't be converted to the nested threaded GPIO irqchip)
|
If required (and if it can't be converted to the nested threaded GPIO irqchip)
|
||||||
- chained IRQ handler can be converted to generic irq handler and this way
|
a chained IRQ handler can be converted to generic irq handler and this way
|
||||||
it will be threaded IRQ handler on -RT and hard IRQ handler on non-RT
|
it will be a threaded IRQ handler on -RT and a hard IRQ handler on non-RT
|
||||||
(for example, see [3]).
|
(for example, see [3]).
|
||||||
Know W/A: The generic_handle_irq() is expected to be called with IRQ disabled,
|
Know W/A: The generic_handle_irq() is expected to be called with IRQ disabled,
|
||||||
so IRQ core will complain if it will be called from IRQ handler which is
|
so the IRQ core will complain if it is called from an IRQ handler which is
|
||||||
forced thread. The "fake?" raw lock can be used to W/A this problem:
|
forced to a thread. The "fake?" raw lock can be used to W/A this problem:
|
||||||
|
|
||||||
raw_spinlock_t wa_lock;
|
raw_spinlock_t wa_lock;
|
||||||
static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
|
static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
|
||||||
|
@ -243,7 +242,7 @@ GPIO irqchips usually fall in one of two categories:
|
||||||
by the driver. The hallmark of this driver is to call something like
|
by the driver. The hallmark of this driver is to call something like
|
||||||
this in its interrupt handler:
|
this in its interrupt handler:
|
||||||
|
|
||||||
static irqreturn_t tc3589x_gpio_irq(int irq, void *data)
|
static irqreturn_t foo_gpio_irq(int irq, void *data)
|
||||||
...
|
...
|
||||||
handle_nested_irq(irq);
|
handle_nested_irq(irq);
|
||||||
|
|
||||||
|
@ -256,23 +255,31 @@ associated irqdomain and resource allocation callbacks, the gpiolib has
|
||||||
some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig
|
some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig
|
||||||
symbol:
|
symbol:
|
||||||
|
|
||||||
* gpiochip_irqchip_add(): adds an irqchip to a gpiochip. It will pass
|
* gpiochip_irqchip_add(): adds a chained irqchip to a gpiochip. It will pass
|
||||||
the struct gpio_chip* for the chip to all IRQ callbacks, so the callbacks
|
the struct gpio_chip* for the chip to all IRQ callbacks, so the callbacks
|
||||||
need to embed the gpio_chip in its state container and obtain a pointer
|
need to embed the gpio_chip in its state container and obtain a pointer
|
||||||
to the container using container_of().
|
to the container using container_of().
|
||||||
(See Documentation/driver-model/design-patterns.txt)
|
(See Documentation/driver-model/design-patterns.txt)
|
||||||
|
|
||||||
If there is a need to exclude certain GPIOs from the IRQ domain, one can
|
* gpiochip_irqchip_add_nested(): adds a nested irqchip to a gpiochip.
|
||||||
set .irq_need_valid_mask of the gpiochip before gpiochip_add_data() is
|
Apart from that it works exactly like the chained irqchip.
|
||||||
called. This allocates .irq_valid_mask with as many bits set as there are
|
|
||||||
GPIOs in the chip. Drivers can exclude GPIOs by clearing bits from this
|
|
||||||
mask. The mask must be filled in before gpiochip_irqchip_add() is called.
|
|
||||||
|
|
||||||
* gpiochip_set_chained_irqchip(): sets up a chained irq handler for a
|
* gpiochip_set_chained_irqchip(): sets up a chained irq handler for a
|
||||||
gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler
|
gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler
|
||||||
data. (Notice handler data, since the irqchip data is likely used by the
|
data. (Notice handler data, since the irqchip data is likely used by the
|
||||||
parent irqchip!) This is for the chained type of chip. This is also used
|
parent irqchip!).
|
||||||
to set up a nested irqchip if NULL is passed as handler.
|
|
||||||
|
* gpiochip_set_nested_irqchip(): sets up a nested irq handler for a
|
||||||
|
gpio_chip from a parent IRQ. As the parent IRQ has usually been
|
||||||
|
explicitly requested by the driver, this does very little more than
|
||||||
|
mark all the child IRQs as having the other IRQ as parent.
|
||||||
|
|
||||||
|
If there is a need to exclude certain GPIOs from the IRQ domain, you can
|
||||||
|
set .irq_need_valid_mask of the gpiochip before gpiochip_add_data() is
|
||||||
|
called. This allocates an .irq_valid_mask with as many bits set as there
|
||||||
|
are GPIOs in the chip. Drivers can exclude GPIOs by clearing bits from this
|
||||||
|
mask. The mask must be filled in before gpiochip_irqchip_add() or
|
||||||
|
gpiochip_irqchip_add_nested() is called.
|
||||||
|
|
||||||
To use the helpers please keep the following in mind:
|
To use the helpers please keep the following in mind:
|
||||||
|
|
||||||
|
@ -323,6 +330,9 @@ When implementing an irqchip inside a GPIO driver, these two functions should
|
||||||
typically be called in the .startup() and .shutdown() callbacks from the
|
typically be called in the .startup() and .shutdown() callbacks from the
|
||||||
irqchip.
|
irqchip.
|
||||||
|
|
||||||
|
When using the gpiolib irqchip helpers, these callback are automatically
|
||||||
|
assigned.
|
||||||
|
|
||||||
Real-Time compliance for GPIO IRQ chips
|
Real-Time compliance for GPIO IRQ chips
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -468,11 +468,11 @@ static int adnp_irq_setup(struct adnp *adnp)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gpiochip_irqchip_add(chip,
|
err = gpiochip_irqchip_add_nested(chip,
|
||||||
&adnp_irq_chip,
|
&adnp_irq_chip,
|
||||||
0,
|
0,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(chip->parent,
|
dev_err(chip->parent,
|
||||||
"could not connect irqchip to gpiochip\n");
|
"could not connect irqchip to gpiochip\n");
|
||||||
|
|
|
@ -351,8 +351,8 @@ static int crystalcove_gpio_probe(struct platform_device *pdev)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiochip_irqchip_add(&cg->chip, &crystalcove_irqchip, 0,
|
gpiochip_irqchip_add_nested(&cg->chip, &crystalcove_irqchip, 0,
|
||||||
handle_simple_irq, IRQ_TYPE_NONE);
|
handle_simple_irq, IRQ_TYPE_NONE);
|
||||||
|
|
||||||
retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler,
|
retval = request_threaded_irq(irq, NULL, crystalcove_gpio_irq_handler,
|
||||||
IRQF_ONESHOT, KBUILD_MODNAME, cg);
|
IRQF_ONESHOT, KBUILD_MODNAME, cg);
|
||||||
|
|
|
@ -467,7 +467,6 @@ static int dln2_gpio_probe(struct platform_device *pdev)
|
||||||
dln2->gpio.base = -1;
|
dln2->gpio.base = -1;
|
||||||
dln2->gpio.ngpio = pins;
|
dln2->gpio.ngpio = pins;
|
||||||
dln2->gpio.can_sleep = true;
|
dln2->gpio.can_sleep = true;
|
||||||
dln2->gpio.irq_not_threaded = true;
|
|
||||||
dln2->gpio.set = dln2_gpio_set;
|
dln2->gpio.set = dln2_gpio_set;
|
||||||
dln2->gpio.get = dln2_gpio_get;
|
dln2->gpio.get = dln2_gpio_get;
|
||||||
dln2->gpio.request = dln2_gpio_request;
|
dln2->gpio.request = dln2_gpio_request;
|
||||||
|
|
|
@ -520,20 +520,19 @@ static int max732x_irq_setup(struct max732x_chip *chip,
|
||||||
client->irq);
|
client->irq);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = gpiochip_irqchip_add(&chip->gpio_chip,
|
ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
|
||||||
&max732x_irq_chip,
|
&max732x_irq_chip,
|
||||||
irq_base,
|
irq_base,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"could not connect irqchip to gpiochip\n");
|
"could not connect irqchip to gpiochip\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
gpiochip_set_chained_irqchip(&chip->gpio_chip,
|
gpiochip_set_nested_irqchip(&chip->gpio_chip,
|
||||||
&max732x_irq_chip,
|
&max732x_irq_chip,
|
||||||
client->irq,
|
client->irq);
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -473,21 +473,20 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gpiochip_irqchip_add(chip,
|
err = gpiochip_irqchip_add_nested(chip,
|
||||||
&mcp23s08_irq_chip,
|
&mcp23s08_irq_chip,
|
||||||
0,
|
0,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(chip->parent,
|
dev_err(chip->parent,
|
||||||
"could not connect irqchip to gpiochip: %d\n", err);
|
"could not connect irqchip to gpiochip: %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiochip_set_chained_irqchip(chip,
|
gpiochip_set_nested_irqchip(chip,
|
||||||
&mcp23s08_irq_chip,
|
&mcp23s08_irq_chip,
|
||||||
mcp->irq,
|
mcp->irq);
|
||||||
NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -635,20 +635,20 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiochip_irqchip_add(&chip->gpio_chip,
|
ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
|
||||||
&pca953x_irq_chip,
|
&pca953x_irq_chip,
|
||||||
irq_base,
|
irq_base,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"could not connect irqchip to gpiochip\n");
|
"could not connect irqchip to gpiochip\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiochip_set_chained_irqchip(&chip->gpio_chip,
|
gpiochip_set_nested_irqchip(&chip->gpio_chip,
|
||||||
&pca953x_irq_chip,
|
&pca953x_irq_chip,
|
||||||
client->irq, NULL);
|
client->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -378,9 +378,10 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
/* Enable irqchip if we have an interrupt */
|
/* Enable irqchip if we have an interrupt */
|
||||||
if (client->irq) {
|
if (client->irq) {
|
||||||
status = gpiochip_irqchip_add(&gpio->chip, &pcf857x_irq_chip,
|
status = gpiochip_irqchip_add_nested(&gpio->chip,
|
||||||
0, handle_level_irq,
|
&pcf857x_irq_chip,
|
||||||
IRQ_TYPE_NONE);
|
0, handle_level_irq,
|
||||||
|
IRQ_TYPE_NONE);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&client->dev, "cannot add irqchip\n");
|
dev_err(&client->dev, "cannot add irqchip\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -393,8 +394,8 @@ static int pcf857x_probe(struct i2c_client *client,
|
||||||
if (status)
|
if (status)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
|
gpiochip_set_nested_irqchip(&gpio->chip, &pcf857x_irq_chip,
|
||||||
client->irq, NULL);
|
client->irq);
|
||||||
gpio->irq_parent = client->irq;
|
gpio->irq_parent = client->irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -484,21 +484,20 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
|
||||||
if (stmpe_gpio->norequest_mask & BIT(i))
|
if (stmpe_gpio->norequest_mask & BIT(i))
|
||||||
clear_bit(i, stmpe_gpio->chip.irq_valid_mask);
|
clear_bit(i, stmpe_gpio->chip.irq_valid_mask);
|
||||||
}
|
}
|
||||||
ret = gpiochip_irqchip_add(&stmpe_gpio->chip,
|
ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip,
|
||||||
&stmpe_gpio_irq_chip,
|
&stmpe_gpio_irq_chip,
|
||||||
0,
|
0,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"could not connect irqchip to gpiochip\n");
|
"could not connect irqchip to gpiochip\n");
|
||||||
goto out_disable;
|
goto out_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiochip_set_chained_irqchip(&stmpe_gpio->chip,
|
gpiochip_set_nested_irqchip(&stmpe_gpio->chip,
|
||||||
&stmpe_gpio_irq_chip,
|
&stmpe_gpio_irq_chip,
|
||||||
irq,
|
irq);
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, stmpe_gpio);
|
platform_set_drvdata(pdev, stmpe_gpio);
|
||||||
|
|
|
@ -337,21 +337,20 @@ static int tc3589x_gpio_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiochip_irqchip_add(&tc3589x_gpio->chip,
|
ret = gpiochip_irqchip_add_nested(&tc3589x_gpio->chip,
|
||||||
&tc3589x_gpio_irq_chip,
|
&tc3589x_gpio_irq_chip,
|
||||||
0,
|
0,
|
||||||
handle_simple_irq,
|
handle_simple_irq,
|
||||||
IRQ_TYPE_NONE);
|
IRQ_TYPE_NONE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"could not connect irqchip to gpiochip\n");
|
"could not connect irqchip to gpiochip\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiochip_set_chained_irqchip(&tc3589x_gpio->chip,
|
gpiochip_set_nested_irqchip(&tc3589x_gpio->chip,
|
||||||
&tc3589x_gpio_irq_chip,
|
&tc3589x_gpio_irq_chip,
|
||||||
irq,
|
irq);
|
||||||
NULL);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, tc3589x_gpio);
|
platform_set_drvdata(pdev, tc3589x_gpio);
|
||||||
|
|
||||||
|
|
|
@ -426,8 +426,8 @@ static int wcove_gpio_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiochip_irqchip_add(&wg->chip, &wcove_irqchip, 0,
|
ret = gpiochip_irqchip_add_nested(&wg->chip, &wcove_irqchip, 0,
|
||||||
handle_simple_irq, IRQ_TYPE_NONE);
|
handle_simple_irq, IRQ_TYPE_NONE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Failed to add irqchip: %d\n", ret);
|
dev_err(dev, "Failed to add irqchip: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -1439,7 +1439,7 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
|
* gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip
|
||||||
* @gpiochip: the gpiochip to set the irqchip chain to
|
* @gpiochip: the gpiochip to set the irqchip chain to
|
||||||
* @irqchip: the irqchip to chain to the gpiochip
|
* @irqchip: the irqchip to chain to the gpiochip
|
||||||
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
||||||
|
@ -1448,10 +1448,10 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
||||||
* coming out of the gpiochip. If the interrupt is nested rather than
|
* coming out of the gpiochip. If the interrupt is nested rather than
|
||||||
* cascaded, pass NULL in this handler argument
|
* cascaded, pass NULL in this handler argument
|
||||||
*/
|
*/
|
||||||
void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
int parent_irq,
|
int parent_irq,
|
||||||
irq_flow_handler_t parent_handler)
|
irq_flow_handler_t parent_handler)
|
||||||
{
|
{
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
|
|
||||||
|
@ -1475,7 +1475,7 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
||||||
irq_set_chained_handler_and_data(parent_irq, parent_handler,
|
irq_set_chained_handler_and_data(parent_irq, parent_handler,
|
||||||
gpiochip);
|
gpiochip);
|
||||||
|
|
||||||
gpiochip->irq_parent = parent_irq;
|
gpiochip->irq_chained_parent = parent_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the parent IRQ for all affected IRQs */
|
/* Set the parent IRQ for all affected IRQs */
|
||||||
|
@ -1486,8 +1486,47 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
||||||
parent_irq);
|
parent_irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiochip_set_chained_irqchip() - connects a chained irqchip to a gpiochip
|
||||||
|
* @gpiochip: the gpiochip to set the irqchip chain to
|
||||||
|
* @irqchip: the irqchip to chain to the gpiochip
|
||||||
|
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
||||||
|
* chained irqchip
|
||||||
|
* @parent_handler: the parent interrupt handler for the accumulated IRQ
|
||||||
|
* coming out of the gpiochip. If the interrupt is nested rather than
|
||||||
|
* cascaded, pass NULL in this handler argument
|
||||||
|
*/
|
||||||
|
void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
||||||
|
struct irq_chip *irqchip,
|
||||||
|
int parent_irq,
|
||||||
|
irq_flow_handler_t parent_handler)
|
||||||
|
{
|
||||||
|
gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq,
|
||||||
|
parent_handler);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
|
EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip
|
||||||
|
* @gpiochip: the gpiochip to set the irqchip nested handler to
|
||||||
|
* @irqchip: the irqchip to nest to the gpiochip
|
||||||
|
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
||||||
|
* nested irqchip
|
||||||
|
*/
|
||||||
|
void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip,
|
||||||
|
struct irq_chip *irqchip,
|
||||||
|
int parent_irq)
|
||||||
|
{
|
||||||
|
if (!gpiochip->irq_nested) {
|
||||||
|
chip_err(gpiochip, "tried to nest a chained gpiochip\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
|
* gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
|
||||||
* @d: the irqdomain used by this irqchip
|
* @d: the irqdomain used by this irqchip
|
||||||
|
@ -1510,8 +1549,8 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
|
||||||
*/
|
*/
|
||||||
irq_set_lockdep_class(irq, chip->lock_key);
|
irq_set_lockdep_class(irq, chip->lock_key);
|
||||||
irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
|
irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
|
||||||
/* Chips that can sleep need nested thread handlers */
|
/* Chips that use nested thread handlers have them marked */
|
||||||
if (chip->can_sleep && !chip->irq_not_threaded)
|
if (chip->irq_nested)
|
||||||
irq_set_nested_thread(irq, 1);
|
irq_set_nested_thread(irq, 1);
|
||||||
irq_set_noprobe(irq);
|
irq_set_noprobe(irq);
|
||||||
|
|
||||||
|
@ -1529,7 +1568,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
|
||||||
{
|
{
|
||||||
struct gpio_chip *chip = d->host_data;
|
struct gpio_chip *chip = d->host_data;
|
||||||
|
|
||||||
if (chip->can_sleep)
|
if (chip->irq_nested)
|
||||||
irq_set_nested_thread(irq, 0);
|
irq_set_nested_thread(irq, 0);
|
||||||
irq_set_chip_and_handler(irq, NULL, NULL);
|
irq_set_chip_and_handler(irq, NULL, NULL);
|
||||||
irq_set_chip_data(irq, NULL);
|
irq_set_chip_data(irq, NULL);
|
||||||
|
@ -1584,9 +1623,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
||||||
|
|
||||||
acpi_gpiochip_free_interrupts(gpiochip);
|
acpi_gpiochip_free_interrupts(gpiochip);
|
||||||
|
|
||||||
if (gpiochip->irq_parent) {
|
if (gpiochip->irq_chained_parent) {
|
||||||
irq_set_chained_handler(gpiochip->irq_parent, NULL);
|
irq_set_chained_handler(gpiochip->irq_chained_parent, NULL);
|
||||||
irq_set_handler_data(gpiochip->irq_parent, NULL);
|
irq_set_handler_data(gpiochip->irq_chained_parent, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove all IRQ mappings and delete the domain */
|
/* Remove all IRQ mappings and delete the domain */
|
||||||
|
@ -1610,7 +1649,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiochip_irqchip_add() - adds an irqchip to a gpiochip
|
* _gpiochip_irqchip_add() - adds an irqchip to a gpiochip
|
||||||
* @gpiochip: the gpiochip to add the irqchip to
|
* @gpiochip: the gpiochip to add the irqchip to
|
||||||
* @irqchip: the irqchip to add to the gpiochip
|
* @irqchip: the irqchip to add to the gpiochip
|
||||||
* @first_irq: if not dynamically assigned, the base (first) IRQ to
|
* @first_irq: if not dynamically assigned, the base (first) IRQ to
|
||||||
|
@ -1618,6 +1657,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
||||||
* @handler: the irq handler to use (often a predefined irq core function)
|
* @handler: the irq handler to use (often a predefined irq core function)
|
||||||
* @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE
|
* @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE
|
||||||
* to have the core avoid setting up any default type in the hardware.
|
* to have the core avoid setting up any default type in the hardware.
|
||||||
|
* @nested: whether this is a nested irqchip calling handle_nested_irq()
|
||||||
|
* in its IRQ handler
|
||||||
* @lock_key: lockdep class
|
* @lock_key: lockdep class
|
||||||
*
|
*
|
||||||
* This function closely associates a certain irqchip with a certain
|
* This function closely associates a certain irqchip with a certain
|
||||||
|
@ -1639,6 +1680,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
unsigned int type,
|
unsigned int type,
|
||||||
|
bool nested,
|
||||||
struct lock_class_key *lock_key)
|
struct lock_class_key *lock_key)
|
||||||
{
|
{
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
|
@ -1653,6 +1695,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
||||||
pr_err("missing gpiochip .dev parent pointer\n");
|
pr_err("missing gpiochip .dev parent pointer\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
gpiochip->irq_nested = nested;
|
||||||
of_node = gpiochip->parent->of_node;
|
of_node = gpiochip->parent->of_node;
|
||||||
#ifdef CONFIG_OF_GPIO
|
#ifdef CONFIG_OF_GPIO
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -82,8 +82,6 @@ enum single_ended_mode {
|
||||||
* implies that if the chip supports IRQs, these IRQs need to be threaded
|
* implies that if the chip supports IRQs, these IRQs need to be threaded
|
||||||
* as the chip access may sleep when e.g. reading out the IRQ status
|
* as the chip access may sleep when e.g. reading out the IRQ status
|
||||||
* registers.
|
* registers.
|
||||||
* @irq_not_threaded: flag must be set if @can_sleep is set but the
|
|
||||||
* IRQs don't need to be threaded
|
|
||||||
* @read_reg: reader function for generic GPIO
|
* @read_reg: reader function for generic GPIO
|
||||||
* @write_reg: writer function for generic GPIO
|
* @write_reg: writer function for generic GPIO
|
||||||
* @pin2mask: some generic GPIO controllers work with the big-endian bits
|
* @pin2mask: some generic GPIO controllers work with the big-endian bits
|
||||||
|
@ -109,8 +107,10 @@ enum single_ended_mode {
|
||||||
* for GPIO IRQs, provided by GPIO driver
|
* for GPIO IRQs, provided by GPIO driver
|
||||||
* @irq_default_type: default IRQ triggering type applied during GPIO driver
|
* @irq_default_type: default IRQ triggering type applied during GPIO driver
|
||||||
* initialization, provided by GPIO driver
|
* initialization, provided by GPIO driver
|
||||||
* @irq_parent: GPIO IRQ chip parent/bank linux irq number,
|
* @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number,
|
||||||
* provided by GPIO driver
|
* provided by GPIO driver for chained interrupt (not for nested
|
||||||
|
* interrupts).
|
||||||
|
* @irq_nested: True if set the interrupt handling is nested.
|
||||||
* @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
|
* @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
|
||||||
* bits set to one
|
* bits set to one
|
||||||
* @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
|
* @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
|
||||||
|
@ -166,7 +166,6 @@ struct gpio_chip {
|
||||||
u16 ngpio;
|
u16 ngpio;
|
||||||
const char *const *names;
|
const char *const *names;
|
||||||
bool can_sleep;
|
bool can_sleep;
|
||||||
bool irq_not_threaded;
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
|
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
|
||||||
unsigned long (*read_reg)(void __iomem *reg);
|
unsigned long (*read_reg)(void __iomem *reg);
|
||||||
|
@ -192,7 +191,8 @@ struct gpio_chip {
|
||||||
unsigned int irq_base;
|
unsigned int irq_base;
|
||||||
irq_flow_handler_t irq_handler;
|
irq_flow_handler_t irq_handler;
|
||||||
unsigned int irq_default_type;
|
unsigned int irq_default_type;
|
||||||
int irq_parent;
|
int irq_chained_parent;
|
||||||
|
bool irq_nested;
|
||||||
bool irq_need_valid_mask;
|
bool irq_need_valid_mask;
|
||||||
unsigned long *irq_valid_mask;
|
unsigned long *irq_valid_mask;
|
||||||
struct lock_class_key *lock_key;
|
struct lock_class_key *lock_key;
|
||||||
|
@ -270,24 +270,40 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
||||||
int parent_irq,
|
int parent_irq,
|
||||||
irq_flow_handler_t parent_handler);
|
irq_flow_handler_t parent_handler);
|
||||||
|
|
||||||
|
void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip,
|
||||||
|
struct irq_chip *irqchip,
|
||||||
|
int parent_irq);
|
||||||
|
|
||||||
int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
unsigned int type,
|
unsigned int type,
|
||||||
|
bool nested,
|
||||||
struct lock_class_key *lock_key);
|
struct lock_class_key *lock_key);
|
||||||
|
|
||||||
|
/* FIXME: I assume threaded IRQchips do not have the lockdep problem */
|
||||||
|
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gpiochip,
|
||||||
|
struct irq_chip *irqchip,
|
||||||
|
unsigned int first_irq,
|
||||||
|
irq_flow_handler_t handler,
|
||||||
|
unsigned int type)
|
||||||
|
{
|
||||||
|
return _gpiochip_irqchip_add(gpiochip, irqchip, first_irq,
|
||||||
|
handler, type, true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
#define gpiochip_irqchip_add(...) \
|
#define gpiochip_irqchip_add(...) \
|
||||||
( \
|
( \
|
||||||
({ \
|
({ \
|
||||||
static struct lock_class_key _key; \
|
static struct lock_class_key _key; \
|
||||||
_gpiochip_irqchip_add(__VA_ARGS__, &_key); \
|
_gpiochip_irqchip_add(__VA_ARGS__, false, &_key); \
|
||||||
}) \
|
}) \
|
||||||
)
|
)
|
||||||
#else
|
#else
|
||||||
#define gpiochip_irqchip_add(...) \
|
#define gpiochip_irqchip_add(...) \
|
||||||
_gpiochip_irqchip_add(__VA_ARGS__, NULL)
|
_gpiochip_irqchip_add(__VA_ARGS__, false, NULL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* CONFIG_GPIOLIB_IRQCHIP */
|
#endif /* CONFIG_GPIOLIB_IRQCHIP */
|
||||||
|
|
Loading…
Reference in New Issue