linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sowjanya Komatineni <skomatineni@nvidia.com>
To: <thierry.reding@gmail.com>, <jonathanh@nvidia.com>
Cc: <jckuo@nvidia.com>, <talho@nvidia.com>, <josephl@nvidia.com>,
	<skomatineni@nvidia.com>, <linux-tegra@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Subject: [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO
Date: Tue, 21 May 2019 16:31:21 -0700	[thread overview]
Message-ID: <1558481483-22254-11-git-send-email-skomatineni@nvidia.com> (raw)
In-Reply-To: <1558481483-22254-1-git-send-email-skomatineni@nvidia.com>

The GPIO controller doesn't have any controls to enable the system to
wake up from low power states based on activity on GPIO pins. An extra
hardware block that is part of the power management controller (PMC)
contains these controls. In order for the GPIO controller to be able
to cooperate with the PMC, obtain a reference to the PMC's IRQ domain
and make it a parent to the GPIO controller's IRQ domain. This way the
PMC gets an opportunity to program the additional registers required
to enable wakeup sources on suspend.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/gpio/gpio-tegra.c | 109 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 103 insertions(+), 6 deletions(-)

diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 6d9b6906b9d0..d57e33050d0c 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -32,6 +32,8 @@
 #include <linux/pinctrl/consumer.h>
 #include <linux/pm.h>
 
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
 #define GPIO_BANK(x)		((x) >> 5)
 #define GPIO_PORT(x)		(((x) >> 3) & 0x3)
 #define GPIO_BIT(x)		((x) & 0x7)
@@ -275,8 +277,22 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
 static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
 {
 	struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
+	struct irq_domain *domain = tgi->irq_domain;
+
+	if (!gpiochip_irqchip_irq_valid(chip, offset))
+		return -ENXIO;
+
+	if (irq_domain_is_hierarchy(domain)) {
+		struct irq_fwspec spec;
+
+		spec.fwnode = domain->fwnode;
+		spec.param_count = 2;
+		spec.param[0] = offset;
+		spec.param[1] = IRQ_TYPE_NONE;
+		return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &spec);
+	}
 
-	return irq_find_mapping(tgi->irq_domain, offset);
+	return irq_find_mapping(domain, offset);
 }
 
 static void tegra_gpio_irq_ack(struct irq_data *d)
@@ -365,7 +381,10 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
 		irq_set_handler_locked(d, handle_edge_irq);
 
-	return 0;
+	if (d->parent_data)
+		return irq_chip_set_type_parent(d, type);
+	else
+		return 0;
 }
 
 static void tegra_gpio_irq_shutdown(struct irq_data *d)
@@ -566,10 +585,79 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
 };
 
+static int tegra_gpio_irq_domain_translate(struct irq_domain *domain,
+					   struct irq_fwspec *fwspec,
+					   unsigned long *hwirq,
+					   unsigned int *type)
+{
+	if (WARN_ON(fwspec->param_count < 2))
+		return -EINVAL;
+
+	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+	*hwirq = fwspec->param[0];
+
+	return 0;
+}
+
+static int tegra_gpio_irq_domain_alloc(struct irq_domain *domain,
+				       unsigned int virq,
+				       unsigned int num_irqs, void *data)
+{
+	struct tegra_gpio_info *tgi = gpiochip_get_data(domain->host_data);
+	struct irq_fwspec *fwspec = data;
+	struct irq_fwspec spec;
+	struct tegra_gpio_bank *bank;
+	unsigned long hwirq;
+	unsigned int type;
+	int err = 0;
+
+	if (WARN_ON(fwspec->param_count < 2))
+		return -EINVAL;
+
+	if (!irq_domain_get_of_node(domain->parent))
+		return -EINVAL;
+
+	err = tegra_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type);
+	if (err)
+		return err;
+
+	bank = &tgi->bank_info[GPIO_BANK(hwirq)];
+	err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+					    &tgi->ic, bank);
+
+	if (err < 0)
+		return err;
+
+	spec.fwnode = domain->parent->fwnode;
+	spec.param_count = 3;
+	spec.param[0] = GIC_SPI;
+	spec.param[1] = fwspec->param[0];
+	spec.param[2] = fwspec->param[1];
+
+	return irq_domain_alloc_irqs_parent(domain, virq, 1, &spec);
+}
+
+static const struct irq_domain_ops tegra_gpio_irq_domain_ops = {
+	.translate = tegra_gpio_irq_domain_translate,
+	.alloc = tegra_gpio_irq_domain_alloc,
+};
+
+static const struct of_device_id tegra_pmc_of_match[] = {
+	{ .compatible = "nvidia,tegra210-pmc" },
+	{ .compatible = "nvidia,tegra132-pmc" },
+	{ .compatible = "nvidia,tegra124-pmc" },
+	{ .compatible = "nvidia,tegra114-pmc" },
+	{ .compatible = "nvidia,tegra30-pmc" },
+	{ .compatible = "nvidia,tegra20-pmc" },
+	{ }
+};
+
 static int tegra_gpio_probe(struct platform_device *pdev)
 {
 	struct tegra_gpio_info *tgi;
 	struct tegra_gpio_bank *bank;
+	struct device_node *np;
+	struct irq_domain *parent_domain = NULL;
 	unsigned int gpio, i, j;
 	int ret;
 
@@ -612,8 +700,15 @@ static int tegra_gpio_probe(struct platform_device *pdev)
 	tgi->ic.irq_set_type		= tegra_gpio_irq_set_type;
 	tgi->ic.irq_shutdown		= tegra_gpio_irq_shutdown;
 #ifdef CONFIG_PM_SLEEP
-	tgi->ic.irq_set_wake		= tegra_gpio_irq_set_wake;
+	tgi->ic.irq_set_wake		= irq_chip_set_wake_parent;
 #endif
+	np = of_find_matching_node(NULL, tegra_pmc_of_match);
+	if (np) {
+		parent_domain = irq_find_host(np);
+		of_node_put(np);
+		if (!parent_domain)
+			return -EPROBE_DEFER;
+	}
 
 	platform_set_drvdata(pdev, tgi);
 
@@ -625,9 +720,11 @@ static int tegra_gpio_probe(struct platform_device *pdev)
 	if (!tgi->bank_info)
 		return -ENOMEM;
 
-	tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
-						tgi->gc.ngpio,
-						&irq_domain_simple_ops, NULL);
+	tgi->irq_domain = irq_domain_add_hierarchy(parent_domain, 0,
+						   tgi->gc.ngpio,
+						   pdev->dev.of_node,
+						   &tegra_gpio_irq_domain_ops,
+						   &tgi->gc);
 	if (!tgi->irq_domain)
 		return -ENODEV;
 
-- 
2.7.4


  parent reply	other threads:[~2019-05-21 23:31 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 01/12] irqchip: tegra: do not disable COP IRQ during suspend Sowjanya Komatineni
2019-05-22 12:12   ` Thierry Reding
2019-05-21 23:31 ` [PATCH V1 02/12] pinctrl: tegra: add suspend and resume support Sowjanya Komatineni
2019-05-22 12:37   ` Thierry Reding
2019-05-21 23:31 ` [PATCH V1 03/12] clk: tegra: save and restore PLLs state for system Sowjanya Komatineni
2019-05-22 13:31   ` Thierry Reding
2019-05-21 23:31 ` [PATCH V1 04/12] clk: tegra: add support for peripheral clock suspend and resume Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 05/12] clk: tegra: add support for OSC clock resume Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 06/12] clk: tegra: add suspend resume support for DFLL clock Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 07/12] clk: tegra: support for Tegra210 clocks suspend-resume Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models Sowjanya Komatineni
2019-05-22 12:49   ` Thierry Reding
2019-05-22 13:02   ` Thierry Reding
2019-05-21 23:31 ` [PATCH V1 09/12] soc/tegra: pmc: add pmc wake support for tegra210 Sowjanya Komatineni
2019-05-22 13:01   ` Thierry Reding
2019-05-21 23:31 ` Sowjanya Komatineni [this message]
2019-05-22 13:24   ` [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO Thierry Reding
2019-05-25 20:39     ` Sowjanya Komatineni
2019-05-21 23:31 ` [PATCH V1 11/12] soc/tegra: pmc: configure tegra deep sleep control settings Sowjanya Komatineni
2019-05-22 13:28   ` Thierry Reding
2019-05-21 23:31 ` [PATCH V1 12/12] arm64: tegra: enable wake from deep sleep on RTC alarm Sowjanya Komatineni
2019-05-22 13:33 ` [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Thierry Reding

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1558481483-22254-11-git-send-email-skomatineni@nvidia.com \
    --to=skomatineni@nvidia.com \
    --cc=jckuo@nvidia.com \
    --cc=jonathanh@nvidia.com \
    --cc=josephl@nvidia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=talho@nvidia.com \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).