Re: [PATCH v2 14/16] gpio: Add support for banked GPIO controllers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 09/28/2017 04:56 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@xxxxxxxxxx>
> 
> Some GPIO controllers are subdivided into multiple logical blocks called
> banks (or ports). This is often caused by the design assigning separate
> resources, such as register regions or interrupts, to each bank, or some
> set of banks.
> 
> This commit adds support for describing controllers that have such a
> banked design and provides common code for dealing with them.
> 
> Signed-off-by: Thierry Reding <treding@xxxxxxxxxx>
> ---
>   drivers/gpio/gpiolib-of.c   | 101 +++++++++++++++++++++++++++++++++++++++++
>   drivers/gpio/gpiolib.c      |  98 ++++++++++++++++++++++++++++++++++++++++
>   include/linux/gpio/driver.h | 108 ++++++++++++++++++++++++++++++++++++++++++++
>   include/linux/of_gpio.h     |  10 ++++
>   4 files changed, 317 insertions(+)
> 
> diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
> index bfcd20699ec8..9baabe00966d 100644
> --- a/drivers/gpio/gpiolib-of.c
> +++ b/drivers/gpio/gpiolib-of.c
> @@ -309,6 +309,107 @@ int of_gpio_simple_xlate(struct gpio_chip *gc,
>   }
>   EXPORT_SYMBOL(of_gpio_simple_xlate);
>   
> +/**
> + * gpio_banked_irq_domain_xlate - decode an IRQ specifier for banked chips
> + * @domain: IRQ domain
> + * @np: device tree node
> + * @spec: IRQ specifier
> + * @size: number of cells in IRQ specifier
> + * @hwirq: return location for the hardware IRQ number
> + * @type: return location for the IRQ type
> + *
> + * Translates the IRQ specifier found in device tree into a hardware IRQ
> + * number and an interrupt type.
> + *
> + * Returns:
> + * 0 on success or a negative error code on failure.
> + */
> +int gpio_banked_irq_domain_xlate(struct irq_domain *domain,
> +				 struct device_node *np,
> +				 const u32 *spec, unsigned int size,
> +				 unsigned long *hwirq,
> +				 unsigned int *type)
> +{
> +	struct gpio_chip *gc = domain->host_data;
> +	unsigned int bank, line, i, offset = 0;
> +
> +	if (size < 2)
> +		return -EINVAL;
> +
> +	bank = (spec[0] >> gc->of_gpio_bank_mask) & gc->of_gpio_bank_shift;
> +	line = (spec[0] >> gc->of_gpio_line_mask) & gc->of_gpio_line_shift;
> +
> +	if (bank >= gc->num_banks) {
> +		dev_err(gc->parent, "invalid bank number: %u\n", bank);
> +		return -EINVAL;
> +	}
> +
> +	if (line >= gc->banks[bank]->num_lines) {
> +		dev_err(gc->parent, "invalid line number: %u\n", line);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < bank; i++)
> +		offset += gc->banks[i]->num_lines;

Just to clarify, why is above iteration required?

> +
> +	*type = spec[1] & IRQ_TYPE_SENSE_MASK;
> +	*hwirq = offset + line;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(gpio_banked_irq_domain_xlate);
> +
> +/**
> + * of_gpio_banked_xlate - translate GPIO specifier to a GPIO number and flags
> + * @gc: GPIO chip
> + * @gpiospec: GPIO specifier
> + * @flags: return location for flags parsed from the GPIO specifier
> + *
> + * This translation function takes into account multiple banks that can make
> + * up a single controller. Each bank can contain one or more pins. A single
> + * cell in the specifier is used to represent a (bank, pin) pair, with each
> + * encoded in different fields. The &gpio_chip.of_gpio_bank_shift and
> + * &gpio_chip.of_gpio_bank_mask fields, and &gpio_chip.of_gpio_line_shift and
> + * &gpio_chip.of_gpio_line_mask are used to specify the encoding.
> + *
> + * Returns:
> + * The chip-relative index of the pin given by the GPIO specifier.
> + */
> +int of_gpio_banked_xlate(struct gpio_chip *gc,
> +			 const struct of_phandle_args *gpiospec, u32 *flags)
> +{
> +	unsigned int offset = 0, bank, line, i;
> +	const u32 *spec = gpiospec->args;
> +
> +	if (WARN_ON(gc->of_gpio_n_cells < 2))
> +		return -EINVAL;
> +
> +	if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
> +		return -EINVAL;
> +
> +	bank = (spec[0] >> gc->of_gpio_bank_shift) & gc->of_gpio_bank_mask;
> +	line = (spec[0] >> gc->of_gpio_line_shift) & gc->of_gpio_line_mask;
> +
> +	if (bank >= gc->num_banks) {
> +		dev_err(gc->parent, "invalid bank number: %u\n", bank);
> +		return -EINVAL;
> +	}
> +
> +	if (line >= gc->banks[bank]->num_lines) {
> +		dev_err(gc->parent, "invalid line number: %u\n", line);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < bank; i++)
> +		offset += gc->banks[i]->num_lines;
> +
> +	if (flags)
> +		*flags = spec[1];
> +
> +	return offset + line;
> +}
> +EXPORT_SYMBOL(of_gpio_banked_xlate);

Adding above two functions means adding new GPIO bindings (may be optional,
but common).

> +
>   /**
>    * of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank)
>    * @np:		device node of the GPIO chip
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index c15fb858848a..b3bd19b793d3 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -1765,6 +1765,57 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip)
>   	gpiochip->to_irq = gpiochip_to_irq;
>   	gpiochip->irq.default_type = type;
>   
> +	if (gpiochip->num_banks > 0 && !gpiochip->irq.map) {
> +		struct gpio_irq_chip *irq = &gpiochip->irq;
> +		unsigned int i, j, offset = 0;
> +
> +		if (!irq->parents) {
> +			chip_err(gpiochip, "no parent interrupts defined\n");
> +			return -EINVAL;
> +		}
> +
> +		irq->map = devm_kcalloc(gpiochip->parent, gpiochip->ngpio,
> +					sizeof(*irq->map), GFP_KERNEL);
> +		if (!irq->map)
> +			return -ENOMEM;
> +
> +		for (i = 0; i < gpiochip->num_banks; i++) {
> +			struct gpio_bank *bank = gpiochip->banks[i];
> +			unsigned int parent = bank->parent_irq;



> +
> +			for (j = 0; j < bank->num_lines; j++) {
> +				if (parent >= irq->num_parents) {
> +					chip_err(gpiochip,
> +						 "invalid parent interrupt: %u\n",
> +						 parent);
> +					return -EINVAL;
> +				}
> +
> +				irq->map[offset + j] = irq->parents[parent];
> +			}
> +
> +			offset += bank->num_lines;

Most of gpio drivers, you've listed in [1], have only one parent
(waste of memory). There should be way not to store it permanently.

> +		}
> +	}
> +
> +	if (gpiochip->num_banks > 0) {
> +		unsigned int i;
> +
> +		for (i = 0; i < gpiochip->num_banks; i++) {
> +			struct gpio_bank *bank = gpiochip->banks[i];
> +			unsigned int num_lines = bank->num_lines;
> +
> +			bank->pending = devm_kcalloc(gpiochip->parent,
> +						     BITS_TO_LONGS(num_lines),
> +						     sizeof(unsigned long),
> +						     GFP_KERNEL);
> +			if (!bank->pending)
> +				return -ENOMEM;
> +
> +			bank->chip = gpiochip;
> +		}
> +	}
> +
>   	if (gpiochip->irq.domain_ops)
>   		ops = gpiochip->irq.domain_ops;
>   	else
> @@ -1973,6 +2024,53 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
>   }
>   EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
>   
> +/**
> + * gpio_irq_chip_banked_chained_handler - interrupt handler for banked IRQ chips
> + * @desc: IRQ descriptor
> + *
> + * Drivers can use this interrupt handler for banked GPIO controllers. This
> + * implementation iterates over all banks and handles pending interrupts of
> + * the pins associated with the bank.
> + *
> + * This function uses driver specific parts, split out into the
> + * &gpio_chip.update_bank() callback, to retrieves the interrupt pending
> + * state for each of the GPIOs exposed by the given bank.
> + */
> +void gpio_irq_chip_banked_chained_handler(struct irq_desc *desc)
> +{
> +	struct gpio_chip *gpio = irq_desc_get_handler_data(desc);

As per Patch 1 - there are no restriction to use parent_handler_data with
this standard handler - in this case parent_handler_data might not be struct gpio_chip.

> +	struct irq_chip *irq = irq_desc_get_chip(desc);
> +	unsigned int parent = irq_desc_get_irq(desc);
> +	struct gpio_irq_chip *chip = &gpio->irq;
> +	unsigned int i, offset = 0;
> +
> +	chained_irq_enter(irq, desc);
> +
> +	for (i = 0; i < gpio->num_banks; i++) {
> +		struct gpio_bank *bank = gpio->banks[i];
> +		unsigned int line, virq;
> +
> +		if (parent != chip->parents[bank->parent_irq])
> +			goto skip;

You've used this handler in gpio-tegra.c. 
So for compatible = "nvidia,tegra20-gpio":
- there are will be 7 parent irqs/banks.
- gpiochip will be used as chained_handler data.

So, how will it work:
- for bank0 it will take 1 iteration to get correct bank structure
- but for bank7 - 7 iteration always (in hot path?)

> +
> +		chip->update_bank(bank);

Half of gpio drivers, you've listed in [1] required access to common
Interrupt status registers before proceeding to banks.

> +
> +		for_each_set_bit(line, bank->pending, bank->num_lines) {
> +			virq = irq_find_mapping(chip->domain, offset + line);
> +			if (WARN_ON(virq == 0))
> +				continue;

drivers might require to do additional action before/after generic_handle_irq()
(intel_mid_irq_handler())

> +
> +			generic_handle_irq(virq);
> +		}
> +
> +skip:
> +		offset += bank->num_lines;
> +	}
> +
> +	chained_irq_exit(irq, desc);
> +}
> +EXPORT_SYMBOL_GPL(gpio_irq_chip_banked_chained_handler);

chained IRQ handler is not RT friendly (no control from User space)

> +
>   #else /* CONFIG_GPIOLIB_IRQCHIP */
>   
>   static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip)
> diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
> index c453e0716228..3caa08b3d2b6 100644

[...]

>   
>   /**
> 



[1] https://www.spinics.net/lists/linux-tegra/msg31105.html

-- 
regards,
-grygorii
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux