diff mbox series

[v2,4/4] pinctrl: bcm2835: Add support for wake-up interrupts

Message ID 20200529191522.27938-5-f.fainelli@gmail.com
State New
Headers show
Series pinctrl: bcm2835: Support for wake-up interrupts | expand

Commit Message

Florian Fainelli May 29, 2020, 7:15 p.m. UTC
Leverage the IRQCHIP_MASK_ON_SUSPEND flag in order to avoid having to
specifically treat the GPIO interrupts during suspend and resume, and
simply implement an irq_set_wake() callback that is responsible for
enabling the parent wake-up interrupt as a wake-up interrupt.

To avoid allocating unnecessary resources for other chips, the wake-up
interrupts are only initialized if we have a brcm,bcm7211-gpio
compatibility string.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/pinctrl/bcm/pinctrl-bcm2835.c | 76 ++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

Comments

Stefan Wahren May 30, 2020, 7:49 a.m. UTC | #1
Hi Florian,

Am 29.05.20 um 21:15 schrieb Florian Fainelli:
> Leverage the IRQCHIP_MASK_ON_SUSPEND flag in order to avoid having to
> specifically treat the GPIO interrupts during suspend and resume, and
> simply implement an irq_set_wake() callback that is responsible for
> enabling the parent wake-up interrupt as a wake-up interrupt.
>
> To avoid allocating unnecessary resources for other chips, the wake-up
> interrupts are only initialized if we have a brcm,bcm7211-gpio
> compatibility string.
>
> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
> ---
>  drivers/pinctrl/bcm/pinctrl-bcm2835.c | 76 ++++++++++++++++++++++++++-
>  1 file changed, 75 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> index 1b00d93aa66e..1fbf067a3eed 100644
> --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
> @@ -19,6 +19,7 @@
>  #include <linux/irq.h>
>  #include <linux/irqdesc.h>
>  #include <linux/init.h>
> +#include <linux/interrupt.h>
>  #include <linux/of_address.h>
>  #include <linux/of.h>
>  #include <linux/of_irq.h>
> @@ -76,6 +77,7 @@
>  struct bcm2835_pinctrl {
>  	struct device *dev;
>  	void __iomem *base;
> +	int *wake_irq;
>  
>  	/* note: locking assumes each bank will have its own unsigned long */
>  	unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
> @@ -435,6 +437,11 @@ static void bcm2835_gpio_irq_handler(struct irq_desc *desc)
>  	chained_irq_exit(host_chip, desc);
>  }
>  
> +static irqreturn_t bcm2835_gpio_wake_irq_handler(int irq, void *dev_id)
> +{
> +	return IRQ_HANDLED;
> +}
> +
>  static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
>  	unsigned reg, unsigned offset, bool enable)
>  {
> @@ -634,6 +641,34 @@ static void bcm2835_gpio_irq_ack(struct irq_data *data)
>  	bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
>  }
>  
> +static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
> +{
> +	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
> +	struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
> +	unsigned gpio = irqd_to_hwirq(data);
> +	unsigned int irqgroup;
> +	int ret = -EINVAL;
> +
> +	if (!pc->wake_irq)
> +		return ret;
> +
> +	if (gpio <= 27)
> +		irqgroup = 0;
> +	else if (gpio >= 28 && gpio <= 45)
> +		irqgroup = 1;
> +	else if (gpio >= 46 && gpio <= 53)
> +		irqgroup = 2;
in case the BCM7211 has 58 GPIOs, but the wake up interrupts are only
available for the first 54 this should deserve a comment.
> +	else
> +		return ret;
> +
> +	if (on)
> +		ret = enable_irq_wake(pc->wake_irq[irqgroup]);
> +	else
> +		ret = disable_irq_wake(pc->wake_irq[irqgroup]);
> +
> +	return ret;
> +}
> +
>  static struct irq_chip bcm2835_gpio_irq_chip = {
>  	.name = MODULE_NAME,
>  	.irq_enable = bcm2835_gpio_irq_enable,
> @@ -642,6 +677,8 @@ static struct irq_chip bcm2835_gpio_irq_chip = {
>  	.irq_ack = bcm2835_gpio_irq_ack,
>  	.irq_mask = bcm2835_gpio_irq_disable,
>  	.irq_unmask = bcm2835_gpio_irq_enable,
> +	.irq_set_wake = bcm2835_gpio_irq_set_wake,
> +	.flags = IRQCHIP_MASK_ON_SUSPEND,
>  };
>  
>  static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev)
> @@ -1154,6 +1191,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
>  	struct resource iomem;
>  	int err, i;
>  	const struct of_device_id *match;
> +	int is_7211 = 0;
>  
>  	BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_pins) != BCM2711_NUM_GPIOS);
>  	BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_groups) != BCM2711_NUM_GPIOS);
> @@ -1180,6 +1218,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
>  		return -EINVAL;
>  
>  	pdata = match->data;
> +	is_7211 = of_device_is_compatible(np, "brcm,bcm7211-gpio");
>  
>  	pc->gpio_chip = *pdata->gpio_chip;
>  	pc->gpio_chip.parent = dev;
> @@ -1214,6 +1253,15 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
>  				     GFP_KERNEL);
>  	if (!girq->parents)
>  		return -ENOMEM;
> +
> +	if (is_7211) {
> +		pc->wake_irq = devm_kcalloc(dev, BCM2835_NUM_IRQS,
> +					    sizeof(*pc->wake_irq),
> +					    GFP_KERNEL);
> +		if (!pc->wake_irq)
> +			return -ENOMEM;
> +	}
> +
>  	/*
>  	 * Use the same handler for all groups: this is necessary
>  	 * since we use one gpiochip to cover all lines - the
> @@ -1221,8 +1269,34 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
>  	 * bank that was firing the IRQ and look up the per-group
>  	 * and bank data.
>  	 */
> -	for (i = 0; i < BCM2835_NUM_IRQS; i++)
> +	for (i = 0; i < BCM2835_NUM_IRQS; i++) {
> +		int len;
> +		char *name;
> +
>  		girq->parents[i] = irq_of_parse_and_map(np, i);
> +		if (!is_7211)
> +			continue;
> +
> +		/* Skip over the all banks interrupts */
> +		pc->wake_irq[i] = irq_of_parse_and_map(np, i +
> +						       BCM2835_NUM_IRQS + 1);
> +
> +		len = strlen(dev_name(pc->dev)) + 16;
> +		name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
> +		if (!name)
> +			return -ENOMEM;
> +
> +		snprintf(name, len, "%s:bank%d", dev_name(pc->dev), i);
> +
> +		/* These are optional interrupts */
> +		err = devm_request_irq(dev, pc->wake_irq[i],
> +				       bcm2835_gpio_wake_irq_handler,
> +				       IRQF_SHARED, name, pc);
> +		if (err)
> +			dev_warn(dev, "unable to request wake IRQ %d\n",
> +				 pc->wake_irq[i]);
> +	}
> +
>  	girq->default_type = IRQ_TYPE_NONE;
>  	girq->handler = handle_level_irq;
>
Florian Fainelli May 30, 2020, 9:19 p.m. UTC | #2
On 5/30/2020 12:49 AM, Stefan Wahren wrote:
> Hi Florian,
> 
> Am 29.05.20 um 21:15 schrieb Florian Fainelli:
>> Leverage the IRQCHIP_MASK_ON_SUSPEND flag in order to avoid having to
>> specifically treat the GPIO interrupts during suspend and resume, and
>> simply implement an irq_set_wake() callback that is responsible for
>> enabling the parent wake-up interrupt as a wake-up interrupt.
>>
>> To avoid allocating unnecessary resources for other chips, the wake-up
>> interrupts are only initialized if we have a brcm,bcm7211-gpio
>> compatibility string.
>>
>> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
>> ---
>>  drivers/pinctrl/bcm/pinctrl-bcm2835.c | 76 ++++++++++++++++++++++++++-
>>  1 file changed, 75 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
>> index 1b00d93aa66e..1fbf067a3eed 100644
>> --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
>> +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
>> @@ -19,6 +19,7 @@
>>  #include <linux/irq.h>
>>  #include <linux/irqdesc.h>
>>  #include <linux/init.h>
>> +#include <linux/interrupt.h>
>>  #include <linux/of_address.h>
>>  #include <linux/of.h>
>>  #include <linux/of_irq.h>
>> @@ -76,6 +77,7 @@
>>  struct bcm2835_pinctrl {
>>  	struct device *dev;
>>  	void __iomem *base;
>> +	int *wake_irq;
>>  
>>  	/* note: locking assumes each bank will have its own unsigned long */
>>  	unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
>> @@ -435,6 +437,11 @@ static void bcm2835_gpio_irq_handler(struct irq_desc *desc)
>>  	chained_irq_exit(host_chip, desc);
>>  }
>>  
>> +static irqreturn_t bcm2835_gpio_wake_irq_handler(int irq, void *dev_id)
>> +{
>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
>>  	unsigned reg, unsigned offset, bool enable)
>>  {
>> @@ -634,6 +641,34 @@ static void bcm2835_gpio_irq_ack(struct irq_data *data)
>>  	bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
>>  }
>>  
>> +static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
>> +{
>> +	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
>> +	struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
>> +	unsigned gpio = irqd_to_hwirq(data);
>> +	unsigned int irqgroup;
>> +	int ret = -EINVAL;
>> +
>> +	if (!pc->wake_irq)
>> +		return ret;
>> +
>> +	if (gpio <= 27)
>> +		irqgroup = 0;
>> +	else if (gpio >= 28 && gpio <= 45)
>> +		irqgroup = 1;
>> +	else if (gpio >= 46 && gpio <= 53)
>> +		irqgroup = 2;
> in case the BCM7211 has 58 GPIOs, but the wake up interrupts are only
> available for the first 54 this should deserve a comment.

irqgroup 2 covers GPIOs 46 through 57, thanks for noticing. Do you have
more comments before I spin a v3? Thank you for reviewing.
Stefan Wahren May 30, 2020, 11:30 p.m. UTC | #3
Hi Florian,

Am 30.05.20 um 23:19 schrieb Florian Fainelli:
>
> On 5/30/2020 12:49 AM, Stefan Wahren wrote:
>> Hi Florian,
>>
>> Am 29.05.20 um 21:15 schrieb Florian Fainelli:
>>>  }
>>>  
>>> +static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
>>> +{
>>> +	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
>>> +	struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
>>> +	unsigned gpio = irqd_to_hwirq(data);
>>> +	unsigned int irqgroup;
>>> +	int ret = -EINVAL;
>>> +
>>> +	if (!pc->wake_irq)
>>> +		return ret;
>>> +
>>> +	if (gpio <= 27)
>>> +		irqgroup = 0;
>>> +	else if (gpio >= 28 && gpio <= 45)
>>> +		irqgroup = 1;
>>> +	else if (gpio >= 46 && gpio <= 53)
>>> +		irqgroup = 2;
>> in case the BCM7211 has 58 GPIOs, but the wake up interrupts are only
>> available for the first 54 this should deserve a comment.
> irqgroup 2 covers GPIOs 46 through 57, thanks for noticing. Do you have
> more comments before I spin a v3? Thank you for reviewing.

no, i don't.

Regards
Stefan
diff mbox series

Patch

diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index 1b00d93aa66e..1fbf067a3eed 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -19,6 +19,7 @@ 
 #include <linux/irq.h>
 #include <linux/irqdesc.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/of_address.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -76,6 +77,7 @@ 
 struct bcm2835_pinctrl {
 	struct device *dev;
 	void __iomem *base;
+	int *wake_irq;
 
 	/* note: locking assumes each bank will have its own unsigned long */
 	unsigned long enabled_irq_map[BCM2835_NUM_BANKS];
@@ -435,6 +437,11 @@  static void bcm2835_gpio_irq_handler(struct irq_desc *desc)
 	chained_irq_exit(host_chip, desc);
 }
 
+static irqreturn_t bcm2835_gpio_wake_irq_handler(int irq, void *dev_id)
+{
+	return IRQ_HANDLED;
+}
+
 static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc,
 	unsigned reg, unsigned offset, bool enable)
 {
@@ -634,6 +641,34 @@  static void bcm2835_gpio_irq_ack(struct irq_data *data)
 	bcm2835_gpio_set_bit(pc, GPEDS0, gpio);
 }
 
+static int bcm2835_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
+	struct bcm2835_pinctrl *pc = gpiochip_get_data(chip);
+	unsigned gpio = irqd_to_hwirq(data);
+	unsigned int irqgroup;
+	int ret = -EINVAL;
+
+	if (!pc->wake_irq)
+		return ret;
+
+	if (gpio <= 27)
+		irqgroup = 0;
+	else if (gpio >= 28 && gpio <= 45)
+		irqgroup = 1;
+	else if (gpio >= 46 && gpio <= 53)
+		irqgroup = 2;
+	else
+		return ret;
+
+	if (on)
+		ret = enable_irq_wake(pc->wake_irq[irqgroup]);
+	else
+		ret = disable_irq_wake(pc->wake_irq[irqgroup]);
+
+	return ret;
+}
+
 static struct irq_chip bcm2835_gpio_irq_chip = {
 	.name = MODULE_NAME,
 	.irq_enable = bcm2835_gpio_irq_enable,
@@ -642,6 +677,8 @@  static struct irq_chip bcm2835_gpio_irq_chip = {
 	.irq_ack = bcm2835_gpio_irq_ack,
 	.irq_mask = bcm2835_gpio_irq_disable,
 	.irq_unmask = bcm2835_gpio_irq_enable,
+	.irq_set_wake = bcm2835_gpio_irq_set_wake,
+	.flags = IRQCHIP_MASK_ON_SUSPEND,
 };
 
 static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev)
@@ -1154,6 +1191,7 @@  static int bcm2835_pinctrl_probe(struct platform_device *pdev)
 	struct resource iomem;
 	int err, i;
 	const struct of_device_id *match;
+	int is_7211 = 0;
 
 	BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_pins) != BCM2711_NUM_GPIOS);
 	BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_groups) != BCM2711_NUM_GPIOS);
@@ -1180,6 +1218,7 @@  static int bcm2835_pinctrl_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	pdata = match->data;
+	is_7211 = of_device_is_compatible(np, "brcm,bcm7211-gpio");
 
 	pc->gpio_chip = *pdata->gpio_chip;
 	pc->gpio_chip.parent = dev;
@@ -1214,6 +1253,15 @@  static int bcm2835_pinctrl_probe(struct platform_device *pdev)
 				     GFP_KERNEL);
 	if (!girq->parents)
 		return -ENOMEM;
+
+	if (is_7211) {
+		pc->wake_irq = devm_kcalloc(dev, BCM2835_NUM_IRQS,
+					    sizeof(*pc->wake_irq),
+					    GFP_KERNEL);
+		if (!pc->wake_irq)
+			return -ENOMEM;
+	}
+
 	/*
 	 * Use the same handler for all groups: this is necessary
 	 * since we use one gpiochip to cover all lines - the
@@ -1221,8 +1269,34 @@  static int bcm2835_pinctrl_probe(struct platform_device *pdev)
 	 * bank that was firing the IRQ and look up the per-group
 	 * and bank data.
 	 */
-	for (i = 0; i < BCM2835_NUM_IRQS; i++)
+	for (i = 0; i < BCM2835_NUM_IRQS; i++) {
+		int len;
+		char *name;
+
 		girq->parents[i] = irq_of_parse_and_map(np, i);
+		if (!is_7211)
+			continue;
+
+		/* Skip over the all banks interrupts */
+		pc->wake_irq[i] = irq_of_parse_and_map(np, i +
+						       BCM2835_NUM_IRQS + 1);
+
+		len = strlen(dev_name(pc->dev)) + 16;
+		name = devm_kzalloc(pc->dev, len, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+
+		snprintf(name, len, "%s:bank%d", dev_name(pc->dev), i);
+
+		/* These are optional interrupts */
+		err = devm_request_irq(dev, pc->wake_irq[i],
+				       bcm2835_gpio_wake_irq_handler,
+				       IRQF_SHARED, name, pc);
+		if (err)
+			dev_warn(dev, "unable to request wake IRQ %d\n",
+				 pc->wake_irq[i]);
+	}
+
 	girq->default_type = IRQ_TYPE_NONE;
 	girq->handler = handle_level_irq;