[PATCH v1 2/5] pinctrl: st: Add Interrupt support.

Linus Walleij linus.walleij at linaro.org
Wed Jan 15 09:15:34 EST 2014


On Tue, Jan 14, 2014 at 3:52 PM,  <srinivas.kandagatla at st.com> wrote:

> ST Pincontroller GPIO bank can have one of the two possible types of
> interrupt-wirings.

Interesting :-)

> +Pin controller node:
> +Required properties:
>  - compatible   : should be "st,<SOC>-<pio-block>-pinctrl"
>         like st,stih415-sbc-pinctrl, st,stih415-front-pinctrl and so on.
> -- gpio-controller : Indicates this device is a GPIO controller
> -- #gpio-cells    : Should be one. The first cell is the pin number.
> +- st,syscfg            : Should be a phandle of the syscfg node.

So why do you add this? This is totally unused by your driver.

>  - st,retime-pin-mask   : Should be mask to specify which pins can be retimed.
>         If the property is not present, it is assumed that all the pins in the
>         bank are capable of retiming. Retiming is mainly used to improve the
>         IO timing margins of external synchronous interfaces.
> -- st,bank-name         : Should be a name string for this bank as
> -                       specified in datasheet.

Why do you have this property? The driver is not using it.

And what is wrong with just using that name for the node?

> -- st,syscfg            : Should be a phandle of the syscfg node.
> +- ranges       : specifies the ranges for the pin controller memory.

And what are they used for? I've never seen this before.

> +Optional properties:
> +- interrupts   : Interrupt number of the irqmux. If the interrupt is shared
> +  with other gpio banks via irqmux.
> +  a irqline and gpio banks.
> +- reg          : irqmux memory resource. If irqmux is present.
> +- reg-names    : irqmux resource should be named as "irqmux".
> +
> +GPIO controller node.
> +Required properties:
> +- gpio-controller : Indicates this device is a GPIO controller
> +- #gpio-cells    : Should be one. The first cell is the pin number.
> +- st,bank-name   : Should be a name string for this bank as specified in
> +  datasheet.

Again, why?

> +Optional properties:
> +- interrupts   : Interrupt number for this gpio bank. If there is a dedicated
> +  interrupt wired up for this gpio bank.
> +
> +- interrupt-controller : Indicates this device is a interrupt controller. GPIO
> +  bank can be an interrupt controller iff one of the interrupt type either via
> +irqmux or a dedicated interrupt per bank is specified.
> +
> +- #interrupt-cells: the value of this property should be 2.
> +     - First Cell: represents the external gpio interrupt number local to the
> +       external gpio interrupt space of the controller.
> +     - Second Cell: flags to identify the type of the interrupt
> +       - 1 = rising edge triggered
> +       - 2 = falling edge triggered
> +       - 3 = rising and falling edge triggered
> +       - 4 = high level triggered
> +       - 8 = low level triggered

Correct, but reference symbols from:
include/dt-bindings/interrupt-controller/irq.h
in example.


>
>  Example:
>         pin-controller-sbc {

Please put in an updated example making use of all the
new props.


(...)
> diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c

> @@ -271,6 +276,8 @@ struct st_gpio_bank {
>         struct pinctrl_gpio_range       range;
>         void __iomem                    *base;
>         struct st_pio_control           pc;
> +       struct  irq_domain              *domain;
> +       int                             gpio_irq;

Why are you putting this IRQ into the state container when it can be
a function local variable in probe()?

>  struct st_pinctrl {
> @@ -284,6 +291,8 @@ struct st_pinctrl {
>         int                             ngroups;
>         struct regmap                   *regmap;
>         const struct st_pctl_data       *data;
> +       void __iomem                    *irqmux_base;
> +       int                             irqmux_irq;

Dito. I think.

> +static int st_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> +       struct st_gpio_bank *bank = gpio_chip_to_bank(chip);
> +       int virq;

Just name this variable "irq". It is no more virtual than any other
IRQ.

> +
> +       if (bank->domain && (offset < chip->ngpio))
> +               virq = irq_create_mapping(bank->domain, offset);

No, don't do the create_mapping() call in the .to_irq() function.
Call irq_create_mapping() for *all* valid hwirqs in probe() and use
irq_find_mapping() here.

> +static void st_gpio_irq_disable(struct irq_data *d)
> +{
> +       struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d);
> +
> +       writel(BIT(d->hwirq), bank->base + REG_PIO_CLR_PMASK);
> +}
> +
> +static void st_gpio_irq_enable(struct irq_data *d)
> +{
> +       struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d);
> +
> +       writel(BIT(d->hwirq), bank->base + REG_PIO_SET_PMASK);
> +}

I *strongly* suspect that these two should be replaced with
_mask()/_unmask() rather than using disable/enable.

Because that seems to be what they are doing.

(...)
> +static void __gpio_irq_handler(struct st_gpio_bank *bank)
> +{
> +       unsigned long port_in, port_mask, port_comp, port_active;
> +       int n;
> +
> +       port_in = readl(bank->base + REG_PIO_PIN);
> +       port_comp = readl(bank->base + REG_PIO_PCOMP);
> +       port_mask = readl(bank->base + REG_PIO_PMASK);
> +
> +       port_active = (port_in ^ port_comp) & port_mask;
> +
> +       for_each_set_bit(n, &port_active, BITS_PER_LONG) {
> +               generic_handle_irq(irq_find_mapping(bank->domain, n));

So what happens if new IRQs appear in the register while you are
inside this loop?

Check this recent patch:
http://marc.info/?l=linux-arm-kernel&m=138979164119464&w=2

Especially this:

+ for (;;) {
+     mask = ioread8(host->base + CLRHILVINT) & 0xff;
+     mask |= (ioread8(host->base + SECOINT) & SECOINT_MASK) << 8;
+     mask |= (ioread8(host->base + PRIMINT) & PRIMINT_MASK) << 8;
+     mask &= host->irq_high_enabled | (host->irq_sys_enabled << 8);
+     if (mask == 0)
+         break;
+     for_each_set_bit(n, &mask, BITS_PER_LONG)
+     generic_handle_irq(irq_find_mapping(host->domain, n));
+ }


> +static struct irq_chip st_gpio_irqchip = {
> +       .name           = "GPIO",
> +       .irq_disable    = st_gpio_irq_disable,
> +       .irq_mask       = st_gpio_irq_disable,
> +       .irq_unmask     = st_gpio_irq_enable,

Just implement mask/unmask as indicated earlier.

> +       .irq_set_type   = st_gpio_irq_set_type,
> +};

You need to mark IRQ GPIO lines as used for IRQ.

Add startup() and shutdown() hooks similar to those I add
in this patch:

http://marc.info/?l=linux-gpio&m=138977709114785&w=2

> +static int st_gpio_irq_domain_xlate(struct irq_domain *d,
> +       struct device_node *ctrlr, const u32 *intspec, unsigned int intsize,
> +       irq_hw_number_t *out_hwirq, unsigned int *out_type)
> +{
> +       struct st_gpio_bank *bank = d->host_data;
> +       int ret;
> +       int pin = bank->gpio_chip.base + intspec[0];
> +
> +       if (WARN_ON(intsize < 2))
> +               return -EINVAL;
> +
> +       *out_hwirq = intspec[0];
> +       *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
> +
> +       ret = gpio_request(pin, ctrlr->full_name);
> +       if (ret)
> +               return ret;
> +
> +       ret = gpio_direction_input(pin);
> +       if (ret)
> +               return ret;

We recently concluded that you should *NOT* call
gpio_request() and gpio_direction_input() from xlate or similar.

Instead: set up the hardware directly in mask/unmask callbacks
so that the irqchip can trigger IRQs directly without any
interaction with the GPIO subsystem.

By implementing the startup/shutdown hooks as indicated
above you can still indicate to the GPIO subsystem what is
going on and it will enforce that e.g. the line is not set to
output.

>  static int st_gpiolib_register_bank(struct st_pinctrl *info,
> @@ -1219,7 +1378,7 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info,
>         struct pinctrl_gpio_range *range = &bank->range;
>         struct device *dev = info->dev;
>         int bank_num = of_alias_get_id(np, "gpio");
> -       struct resource res;
> +       struct resource res, irq_res;
>         int err;
>
>         if (of_address_to_resource(np, 0, &res))
> @@ -1248,6 +1407,43 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info,
>         }
>         dev_info(dev, "%s bank added.\n", range->name);
>
> +       /**
> +        * GPIO bank can have one of the two possible types of
> +        * interrupt-wirings.
> +        *
> +        * First type is via irqmux, single interrupt is used by multiple
> +        * gpio banks. This reduces number of overall interrupts numbers
> +        * required. All these banks belong to a single pincontroller.
> +        *                _________
> +        *               |         |----> [gpio-bank (n)    ]
> +        *               |         |----> [gpio-bank (n + 1)]
> +        *      [irqN]-- | irq-mux |----> [gpio-bank (n + 2)]
> +        *               |         |----> [gpio-bank (...  )]
> +        *               |_________|----> [gpio-bank (n + 7)]
> +        *
> +        * Second type has a dedicated interrupt per each gpio bank.
> +        *
> +        *      [irqN]----> [gpio-bank (n)]
> +        */
> +
> +       if (!of_irq_to_resource(np, 0, &irq_res)) {
> +               bank->gpio_irq = irq_res.start;
> +               irq_set_chained_handler(bank->gpio_irq, st_gpio_irq_handler);
> +               irq_set_handler_data(bank->gpio_irq, bank);
> +       }
> +
> +       if (info->irqmux_irq > 0 || bank->gpio_irq > 0) {
> +               /* Setup IRQ domain */
> +               bank->domain  = irq_domain_add_linear(np,
> +                                               ST_GPIO_PINS_PER_BANK,
> +                                               &st_gpio_irq_ops, bank);
> +               if (!bank->domain)
> +                       dev_err(dev, "Failed to add irq domain for [%s]\n",
> +                               np->full_name);
> +       } else {
> +               dev_info(dev, "No IRQ support for [%s] bank\n", np->full_name);

Why is the [bank name] inside [brackets]?

Yours,
Linus Walleij



More information about the linux-arm-kernel mailing list