Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 336352
b: refs/heads/master
c: f23f151
h: refs/heads/master
v: v3
  • Loading branch information
Shiraz Hashim authored and Linus Walleij committed Nov 11, 2012
1 parent ae2cd34 commit 7b06d81
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7e10ee68f8ccc62e0934ff02f39ce541f3879844
refs/heads/master: f23f1516b6757c326cc638bed8c402c77e2a596e
36 changes: 36 additions & 0 deletions trunk/Documentation/devicetree/bindings/gpio/gpio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,40 @@ Example of two SOC GPIO banks defined as gpio-controller nodes:
gpio-controller;
};

2.1) gpio-controller and pinctrl subsystem
------------------------------------------

gpio-controller on a SOC might be tightly coupled with the pinctrl
subsystem, in the sense that the pins can be used by other functions
together with optional gpio feature.

While the pin allocation is totally managed by the pin ctrl subsystem,
gpio (under gpiolib) is still maintained by gpio drivers. It may happen
that different pin ranges in a SoC is managed by different gpio drivers.

This makes it logical to let gpio drivers announce their pin ranges to
the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
request the corresponding pin before any gpio usage.

For this, the gpio controller can use a pinctrl phandle and pins to
announce the pinrange to the pin ctrl subsystem. For example,

qe_pio_e: gpio-controller@1460 {
#gpio-cells = <2>;
compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
reg = <0x1460 0x18>;
gpio-controller;
gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>;

}

where,
&pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.

Next values specify the base pin and number of pins for the range
handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled
by this gpio controller.

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
42 changes: 42 additions & 0 deletions trunk/Documentation/gpio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,48 @@ slower clock delays the rising edge of SCK, and the I2C master adjusts its
signaling rate accordingly.


GPIO controllers and the pinctrl subsystem
------------------------------------------

A GPIO controller on a SOC might be tightly coupled with the pinctrl
subsystem, in the sense that the pins can be used by other functions
together with an optional gpio feature. We have already covered the
case where e.g. a GPIO controller need to reserve a pin or set the
direction of a pin by calling any of:

pinctrl_request_gpio()
pinctrl_free_gpio()
pinctrl_gpio_direction_input()
pinctrl_gpio_direction_output()

But how does the pin control subsystem cross-correlate the GPIO
numbers (which are a global business) to a certain pin on a certain
pin controller?

This is done by registering "ranges" of pins, which are essentially
cross-reference tables. These are described in
Documentation/pinctrl.txt

While the pin allocation is totally managed by the pinctrl subsystem,
gpio (under gpiolib) is still maintained by gpio drivers. It may happen
that different pin ranges in a SoC is managed by different gpio drivers.

This makes it logical to let gpio drivers announce their pin ranges to
the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order
to request the corresponding pin to be prepared by the pinctrl subsystem
before any gpio usage.

For this, the gpio controller can register its pin range with pinctrl
subsystem. There are two ways of doing it currently: with or without DT.

For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt.

For non-DT support, user can call gpiochip_add_pin_range() with appropriate
parameters to register a range of gpio pins with a pinctrl driver. For this
exact name string of pinctrl device has to be passed as one of the
argument to this routine.


What do these conventions omit?
===============================
One of the biggest things these conventions omit is pin multiplexing, since
Expand Down
3 changes: 3 additions & 0 deletions trunk/Documentation/pinctrl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ will get an pin number into its handled number range. Further it is also passed
the range ID value, so that the pin controller knows which range it should
deal with.

Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see
section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind
pinctrl and gpio drivers.

PINMUX interfaces
=================
Expand Down
56 changes: 56 additions & 0 deletions trunk/drivers/gpio/gpiolib-of.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>

/* Private data structure for of_gpiochip_find_and_xlate */
Expand Down Expand Up @@ -216,6 +217,58 @@ int of_mm_gpiochip_add(struct device_node *np,
}
EXPORT_SYMBOL(of_mm_gpiochip_add);

#ifdef CONFIG_PINCTRL
void of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
struct device_node *np = chip->of_node;
struct gpio_pin_range *pin_range;
struct of_phandle_args pinspec;
int index = 0, ret;

if (!np)
return;

do {
ret = of_parse_phandle_with_args(np, "gpio-ranges",
"#gpio-range-cells", index, &pinspec);
if (ret)
break;

pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range),
GFP_KERNEL);
if (!pin_range) {
pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
chip->label);
break;
}

pin_range->range.name = chip->label;
pin_range->range.base = chip->base;
pin_range->range.pin_base = pinspec.args[0];
pin_range->range.npins = pinspec.args[1];
pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np,
&pin_range->range);

list_add_tail(&pin_range->node, &chip->pin_ranges);

} while (index++);
}

void of_gpiochip_remove_pin_range(struct gpio_chip *chip)
{
struct gpio_pin_range *pin_range, *tmp;

list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
list_del(&pin_range->node);
pinctrl_remove_gpio_range(pin_range->pctldev,
&pin_range->range);
}
}
#else
void of_gpiochip_add_pin_range(struct gpio_chip *chip) {}
void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {}
#endif

void of_gpiochip_add(struct gpio_chip *chip)
{
if ((!chip->of_node) && (chip->dev))
Expand All @@ -229,11 +282,14 @@ void of_gpiochip_add(struct gpio_chip *chip)
chip->of_xlate = of_gpio_simple_xlate;
}

of_gpiochip_add_pin_range(chip);
of_node_get(chip->of_node);
}

void of_gpiochip_remove(struct gpio_chip *chip)
{
of_gpiochip_remove_pin_range(chip);

if (chip->of_node)
of_node_put(chip->of_node);
}
43 changes: 43 additions & 0 deletions trunk/drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,10 @@ int gpiochip_add(struct gpio_chip *chip)
}
}

#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&chip->pin_ranges);
#endif

of_gpiochip_add(chip);

unlock:
Expand Down Expand Up @@ -1180,6 +1184,45 @@ struct gpio_chip *gpiochip_find(void *data,
}
EXPORT_SYMBOL_GPL(gpiochip_find);

#ifdef CONFIG_PINCTRL
void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int pin_base, unsigned int npins)
{
struct gpio_pin_range *pin_range;

pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL);
if (!pin_range) {
pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
chip->label);
return;
}

pin_range->range.name = chip->label;
pin_range->range.base = chip->base;
pin_range->range.pin_base = pin_base;
pin_range->range.npins = npins;
pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name,
&pin_range->range);

list_add_tail(&pin_range->node, &chip->pin_ranges);
}

void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
struct gpio_pin_range *pin_range, *tmp;

list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
list_del(&pin_range->node);
pinctrl_remove_gpio_range(pin_range->pctldev,
&pin_range->range);
}
}
#else
void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int pin_base, unsigned int npins) {}
void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {}
#endif

/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
Expand Down
13 changes: 13 additions & 0 deletions trunk/drivers/pinctrl/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
}
EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges);

struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range)
{
struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname);

if (!pctldev)
return NULL;

pinctrl_add_gpio_range(pctldev, range);
return pctldev;
}
EXPORT_SYMBOL_GPL(find_pinctrl_and_add_gpio_range);

/**
* pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller
* @pctldev: pin controller device to remove the range from
Expand Down
13 changes: 13 additions & 0 deletions trunk/drivers/pinctrl/devicetree.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np)
return NULL;
}

struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np,
struct pinctrl_gpio_range *range)
{
struct pinctrl_dev *pctldev;

pctldev = find_pinctrl_by_of_node(np);
if (!pctldev)
return NULL;

pinctrl_add_gpio_range(pctldev, range);
return pctldev;
}

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
struct device_node *np_config)
{
Expand Down
25 changes: 25 additions & 0 deletions trunk/include/asm-generic/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/pinctrl/pinctrl.h>

#ifdef CONFIG_GPIOLIB

Expand Down Expand Up @@ -47,6 +48,21 @@ struct seq_file;
struct module;
struct device_node;

#ifdef CONFIG_PINCTRL
/**
* struct gpio_pin_range - pin range controlled by a gpio chip
* @head: list for maintaining set of pin ranges, used internally
* @pctldev: pinctrl device which handles corresponding pins
* @range: actual range of pins controlled by a gpio controller
*/

struct gpio_pin_range {
struct list_head node;
struct pinctrl_dev *pctldev;
struct pinctrl_gpio_range range;
};
#endif

/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
Expand Down Expand Up @@ -134,6 +150,15 @@ struct gpio_chip {
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
#ifdef CONFIG_PINCTRL
/*
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
* describe the actual pin range which they serve in an SoC. This
* information would be used by pinctrl subsystem to configure
* corresponding pins for gpio usage.
*/
struct list_head pin_ranges;
#endif
};

extern const char *gpiochip_is_requested(struct gpio_chip *chip,
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ static inline int irq_to_gpio(unsigned irq)
return -EINVAL;
}

void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int pin_base, unsigned int npins);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
#endif

#endif /* __LINUX_GPIO_H */
17 changes: 17 additions & 0 deletions trunk/include/linux/pinctrl/pinctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@ extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
unsigned nranges);
extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);

extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);

#ifdef CONFIG_OF
extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np,
struct pinctrl_gpio_range *range);
#else
static inline
struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np,
struct pinctrl_gpio_range *range)
{
return NULL;
}

#endif /* CONFIG_OF */

extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev);
#else
Expand Down

0 comments on commit 7b06d81

Please sign in to comment.