2019-05-07 20:42:01

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 00/11] Support wakeup capable GPIOs

Hi all,

This is a re-spin of the wakeup capable GPIO support for QCOM SoCs.
The earlier version of the patch revision 4, was published [1] and had
some good discussions. The comments from the review have also been
addressed and the code rebased on top of 5.1 in this spin. There a few
changes in this spin:
- Review comments from Stephen, Marc
- Bug fixes in irqdomain-map
- Fix invalid interrupt case
- Update documentation
- Attempt generalizing gpiochip_to_irq() for hierarchical domain

In patch v4, we were discussing about the IRQ type of GPIO defaulting to
IRQ_TYPE_NONE (as is was the custom for older implementation). In the
SDM845 SoC select GPIOs are routed to an always-on interrupt controller
called the PDC and then to the GIC.

Wakeup capabable:
GPIO ---> PDC ------> GIC

Requesting a GPIO as an interrupt through the gpio_to_irq() call would
setup an interrupt hierarchy as above and return the linux interrupt
number. However, since the trigger type of the GPIO is unknown at this
time, gpiolib defaults to IRQ_TYPE_NONE. This triggers a warning at the
GIC, which expects a valid trigger type be set correctly in the fwspec.
The solution to this problem is still at large and I would like to
solicit feedback on this.

Appreciate your time.

Thanks,
Lina

[1]. https://patchwork.kernel.org/cover/10851807/

Lina Iyer (9):
gpio: allow gpio_to_irq to use OF variants for gpiochips
irqdomain: add bus token DOMAIN_BUS_WAKEUP
of: irq: document properties for wakeup interrupt parent
drivers: irqchip: add PDC irqdomain for wakeup capable GPIOs
dt-bindings: sdm845-pinctrl: add wakeup interrupt parent for GPIO
drivers: pinctrl: msm: setup GPIO irqchip hierarchy
arm64: dts: qcom: add PDC interrupt controller for SDM845
arm64: defconfig: enable PDC interrupt controller for Qualcomm SDM845
arm64: dts: qcom: setup PDC as wakeup parent for GPIOs for SDM845

Stephen Boyd (1):
of: irq: add helper to remap interrupts to another irqdomain

Thierry Reding (1):
gpio: Add support for hierarchical IRQ domains

.../interrupt-controller/interrupts.txt | 54 +++++++
.../bindings/pinctrl/qcom,sdm845-pinctrl.txt | 79 +++++++++-
arch/arm64/boot/dts/qcom/sdm845.dtsi | 88 +++++++++++
arch/arm64/configs/defconfig | 1 +
drivers/gpio/gpiolib.c | 28 +++-
drivers/irqchip/qcom-pdc.c | 98 +++++++++++--
drivers/of/irq.c | 129 +++++++++++++++++
drivers/pinctrl/qcom/pinctrl-msm.c | 137 +++++++++++++++---
include/linux/gpio/driver.h | 6 +
include/linux/irqdomain.h | 1 +
include/linux/of_irq.h | 1 +
include/linux/soc/qcom/irq.h | 25 ++++
12 files changed, 610 insertions(+), 37 deletions(-)
create mode 100644 include/linux/soc/qcom/irq.h

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2019-05-07 20:42:05

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 02/11] gpio: allow gpio_to_irq to use OF variants for gpiochips

The chip may define additional information in the DT that may be useful
for translating and allocting a linux interrupt for the GPIO. When
drivers do not specify a .to_irq function, the gpiolib defaults to
gpiochip_to_irq() function. The defalt function uses creates an IRQ
without referencing the OF definition of the gpiochip. Let's add this
OF support to the default gpiochip_to_irq function.

When requesting an interrupt for the GPIO, let's stick to IRQ_TYPE_NONE
for the trigger type, for we don't have the information what trigger
type the driver may set when requesting the IRQ.

Signed-off-by: Lina Iyer <[email protected]>
---
drivers/gpio/gpiolib.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4a9a6d4afe6e..77317435e2b2 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1825,6 +1825,19 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate);

static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
{
+#ifdef CONFIG_OF_GPIO
+ struct irq_fwspec fwspec;
+
+ if (chip->of_node) {
+ fwspec.fwnode = of_node_to_fwnode(chip->of_node);
+ fwspec.param[0] = offset;
+ fwspec.param[1] = IRQ_TYPE_NONE;
+ fwspec.param_count = 2;
+
+ return irq_create_fwspec_mapping(&fwspec);
+ }
+#endif
+
if (!gpiochip_irqchip_irq_valid(chip, offset))
return -ENXIO;

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:17

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 05/11] of: irq: add helper to remap interrupts to another irqdomain

From: Stephen Boyd <[email protected]>

Sometimes interrupts are routed from an interrupt controller to another
in no specific order. Having these in the drivers makes it difficult to
maintain when the same drivers supports multiple variants with different
mapping. Also, specifying them in DT makes little sense with a bunch of
numbers like -
<0, 13>, <5, 32>,

It makes more sense when we can have the parent handle along with
interrupt specifiers for the incoming interrupt as well as that of the
outgoing interrupt like -
<22 0 &intc 36 0>,
<24 0 &intc 37 0>,
<26 0 &intc 38 0>,

And the interrupt specifiers can be interpreted using these optional
properties -
irqdomain-map-mask = <0xff 0>;
irqdomain-map-pass-thru = <0 0xff>;

The irqdomain-map-mask reads the input interrupt specifier to parse the
incoming interrupt port. The format of the output port is specified with
the irqdomain-map-pass-thru property.

Let's add a helper function to parse this from DT and match a struct
irq_fwspec using the input interrupt specifier from the irqdomain-map
and the valid bits specified in the irqdomain-map-mask and copy the
output interrupt specifier from the map to irq_fwspec per the mask in
irqdomain-map-pass-thru property for the matched interrupt.

Signed-off-by: Stephen Boyd <[email protected]>
Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v5:
- Fix returning 0 when no match is found
Changes in v4:
- Fix commit text spelling and verbosity
---
drivers/of/irq.c | 129 +++++++++++++++++++++++++++++++++++++++++
include/linux/of_irq.h | 1 +
2 files changed, 130 insertions(+)

diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index e1f6f392a4c0..6186904b2b6b 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -273,6 +273,135 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
}
EXPORT_SYMBOL_GPL(of_irq_parse_raw);

+int of_irq_domain_map(const struct irq_fwspec *in, struct irq_fwspec *out)
+{
+ char *stem_name;
+ char *cells_name, *map_name = NULL, *mask_name = NULL;
+ char *pass_name = NULL;
+ struct device_node *cur, *new = NULL;
+ const __be32 *map, *mask, *pass;
+ static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
+ static const __be32 dummy_pass[] = { [0 ... MAX_PHANDLE_ARGS] = 0 };
+ __be32 initial_match_array[MAX_PHANDLE_ARGS];
+ const __be32 *match_array = initial_match_array;
+ int i, ret, map_len, match;
+ u32 in_size, out_size;
+
+ stem_name = "";
+ cells_name = "#interrupt-cells";
+
+ ret = -ENOMEM;
+ map_name = kasprintf(GFP_KERNEL, "irqdomain%s-map", stem_name);
+ if (!map_name)
+ goto free;
+
+ mask_name = kasprintf(GFP_KERNEL, "irqdomain%s-map-mask", stem_name);
+ if (!mask_name)
+ goto free;
+
+ pass_name = kasprintf(GFP_KERNEL, "irqdomain%s-map-pass-thru", stem_name);
+ if (!pass_name)
+ goto free;
+
+ /* Get the #interrupt-cells property */
+ cur = to_of_node(in->fwnode);
+ ret = of_property_read_u32(cur, cells_name, &in_size);
+ if (ret < 0)
+ goto put;
+
+ /* Precalculate the match array - this simplifies match loop */
+ for (i = 0; i < in_size; i++)
+ initial_match_array[i] = cpu_to_be32(in->param[i]);
+
+ ret = -EINVAL;
+ /* Get the irqdomain-map property */
+ map = of_get_property(cur, map_name, &map_len);
+ if (!map) {
+ ret = 0;
+ goto free;
+ }
+ map_len /= sizeof(u32);
+
+ /* Get the irqdomain-map-mask property (optional) */
+ mask = of_get_property(cur, mask_name, NULL);
+ if (!mask)
+ mask = dummy_mask;
+ /* Iterate through irqdomain-map property */
+ match = 0;
+ while (map_len > (in_size + 1) && !match) {
+ /* Compare specifiers */
+ match = 1;
+ for (i = 0; i < in_size; i++, map_len--)
+ match &= !((match_array[i] ^ *map++) & mask[i]);
+
+ of_node_put(new);
+ new = of_find_node_by_phandle(be32_to_cpup(map));
+ map++;
+ map_len--;
+
+ /* Check if not found */
+ if (!new)
+ goto put;
+
+ if (!of_device_is_available(new))
+ match = 0;
+
+ ret = of_property_read_u32(new, cells_name, &out_size);
+ if (ret)
+ goto put;
+
+ /* Check for malformed properties */
+ if (WARN_ON(out_size > MAX_PHANDLE_ARGS))
+ goto put;
+ if (map_len < out_size)
+ goto put;
+
+ /* Move forward by new node's #interrupt-cells amount */
+ map += out_size;
+ map_len -= out_size;
+ }
+
+ if (!match) {
+ ret = -EINVAL;
+ goto put;
+ }
+
+ /* Get the irqdomain-map-pass-thru property (optional) */
+ pass = of_get_property(cur, pass_name, NULL);
+ if (!pass)
+ pass = dummy_pass;
+
+ /*
+ * Successfully parsed a irqdomain-map translation; copy new
+ * specifier into the out structure, keeping the
+ * bits specified in irqdomain-map-pass-thru.
+ */
+ match_array = map - out_size;
+ for (i = 0; i < out_size; i++) {
+ __be32 val = *(map - out_size + i);
+
+ out->param[i] = in->param[i];
+ if (i < in_size) {
+ val &= ~pass[i];
+ val |= cpu_to_be32(out->param[i]) & pass[i];
+ }
+
+ out->param[i] = be32_to_cpu(val);
+ }
+ out->param_count = in_size = out_size;
+ out->fwnode = of_node_to_fwnode(new);
+put:
+ of_node_put(cur);
+ of_node_put(new);
+free:
+ kfree(mask_name);
+ kfree(map_name);
+ kfree(pass_name);
+
+ return ret;
+}
+EXPORT_SYMBOL(of_irq_domain_map);
+
/**
* of_irq_parse_one - Resolve an interrupt for a device
* @device: the device whose interrupt is to be resolved
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 1214cabb2247..86342502a62a 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -32,6 +32,7 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index,
}
#endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */

+extern int of_irq_domain_map(const struct irq_fwspec *in, struct irq_fwspec *out);
extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq);
extern int of_irq_parse_one(struct device_node *device, int index,
struct of_phandle_args *out_irq);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:19

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 08/11] drivers: pinctrl: msm: setup GPIO irqchip hierarchy

To allow GPIOs to wakeup the system from suspend or deep idle, the
wakeup capable GPIOs are setup in hierarchy with interrupts from the
wakeup-parent irqchip.

In older SoC's, the TLMM will handover detection to the parent irqchip
and in newer SoC's, the parent irqchip may also be active as well as the
TLMM and therefore the GPIOs need to be masked at TLMM to avoid
duplicate interrupts. To enable both these configurations to exist,
allow the parent irqchip to dictate the TLMM irqchip's behavior when
masking/unmasking the interrupt.

Co-developed-by: Stephen Boyd <[email protected]>
Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v5:
- Fix when GPIO does not have a wakeup interrupt parent
Changes in v4:
- Remove irq_set_wake() on summary IRQ interrupt
Changes in v3:
- Use of_irq_domain_map() and pass PDC pin to parent irqdomain
Changes in v2:
- Call parent mask when masking GPIO interrupt
Changes in v1:
- Fix bug when unmasking PDC interrupt
---
drivers/pinctrl/qcom/pinctrl-msm.c | 137 ++++++++++++++++++++++++-----
1 file changed, 114 insertions(+), 23 deletions(-)

diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index ee8119879c4c..e52c1c300bcc 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -17,6 +17,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
@@ -27,6 +28,7 @@
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
+#include <linux/soc/qcom/irq.h>
#include <linux/reboot.h>
#include <linux/pm.h>
#include <linux/log2.h>
@@ -69,6 +71,7 @@ struct msm_pinctrl {

DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
+ DECLARE_BITMAP(wakeup_masked_irqs, MAX_NR_GPIO);

const struct msm_pinctrl_soc_data *soc;
void __iomem *regs[MAX_NR_TILES];
@@ -703,6 +706,13 @@ static void msm_gpio_irq_mask(struct irq_data *d)

g = &pctrl->soc->groups[d->hwirq];

+ if (d->parent_data)
+ irq_chip_mask_parent(d);
+
+ /* Monitored by parent wakeup controller?*/
+ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs))
+ return;
+
raw_spin_lock_irqsave(&pctrl->lock, flags);

val = msm_readl_intr_cfg(pctrl, g);
@@ -747,6 +757,13 @@ static void msm_gpio_irq_unmask(struct irq_data *d)

g = &pctrl->soc->groups[d->hwirq];

+ if (d->parent_data)
+ irq_chip_unmask_parent(d);
+
+ /* Monitored by parent wakeup controller? Keep masked */
+ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs))
+ return;
+
raw_spin_lock_irqsave(&pctrl->lock, flags);

val = msm_readl_intr_cfg(pctrl, g);
@@ -767,6 +784,10 @@ static void msm_gpio_irq_ack(struct irq_data *d)
unsigned long flags;
u32 val;

+ /* Handled by parent wakeup controller? Do nothing */
+ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs))
+ return;
+
g = &pctrl->soc->groups[d->hwirq];

raw_spin_lock_irqsave(&pctrl->lock, flags);
@@ -794,6 +815,13 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)

g = &pctrl->soc->groups[d->hwirq];

+ if (d->parent_data)
+ irq_chip_set_type_parent(d, type);
+
+ /* Monitored by parent wakeup controller? Keep masked */
+ if (test_bit(d->hwirq, pctrl->wakeup_masked_irqs))
+ return 0;
+
raw_spin_lock_irqsave(&pctrl->lock, flags);

/*
@@ -880,17 +908,10 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)

static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
- unsigned long flags;
-
- raw_spin_lock_irqsave(&pctrl->lock, flags);
-
- irq_set_irq_wake(pctrl->irq, on);
-
- raw_spin_unlock_irqrestore(&pctrl->lock, flags);
+ if (d->parent_data)
+ return irq_chip_set_wake_parent(d, on);

- return 0;
+ return -ENODEV;
}

static int msm_gpio_irq_reqres(struct irq_data *d)
@@ -967,11 +988,75 @@ static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0;
}

+static int msm_gpio_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq, unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count < 2)
+ return -EINVAL;
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int msm_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ int ret;
+ irq_hw_number_t hwirq;
+ struct gpio_chip *gc = domain->host_data;
+ struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+ struct irq_fwspec *fwspec = arg;
+ struct qcom_irq_fwspec parent = { };
+ unsigned int type;
+
+ ret = msm_gpio_domain_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &pctrl->irq_chip, gc);
+ if (ret < 0)
+ return ret;
+
+ if (!domain->parent)
+ return 0;
+
+ parent.fwspec.param_count = 2;
+ parent.fwspec.param[0] = GPIO_NO_WAKE_IRQ;
+ parent.fwspec.param[1] = fwspec->param[1];
+ ret = of_irq_domain_map(fwspec, &parent.fwspec);
+ if (ret == -ENOMEM)
+ return ret;
+
+ parent.fwspec.fwnode = domain->parent->fwnode;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent);
+ if (ret)
+ return ret;
+
+ if (parent.mask)
+ set_bit(hwirq, pctrl->wakeup_masked_irqs);
+
+ return 0;
+}
+
+static const struct irq_domain_ops msm_gpio_domain_ops = {
+ .translate = msm_gpio_domain_translate,
+ .alloc = msm_gpio_domain_alloc,
+ .free = irq_domain_free_irqs_top,
+};
+
static int msm_gpio_init(struct msm_pinctrl *pctrl)
{
struct gpio_chip *chip;
int ret;
unsigned ngpio = pctrl->soc->ngpios;
+ struct device_node *dn;

if (WARN_ON(ngpio > MAX_NR_GPIO))
return -EINVAL;
@@ -986,6 +1071,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
chip->need_valid_mask = msm_gpio_needs_valid_mask(pctrl);

pctrl->irq_chip.name = "msmgpio";
+ pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent;
pctrl->irq_chip.irq_mask = msm_gpio_irq_mask;
pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask;
pctrl->irq_chip.irq_ack = msm_gpio_irq_ack;
@@ -994,6 +1080,20 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres;
pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;

+ chip->irq.chip = &pctrl->irq_chip;
+ chip->irq.domain_ops = &msm_gpio_domain_ops;
+ chip->irq.handler = handle_edge_irq;
+ chip->irq.default_type = IRQ_TYPE_NONE;
+
+ dn = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
+ if (dn) {
+ chip->irq.parent_domain = irq_find_matching_host(dn,
+ DOMAIN_BUS_WAKEUP);
+ of_node_put(dn);
+ if (!chip->irq.parent_domain)
+ return -EPROBE_DEFER;
+ }
+
ret = gpiochip_add_data(&pctrl->chip, pctrl);
if (ret) {
dev_err(pctrl->dev, "Failed register gpiochip\n");
@@ -1015,26 +1115,17 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
dev_name(pctrl->dev), 0, 0, chip->ngpio);
if (ret) {
dev_err(pctrl->dev, "Failed to add pin range\n");
- gpiochip_remove(&pctrl->chip);
- return ret;
+ goto fail;
}
}

- ret = gpiochip_irqchip_add(chip,
- &pctrl->irq_chip,
- 0,
- handle_edge_irq,
- IRQ_TYPE_NONE);
- if (ret) {
- dev_err(pctrl->dev, "Failed to add irqchip to gpiochip\n");
- gpiochip_remove(&pctrl->chip);
- return -ENOSYS;
- }
-
gpiochip_set_chained_irqchip(chip, &pctrl->irq_chip, pctrl->irq,
msm_gpio_irq_handler);

return 0;
+fail:
+ gpiochip_remove(&pctrl->chip);
+ return ret;
}

static int msm_ps_hold_restart(struct notifier_block *nb, unsigned long action,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:27

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 07/11] dt-bindings: sdm845-pinctrl: add wakeup interrupt parent for GPIO

SDM845 SoC has an always-on interrupt controller (PDC) with select GPIO
routed to the PDC as interrupts that can be used to wake the system up
from deep low power modes and suspend.

Update the sdm845 pinctrl device bindings to reference the PDC wakeup
interrupt controller and the GPIO PDC interrupt map.

Cc: [email protected]
Signed-off-by: Lina Iyer <[email protected]>
---
.../bindings/pinctrl/qcom,sdm845-pinctrl.txt | 79 ++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.txt
index 665aadb5ea28..895832127193 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.txt
@@ -53,7 +53,6 @@ pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
parameters, such as pull-up, drive strength, etc.

-
PIN CONFIGURATION NODES:

The name of each subnode is not important; all subnodes should be enumerated
@@ -160,6 +159,84 @@ Example:
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
+ wakeup-parent = <&pdc_intc>;
+ irqdomain-map = <1 0 &pdc_intc 30 0>,
+ <3 0 &pdc_intc 31 0>,
+ <5 0 &pdc_intc 32 0>,
+ <10 0 &pdc_intc 33 0>,
+ <11 0 &pdc_intc 34 0>,
+ <20 0 &pdc_intc 35 0>,
+ <22 0 &pdc_intc 36 0>,
+ <24 0 &pdc_intc 37 0>,
+ <26 0 &pdc_intc 38 0>,
+ <30 0 &pdc_intc 39 0>,
+ <31 0 &pdc_intc 117 0>,
+ <32 0 &pdc_intc 41 0>,
+ <34 0 &pdc_intc 42 0>,
+ <36 0 &pdc_intc 43 0>,
+ <37 0 &pdc_intc 44 0>,
+ <38 0 &pdc_intc 45 0>,
+ <39 0 &pdc_intc 46 0>,
+ <40 0 &pdc_intc 47 0>,
+ <41 0 &pdc_intc 115 0>,
+ <43 0 &pdc_intc 49 0>,
+ <44 0 &pdc_intc 50 0>,
+ <46 0 &pdc_intc 51 0>,
+ <48 0 &pdc_intc 52 0>,
+ <49 0 &pdc_intc 118 0>,
+ <52 0 &pdc_intc 54 0>,
+ <53 0 &pdc_intc 55 0>,
+ <54 0 &pdc_intc 56 0>,
+ <56 0 &pdc_intc 57 0>,
+ <57 0 &pdc_intc 58 0>,
+ <58 0 &pdc_intc 59 0>,
+ <59 0 &pdc_intc 60 0>,
+ <60 0 &pdc_intc 61 0>,
+ <61 0 &pdc_intc 62 0>,
+ <62 0 &pdc_intc 63 0>,
+ <63 0 &pdc_intc 64 0>,
+ <64 0 &pdc_intc 65 0>,
+ <66 0 &pdc_intc 66 0>,
+ <68 0 &pdc_intc 67 0>,
+ <71 0 &pdc_intc 68 0>,
+ <73 0 &pdc_intc 69 0>,
+ <77 0 &pdc_intc 70 0>,
+ <78 0 &pdc_intc 71 0>,
+ <79 0 &pdc_intc 72 0>,
+ <80 0 &pdc_intc 73 0>,
+ <84 0 &pdc_intc 74 0>,
+ <85 0 &pdc_intc 75 0>,
+ <86 0 &pdc_intc 76 0>,
+ <88 0 &pdc_intc 77 0>,
+ <89 0 &pdc_intc 116 0>,
+ <91 0 &pdc_intc 79 0>,
+ <92 0 &pdc_intc 80 0>,
+ <95 0 &pdc_intc 81 0>,
+ <96 0 &pdc_intc 82 0>,
+ <97 0 &pdc_intc 83 0>,
+ <101 0 &pdc_intc 84 0>,
+ <103 0 &pdc_intc 85 0>,
+ <104 0 &pdc_intc 86 0>,
+ <115 0 &pdc_intc 90 0>,
+ <116 0 &pdc_intc 91 0>,
+ <117 0 &pdc_intc 92 0>,
+ <118 0 &pdc_intc 93 0>,
+ <119 0 &pdc_intc 94 0>,
+ <120 0 &pdc_intc 95 0>,
+ <121 0 &pdc_intc 96 0>,
+ <122 0 &pdc_intc 97 0>,
+ <123 0 &pdc_intc 98 0>,
+ <124 0 &pdc_intc 99 0>,
+ <125 0 &pdc_intc 100 0>,
+ <127 0 &pdc_intc 102 0>,
+ <128 0 &pdc_intc 103 0>,
+ <129 0 &pdc_intc 104 0>,
+ <130 0 &pdc_intc 105 0>,
+ <132 0 &pdc_intc 106 0>,
+ <133 0 &pdc_intc 107 0>,
+ <145 0 &pdc_intc 108 0>;
+ irqdomain-map-mask = <0xff 0>;
+ irqdomain-map-pass-thru = <0 0xff>;

qup9_active: qup9-active {
mux {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:38

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 11/11] arm64: dts: qcom: setup PDC as wakeup parent for GPIOs for SDM845

Setup PDC wakeup parent for TLMM for SDM845 SoC.

Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v3:
- Provide irqdomain-map for GPIOs that map to PDC
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 79 ++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 7d4b11c9314e..59da6944b106 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1105,6 +1105,85 @@
#interrupt-cells = <2>;
gpio-ranges = <&tlmm 0 0 150>;

+ wakeup-parent = <&pdc_intc>;
+ irqdomain-map = <1 0 &pdc_intc 30 0>,
+ <3 0 &pdc_intc 31 0>,
+ <5 0 &pdc_intc 32 0>,
+ <10 0 &pdc_intc 33 0>,
+ <11 0 &pdc_intc 34 0>,
+ <20 0 &pdc_intc 35 0>,
+ <22 0 &pdc_intc 36 0>,
+ <24 0 &pdc_intc 37 0>,
+ <26 0 &pdc_intc 38 0>,
+ <30 0 &pdc_intc 39 0>,
+ <31 0 &pdc_intc 117 0>,
+ <32 0 &pdc_intc 41 0>,
+ <34 0 &pdc_intc 42 0>,
+ <36 0 &pdc_intc 43 0>,
+ <37 0 &pdc_intc 44 0>,
+ <38 0 &pdc_intc 45 0>,
+ <39 0 &pdc_intc 46 0>,
+ <40 0 &pdc_intc 47 0>,
+ <41 0 &pdc_intc 115 0>,
+ <43 0 &pdc_intc 49 0>,
+ <44 0 &pdc_intc 50 0>,
+ <46 0 &pdc_intc 51 0>,
+ <48 0 &pdc_intc 52 0>,
+ <49 0 &pdc_intc 118 0>,
+ <52 0 &pdc_intc 54 0>,
+ <53 0 &pdc_intc 55 0>,
+ <54 0 &pdc_intc 56 0>,
+ <56 0 &pdc_intc 57 0>,
+ <57 0 &pdc_intc 58 0>,
+ <58 0 &pdc_intc 59 0>,
+ <59 0 &pdc_intc 60 0>,
+ <60 0 &pdc_intc 61 0>,
+ <61 0 &pdc_intc 62 0>,
+ <62 0 &pdc_intc 63 0>,
+ <63 0 &pdc_intc 64 0>,
+ <64 0 &pdc_intc 65 0>,
+ <66 0 &pdc_intc 66 0>,
+ <68 0 &pdc_intc 67 0>,
+ <71 0 &pdc_intc 68 0>,
+ <73 0 &pdc_intc 69 0>,
+ <77 0 &pdc_intc 70 0>,
+ <78 0 &pdc_intc 71 0>,
+ <79 0 &pdc_intc 72 0>,
+ <80 0 &pdc_intc 73 0>,
+ <84 0 &pdc_intc 74 0>,
+ <85 0 &pdc_intc 75 0>,
+ <86 0 &pdc_intc 76 0>,
+ <88 0 &pdc_intc 77 0>,
+ <89 0 &pdc_intc 116 0>,
+ <91 0 &pdc_intc 79 0>,
+ <92 0 &pdc_intc 80 0>,
+ <95 0 &pdc_intc 81 0>,
+ <96 0 &pdc_intc 82 0>,
+ <97 0 &pdc_intc 83 0>,
+ <101 0 &pdc_intc 84 0>,
+ <103 0 &pdc_intc 85 0>,
+ <104 0 &pdc_intc 86 0>,
+ <115 0 &pdc_intc 90 0>,
+ <116 0 &pdc_intc 91 0>,
+ <117 0 &pdc_intc 92 0>,
+ <118 0 &pdc_intc 93 0>,
+ <119 0 &pdc_intc 94 0>,
+ <120 0 &pdc_intc 95 0>,
+ <121 0 &pdc_intc 96 0>,
+ <122 0 &pdc_intc 97 0>,
+ <123 0 &pdc_intc 98 0>,
+ <124 0 &pdc_intc 99 0>,
+ <125 0 &pdc_intc 100 0>,
+ <127 0 &pdc_intc 102 0>,
+ <128 0 &pdc_intc 103 0>,
+ <129 0 &pdc_intc 104 0>,
+ <130 0 &pdc_intc 105 0>,
+ <132 0 &pdc_intc 106 0>,
+ <133 0 &pdc_intc 107 0>,
+ <145 0 &pdc_intc 108 0>;
+ irqdomain-map-mask = <0xff 0>;
+ irqdomain-map-pass-thru = <0 0xff>;
+
qspi_clk: qspi-clk {
pinmux {
pins = "gpio95";
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:43

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 09/11] arm64: dts: qcom: add PDC interrupt controller for SDM845

Add PDC interrupt controller device bindings for SDM845.

Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v1:
- Use updated address specification in reg
- Rename to pdc_intc
- Sort per address in DT
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 5308f1671824..7d4b11c9314e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -1934,6 +1934,15 @@
#power-domain-cells = <1>;
};

+ pdc_intc: interrupt-controller@b220000 {
+ compatible = "qcom,sdm845-pdc";
+ reg = <0 0x0b220000 0 0x30000>;
+ qcom,pdc-ranges = <0 480 94>, <94 609 15>, <115 630 7>;
+ #interrupt-cells = <2>;
+ interrupt-parent = <&intc>;
+ interrupt-controller;
+ };
+
pdc_reset: reset-controller@b2e0000 {
compatible = "qcom,sdm845-pdc-global";
reg = <0 0x0b2e0000 0 0x20000>;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:42:48

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 10/11] arm64: defconfig: enable PDC interrupt controller for Qualcomm SDM845

Enable PDC interrupt controller for SDM845 devices. The interrupt
controller can detect wakeup capable interrupts when the SoC is in a low
power state.

Signed-off-by: Lina Iyer <[email protected]>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 2d9c39033c1a..4e5e681c4b11 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -685,6 +685,7 @@ CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD_RPM=y
CONFIG_QCOM_SMP2P=y
CONFIG_QCOM_SMSM=y
+CONFIG_QCOM_PDC=y
CONFIG_ROCKCHIP_PM_DOMAINS=y
CONFIG_ARCH_TEGRA_132_SOC=y
CONFIG_ARCH_TEGRA_210_SOC=y
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:43:12

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 01/11] gpio: Add support for hierarchical IRQ domains

From: Thierry Reding <[email protected]>

Hierarchical IRQ domains can be used to stack different IRQ controllers
on top of each other. One specific use-case where this can be useful is
if a power management controller has top-level controls for wakeup
interrupts. In such cases, the power management controller can be a
parent to other interrupt controllers and program additional registers
when an IRQ has its wake capability enabled or disabled.

Signed-off-by: Thierry Reding <[email protected]>
Signed-off-by: Lina Iyer <[email protected]>
---
drivers/gpio/gpiolib.c | 15 +++++++++++----
include/linux/gpio/driver.h | 6 ++++++
2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index bca3e7740ef6..4a9a6d4afe6e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1936,7 +1936,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
type = IRQ_TYPE_NONE;
}

- gpiochip->to_irq = gpiochip_to_irq;
+ if (!gpiochip->to_irq)
+ gpiochip->to_irq = gpiochip_to_irq;
+
gpiochip->irq.default_type = type;
gpiochip->irq.lock_key = lock_key;
gpiochip->irq.request_key = request_key;
@@ -1946,9 +1948,14 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
else
ops = &gpiochip_domain_ops;

- gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio,
- gpiochip->irq.first,
- ops, gpiochip);
+ if (gpiochip->irq.parent_domain)
+ gpiochip->irq.domain = irq_domain_add_hierarchy(gpiochip->irq.parent_domain,
+ 0, gpiochip->ngpio,
+ np, ops, gpiochip);
+ else
+ gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio,
+ gpiochip->irq.first,
+ ops, gpiochip);
if (!gpiochip->irq.domain)
return -EINVAL;

diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 01497910f023..f481862f1bb0 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -48,6 +48,12 @@ struct gpio_irq_chip {
*/
const struct irq_domain_ops *domain_ops;

+ /**
+ * @parent_domain:
+ *
+ */
+ struct irq_domain *parent_domain;
+
/**
* @handler:
*
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:44:17

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 06/11] drivers: irqchip: add PDC irqdomain for wakeup capable GPIOs

Introduce a new domain for wakeup capable GPIOs. The domain can be
requested using the bus token DOMAIN_BUS_WAKEUP. In the following
patches, we will specify PDC as the wakeup-parent for the TLMM GPIO
irqchip. Requesting a wakeup GPIO will setup the GPIO and the
corresponding PDC interrupt as its parent.

Co-developed-by: Stephen Boyd <[email protected]>
Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v5:
- Define invalid wakeup interrupt
Changes in v4:
- Remove vestigial changes from v2
Changes in v3:
- Remove PDC GPIO map data (moved to DT)
- hwirq passed in .alloc() is a PDC pin now
Changes in v2:
- Remove separate file for PDC GPIO map data
- Error checks and return
- Whitespace fixes
---
drivers/irqchip/qcom-pdc.c | 98 ++++++++++++++++++++++++++++++++----
include/linux/soc/qcom/irq.h | 25 +++++++++
2 files changed, 114 insertions(+), 9 deletions(-)
create mode 100644 include/linux/soc/qcom/irq.h

diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c
index faa7d61b9d6c..ef0135fbc41a 100644
--- a/drivers/irqchip/qcom-pdc.c
+++ b/drivers/irqchip/qcom-pdc.c
@@ -13,12 +13,13 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/soc/qcom/irq.h>
#include <linux/spinlock.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>

#define PDC_MAX_IRQS 126
+#define PDC_MAX_GPIO_IRQS 256

#define CLEAR_INTR(reg, intr) (reg & ~(1 << intr))
#define ENABLE_INTR(reg, intr) (reg | (1 << intr))
@@ -26,6 +27,8 @@
#define IRQ_ENABLE_BANK 0x10
#define IRQ_i_CFG 0x110

+#define PDC_NO_PARENT_IRQ ~0UL
+
struct pdc_pin_region {
u32 pin_base;
u32 parent_base;
@@ -65,12 +68,18 @@ static void pdc_enable_intr(struct irq_data *d, bool on)

static void qcom_pdc_gic_mask(struct irq_data *d)
{
+ if (d->hwirq == GPIO_NO_WAKE_IRQ)
+ return;
+
pdc_enable_intr(d, false);
irq_chip_mask_parent(d);
}

static void qcom_pdc_gic_unmask(struct irq_data *d)
{
+ if (d->hwirq == GPIO_NO_WAKE_IRQ)
+ return;
+
pdc_enable_intr(d, true);
irq_chip_unmask_parent(d);
}
@@ -114,6 +123,9 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
int pin_out = d->hwirq;
enum pdc_irq_config_bits pdc_type;

+ if (pin_out == GPIO_NO_WAKE_IRQ)
+ return 0;
+
switch (type) {
case IRQ_TYPE_EDGE_RISING:
pdc_type = PDC_EDGE_RISING;
@@ -169,8 +181,7 @@ static irq_hw_number_t get_parent_hwirq(int pin)
return (region->parent_base + pin - region->pin_base);
}

- WARN_ON(1);
- return ~0UL;
+ return PDC_NO_PARENT_IRQ;
}

static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
@@ -199,17 +210,17 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,

ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
if (ret)
- return -EINVAL;
-
- parent_hwirq = get_parent_hwirq(hwirq);
- if (parent_hwirq == ~0UL)
- return -EINVAL;
+ return ret;

ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
&qcom_pdc_gic_chip, NULL);
if (ret)
return ret;

+ parent_hwirq = get_parent_hwirq(hwirq);
+ if (parent_hwirq == PDC_NO_PARENT_IRQ)
+ return 0;
+
if (type & IRQ_TYPE_EDGE_BOTH)
type = IRQ_TYPE_EDGE_RISING;

@@ -232,6 +243,63 @@ static const struct irq_domain_ops qcom_pdc_ops = {
.free = irq_domain_free_irqs_common,
};

+static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct qcom_irq_fwspec *qcom_fwspec = data;
+ struct irq_fwspec *fwspec = &qcom_fwspec->fwspec;
+ struct irq_fwspec parent_fwspec;
+ irq_hw_number_t hwirq, parent_hwirq;
+ unsigned int type;
+ int ret;
+
+ ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &qcom_pdc_gic_chip, NULL);
+ if (ret)
+ return ret;
+
+ if (hwirq == GPIO_NO_WAKE_IRQ)
+ return 0;
+
+ parent_hwirq = get_parent_hwirq(hwirq);
+ if (parent_hwirq == PDC_NO_PARENT_IRQ)
+ return 0;
+
+ qcom_fwspec->mask = true;
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ type = IRQ_TYPE_EDGE_RISING;
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ type = IRQ_TYPE_LEVEL_HIGH;
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 3;
+ parent_fwspec.param[0] = 0;
+ parent_fwspec.param[1] = parent_hwirq;
+ parent_fwspec.param[2] = type;
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static int qcom_pdc_gpio_domain_select(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ return (bus_token == DOMAIN_BUS_WAKEUP);
+}
+
+static const struct irq_domain_ops qcom_pdc_gpio_ops = {
+ .select = qcom_pdc_gpio_domain_select,
+ .alloc = qcom_pdc_gpio_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
static int pdc_setup_pin_mapping(struct device_node *np)
{
int ret, n;
@@ -270,7 +338,7 @@ static int pdc_setup_pin_mapping(struct device_node *np)

static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
{
- struct irq_domain *parent_domain, *pdc_domain;
+ struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain;
int ret;

pdc_base = of_iomap(node, 0);
@@ -301,6 +369,18 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
goto fail;
}

+ pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain, 0,
+ PDC_MAX_GPIO_IRQS,
+ of_fwnode_handle(node),
+ &qcom_pdc_gpio_ops, NULL);
+ if (!pdc_gpio_domain) {
+ pr_err("%pOF: GIC domain add failed for GPIO domain\n", node);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ irq_domain_update_bus_token(pdc_gpio_domain, DOMAIN_BUS_WAKEUP);
+
return 0;

fail:
diff --git a/include/linux/soc/qcom/irq.h b/include/linux/soc/qcom/irq.h
new file mode 100644
index 000000000000..468a811141ad
--- /dev/null
+++ b/include/linux/soc/qcom/irq.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __QCOM_IRQ_H
+#define __QCOM_IRQ_H
+
+#include <linux/irqdomain.h>
+
+#define GPIO_NO_WAKE_IRQ ~0U
+
+/**
+ * struct qcom_irq_fwspec - qcom specific irq fwspec wrapper
+ * @fwspec: irq fwspec
+ * @mask: if true, keep the irq masked in the gpio controller
+ *
+ * Use this structure to communicate between the parent irq chip, MPM or PDC,
+ * to the gpio chip, TLMM, about the gpio being allocated in the parent
+ * and if the gpio chip should keep the line masked because the parent irq
+ * chip is handling everything about the irq line.
+ */
+struct qcom_irq_fwspec {
+ struct irq_fwspec fwspec;
+ bool mask;
+};
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:44:21

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 04/11] of: irq: document properties for wakeup interrupt parent

Some interrupt controllers in a SoC, are always powered on and have a
select interrupts routed to them, so that they can wakeup the SoC from
suspend. Add wakeup-parent DT property to refer to these interrupt
controllers.

If the interrupts routed to the wakeup parent are not sequential, than a
map needs to exist to associate the same interrupt line on multiple
interrupt controllers. Providing this map in every driver is cumbersome.
Let's add this in the device tree and document the properties to map the
interrupt specifiers

Cc: [email protected]
Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v5:
- Update documentation to describe masks in the example
Changes in v4:
- Added this documentation
---
.../interrupt-controller/interrupts.txt | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
index 8a3c40829899..e3e43f5d5566 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
@@ -108,3 +108,57 @@ commonly used:
sensitivity = <7>;
};
};
+
+3) Interrupt wakeup parent
+--------------------------
+
+Some interrupt controllers in a SoC, are always powered on and have a select
+interrupts routed to them, so that they can wakeup the SoC from suspend. These
+interrupt controllers do not fall into the category of a parent interrupt
+controller and can be specified by the "wakeup-parent" property and contain a
+single phandle referring to the wakeup capable interrupt controller.
+
+ Example:
+ wakeup-parent = <&pdc_intc>;
+
+
+4) Interrupt mapping
+--------------------
+
+Sometimes interrupts may be detected by more than one interrupt controller
+(depending on which controller is active). The interrupt controllers may not
+be in hierarchy and therefore the interrupt controller driver is required to
+establish the relationship between the same interrupt at different interrupt
+controllers. If these interrupts are not sequential then a map needs to be
+specified to help identify these interrupts.
+
+Mapping the interrupt specifiers in the device tree can be done using the
+"irqdomain-map" property. The property contains interrupt specifier at the
+current interrupt controller followed by the interrupt specifier at the mapped
+interrupt controller.
+
+ irqdomain-map = <incoming-interrupt-specifier mapped-interrupt-specifier>
+
+The optional properties "irqdomain-map-mask" and "irqdomain-map-pass-thru" may
+be provided to help interpret the valid bits of the incoming and mapped
+interrupt specifiers respectively.
+
+ Example:
+ intc: interrupt-controller@17a00000 {
+ #interrupt-cells = <3>;
+ };
+
+ pinctrl@3400000 {
+ #interrupt-cells = <2>;
+ irqdomain-map = <22 0 &intc 36 0>, <24 0 &intc 37 0>;
+ irqdomain-map-mask = <0xff 0>;
+ irqdomain-map-pass-thru = <0 0xff>;
+ };
+
+In the above example, the input interrupt specifier map-mask <0xff 0> applied
+on the incoming interrupt specifier of the map <22 0>, <24 0>, returns the
+input interrupt 22, 24 etc. The second argument being irq type is immaterial
+from the map and is used from the incoming request instead. The pass-thru
+specifier parses the output interrupt specifier from the rest of the unparsed
+argments from the map <&intc 36 0>, <&intc 37 0> etc to return the output
+interrupt 36, 37 etc.
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-07 20:44:34

by Lina Iyer

[permalink] [raw]
Subject: [PATCH v5 03/11] irqdomain: add bus token DOMAIN_BUS_WAKEUP

A single controller can handle normal interrupts and wake-up interrupts
independently, with a different numbering space. It is thus crucial to
allow the driver for such a controller discriminate between the two.

A simple way to do so is to tag the wake-up irqdomain with a "bus token"
that indicates the wake-up domain. This slightly abuses the notion of
bus, but also radically simplifies the design of such a driver. Between
two evils, we choose the least damaging.

Suggested-by: Stephen Boyd <[email protected]>
Signed-off-by: Lina Iyer <[email protected]>
---
Changes in v4:
- Update commit text
---
include/linux/irqdomain.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 61706b430907..4eaf17dfd2f8 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -82,6 +82,7 @@ enum irq_domain_bus_token {
DOMAIN_BUS_NEXUS,
DOMAIN_BUS_IPI,
DOMAIN_BUS_FSL_MC_MSI,
+ DOMAIN_BUS_WAKEUP,
};

/**
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2019-05-10 22:42:23

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5 04/11] of: irq: document properties for wakeup interrupt parent

On Tue, May 7, 2019 at 3:41 PM Lina Iyer <[email protected]> wrote:
>
> Some interrupt controllers in a SoC, are always powered on and have a
> select interrupts routed to them, so that they can wakeup the SoC from
> suspend. Add wakeup-parent DT property to refer to these interrupt
> controllers.
>
> If the interrupts routed to the wakeup parent are not sequential, than a
> map needs to exist to associate the same interrupt line on multiple
> interrupt controllers. Providing this map in every driver is cumbersome.
> Let's add this in the device tree and document the properties to map the
> interrupt specifiers
>
> Cc: [email protected]
> Signed-off-by: Lina Iyer <[email protected]>
> ---
> Changes in v5:
> - Update documentation to describe masks in the example
> Changes in v4:
> - Added this documentation
> ---
> .../interrupt-controller/interrupts.txt | 54 +++++++++++++++++++
> 1 file changed, 54 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> index 8a3c40829899..e3e43f5d5566 100644
> --- a/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> +++ b/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> @@ -108,3 +108,57 @@ commonly used:
> sensitivity = <7>;
> };
> };
> +
> +3) Interrupt wakeup parent
> +--------------------------
> +
> +Some interrupt controllers in a SoC, are always powered on and have a select
> +interrupts routed to them, so that they can wakeup the SoC from suspend. These
> +interrupt controllers do not fall into the category of a parent interrupt
> +controller and can be specified by the "wakeup-parent" property and contain a
> +single phandle referring to the wakeup capable interrupt controller.
> +
> + Example:
> + wakeup-parent = <&pdc_intc>;
> +
> +
> +4) Interrupt mapping
> +--------------------
> +
> +Sometimes interrupts may be detected by more than one interrupt controller
> +(depending on which controller is active). The interrupt controllers may not
> +be in hierarchy and therefore the interrupt controller driver is required to
> +establish the relationship between the same interrupt at different interrupt
> +controllers. If these interrupts are not sequential then a map needs to be
> +specified to help identify these interrupts.
> +
> +Mapping the interrupt specifiers in the device tree can be done using the
> +"irqdomain-map" property. The property contains interrupt specifier at the
> +current interrupt controller followed by the interrupt specifier at the mapped
> +interrupt controller.
> +
> + irqdomain-map = <incoming-interrupt-specifier mapped-interrupt-specifier>

I'm wondering why we need a new map property rather than just using
interrupt-map? Contrary to what Linus said, it is not PCI only.

It would be an extension of the current behavior. It's generally used
to map each interrupt to different parents or swizzle the routing (in
the PCI case). Generally, a node would be either an
'interrupt-controller' or an 'interrupt-map' node. The interrupt
parsing code (for the kernel at least) prioritizes
'interrupt-controller' path, so adding 'interrupt-map' could be done
without changing behavior.

Another concern I have with this is it only solves the problem of an
IRQ routed to multiple parents for the case of 2 parents. What happens
when we have an IRQ routed to 3 different parents? Maybe the solution
is the incoming-interrupt-specifier can be listed more than once. Marc
already expressed concerns with the scalability of interrupt-map
property, so that's maybe not an ideal solution.

> +
> +The optional properties "irqdomain-map-mask" and "irqdomain-map-pass-thru" may
> +be provided to help interpret the valid bits of the incoming and mapped
> +interrupt specifiers respectively.
> +
> + Example:
> + intc: interrupt-controller@17a00000 {
> + #interrupt-cells = <3>;

The phandle doesn't count as a cell, so this should be 2.

> + };
> +
> + pinctrl@3400000 {
> + #interrupt-cells = <2>;
> + irqdomain-map = <22 0 &intc 36 0>, <24 0 &intc 37 0>;
> + irqdomain-map-mask = <0xff 0>;
> + irqdomain-map-pass-thru = <0 0xff>;
> + };
> +
> +In the above example, the input interrupt specifier map-mask <0xff 0> applied
> +on the incoming interrupt specifier of the map <22 0>, <24 0>, returns the
> +input interrupt 22, 24 etc. The second argument being irq type is immaterial
> +from the map and is used from the incoming request instead. The pass-thru
> +specifier parses the output interrupt specifier from the rest of the unparsed
> +argments from the map <&intc 36 0>, <&intc 37 0> etc to return the output
> +interrupt 36, 37 etc.
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>