[PATCH 2/4] pinctrl: single: Add hardware specific hooks for IRQ and GPIO wake-up events
Haojian Zhuang
haojian.zhuang at gmail.com
Sun Jun 9 14:46:02 EST 2013
On Sat, Jun 8, 2013 at 4:50 AM, Tony Lindgren <tony at atomide.com> wrote:
> At least on omaps, each board typically has at least one device
> configured as wake-up capable from deeper idle modes. In the
> deeper idle modes the normal interrupt wake-up path won't work
> as the logic is powered off and separate wake-up hardware is
> available either via IO ring or GPIO hardware. The wake-up
> event can be device specific, or may need to be dynamically
> remuxed to GPIO input for wake-up events. When the wake-up
> event happens, it's IRQ need to be called so the device won't
> lose interrupts.
>
> Allow supporting IRQ and GPIO wake-up events if a hardware
> spefific module is registered for the enable and disable
> calls.
>
> Done in collaboration with Roger Quadros <rogerq at ti.com>.
>
> Cc: Haojian Zhuang <haojian.zhuang at gmail.com>
> Cc: Peter Ujfalusi <peter.ujfalusi at ti.com>
> Cc: devicetree-discuss at lists.ozlabs.org
> Signed-off-by: Roger Quadros <rogerq at ti.com>
> Signed-off-by: Tony Lindgren <tony at atomide.com>
> ---
> .../devicetree/bindings/pinctrl/pinctrl-single.txt | 5 +
> drivers/pinctrl/pinctrl-single.c | 104 +++++++++++++++++---
> drivers/pinctrl/pinctrl-single.h | 28 +++++
> 3 files changed, 123 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> index 08f0c3d..5dfd74b 100644
> --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt
> @@ -68,6 +68,10 @@ Optional properties:
> The number of parameters is depend on #pinctrl-single,gpio-range-cells
> property.
>
> +- interrrupts : the interrupt that a function may have for a wake-up event
> +
> +- gpios: the gpio that a function may have for a wake-up event
> +
> /* pin base, nr pins & gpio function */
> pinctrl-single,gpio-range = <&range 0 3 0 &range 3 9 1>;
>
> @@ -204,6 +208,7 @@ pmx_gpio: pinmux at d401e000 {
> 0xdc 0x118
> 0xde 0
> >;
> + interrupts = <74>;
> };
> };
>
> diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
> index 0f178d1..7cb7940 100644
> --- a/drivers/pinctrl/pinctrl-single.c
> +++ b/drivers/pinctrl/pinctrl-single.c
> @@ -19,6 +19,8 @@
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/of_address.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_irq.h>
>
> #include <linux/pinctrl/pinctrl.h>
> #include <linux/pinctrl/pinmux.h>
> @@ -95,6 +97,8 @@ struct pcs_conf_type {
> * @nvals: number of entries in vals array
> * @pgnames: array of pingroup names the function uses
> * @npgnames: number of pingroup names the function uses
> + * @irq: optional irq associated with the function
> + * @gpio: optional gpio associated with the function
> * @node: list node
> */
> struct pcs_function {
> @@ -105,6 +109,8 @@ struct pcs_function {
> int npgnames;
> struct pcs_conf_vals *conf;
> int nconfs;
> + int irq;
> + int gpio;
> struct list_head node;
> };
>
> @@ -410,6 +416,18 @@ static int pcs_get_function(struct pinctrl_dev *pctldev, unsigned pin,
> return 0;
> }
>
> +static void pcs_reg_init(struct pcs_reg *p, struct pcs_device *pcs,
> + struct pcs_function *func,
> + void __iomem *reg, unsigned val)
> +{
> + p->read = pcs->read;
> + p->write = pcs->write;
> + p->irq = func->irq;
> + p->gpio = func->gpio;
> + p->reg = reg;
> + p->val = val;
> +}
> +
> static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,
> unsigned group)
> {
> @@ -442,6 +460,12 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector,
> val &= ~mask;
> val |= (vals->val & mask);
> pcs->write(val, vals->reg);
> + if ((func->irq || func->gpio) && pcs->soc && pcs->soc->enable) {
> + struct pcs_reg pcsr;
> +
> + pcs_reg_init(&pcsr, pcs, func, vals->reg, val);
> + pcs->soc->enable(pcs->soc, &pcsr);
> + }
> }
>
> return 0;
> @@ -466,18 +490,6 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector,
> return;
> }
>
> - /*
> - * Ignore disable if function-off is not specified. Some hardware
> - * does not have clearly defined disable function. For pin specific
> - * off modes, you can use alternate named states as described in
> - * pinctrl-bindings.txt.
> - */
> - if (pcs->foff == PCS_OFF_DISABLED) {
> - dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
> - func->name, fselector);
> - return;
> - }
> -
> dev_dbg(pcs->dev, "disabling function%i %s\n",
> fselector, func->name);
>
> @@ -488,8 +500,28 @@ static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector,
> vals = &func->vals[i];
> val = pcs->read(vals->reg);
> val &= ~pcs->fmask;
> - val |= pcs->foff << pcs->fshift;
> - pcs->write(val, vals->reg);
> +
> + /*
> + * Ignore disable if function-off is not specified. Some
> + * hardware does not have clearly defined disable function.
> + * For pin specific off modes, you can use alternate named
> + * states as described in pinctrl-bindings.txt.
> + */
> + if (pcs->foff == PCS_OFF_DISABLED) {
> + dev_dbg(pcs->dev, "ignoring disable for %s function%i\n",
> + func->name, fselector);
> + } else {
> + val |= pcs->foff << pcs->fshift;
> + pcs->write(val, vals->reg);
> + }
> +
> + if ((func->irq || func->gpio) &&
> + pcs->soc && pcs->soc->disable) {
> + struct pcs_reg pcsr;
> +
> + pcs_reg_init(&pcsr, pcs, func, vals->reg, val);
> + pcs->soc->disable(pcs->soc, &pcsr);
> + }
> }
> }
>
> @@ -1007,6 +1039,32 @@ static void pcs_add_conf4(struct pcs_device *pcs, struct device_node *np,
> add_setting(settings, param, ret);
> }
>
> +static int pcs_parse_wakeup(struct pcs_device *pcs, struct device_node *np,
> + struct pcs_function *function)
> +{
> + struct pcs_reg pcsr;
> + int i, ret = 0;
> +
> + for (i = 0; i < function->nvals; i++) {
> + struct pcs_func_vals *vals;
> + unsigned mask;
> +
> + vals = &function->vals[i];
> + if (!vals->mask)
> + mask = pcs->fmask;
> + else
> + mask = pcs->fmask & vals->mask;
I assume that this patch is used in both v1 & v2 version. Since Manjunathappa
changed the logic of distinguishing bits and pins in blew.
if (pcs->bits_per_mux)
mask = vals->mask;
else
mask = pcs->fmask
Would you like to sync with his style?
> +
> + pcs_reg_init(&pcsr, pcs, function, vals->reg,
> + vals->val & mask);
> + ret = pcs->soc->reg_init(pcs->soc, &pcsr);
> + if (ret)
> + break;
> + }
> +
> + return ret;
> +}
> +
> static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np,
> struct pcs_function *func,
> struct pinctrl_map **map)
> @@ -1176,6 +1234,24 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs,
> } else {
> *num_maps = 1;
> }
> +
> + if (pcs->flags & PCS_HAS_FUNCTION_IRQ)
> + function->irq = irq_of_parse_and_map(np, 0);
> +
> + if (pcs->flags & PCS_HAS_FUNCTION_GPIO) {
> + function->gpio = of_get_gpio(np, 0);
> + if (function->gpio > 0 && !function->irq) {
> + if (gpio_is_valid(function->gpio))
> + function->irq = gpio_to_irq(function->gpio);
> + }
> + }
> +
> + if (function->irq > 0 && pcs->soc && pcs->soc->reg_init) {
> + res = pcs_parse_wakeup(pcs, np, function);
> + if (res)
> + goto free_pingroups;
> + }
> +
> return 0;
>
> free_pingroups:
> diff --git a/drivers/pinctrl/pinctrl-single.h b/drivers/pinctrl/pinctrl-single.h
> index 18f3205..c2dcc7a 100644
> --- a/drivers/pinctrl/pinctrl-single.h
> +++ b/drivers/pinctrl/pinctrl-single.h
> @@ -1,13 +1,41 @@
> +/**
> + * struct pcs_reg - pinctrl register
> + * @read: pinctrl-single provided register read function
> + * @write: pinctrl-single provided register write function
> + * @reg: virtual address of a register
> + * @val: pinctrl configured value of the register
> + * @irq: optional irq specified for wake-up for example
> + * @gpio: optional gpio specified for wake-up for example
> + * @node: optional list
> + */
> +struct pcs_reg {
> + unsigned (*read)(void __iomem *reg);
> + void (*write)(unsigned val, void __iomem *reg);
> + void __iomem *reg;
> + unsigned val;
> + int irq;
> + int gpio;
> + struct list_head node;
> +};
> +
> +#define PCS_HAS_FUNCTION_GPIO (1 << 2)
> +#define PCS_HAS_FUNCTION_IRQ (1 << 1)
> #define PCS_HAS_PINCONF (1 << 0)
>
> /**
> * struct pcs_soc - SoC specific interface to pinctrl-single
> * @data: SoC specific data pointer
> * @flags: mask of PCS_HAS_xxx values
> + * @reg_init: SoC specific register init function
> + * @enable: SoC specific enable function
> + * @disable: SoC specific disable function
> */
> struct pcs_soc {
> void *data;
> unsigned flags;
> + int (*reg_init)(const struct pcs_soc *soc, struct pcs_reg *r);
> + int (*enable)(const struct pcs_soc *soc, struct pcs_reg *r);
> + void (*disable)(const struct pcs_soc *soc, struct pcs_reg *r);
> };
>
> extern int pinctrl_single_probe(struct platform_device *pdev,
>
For others: Acked-by: Haojian Zhuang <haojian.zhuang at gmail.com>
More information about the devicetree-discuss
mailing list