LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH V1 00/12] LP0 entry and exit support for Tegra210
@ 2019-05-21 23:31 Sowjanya Komatineni
  2019-05-21 23:31 ` [PATCH V1 01/12] irqchip: tegra: do not disable COP IRQ during suspend Sowjanya Komatineni
                   ` (12 more replies)
  0 siblings, 13 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch series includes Tegra210 deepsleep or LP0 support with
deep sleep exit through RTC alarm wake and power button wake events.

This series also includes save and restore of PLLs, clocks, OSC contexts
for basic LP0 exit.

This patch series is doesn't support for 100% suspend/resume to fully
functional state and we are working on some more drivers suspend and
resume implementations.

Sowjanya Komatineni (12):
  irqchip: tegra: do not disable COP IRQ during suspend
  pinctrl: tegra: add suspend and resume support
  clk: tegra: save and restore PLLs state for system
  clk: tegra: add support for peripheral clock suspend and resume
  clk: tegra: add support for OSC clock resume
  clk: tegra: add suspend resume support for DFLL clock
  clk: tegra: support for Tegra210 clocks suspend-resume
  soc/tegra: pmc: allow support for more tegra wake models
  soc/tegra: pmc: add pmc wake support for tegra210
  gpio: tegra: implement wake event support for Tegra210 and prior GPIO
  soc/tegra: pmc: configure tegra deep sleep control settings
  arm64: tegra: enable wake from deep sleep on RTC alarm.

 arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |   7 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi       |   5 +-
 drivers/clk/tegra/clk-dfll.c                   |  82 ++++++
 drivers/clk/tegra/clk-dfll.h                   |   2 +
 drivers/clk/tegra/clk-divider.c                |  19 ++
 drivers/clk/tegra/clk-pll-out.c                |  25 ++
 drivers/clk/tegra/clk-pll.c                    | 220 ++++++++++++--
 drivers/clk/tegra/clk-tegra-fixed.c            |  15 +
 drivers/clk/tegra/clk-tegra210.c               | 382 +++++++++++++++++++++++++
 drivers/clk/tegra/clk.c                        |  74 ++++-
 drivers/clk/tegra/clk.h                        |  18 ++
 drivers/gpio/gpio-tegra.c                      | 109 ++++++-
 drivers/irqchip/irq-tegra.c                    |  10 +-
 drivers/pinctrl/tegra/pinctrl-tegra.c          |  68 ++++-
 drivers/pinctrl/tegra/pinctrl-tegra.h          |   3 +
 drivers/pinctrl/tegra/pinctrl-tegra114.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra124.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra20.c        |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra210.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra30.c        |   1 +
 drivers/soc/tegra/pmc.c                        | 159 +++++++++-
 21 files changed, 1167 insertions(+), 36 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 01/12] irqchip: tegra: do not disable COP IRQ during suspend
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
@ 2019-05-21 23:31 ` 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
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

BPMP-lite still need IRQ function to finish SC7 suspend sequence for
Tegra210.

This patch has fix for leaving the COP IRQ enabled for Tegra210 during
interrupt controller suspend operation.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/irqchip/irq-tegra.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c
index 0abc0cd1c32e..1882373fa1fd 100644
--- a/drivers/irqchip/irq-tegra.c
+++ b/drivers/irqchip/irq-tegra.c
@@ -53,18 +53,24 @@ static unsigned int num_ictlrs;
 
 struct tegra_ictlr_soc {
 	unsigned int num_ictlrs;
+	bool has_bpmpl;
 };
 
+static const struct tegra_ictlr_soc *soc;
+
 static const struct tegra_ictlr_soc tegra20_ictlr_soc = {
 	.num_ictlrs = 4,
+	.has_bpmpl = false,
 };
 
 static const struct tegra_ictlr_soc tegra30_ictlr_soc = {
 	.num_ictlrs = 5,
+	.has_bpmpl = false,
 };
 
 static const struct tegra_ictlr_soc tegra210_ictlr_soc = {
 	.num_ictlrs = 6,
+	.has_bpmpl = true,
 };
 
 static const struct of_device_id ictlr_matches[] = {
@@ -157,7 +163,8 @@ static int tegra_ictlr_suspend(void)
 		lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
 
 		/* Disable COP interrupts */
-		writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
+		if (!soc->has_bpmpl)
+			writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
 
 		/* Disable CPU interrupts */
 		writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
@@ -286,7 +293,6 @@ static int __init tegra_ictlr_init(struct device_node *node,
 {
 	struct irq_domain *parent_domain, *domain;
 	const struct of_device_id *match;
-	const struct tegra_ictlr_soc *soc;
 	unsigned int i;
 	int err;
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 02/12] pinctrl: tegra: add suspend and resume support
  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-21 23:31 ` 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
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch adds suspend and resume support for Tegra pinctrl driver
and registers them to syscore so the pinmux settings are restored
before the devices resume.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/pinctrl/tegra/pinctrl-tegra.c    | 68 +++++++++++++++++++++++++++++++-
 drivers/pinctrl/tegra/pinctrl-tegra.h    |  3 ++
 drivers/pinctrl/tegra/pinctrl-tegra114.c |  1 +
 drivers/pinctrl/tegra/pinctrl-tegra124.c |  1 +
 drivers/pinctrl/tegra/pinctrl-tegra20.c  |  1 +
 drivers/pinctrl/tegra/pinctrl-tegra210.c |  1 +
 drivers/pinctrl/tegra/pinctrl-tegra30.c  |  1 +
 7 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c
index a5008c066bac..585debbc4291 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra.c
@@ -28,11 +28,18 @@
 #include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
 
 #include "../core.h"
 #include "../pinctrl-utils.h"
 #include "pinctrl-tegra.h"
 
+#define EMMC2_PAD_CFGPADCTRL_0			0x1c8
+#define EMMC4_PAD_CFGPADCTRL_0			0x1e0
+#define EMMC_DPD_PARKING			(0x1FFF << 14)
+
+static struct tegra_pmx *pmx;
+
 static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
 {
 	return readl(pmx->regs[bank] + reg);
@@ -629,6 +636,50 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx)
 	}
 }
 
+static int __maybe_unused tegra_pinctrl_suspend(void)
+{
+	u32 *pg_data = pmx->pg_data;
+	u32 *regs;
+	int i, j;
+
+	for (i = 0; i < pmx->nbanks; i++) {
+		regs = pmx->regs[i];
+		for (j = 0; j < pmx->reg_bank_size[i] / 4; j++)
+			*pg_data++ = readl(regs++);
+	}
+
+	return pinctrl_force_sleep(pmx->pctl);
+}
+
+static void __maybe_unused tegra_pinctrl_resume(void)
+{
+	u32 *pg_data = pmx->pg_data;
+	u32 *regs;
+	u32 val;
+	int i, j;
+
+	for (i = 0; i < pmx->nbanks; i++) {
+		regs = pmx->regs[i];
+		for (j = 0; j < pmx->reg_bank_size[i] / 4; j++)
+			writel(*pg_data++, regs++);
+	}
+
+	if (pmx->soc->has_park_padcfg) {
+		val = pmx_readl(pmx, 0, EMMC2_PAD_CFGPADCTRL_0);
+		val &= ~EMMC_DPD_PARKING;
+		pmx_writel(pmx, val, 0, EMMC2_PAD_CFGPADCTRL_0);
+
+		val = pmx_readl(pmx, 0, EMMC4_PAD_CFGPADCTRL_0);
+		val &= ~EMMC_DPD_PARKING;
+		pmx_writel(pmx, val, 0, EMMC4_PAD_CFGPADCTRL_0);
+	}
+}
+
+static struct syscore_ops pinctrl_syscore_ops = {
+	.suspend = tegra_pinctrl_suspend,
+	.resume = tegra_pinctrl_resume,
+};
+
 static bool gpio_node_has_range(const char *compatible)
 {
 	struct device_node *np;
@@ -648,11 +699,11 @@ static bool gpio_node_has_range(const char *compatible)
 int tegra_pinctrl_probe(struct platform_device *pdev,
 			const struct tegra_pinctrl_soc_data *soc_data)
 {
-	struct tegra_pmx *pmx;
 	struct resource *res;
 	int i;
 	const char **group_pins;
 	int fn, gn, gfn;
+	int pg_data_size = 0;
 
 	pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
 	if (!pmx)
@@ -705,6 +756,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
 		if (!res)
 			break;
+		pg_data_size += resource_size(res);
 	}
 	pmx->nbanks = i;
 
@@ -712,12 +764,25 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 				 GFP_KERNEL);
 	if (!pmx->regs)
 		return -ENOMEM;
+#ifdef CONFIG_PM_SLEEP
+	pmx->reg_bank_size = devm_kcalloc(&pdev->dev, pmx->nbanks,
+					  sizeof(*pmx->reg_bank_size),
+					  GFP_KERNEL);
+	if (!pmx->reg_bank_size)
+		return -ENOMEM;
 
+	pmx->pg_data = devm_kzalloc(&pdev->dev, pg_data_size, GFP_KERNEL);
+	if (!pmx->pg_data)
+		return -ENOMEM;
+#endif
 	for (i = 0; i < pmx->nbanks; i++) {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
 		pmx->regs[i] = devm_ioremap_resource(&pdev->dev, res);
 		if (IS_ERR(pmx->regs[i]))
 			return PTR_ERR(pmx->regs[i]);
+#ifdef CONFIG_PM_SLEEP
+		pmx->reg_bank_size[i] = resource_size(res);
+#endif
 	}
 
 	pmx->pctl = devm_pinctrl_register(&pdev->dev, &tegra_pinctrl_desc, pmx);
@@ -732,6 +797,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
 		pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
 
 	platform_set_drvdata(pdev, pmx);
+	register_syscore_ops(&pinctrl_syscore_ops);
 
 	dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n");
 
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.h b/drivers/pinctrl/tegra/pinctrl-tegra.h
index 44c71941b5f8..8c7cd94124ea 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra.h
+++ b/drivers/pinctrl/tegra/pinctrl-tegra.h
@@ -25,6 +25,8 @@ struct tegra_pmx {
 
 	int nbanks;
 	void __iomem **regs;
+	int *reg_bank_size;
+	u32 *pg_data;
 };
 
 enum tegra_pinconf_param {
@@ -199,6 +201,7 @@ struct tegra_pinctrl_soc_data {
 	bool hsm_in_mux;
 	bool schmitt_in_mux;
 	bool drvtype_in_mux;
+	bool has_park_padcfg;
 };
 
 int tegra_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra114.c b/drivers/pinctrl/tegra/pinctrl-tegra114.c
index d43c209e9c30..4ac44f34dccf 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra114.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra114.c
@@ -1849,6 +1849,7 @@ static const struct tegra_pinctrl_soc_data tegra114_pinctrl = {
 	.hsm_in_mux = false,
 	.schmitt_in_mux = false,
 	.drvtype_in_mux = false,
+	.has_park_padcfg = false,
 };
 
 static int tegra114_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra124.c b/drivers/pinctrl/tegra/pinctrl-tegra124.c
index 5b07a5834d15..1dac7648b41f 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra124.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra124.c
@@ -2061,6 +2061,7 @@ static const struct tegra_pinctrl_soc_data tegra124_pinctrl = {
 	.hsm_in_mux = false,
 	.schmitt_in_mux = false,
 	.drvtype_in_mux = false,
+	.has_park_padcfg = false,
 };
 
 static int tegra124_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c
index 1fc82a9576e0..9d2b25200f32 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra20.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c
@@ -2231,6 +2231,7 @@ static const struct tegra_pinctrl_soc_data tegra20_pinctrl = {
 	.hsm_in_mux = false,
 	.schmitt_in_mux = false,
 	.drvtype_in_mux = false,
+	.has_park_padcfg = false,
 };
 
 static const char *cdev1_parents[] = {
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra210.c b/drivers/pinctrl/tegra/pinctrl-tegra210.c
index 3e77f5474dd8..dc06c36e698a 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra210.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra210.c
@@ -1563,6 +1563,7 @@ static const struct tegra_pinctrl_soc_data tegra210_pinctrl = {
 	.hsm_in_mux = true,
 	.schmitt_in_mux = true,
 	.drvtype_in_mux = true,
+	.has_park_padcfg = true,
 };
 
 static int tegra210_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra30.c b/drivers/pinctrl/tegra/pinctrl-tegra30.c
index 10e617003e9c..42182d714950 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra30.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra30.c
@@ -2484,6 +2484,7 @@ static const struct tegra_pinctrl_soc_data tegra30_pinctrl = {
 	.hsm_in_mux = false,
 	.schmitt_in_mux = false,
 	.drvtype_in_mux = false,
+	.has_park_padcfg = false,
 };
 
 static int tegra30_pinctrl_probe(struct platform_device *pdev)
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 03/12] clk: tegra: save and restore PLLs state for system
  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-21 23:31 ` [PATCH V1 02/12] pinctrl: tegra: add suspend and resume support Sowjanya Komatineni
@ 2019-05-21 23:31 ` 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
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch has implementation of saving and restoring PLL's state to
support system suspend and resume operations.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-divider.c |  19 ++++
 drivers/clk/tegra/clk-pll-out.c |  25 +++++
 drivers/clk/tegra/clk-pll.c     | 220 ++++++++++++++++++++++++++++++++++++----
 drivers/clk/tegra/clk.h         |  14 +++
 4 files changed, 258 insertions(+), 20 deletions(-)

diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c
index 2a1822a22740..718694727042 100644
--- a/drivers/clk/tegra/clk-divider.c
+++ b/drivers/clk/tegra/clk-divider.c
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/err.h>
@@ -179,3 +180,21 @@ struct clk *tegra_clk_register_mc(const char *name, const char *parent_name,
 					  reg, 16, 1, CLK_DIVIDER_READ_ONLY,
 					  mc_div_table, lock);
 }
+
+#if defined(CONFIG_PM_SLEEP)
+void tegra_clk_divider_resume(struct clk_hw *hw, unsigned long rate)
+{
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+	unsigned long parent_rate;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_hw_get_rate(parent);
+
+	if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0)
+		WARN_ON(1);
+}
+#endif
diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c
index 257cae0c1488..8b8c3b77d243 100644
--- a/drivers/clk/tegra/clk-pll-out.c
+++ b/drivers/clk/tegra/clk-pll-out.c
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/err.h>
@@ -120,3 +121,27 @@ struct clk *tegra_clk_register_pll_out(const char *name,
 
 	return clk;
 }
+
+#if defined(CONFIG_PM_SLEEP)
+void tegra_clk_pll_out_resume(struct clk *clk, unsigned long rate)
+{
+	struct clk_hw *hw = __clk_get_hw(clk);
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	tegra_clk_divider_resume(parent, rate);
+	clk_pll_out_enable(hw);
+}
+
+void tegra_clk_sync_state_pll_out(struct clk *clk)
+{
+	struct clk_hw *hw = __clk_get_hw(clk);
+
+	if (!__clk_get_enable_count(clk))
+		clk_pll_out_disable(hw);
+}
+#endif
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index 6b976b2514f7..a47950256598 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -20,6 +20,7 @@
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/clk/tegra.h>
 
 #include "clk.h"
 
@@ -1813,6 +1814,28 @@ static int clk_pllu_tegra114_enable(struct clk_hw *hw)
 
 	return ret;
 }
+
+static void _clk_plle_tegra_init_parent(struct tegra_clk_pll *pll)
+{
+	u32 val, val_aux;
+
+	/* ensure parent is set to pll_ref */
+
+	val = pll_readl_base(pll);
+	val_aux = pll_readl(pll->params->aux_reg, pll);
+
+	if (val & PLL_BASE_ENABLE) {
+		if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
+		    (val_aux & PLLE_AUX_PLLP_SEL))
+			WARN(1, "pll_e enabled with unsupported parent %s\n",
+			     (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
+			     "pll_re_vco");
+	} else {
+		val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
+		pll_writel(val_aux, pll->params->aux_reg, pll);
+		fence_udelay(1, pll->clk_base);
+	}
+}
 #endif
 
 static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base,
@@ -2289,6 +2312,21 @@ static const struct clk_ops tegra_clk_pllss_ops = {
 	.set_rate = clk_pllxc_set_rate,
 };
 
+static void _pllss_set_defaults(struct tegra_clk_pll *pll)
+{
+	u32 val;
+
+	pll_writel_misc(PLLSS_MISC_DEFAULT, pll);
+	pll_writel(PLLSS_CFG_DEFAULT, pll->params->ext_misc_reg[0], pll);
+	pll_writel(PLLSS_CTRL1_DEFAULT, pll->params->ext_misc_reg[1], pll);
+	pll_writel(PLLSS_CTRL2_DEFAULT, pll->params->ext_misc_reg[2], pll);
+
+	val = pll_readl_base(pll);
+	val &= ~PLLSS_LOCK_OVERRIDE;
+	pll_writel_base(val, pll);
+
+}
+
 struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
 				void __iomem *clk_base, unsigned long flags,
 				struct tegra_clk_pll_params *pll_params,
@@ -2339,10 +2377,7 @@ struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name,
 
 	_update_pll_mnp(pll, &cfg);
 
-	pll_writel_misc(PLLSS_MISC_DEFAULT, pll);
-	pll_writel(PLLSS_CFG_DEFAULT, pll_params->ext_misc_reg[0], pll);
-	pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[1], pll);
-	pll_writel(PLLSS_CTRL1_DEFAULT, pll_params->ext_misc_reg[2], pll);
+	_pllss_set_defaults(pll);
 
 	val = pll_readl_base(pll);
 	val_iddq = readl_relaxed(clk_base + pll_params->iddq_reg);
@@ -2546,27 +2581,12 @@ struct clk *tegra_clk_register_plle_tegra210(const char *name,
 {
 	struct tegra_clk_pll *pll;
 	struct clk *clk;
-	u32 val, val_aux;
 
 	pll = _tegra_init_pll(clk_base, NULL, pll_params, lock);
 	if (IS_ERR(pll))
 		return ERR_CAST(pll);
 
-	/* ensure parent is set to pll_re_vco */
-
-	val = pll_readl_base(pll);
-	val_aux = pll_readl(pll_params->aux_reg, pll);
-
-	if (val & PLLE_BASE_ENABLE) {
-		if ((val_aux & PLLE_AUX_PLLRE_SEL) ||
-			(val_aux & PLLE_AUX_PLLP_SEL))
-			WARN(1, "pll_e enabled with unsupported parent %s\n",
-			  (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" :
-					"pll_re_vco");
-	} else {
-		val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
-		pll_writel(val_aux, pll_params->aux_reg, pll);
-	}
+	_clk_plle_tegra_init_parent(pll);
 
 	clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
 				      &tegra_clk_plle_tegra210_ops);
@@ -2710,3 +2730,163 @@ struct clk *tegra_clk_register_pllmb(const char *name, const char *parent_name,
 }
 
 #endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARCH_TEGRA_210_SOC)
+void tegra_clk_pll_resume(struct clk *c, unsigned long rate)
+{
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	struct clk_hw *parent = clk_hw_get_parent(hw);
+
+	if (clk_pll_is_enabled(hw))
+		return;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	if (pll->params->set_defaults)
+		pll->params->set_defaults(pll);
+
+	clk_set_rate(c, rate);
+	clk_enable(c);
+}
+
+void tegra_clk_sync_state_pll(struct clk *c)
+{
+	struct clk_hw *hw = __clk_get_hw(c);
+
+	if (!__clk_get_enable_count(c))
+		clk_pll_disable(hw);
+}
+
+void tegra_clk_pllcx_resume(struct clk *c, unsigned long rate)
+{
+	struct clk *parent = clk_get_parent(c);
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	struct tegra_clk_pll_freq_table cfg;
+	unsigned long parent_rate;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_get_rate(parent);
+
+	cfg.n = 0;
+	cfg.p = 0;
+	cfg.m = _pll_fixed_mdiv(pll->params, parent_rate);
+
+	pll_writel_base(0, pll);
+	_update_pll_mnp(pll, &cfg);
+
+	pll_writel_misc(PLLCX_MISC_DEFAULT, pll);
+	pll_writel(PLLCX_MISC1_DEFAULT, pll->params->ext_misc_reg[0], pll);
+	pll_writel(PLLCX_MISC2_DEFAULT, pll->params->ext_misc_reg[1], pll);
+	pll_writel(PLLCX_MISC3_DEFAULT, pll->params->ext_misc_reg[2], pll);
+
+	_pllcx_update_dynamic_coef(pll, parent_rate, cfg.n);
+
+	clk_pllc_set_rate(hw, rate, parent_rate);
+	clk_pllc_enable(hw);
+}
+
+void tegra_clk_pllxc_resume(struct clk *c, unsigned long rate)
+{
+	struct clk *parent = clk_get_parent(c);
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	unsigned long parent_rate;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_get_rate(parent);
+
+	if (_setup_dynamic_ramp(pll->params, pll->clk_base, parent_rate))
+		return;
+
+	clk_pllxc_set_rate(hw, rate, parent_rate);
+	clk_pll_enable(hw);
+}
+
+void tegra_clk_pllre_vco_resume(struct clk *c, unsigned long rate)
+{
+	struct clk *parent = clk_get_parent(c);
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	unsigned long parent_rate;
+	u32 val;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_get_rate(parent);
+
+	/* disable lock override */
+	val = pll_readl_misc(pll);
+	val &= ~BIT(29);
+	pll_writel_misc(val, pll);
+
+	clk_pllre_set_rate(hw, rate, parent_rate);
+	clk_pll_enable(hw);
+}
+
+void tegra_clk_pllu_resume(struct clk *c, unsigned long rate)
+{
+	struct clk *parent = clk_get_parent(c);
+	struct clk_hw *hw = __clk_get_hw(c);
+	unsigned long parent_rate;
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_get_rate(parent);
+	clk_pllre_set_rate(hw, rate, parent_rate);
+	clk_enable(c);
+}
+
+void tegra_clk_plle_tegra210_resume(struct clk *c)
+{
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+
+	_clk_plle_tegra_init_parent(pll);
+}
+
+void tegra_clk_pllss_resume(struct clk *c, unsigned long rate)
+{
+	struct clk_hw *hw = __clk_get_hw(c);
+	struct clk *parent = clk_get_parent(c);
+	struct tegra_clk_pll *pll = to_clk_pll(hw);
+	struct tegra_clk_pll_freq_table cfg;
+	unsigned long parent_rate;
+
+	if (clk_pll_is_enabled(hw))
+		return; /* already resumed */
+
+	if (IS_ERR(parent)) {
+		WARN_ON(1);
+		return;
+	}
+
+	parent_rate = clk_get_rate(parent);
+
+	_get_pll_mnp(pll, &cfg);
+	cfg.m = _pll_fixed_mdiv(pll->params, parent_rate);
+	_update_pll_mnp(pll, &cfg);
+
+	_pllss_set_defaults(pll);
+	clk_pllxc_set_rate(hw, rate, parent_rate);
+	clk_pll_enable(hw);
+}
+#endif
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 09bccbb9640c..c82633686820 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -841,6 +841,20 @@ int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div);
 int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
 		 u8 frac_width, u8 flags);
 
+#ifdef CONFIG_PM_SLEEP
+void tegra_clk_pll_resume(struct clk *c, unsigned long rate);
+void tegra_clk_pllcx_resume(struct clk *c, unsigned long rate);
+void tegra_clk_pllxc_resume(struct clk *c, unsigned long rate);
+void tegra_clk_pllre_vco_resume(struct clk *c, unsigned long rate);
+void tegra_clk_pllu_resume(struct clk *c, unsigned long rate);
+void tegra_clk_pllss_resume(struct clk *c, unsigned long rate);
+void tegra_clk_divider_resume(struct clk_hw *hw, unsigned long rate);
+void tegra_clk_pll_out_resume(struct clk *clk, unsigned long rate);
+void tegra_clk_plle_tegra210_resume(struct clk *c);
+void tegra_clk_sync_state_pll(struct clk *c);
+void tegra_clk_sync_state_pll_out(struct clk *clk);
+#endif
+
 
 /* Combined read fence with delay */
 #define fence_udelay(delay, reg)	\
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 04/12] clk: tegra: add support for peripheral clock suspend and resume
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (2 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 03/12] clk: tegra: save and restore PLLs state for system Sowjanya Komatineni
@ 2019-05-21 23:31 ` Sowjanya Komatineni
  2019-05-21 23:31 ` [PATCH V1 05/12] clk: tegra: add support for OSC clock resume Sowjanya Komatineni
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch implements peripheral clock context save and restore
to support system suspend and resume operation.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++-
 drivers/clk/tegra/clk.h |  3 ++
 2 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index 6f2862eddad7..08b788766564 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -81,6 +81,10 @@ static struct clk **clks;
 static int clk_num;
 static struct clk_onecell_data clk_data;
 
+#ifdef CONFIG_PM_SLEEP
+static u32 *periph_ctx;
+#endif
+
 /* Handlers for SoC-specific reset lines */
 static int (*special_reset_assert)(unsigned long);
 static int (*special_reset_deassert)(unsigned long);
@@ -210,6 +214,65 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
 	}
 }
 
+#ifdef CONFIG_PM_SLEEP
+void tegra_clk_periph_suspend(void __iomem *clk_base)
+{
+	int i, idx;
+
+	idx = 0;
+	for (i = 0; i < periph_banks; i++, idx++)
+		periph_ctx[idx] =
+			readl_relaxed(clk_base + periph_regs[i].rst_reg);
+
+	for (i = 0; i < periph_banks; i++, idx++)
+		periph_ctx[idx] =
+			readl_relaxed(clk_base + periph_regs[i].enb_reg);
+}
+
+void tegra_clk_periph_force_on(u32 *clks_on, int count, void __iomem *clk_base)
+{
+	int i;
+
+	WARN_ON(count != periph_banks);
+
+	for (i = 0; i < count; i++)
+		writel_relaxed(clks_on[i], clk_base + periph_regs[i].enb_reg);
+}
+
+void tegra_clk_periph_resume(void __iomem *clk_base)
+{
+	int i, idx;
+
+	idx = 0;
+	for (i = 0; i < periph_banks; i++, idx++)
+		writel_relaxed(periph_ctx[idx],
+			       clk_base + periph_regs[i].rst_reg);
+
+	/* ensure all resets have propagated */
+	fence_udelay(2, clk_base);
+	tegra_read_chipid();
+
+	for (i = 0; i < periph_banks; i++, idx++)
+		writel_relaxed(periph_ctx[idx],
+			       clk_base + periph_regs[i].enb_reg);
+
+	/* ensure all enables have propagated */
+	fence_udelay(2, clk_base);
+	tegra_read_chipid();
+}
+
+static int tegra_clk_suspend_ctx_init(int banks)
+{
+	int err = 0;
+
+	periph_ctx = kzalloc(2 * banks * sizeof(*periph_ctx), GFP_KERNEL);
+	if (!periph_ctx)
+		err = -ENOMEM;
+
+	return err;
+}
+#endif
+
 struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
 {
 	clk_base = regs;
@@ -226,11 +289,20 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
 	periph_banks = banks;
 
 	clks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL);
-	if (!clks)
+	if (!clks) {
 		kfree(periph_clk_enb_refcnt);
+		return NULL;
+	}
 
 	clk_num = num;
 
+#ifdef CONFIG_PM_SLEEP
+	if (tegra_clk_suspend_ctx_init(banks)) {
+		kfree(periph_clk_enb_refcnt);
+		kfree(clks);
+		return NULL;
+	}
+#endif
 	return clks;
 }
 
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index c82633686820..ef444648fcb1 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -853,6 +853,9 @@ void tegra_clk_pll_out_resume(struct clk *clk, unsigned long rate);
 void tegra_clk_plle_tegra210_resume(struct clk *c);
 void tegra_clk_sync_state_pll(struct clk *c);
 void tegra_clk_sync_state_pll_out(struct clk *clk);
+void tegra_clk_periph_suspend(void __iomem *clk_base);
+void tegra_clk_periph_resume(void __iomem *clk_base);
+void tegra_clk_periph_force_on(u32 *clks_on, int count, void __iomem *clk_base);
 #endif
 
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 05/12] clk: tegra: add support for OSC clock resume
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (3 preceding siblings ...)
  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 ` Sowjanya Komatineni
  2019-05-21 23:31 ` [PATCH V1 06/12] clk: tegra: add suspend resume support for DFLL clock Sowjanya Komatineni
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch adds support for restoring OSC control context on resume.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra-fixed.c | 15 +++++++++++++++
 drivers/clk/tegra/clk.h             |  1 +
 2 files changed, 16 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c
index 91c38f1666c1..7efb35717e06 100644
--- a/drivers/clk/tegra/clk-tegra-fixed.c
+++ b/drivers/clk/tegra/clk-tegra-fixed.c
@@ -28,7 +28,10 @@
 #define OSC_CTRL			0x50
 #define OSC_CTRL_OSC_FREQ_SHIFT		28
 #define OSC_CTRL_PLL_REF_DIV_SHIFT	26
+#define OSC_CTRL_MASK			(0x3f2 |	\
+					(0xf << OSC_CTRL_OSC_FREQ_SHIFT))
 
+static u32 osc_ctrl_ctx;
 int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
 			      unsigned long *input_freqs, unsigned int num,
 			      unsigned int clk_m_div, unsigned long *osc_freq,
@@ -107,3 +110,15 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
 		*dt_clk = clk;
 	}
 }
+
+#ifdef CONFIG_PM_SLEEP
+void tegra_clk_osc_resume(void __iomem *clk_base)
+{
+	u32 val;
+
+	val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK;
+	val |= osc_ctrl_ctx;
+	writel_relaxed(val, clk_base + OSC_CTRL);
+	fence_udelay(2, clk_base);
+}
+#endif
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index ef444648fcb1..7567cf7bd29e 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -856,6 +856,7 @@ void tegra_clk_sync_state_pll_out(struct clk *clk);
 void tegra_clk_periph_suspend(void __iomem *clk_base);
 void tegra_clk_periph_resume(void __iomem *clk_base);
 void tegra_clk_periph_force_on(u32 *clks_on, int count, void __iomem *clk_base);
+void tegra_clk_osc_resume(void __iomem *clk_base);
 #endif
 
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 06/12] clk: tegra: add suspend resume support for DFLL clock
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (4 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 05/12] clk: tegra: add support for OSC clock resume Sowjanya Komatineni
@ 2019-05-21 23:31 ` Sowjanya Komatineni
  2019-05-21 23:31 ` [PATCH V1 07/12] clk: tegra: support for Tegra210 clocks suspend-resume Sowjanya Komatineni
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch adds support for suspend and resume for DFLL clock.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-dfll.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-dfll.h |  2 ++
 2 files changed, 84 insertions(+)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index 1fc71baae13b..d92a5a05fbbc 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -286,6 +286,7 @@ struct tegra_dfll {
 	unsigned long			dvco_rate_min;
 
 	enum dfll_ctrl_mode		mode;
+	enum dfll_ctrl_mode		resume_mode;
 	enum dfll_tune_range		tune_range;
 	struct dentry			*debugfs_dir;
 	struct clk_hw			dfll_clk_hw;
@@ -1873,6 +1874,87 @@ static int dfll_fetch_common_params(struct tegra_dfll *td)
 }
 
 /*
+ * tegra_dfll_suspend
+ * @pdev: DFLL instance
+ *
+ * dfll controls clock/voltage to other devices, including CPU. Therefore,
+ * dfll driver pm suspend callback does not stop cl-dvfs operations. It is
+ * only used to enforce cold voltage limit, since SoC may cool down during
+ * suspend without waking up. The correct temperature zone after suspend will
+ * be updated via dfll cooling device interface during resume of temperature
+ * sensor.
+ */
+void tegra_dfll_suspend(struct platform_device *pdev)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	if (!td)
+		return;
+
+	if (td->mode <= DFLL_DISABLED)
+		return;
+
+	td->resume_mode = td->mode;
+	switch (td->mode) {
+	case DFLL_CLOSED_LOOP:
+		dfll_set_mode(td, DFLL_CLOSED_LOOP);
+		dfll_set_frequency_request(td, &td->last_req);
+
+		dfll_unlock(td);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * tegra_dfll_resume - reprogram the DFLL after context-loss
+ * @pdev: DFLL instance
+ *
+ * Re-initialize and enable target device clock in open loop mode. Called
+ * directly from SoC clock resume syscore operation. Closed loop will be
+ * re-entered in platform syscore ops as well after CPU clock source is
+ * switched to DFLL in open loop.
+ */
+void tegra_dfll_resume(struct platform_device *pdev, bool on_dfll)
+{
+	struct tegra_dfll *td = dev_get_drvdata(&pdev->dev);
+
+	if (!td)
+		return;
+
+	if (on_dfll) {
+		if (td->resume_mode == DFLL_CLOSED_LOOP)
+			dfll_lock(td);
+		td->resume_mode = DFLL_DISABLED;
+		return;
+	}
+
+	reset_control_deassert(td->dvco_rst);
+
+	pm_runtime_get(td->dev);
+
+	/* Re-init DFLL */
+	dfll_init_out_if(td);
+	dfll_set_default_params(td);
+	dfll_set_open_loop_config(td);
+
+	pm_runtime_put(td->dev);
+
+	/* Restore last request and mode up to open loop */
+	switch (td->resume_mode) {
+	case DFLL_CLOSED_LOOP:
+	case DFLL_OPEN_LOOP:
+		dfll_set_mode(td, DFLL_OPEN_LOOP);
+		if (td->pmu_if == TEGRA_DFLL_PMU_I2C)
+			dfll_i2c_set_output_enabled(td, false);
+		break;
+	default:
+		break;
+	}
+}
+
+/*
  * API exported to per-SoC platform drivers
  */
 
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
index 85d0d95223f3..44160b0495fe 100644
--- a/drivers/clk/tegra/clk-dfll.h
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -48,6 +48,8 @@ struct tegra_dfll_soc_data {
 int tegra_dfll_register(struct platform_device *pdev,
 			struct tegra_dfll_soc_data *soc);
 struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev);
+void tegra_dfll_suspend(struct platform_device *pdev);
+void tegra_dfll_resume(struct platform_device *pdev, bool on_dfll);
 int tegra_dfll_runtime_suspend(struct device *dev);
 int tegra_dfll_runtime_resume(struct device *dev);
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 07/12] clk: tegra: support for Tegra210 clocks suspend-resume
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (5 preceding siblings ...)
  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 ` Sowjanya Komatineni
  2019-05-21 23:31 ` [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models Sowjanya Komatineni
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch adds system suspend and resume support for Tegra210
clocks.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/clk/tegra/clk-tegra210.c | 382 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 382 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index ed3c7df75d1e..d012b53ca132 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -20,10 +20,12 @@
 #include <linux/clkdev.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_platform.h>
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/mutex.h>
 #include <linux/clk/tegra.h>
+#include <linux/syscore_ops.h>
 #include <dt-bindings/clock/tegra210-car.h>
 #include <dt-bindings/reset/tegra210-car.h>
 #include <linux/iopoll.h>
@@ -31,6 +33,7 @@
 #include <soc/tegra/pmc.h>
 
 #include "clk.h"
+#include "clk-dfll.h"
 #include "clk-id.h"
 
 /*
@@ -48,6 +51,9 @@
 #define CLK_SOURCE_SDMMC2 0x154
 #define CLK_SOURCE_SDMMC4 0x164
 
+#define CLK_OUT_ENB_Y 0x298
+#define CLK_ENB_PLLP_OUT_CPU BIT(31)
+
 #define PLLC_BASE 0x80
 #define PLLC_OUT 0x84
 #define PLLC_MISC0 0x88
@@ -71,6 +77,8 @@
 #define PLLM_MISC1 0x98
 #define PLLM_MISC2 0x9c
 #define PLLP_BASE 0xa0
+#define PLLP_OUTA 0xa4
+#define PLLP_OUTB 0xa8
 #define PLLP_MISC0 0xac
 #define PLLP_MISC1 0x680
 #define PLLA_BASE 0xb0
@@ -227,9 +235,18 @@
 #define XUSB_PLL_CFG0_UTMIPLL_LOCK_DLY		0x3ff
 #define XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK	(0x3ff << 14)
 
+#define SCLK_BURST_POLICY	0x28
+#define SYSTEM_CLK_RATE		0x30
+#define CLK_MASK_ARM		0x44
+#define MISC_CLK_ENB		0x48
+#define CCLKG_BURST_POLICY	0x368
+#define CCLKLP_BURST_POLICY	0x370
+#define CPU_SOFTRST_CTRL	0x380
+#define SYS_CLK_DIV		0x400
 #define SPARE_REG0 0x55c
 #define CLK_M_DIVISOR_SHIFT 2
 #define CLK_M_DIVISOR_MASK 0x3
+#define BURST_POLICY_REG_SIZE 2
 
 #define RST_DFLL_DVCO 0x2f4
 #define DVFS_DFLL_RESET_SHIFT 0
@@ -3381,6 +3398,367 @@ static struct tegra_clk_init_table init_table[] __initdata = {
 	{ TEGRA210_CLK_CLK_MAX, TEGRA210_CLK_CLK_MAX, 0, 0 },
 };
 
+#ifdef CONFIG_PM_SLEEP
+static unsigned long pll_c_rate, pll_c2_rate, pll_c3_rate, pll_x_rate;
+static unsigned long pll_c4_rate, pll_d2_rate, pll_dp_rate;
+static unsigned long pll_re_vco_rate, pll_d_rate, pll_a_rate, pll_a1_rate;
+static unsigned long pll_c_out1_rate;
+static unsigned long pll_a_out0_rate, pll_c4_out3_rate;
+static unsigned long pll_p_out_rate[5];
+static unsigned long pll_u_out1_rate, pll_u_out2_rate;
+static unsigned long pll_mb_rate;
+static u32 pll_m_v;
+static u32 pll_p_outa, pll_p_outb;
+static u32 pll_re_out_div, pll_re_out_1;
+static u32 cpu_softrst_ctx[3];
+static u32 cclkg_burst_policy_ctx[2];
+static u32 cclklp_burst_policy_ctx[2];
+static u32 sclk_burst_policy_ctx[3];
+static u32 sclk_ctx, spare_ctx, misc_clk_enb_ctx, clk_arm_ctx;
+
+static struct platform_device *dfll_pdev;
+#define car_readl(_base, _off) \
+	readl_relaxed(clk_base + (_base) + ((_off) * 4))
+#define car_writel(_val, _base, _off) \
+	writel_relaxed(_val, clk_base + (_base) + ((_off) * 4))
+
+static u32 *periph_clk_src_ctx;
+struct periph_source_bank {
+	u32 start;
+	u32 end;
+};
+
+static struct periph_source_bank periph_srcs[] = {
+	[0] = {
+		.start = 0x100,
+		.end = 0x198,
+	},
+	[1] = {
+		.start = 0x1a0,
+		.end = 0x1f8,
+	},
+	[2] = {
+		.start = 0x3b4,
+		.end = 0x42c,
+	},
+	[3] = {
+		.start = 0x49c,
+		.end = 0x4b4,
+	},
+	[4] = {
+		.start = 0x560,
+		.end = 0x564,
+	},
+	[5] = {
+		.start = 0x600,
+		.end = 0x678,
+	},
+	[6] = {
+		.start = 0x694,
+		.end = 0x6a0,
+	},
+	[7] = {
+		.start = 0x6b8,
+		.end = 0x718,
+	},
+};
+
+/* This array lists the valid clocks for each periph clk bank */
+static u32 periph_clks_on[] = {
+	0xdcd7dff9,
+	0x87d1f3e7,
+	0xf3fed3fa,
+	0xffc18cfb,
+	0x793fb7ff,
+	0x3fe66fff,
+	0xfc1fc7ff,
+};
+
+static inline unsigned long clk_get_rate_nolock(struct clk *clk)
+{
+	if (IS_ERR_OR_NULL(clk)) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	return clk_hw_get_rate(__clk_get_hw(clk));
+}
+
+static inline struct clk *pll_p_clk(unsigned int x)
+{
+	if (x < 4) {
+		return clks[TEGRA210_CLK_PLL_P_OUT1 + x];
+	} else if (x != 4) {
+		WARN_ON(1);
+		return NULL;
+	} else {
+		return clks[TEGRA210_CLK_PLL_P_OUT5];
+	}
+}
+
+static u32 * __init tegra210_init_suspend_ctx(void)
+{
+	int i, size = 0;
+
+	for (i = 0; i < ARRAY_SIZE(periph_srcs); i++)
+		size += periph_srcs[i].end - periph_srcs[i].start + 4;
+
+	periph_clk_src_ctx = kmalloc(size, GFP_KERNEL);
+
+	return periph_clk_src_ctx;
+}
+
+static int tegra210_clk_suspend(void)
+{
+	int i;
+	unsigned long off;
+	struct device_node *node;
+	u32 *clk_rst_ctx = periph_clk_src_ctx;
+	u32 val;
+
+	pll_a_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_A]);
+	pll_a_out0_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_A_OUT0]);
+	pll_a1_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_A1]);
+	pll_c2_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C2]);
+	pll_c3_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C3]);
+	pll_c_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C]);
+	pll_c_out1_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C_OUT1]);
+	pll_x_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_X]);
+	pll_c4_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C4]);
+	pll_c4_out3_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_C4_OUT3]);
+	pll_dp_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_DP]);
+	pll_d_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_D]);
+	pll_d2_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_D2]);
+	pll_re_vco_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_RE_VCO]);
+	pll_re_out_div = car_readl(PLLRE_BASE, 0) & (0xf << 16);
+	pll_re_out_1 = car_readl(PLLRE_OUT1, 0);
+	pll_u_out1_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_U_OUT1]);
+	pll_u_out2_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_U_OUT2]);
+	pll_mb_rate = clk_get_rate_nolock(clks[TEGRA210_CLK_PLL_MB]);
+	pll_m_v = car_readl(PLLM_BASE, 0);
+	pll_p_outa = car_readl(PLLP_OUTA, 0);
+	pll_p_outb = car_readl(PLLP_OUTB, 0);
+
+	for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+		cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i);
+
+	for (i = 0; i < ARRAY_SIZE(pll_p_out_rate); i++)
+		pll_p_out_rate[i] = clk_get_rate_nolock(pll_p_clk(i));
+
+	for (i = 0; i < BURST_POLICY_REG_SIZE; i++) {
+		cclkg_burst_policy_ctx[i] = car_readl(CCLKG_BURST_POLICY, i);
+		cclklp_burst_policy_ctx[i] = car_readl(CCLKLP_BURST_POLICY, i);
+		sclk_burst_policy_ctx[i] = car_readl(SCLK_BURST_POLICY, i);
+	}
+	sclk_burst_policy_ctx[i] = car_readl(SYS_CLK_DIV, 0);
+
+	sclk_ctx = car_readl(SYSTEM_CLK_RATE, 0);
+	spare_ctx = car_readl(SPARE_REG0, 0);
+	misc_clk_enb_ctx = car_readl(MISC_CLK_ENB, 0);
+	clk_arm_ctx = car_readl(CLK_MASK_ARM, 0);
+
+	for (i = 0; i < ARRAY_SIZE(periph_srcs); i++)
+		for (off = periph_srcs[i].start; off <= periph_srcs[i].end;
+			off += 4)
+			*clk_rst_ctx++ = car_readl(off, 0);
+
+	if (!dfll_pdev) {
+		node = of_find_compatible_node(NULL, NULL,
+					       "nvidia,tegra210-dfll");
+		if (node)
+			dfll_pdev = of_find_device_by_node(node);
+		of_node_put(node);
+		if (!dfll_pdev)
+			pr_err("dfll node not found. no suspend for dfll\n");
+	}
+
+	if (dfll_pdev)
+		tegra_dfll_suspend(dfll_pdev);
+
+	/* Enable PLLP_OUT_CPU after dfll suspend */
+	val = car_readl(CLK_OUT_ENB_Y, 0);
+	val |= CLK_ENB_PLLP_OUT_CPU;
+	car_writel(val, CLK_OUT_ENB_Y, 0);
+
+	tegra_clk_periph_suspend(clk_base);
+	return 0;
+}
+
+static void tegra210_clk_resume(void)
+{
+	int i;
+	unsigned long off;
+	u32 val;
+	u32 *clk_rst_ctx = periph_clk_src_ctx;
+	struct clk_hw *parent;
+
+	tegra_clk_osc_resume(clk_base);
+
+	for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++)
+		car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i);
+
+	/*
+	 * Since we are going to reset devices and switch clock sources in this
+	 * function, plls and secondary dividers is required to be enabled. The
+	 * actual value will be restored back later.
+	 */
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_P_OUT1],
+				 pll_p_out_rate[0]);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_P_OUT3],
+				 pll_p_out_rate[2]);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_P_OUT4],
+				 pll_p_out_rate[3]);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_P_OUT5],
+				 pll_p_out_rate[4]);
+
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_A1], pll_a1_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_C2], pll_c2_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_C3], pll_c3_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_C], pll_c_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_X], pll_x_rate);
+
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_A], pll_a_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_D], pll_d_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_D2], pll_d2_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_DP], pll_dp_rate);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_C4], pll_c4_rate);
+
+	/* enable the PLLD */
+	val = car_readl(PLLD_MISC0, 0);
+	val |= PLLD_MISC0_DSI_CLKENABLE;
+	car_writel(val, PLLD_MISC0, 0);
+
+	/* reprogram PLLRE post divider, VCO, 2ndary divider (in this order) */
+	if (!__clk_is_enabled(clks[TEGRA210_CLK_PLL_RE_VCO])) {
+		val = car_readl(PLLRE_BASE, 0);
+		val &= ~(0xf << 16);
+		car_writel(val | pll_re_out_div, PLLRE_BASE, 0);
+	}
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_RE_VCO], pll_re_vco_rate);
+
+	car_writel(pll_re_out_1, PLLRE_OUT1, 0);
+
+	/* resume PLLU */
+	tegra210_init_pllu();
+
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_U_OUT1],
+				 pll_u_out1_rate);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_U_OUT2],
+				 pll_u_out2_rate);
+
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_C_OUT1],
+				 pll_c_out1_rate);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_A_OUT0],
+				 pll_a_out0_rate);
+	tegra_clk_pll_out_resume(clks[TEGRA210_CLK_PLL_C4_OUT3],
+				 pll_c4_out3_rate);
+	/*
+	 * resume SCLK and CPULP clocks
+	 * for SCLk 1st set safe dividers values, then restore source,
+	 * then restore dividers
+	 */
+	car_writel(0x1, SYSTEM_CLK_RATE, 0);
+	val = car_readl(SYS_CLK_DIV, 0);
+	i = BURST_POLICY_REG_SIZE;
+	if (val < sclk_burst_policy_ctx[i])
+		car_writel(sclk_burst_policy_ctx[i], SYS_CLK_DIV, 0);
+	fence_udelay(2, clk_base);
+	for (i = 0; i < BURST_POLICY_REG_SIZE; i++) {
+		car_writel(cclklp_burst_policy_ctx[i], CCLKLP_BURST_POLICY, i);
+		car_writel(sclk_burst_policy_ctx[i], SCLK_BURST_POLICY, i);
+	}
+	car_writel(sclk_burst_policy_ctx[i], SYS_CLK_DIV, 0);
+
+	car_writel(sclk_ctx, SYSTEM_CLK_RATE, 0);
+	car_writel(spare_ctx, SPARE_REG0, 0);
+	car_writel(misc_clk_enb_ctx, MISC_CLK_ENB, 0);
+	car_writel(clk_arm_ctx, CLK_MASK_ARM, 0);
+
+	/* enable all clocks before configuring clock sources */
+	tegra_clk_periph_force_on(periph_clks_on, ARRAY_SIZE(periph_clks_on),
+				  clk_base);
+
+	wmb();
+	fence_udelay(2, clk_base);
+
+	for (i = 0; i < ARRAY_SIZE(periph_srcs); i++)
+		for (off = periph_srcs[i].start; off <= periph_srcs[i].end;
+		     off += 4)
+			car_writel(*clk_rst_ctx++, off, 0);
+
+	/* propagate and restore resets, restore clock state */
+	fence_udelay(5, clk_base);
+	tegra_clk_periph_resume(clk_base);
+
+	/* restore (sync) the actual PLL and secondary divider values */
+	car_writel(pll_p_outa, PLLP_OUTA, 0);
+	car_writel(pll_p_outb, PLLP_OUTB, 0);
+
+	tegra_clk_sync_state_pll_out(clks[TEGRA210_CLK_PLL_U_OUT1]);
+	tegra_clk_sync_state_pll_out(clks[TEGRA210_CLK_PLL_U_OUT2]);
+
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_A1]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_C2]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_C3]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_C]);
+
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_RE_VCO]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_C4]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_D2]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_DP]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_A]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_D]);
+
+	tegra_clk_sync_state_pll_out(clks[TEGRA210_CLK_PLL_C_OUT1]);
+	tegra_clk_sync_state_pll_out(clks[TEGRA210_CLK_PLL_A_OUT0]);
+
+	/*
+	 * restore CPUG clocks:
+	 * - enable DFLL in open loop mode
+	 * - switch CPUG to DFLL clock source
+	 * - close DFLL loop
+	 * - sync PLLX state
+	 */
+	if (dfll_pdev)
+		tegra_dfll_resume(dfll_pdev, false);
+
+	for (i = 0; i < BURST_POLICY_REG_SIZE; i++)
+		car_writel(cclkg_burst_policy_ctx[i], CCLKG_BURST_POLICY, i);
+	fence_udelay(2, clk_base);
+
+	if (dfll_pdev)
+		tegra_dfll_resume(dfll_pdev, true);
+
+	parent = clk_hw_get_parent(__clk_get_hw(clks[TEGRA210_CLK_CCLK_G]));
+	if (parent != __clk_get_hw(clks[TEGRA210_CLK_PLL_X]))
+		tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_X]);
+
+	/* Disable PLL_OUT_CPU after DFLL resume */
+	val = car_readl(CLK_OUT_ENB_Y, 0);
+	val &= ~CLK_ENB_PLLP_OUT_CPU;
+	car_writel(val, CLK_OUT_ENB_Y, 0);
+
+	car_writel(pll_m_v, PLLM_BASE, 0);
+	tegra_clk_pll_resume(clks[TEGRA210_CLK_PLL_MB], pll_mb_rate);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_M]);
+	tegra_clk_sync_state_pll(clks[TEGRA210_CLK_PLL_MB]);
+
+	tegra_clk_plle_tegra210_resume(clks[TEGRA210_CLK_PLL_E]);
+}
+#else
+#define tegra210_clk_suspend	NULL
+#define tegra210_clk_resume	NULL
+static inline u32 *tegra210_init_suspend_ctx(void)
+{
+	return NULL;
+}
+#endif
+
+static struct syscore_ops tegra_clk_syscore_ops = {
+	.suspend = tegra210_clk_suspend,
+	.resume = tegra210_clk_resume,
+};
+
 /**
  * tegra210_clock_apply_init_table - initialize clocks on Tegra210 SoCs
  *
@@ -3591,5 +3969,9 @@ static void __init tegra210_clock_init(struct device_node *np)
 	tegra210_mbist_clk_init();
 
 	tegra_cpu_car_ops = &tegra210_cpu_car_ops;
+
+	if (tegra210_init_suspend_ctx())
+		register_syscore_ops(&tegra_clk_syscore_ops);
+
 }
 CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (6 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 07/12] clk: tegra: support for Tegra210 clocks suspend-resume Sowjanya Komatineni
@ 2019-05-21 23:31 ` 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
                   ` (4 subsequent siblings)
  12 siblings, 2 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch allows to create separate irq_set_wake and irq_set_type
implementations for different tegra designs PMC that has different
wake models which require difference wake registers and different
programming sequence.

AOWAKE model support is available for Tegra186 and Tegra194 only
and it resides within PMC and supports tiered wake architecture.

Tegra210 and prior tegra designs uses PMC directly to receive wake
events and coordinate the wake sequence.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 5648e5c09ef5..f77ce4b827e3 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -235,6 +235,8 @@ struct tegra_pmc_soc {
 	void (*setup_irq_polarity)(struct tegra_pmc *pmc,
 				   struct device_node *np,
 				   bool invert);
+	int (*pmc_irq_set_wake)(struct irq_data *data, unsigned int on);
+	int (*pmc_irq_set_type)(struct irq_data *data, unsigned int type);
 
 	const char * const *reset_sources;
 	unsigned int num_reset_sources;
@@ -1915,12 +1917,15 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
 	.alloc = tegra_pmc_irq_alloc,
 };
 
-static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
 	unsigned int offset, bit;
 	u32 value;
 
+	if (data->hwirq < 0)
+		return 0;
+
 	offset = data->hwirq / 32;
 	bit = data->hwirq % 32;
 
@@ -1943,12 +1948,12 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 	return 0;
 }
 
-static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
 	u32 value;
 
-	if (data->hwirq == ULONG_MAX)
+	if (data->hwirq < 0)
 		return 0;
 
 	value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
@@ -1996,8 +2001,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
 	pmc->irq.irq_unmask = irq_chip_unmask_parent;
 	pmc->irq.irq_eoi = irq_chip_eoi_parent;
 	pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
-	pmc->irq.irq_set_type = tegra_pmc_irq_set_type;
-	pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake;
+	if (pmc->soc->pmc_irq_set_type)
+		pmc->irq.irq_set_type = pmc->soc->pmc_irq_set_type;
+	if (pmc->soc->pmc_irq_set_wake)
+		pmc->irq.irq_set_wake = pmc->soc->pmc_irq_set_wake;
 
 	pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node,
 					       &tegra_pmc_irq_domain_ops, pmc);
@@ -2670,6 +2677,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
 	.regs = &tegra186_pmc_regs,
 	.init = NULL,
 	.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
+	.pmc_irq_set_wake = tegra186_pmc_irq_set_wake,
+	.pmc_irq_set_type = tegra186_pmc_irq_set_type,
 	.reset_sources = tegra186_reset_sources,
 	.num_reset_sources = ARRAY_SIZE(tegra186_reset_sources),
 	.reset_levels = tegra186_reset_levels,
@@ -2748,6 +2757,8 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
 	.regs = &tegra186_pmc_regs,
 	.init = NULL,
 	.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
+	.pmc_irq_set_wake = tegra186_pmc_irq_set_wake,
+	.pmc_irq_set_type = tegra186_pmc_irq_set_type,
 	.num_wake_events = ARRAY_SIZE(tegra194_wake_events),
 	.wake_events = tegra194_wake_events,
 };
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 09/12] soc/tegra: pmc: add pmc wake support for tegra210
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (7 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models Sowjanya Komatineni
@ 2019-05-21 23:31 ` Sowjanya Komatineni
  2019-05-22 13:01   ` Thierry Reding
  2019-05-21 23:31 ` [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO Sowjanya Komatineni
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch implements PMC wakeup sequence for Tegra210 and defines
common used wake events of RTC alarm and power key.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 drivers/soc/tegra/pmc.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index f77ce4b827e3..5e68e1de1780 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -57,6 +57,7 @@
 #include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h>
 #include <dt-bindings/gpio/tegra186-gpio.h>
 #include <dt-bindings/gpio/tegra194-gpio.h>
+#include <dt-bindings/gpio/tegra-gpio.h>
 
 #define PMC_CNTRL			0x0
 #define  PMC_CNTRL_INTR_POLARITY	BIT(17) /* inverts INTR polarity */
@@ -66,6 +67,12 @@
 #define  PMC_CNTRL_SYSCLK_OE		BIT(11) /* system clock enable */
 #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
 #define  PMC_CNTRL_MAIN_RST		BIT(4)
+#define  PMC_CNTRL_LATCH_WAKEUPS	BIT(5)
+
+#define PMC_WAKE_MASK			0x0c
+#define PMC_WAKE_LEVEL			0x10
+#define PMC_WAKE_STATUS			0x14
+#define PMC_SW_WAKE_STATUS		0x18
 
 #define DPD_SAMPLE			0x020
 #define  DPD_SAMPLE_ENABLE		BIT(0)
@@ -96,6 +103,11 @@
 
 #define PMC_SCRATCH41			0x140
 
+#define PMC_WAKE2_MASK			0x160
+#define PMC_WAKE2_LEVEL			0x164
+#define PMC_WAKE2_STATUS		0x168
+#define PMC_SW_WAKE2_STATUS		0x16c
+
 #define PMC_SENSOR_CTRL			0x1b0
 #define  PMC_SENSOR_CTRL_SCRATCH_WRITE	BIT(2)
 #define  PMC_SENSOR_CTRL_ENABLE_RST	BIT(1)
@@ -1917,6 +1929,65 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
 	.alloc = tegra_pmc_irq_alloc,
 };
 
+static inline void clear_pmc_sw_wake_status(void)
+{
+	tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS);
+	if (tegra_get_chip_id() != TEGRA20)
+		tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS);
+}
+
+static inline void clear_pmc_wake_status(void)
+{
+	u32 reg;
+
+	reg = tegra_pmc_readl(pmc, PMC_WAKE_STATUS);
+	if (reg)
+		tegra_pmc_writel(pmc, reg, PMC_WAKE_STATUS);
+	if (tegra_get_chip_id() != TEGRA20) {
+		reg = tegra_pmc_readl(pmc, PMC_WAKE2_STATUS);
+		if (reg)
+			tegra_pmc_writel(pmc, reg, PMC_WAKE2_STATUS);
+	}
+}
+
+static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+	unsigned int offset, bit;
+	u32 pmc_wake_mask_reg;
+	u32 value;
+
+	if (data->hwirq < 0)
+		return 0;
+
+	offset = data->hwirq / 32;
+	bit = data->hwirq % 32;
+
+	clear_pmc_sw_wake_status();
+
+	/* enable PMC wake */
+	value = tegra_pmc_readl(pmc, PMC_CNTRL);
+	value |= PMC_CNTRL_LATCH_WAKEUPS;
+	tegra_pmc_writel(pmc, value, PMC_CNTRL);
+	usleep_range(110, 120);
+
+	value &= ~PMC_CNTRL_LATCH_WAKEUPS;
+	tegra_pmc_writel(pmc, value, PMC_CNTRL);
+	usleep_range(110, 120);
+
+	clear_pmc_wake_status();
+
+	pmc_wake_mask_reg = (offset) ? PMC_WAKE2_MASK : PMC_WAKE_MASK;
+	value = tegra_pmc_readl(pmc, pmc_wake_mask_reg);
+	if (on)
+		value |= 1 << bit;
+	else
+		value &= ~(1 << bit);
+	tegra_pmc_writel(pmc, value, pmc_wake_mask_reg);
+
+	return 0;
+}
+
 static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -1948,6 +2019,46 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
 	return 0;
 }
 
+static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
+	unsigned int offset, bit;
+	u32 pmc_wake_lvl_reg;
+	u32 value;
+
+	if (data->hwirq < 0)
+		return 0;
+
+	offset = data->hwirq / 32;
+	bit = data->hwirq % 32;
+
+	pmc_wake_lvl_reg = (offset) ? PMC_WAKE2_LEVEL : PMC_WAKE_LEVEL;
+	value = tegra_pmc_readl(pmc, pmc_wake_lvl_reg);
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_LEVEL_HIGH:
+		value |= 1 << bit;
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+	case IRQ_TYPE_LEVEL_LOW:
+		value &= ~(1 << bit);
+		break;
+
+	case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
+		value ^= 1 << bit;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	tegra_pmc_writel(pmc, value, pmc_wake_lvl_reg);
+
+	return 0;
+}
+
 static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
 {
 	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
@@ -2535,6 +2646,11 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = {
 	TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC)
 };
 
+static const struct tegra_wake_event tegra210_wake_events[] = {
+	TEGRA_WAKE_GPIO("power", 24, 0, 189), /*TEGRA_GPIO(X, 5)*/
+	TEGRA_WAKE_IRQ("rtc", 16, 2),
+};
+
 static const struct tegra_pmc_soc tegra210_pmc_soc = {
 	.num_powergates = ARRAY_SIZE(tegra210_powergates),
 	.powergates = tegra210_powergates,
@@ -2552,10 +2668,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
 	.regs = &tegra20_pmc_regs,
 	.init = tegra20_pmc_init,
 	.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
+	.pmc_irq_set_wake = tegra210_pmc_irq_set_wake,
+	.pmc_irq_set_type = tegra210_pmc_irq_set_type,
 	.reset_sources = tegra210_reset_sources,
 	.num_reset_sources = ARRAY_SIZE(tegra210_reset_sources),
 	.reset_levels = NULL,
 	.num_reset_levels = 0,
+	.num_wake_events = ARRAY_SIZE(tegra210_wake_events),
+	.wake_events = tegra210_wake_events,
 };
 
 #define TEGRA186_IO_PAD_TABLE(_pad)					     \
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (8 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 09/12] soc/tegra: pmc: add pmc wake support for tegra210 Sowjanya Komatineni
@ 2019-05-21 23:31 ` Sowjanya Komatineni
  2019-05-22 13:24   ` Thierry Reding
  2019-05-21 23:31 ` [PATCH V1 11/12] soc/tegra: pmc: configure tegra deep sleep control settings Sowjanya Komatineni
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

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


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 11/12] soc/tegra: pmc: configure tegra deep sleep control settings
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (9 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO Sowjanya Komatineni
@ 2019-05-21 23:31 ` 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
  12 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

Tegra210 and prior Tegra chips have power request signal polarity,
deep sleep entry and wake related timings which are platform specific
that should be configured before entering into deep sleep.

Below are the timings specific configurations for deep sleep and wake.
- Core rail power-on stabilization timer
- OSC clock stabilization timer after SOC rail power is stabilized.
- Core power off time is the minimum wake delay to keep the system
in deep sleep state irrespective of any quick wake event.

These values depends on the discharge time of regulators and turn OFF
time of the PMIC to allow the complete system to finish entering into
deep sleep state.

These values vary based on the platform design and are specified
through the device tree.

This patch has implementation to configure these configurations which
are must to have for deep sleep state.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |  7 +++++++
 drivers/soc/tegra/pmc.c                        | 18 ++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 4dcd0d36189a..7ac5e55a30aa 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -266,6 +266,13 @@
 
 	pmc@7000e400 {
 		nvidia,invert-interrupt;
+		nvidia,suspend-mode = <0>;
+		nvidia,cpu-pwr-good-time = <0>;
+		nvidia,cpu-pwr-off-time = <0>;
+		nvidia,core-pwr-good-time = <4587 3876>;
+		nvidia,core-pwr-off-time = <39065>;
+		nvidia,core-power-req-active-high;
+		nvidia,sys-clock-req-active-high;
 	};
 
 	/* eMMC */
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 5e68e1de1780..8d225962d136 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -66,6 +66,7 @@
 #define  PMC_CNTRL_SIDE_EFFECT_LP0	BIT(14) /* LP0 when CPU pwr gated */
 #define  PMC_CNTRL_SYSCLK_OE		BIT(11) /* system clock enable */
 #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
+#define  PMC_CNTRL_PWRREQ_POLARITY	BIT(8)
 #define  PMC_CNTRL_MAIN_RST		BIT(4)
 #define  PMC_CNTRL_LATCH_WAKEUPS	BIT(5)
 
@@ -98,6 +99,8 @@
 
 #define PMC_CPUPWRGOOD_TIMER		0xc8
 #define PMC_CPUPWROFF_TIMER		0xcc
+#define PMC_COREPWRGOOD_TIMER		0x3c
+#define PMC_COREPWROFF_TIMER		0xe0
 
 #define PMC_PWR_DET_VALUE		0xe4
 
@@ -2293,6 +2296,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = {
 static void tegra20_pmc_init(struct tegra_pmc *pmc)
 {
 	u32 value;
+	unsigned long osc, pmu, off;
 
 	/* Always enable CPU power request */
 	value = tegra_pmc_readl(pmc, PMC_CNTRL);
@@ -2306,6 +2310,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
 	else
 		value |= PMC_CNTRL_SYSCLK_POLARITY;
 
+	if (pmc->corereq_high)
+		value &= ~PMC_CNTRL_PWRREQ_POLARITY;
+	else
+		value |= PMC_CNTRL_PWRREQ_POLARITY;
+
 	/* configure the output polarity while the request is tristated */
 	tegra_pmc_writel(pmc, value, PMC_CNTRL);
 
@@ -2313,6 +2322,15 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
 	value = tegra_pmc_readl(pmc, PMC_CNTRL);
 	value |= PMC_CNTRL_SYSCLK_OE;
 	tegra_pmc_writel(pmc, value, PMC_CNTRL);
+
+	osc = DIV_ROUND_UP_ULL(pmc->core_osc_time * 8192, 1000000);
+	pmu = DIV_ROUND_UP_ULL(pmc->core_pmu_time * 32768, 1000000);
+	off = DIV_ROUND_UP_ULL(pmc->core_off_time * 32768, 1000000);
+	if (osc && pmu)
+		tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff),
+				 PMC_COREPWRGOOD_TIMER);
+	if (off)
+		tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER);
 }
 
 static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 12/12] arm64: tegra: enable wake from deep sleep on RTC alarm.
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (10 preceding siblings ...)
  2019-05-21 23:31 ` [PATCH V1 11/12] soc/tegra: pmc: configure tegra deep sleep control settings Sowjanya Komatineni
@ 2019-05-21 23:31 ` Sowjanya Komatineni
  2019-05-22 13:33 ` [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Thierry Reding
  12 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-21 23:31 UTC (permalink / raw)
  To: thierry.reding, jonathanh
  Cc: jckuo, talho, josephl, skomatineni, linux-tegra, linux-kernel

This patch updates device tree for RTC and PMC to allow system wake
from deep sleep on RTC alarm.

Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index a550c0a4d572..cf5c215efb04 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -763,7 +763,8 @@
 	rtc@7000e000 {
 		compatible = "nvidia,tegra210-rtc", "nvidia,tegra20-rtc";
 		reg = <0x0 0x7000e000 0x0 0x100>;
-		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <16 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-parent = <&pmc>;
 		clocks = <&tegra_car TEGRA210_CLK_RTC>;
 		clock-names = "rtc";
 	};
@@ -773,6 +774,8 @@
 		reg = <0x0 0x7000e400 0x0 0x400>;
 		clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>;
 		clock-names = "pclk", "clk32k_in";
+		#interrupt-cells = <2>;
+		interrupt-controller;
 
 		powergates {
 			pd_audio: aud {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 01/12] irqchip: tegra: do not disable COP IRQ during suspend
  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
  0 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 12:12 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2379 bytes --]

On Tue, May 21, 2019 at 04:31:12PM -0700, Sowjanya Komatineni wrote:
> BPMP-lite still need IRQ function to finish SC7 suspend sequence for
> Tegra210.
> 
> This patch has fix for leaving the COP IRQ enabled for Tegra210 during
> interrupt controller suspend operation.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/irqchip/irq-tegra.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c
> index 0abc0cd1c32e..1882373fa1fd 100644
> --- a/drivers/irqchip/irq-tegra.c
> +++ b/drivers/irqchip/irq-tegra.c
> @@ -53,18 +53,24 @@ static unsigned int num_ictlrs;
>  
>  struct tegra_ictlr_soc {
>  	unsigned int num_ictlrs;
> +	bool has_bpmpl;

Maybe spell out "has_bpmp_lite" to avoid potential confusion.

>  };
>  
> +static const struct tegra_ictlr_soc *soc;
> +

Can you make this a field in struct tegra_ictlr_info to avoid having too
many global variables?

>  static const struct tegra_ictlr_soc tegra20_ictlr_soc = {
>  	.num_ictlrs = 4,
> +	.has_bpmpl = false,
>  };
>  
>  static const struct tegra_ictlr_soc tegra30_ictlr_soc = {
>  	.num_ictlrs = 5,
> +	.has_bpmpl = false,
>  };
>  
>  static const struct tegra_ictlr_soc tegra210_ictlr_soc = {
>  	.num_ictlrs = 6,
> +	.has_bpmpl = true,
>  };
>  
>  static const struct of_device_id ictlr_matches[] = {
> @@ -157,7 +163,8 @@ static int tegra_ictlr_suspend(void)
>  		lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
>  
>  		/* Disable COP interrupts */
> -		writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
> +		if (!soc->has_bpmpl)
> +			writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);

Maybe adjust the comment to explain. It may not immediately be obvious
to anyone how BPMP-lite is related to COP.

>  
>  		/* Disable CPU interrupts */
>  		writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
> @@ -286,7 +293,6 @@ static int __init tegra_ictlr_init(struct device_node *node,
>  {
>  	struct irq_domain *parent_domain, *domain;
>  	const struct of_device_id *match;
> -	const struct tegra_ictlr_soc *soc;

As mentioned above, perhaps keep this here and assign this to lic->soc
later on so that we have only a single global variable.

Thierry

>  	unsigned int i;
>  	int err;
>  
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 02/12] pinctrl: tegra: add suspend and resume support
  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
  0 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 12:37 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 9673 bytes --]

On Tue, May 21, 2019 at 04:31:13PM -0700, Sowjanya Komatineni wrote:
> This patch adds suspend and resume support for Tegra pinctrl driver
> and registers them to syscore so the pinmux settings are restored
> before the devices resume.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/pinctrl/tegra/pinctrl-tegra.c    | 68 +++++++++++++++++++++++++++++++-
>  drivers/pinctrl/tegra/pinctrl-tegra.h    |  3 ++
>  drivers/pinctrl/tegra/pinctrl-tegra114.c |  1 +
>  drivers/pinctrl/tegra/pinctrl-tegra124.c |  1 +
>  drivers/pinctrl/tegra/pinctrl-tegra20.c  |  1 +
>  drivers/pinctrl/tegra/pinctrl-tegra210.c |  1 +
>  drivers/pinctrl/tegra/pinctrl-tegra30.c  |  1 +
>  7 files changed, 75 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c
> index a5008c066bac..585debbc4291 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c
> @@ -28,11 +28,18 @@
>  #include <linux/pinctrl/pinmux.h>
>  #include <linux/pinctrl/pinconf.h>
>  #include <linux/slab.h>
> +#include <linux/syscore_ops.h>

I'd like to avoid moving this to syscore_ops if possible. The reason is
that there are other mechanisms that allow proper sequencing of suspend
and resume. syscore_ops might give you the correct sequence with respect
to device drivers, but there's no way to control the sequence with
respect to other syscore_ops. It also sets a bad precedent because this
requires a global variable and is therefore no longer properly
encapsulated (i.e. you can't have more than one instance, otherwise you
would end up overwriting the global variable).

>  
>  #include "../core.h"
>  #include "../pinctrl-utils.h"
>  #include "pinctrl-tegra.h"
>  
> +#define EMMC2_PAD_CFGPADCTRL_0			0x1c8
> +#define EMMC4_PAD_CFGPADCTRL_0			0x1e0
> +#define EMMC_DPD_PARKING			(0x1FFF << 14)

Use lower-case hexadecimal digits consistently.

> +
> +static struct tegra_pmx *pmx;
> +
>  static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
>  {
>  	return readl(pmx->regs[bank] + reg);
> @@ -629,6 +636,50 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx)
>  	}
>  }
>  
> +static int __maybe_unused tegra_pinctrl_suspend(void)
> +{
> +	u32 *pg_data = pmx->pg_data;

This is a very generic name. Perhaps use something more contextual like
pmx->suspend_backup, pmx->saved_regs or something along those lines.

> +	u32 *regs;
> +	int i, j;
> +
> +	for (i = 0; i < pmx->nbanks; i++) {
> +		regs = pmx->regs[i];
> +		for (j = 0; j < pmx->reg_bank_size[i] / 4; j++)
> +			*pg_data++ = readl(regs++);

Perhaps avoid the regs temporary variable and just do regs[i][j]?

> +	}
> +
> +	return pinctrl_force_sleep(pmx->pctl);
> +}
> +
> +static void __maybe_unused tegra_pinctrl_resume(void)
> +{
> +	u32 *pg_data = pmx->pg_data;
> +	u32 *regs;
> +	u32 val;
> +	int i, j;
> +
> +	for (i = 0; i < pmx->nbanks; i++) {
> +		regs = pmx->regs[i];
> +		for (j = 0; j < pmx->reg_bank_size[i] / 4; j++)
> +			writel(*pg_data++, regs++);
> +	}
> +
> +	if (pmx->soc->has_park_padcfg) {
> +		val = pmx_readl(pmx, 0, EMMC2_PAD_CFGPADCTRL_0);
> +		val &= ~EMMC_DPD_PARKING;
> +		pmx_writel(pmx, val, 0, EMMC2_PAD_CFGPADCTRL_0);
> +
> +		val = pmx_readl(pmx, 0, EMMC4_PAD_CFGPADCTRL_0);
> +		val &= ~EMMC_DPD_PARKING;
> +		pmx_writel(pmx, val, 0, EMMC4_PAD_CFGPADCTRL_0);
> +	}
> +}
> +
> +static struct syscore_ops pinctrl_syscore_ops = {
> +	.suspend = tegra_pinctrl_suspend,
> +	.resume = tegra_pinctrl_resume,
> +};
> +
>  static bool gpio_node_has_range(const char *compatible)
>  {
>  	struct device_node *np;
> @@ -648,11 +699,11 @@ static bool gpio_node_has_range(const char *compatible)
>  int tegra_pinctrl_probe(struct platform_device *pdev,
>  			const struct tegra_pinctrl_soc_data *soc_data)
>  {
> -	struct tegra_pmx *pmx;
>  	struct resource *res;
>  	int i;
>  	const char **group_pins;
>  	int fn, gn, gfn;
> +	int pg_data_size = 0;
>  
>  	pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
>  	if (!pmx)
> @@ -705,6 +756,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
>  		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
>  		if (!res)
>  			break;
> +		pg_data_size += resource_size(res);
>  	}
>  	pmx->nbanks = i;
>  
> @@ -712,12 +764,25 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
>  				 GFP_KERNEL);
>  	if (!pmx->regs)
>  		return -ENOMEM;
> +#ifdef CONFIG_PM_SLEEP

Do we really need the #ifdef here? I don't think it buys us much and
PM_SLEEP is likely always going to be enabled.

> +	pmx->reg_bank_size = devm_kcalloc(&pdev->dev, pmx->nbanks,
> +					  sizeof(*pmx->reg_bank_size),
> +					  GFP_KERNEL);
> +	if (!pmx->reg_bank_size)
> +		return -ENOMEM;
>  
> +	pmx->pg_data = devm_kzalloc(&pdev->dev, pg_data_size, GFP_KERNEL);
> +	if (!pmx->pg_data)
> +		return -ENOMEM;
> +#endif
>  	for (i = 0; i < pmx->nbanks; i++) {
>  		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
>  		pmx->regs[i] = devm_ioremap_resource(&pdev->dev, res);
>  		if (IS_ERR(pmx->regs[i]))
>  			return PTR_ERR(pmx->regs[i]);
> +#ifdef CONFIG_PM_SLEEP
> +		pmx->reg_bank_size[i] = resource_size(res);
> +#endif
>  	}
>  
>  	pmx->pctl = devm_pinctrl_register(&pdev->dev, &tegra_pinctrl_desc, pmx);
> @@ -732,6 +797,7 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
>  		pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
>  
>  	platform_set_drvdata(pdev, pmx);
> +	register_syscore_ops(&pinctrl_syscore_ops);
>  
>  	dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n");
>  
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.h b/drivers/pinctrl/tegra/pinctrl-tegra.h
> index 44c71941b5f8..8c7cd94124ea 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra.h
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra.h
> @@ -25,6 +25,8 @@ struct tegra_pmx {
>  
>  	int nbanks;
>  	void __iomem **regs;
> +	int *reg_bank_size;

size_t

However, I wonder if there's not a better way to do this. You already do
pinctrl_force_sleep(). I wonder if it'd make sense to add functionality
to the pinctrl framework to restore the current state.

Adding Linus Walleij. Linus, do you know if there's some way of tracking
the current state for all of the pins? I can't seem to find anything
like that anywhere, but it seems to me like a bit of a waste for every
driver to have to allocate extra system memory to store register values
that could equally well just be reconstructed from state kept in the
pinctrl framework.

Any ideas if this is possible, or whether that'd be worth doing?

Thierry

> +	u32 *pg_data;
>  };
>  
>  enum tegra_pinconf_param {
> @@ -199,6 +201,7 @@ struct tegra_pinctrl_soc_data {
>  	bool hsm_in_mux;
>  	bool schmitt_in_mux;
>  	bool drvtype_in_mux;
> +	bool has_park_padcfg;
>  };
>  
>  int tegra_pinctrl_probe(struct platform_device *pdev,
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra114.c b/drivers/pinctrl/tegra/pinctrl-tegra114.c
> index d43c209e9c30..4ac44f34dccf 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra114.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra114.c
> @@ -1849,6 +1849,7 @@ static const struct tegra_pinctrl_soc_data tegra114_pinctrl = {
>  	.hsm_in_mux = false,
>  	.schmitt_in_mux = false,
>  	.drvtype_in_mux = false,
> +	.has_park_padcfg = false,
>  };
>  
>  static int tegra114_pinctrl_probe(struct platform_device *pdev)
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra124.c b/drivers/pinctrl/tegra/pinctrl-tegra124.c
> index 5b07a5834d15..1dac7648b41f 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra124.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra124.c
> @@ -2061,6 +2061,7 @@ static const struct tegra_pinctrl_soc_data tegra124_pinctrl = {
>  	.hsm_in_mux = false,
>  	.schmitt_in_mux = false,
>  	.drvtype_in_mux = false,
> +	.has_park_padcfg = false,
>  };
>  
>  static int tegra124_pinctrl_probe(struct platform_device *pdev)
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c
> index 1fc82a9576e0..9d2b25200f32 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra20.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c
> @@ -2231,6 +2231,7 @@ static const struct tegra_pinctrl_soc_data tegra20_pinctrl = {
>  	.hsm_in_mux = false,
>  	.schmitt_in_mux = false,
>  	.drvtype_in_mux = false,
> +	.has_park_padcfg = false,
>  };
>  
>  static const char *cdev1_parents[] = {
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra210.c b/drivers/pinctrl/tegra/pinctrl-tegra210.c
> index 3e77f5474dd8..dc06c36e698a 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra210.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra210.c
> @@ -1563,6 +1563,7 @@ static const struct tegra_pinctrl_soc_data tegra210_pinctrl = {
>  	.hsm_in_mux = true,
>  	.schmitt_in_mux = true,
>  	.drvtype_in_mux = true,
> +	.has_park_padcfg = true,
>  };
>  
>  static int tegra210_pinctrl_probe(struct platform_device *pdev)
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra30.c b/drivers/pinctrl/tegra/pinctrl-tegra30.c
> index 10e617003e9c..42182d714950 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra30.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra30.c
> @@ -2484,6 +2484,7 @@ static const struct tegra_pinctrl_soc_data tegra30_pinctrl = {
>  	.hsm_in_mux = false,
>  	.schmitt_in_mux = false,
>  	.drvtype_in_mux = false,
> +	.has_park_padcfg = false,
>  };
>  
>  static int tegra30_pinctrl_probe(struct platform_device *pdev)
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models
  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
  1 sibling, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 12:49 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4149 bytes --]

On Tue, May 21, 2019 at 04:31:19PM -0700, Sowjanya Komatineni wrote:
> This patch allows to create separate irq_set_wake and irq_set_type
> implementations for different tegra designs PMC that has different
> wake models which require difference wake registers and different
> programming sequence.
> 
> AOWAKE model support is available for Tegra186 and Tegra194 only
> and it resides within PMC and supports tiered wake architecture.
> 
> Tegra210 and prior tegra designs uses PMC directly to receive wake
> events and coordinate the wake sequence.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/soc/tegra/pmc.c | 21 ++++++++++++++++-----
>  1 file changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index 5648e5c09ef5..f77ce4b827e3 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -235,6 +235,8 @@ struct tegra_pmc_soc {
>  	void (*setup_irq_polarity)(struct tegra_pmc *pmc,
>  				   struct device_node *np,
>  				   bool invert);
> +	int (*pmc_irq_set_wake)(struct irq_data *data, unsigned int on);
> +	int (*pmc_irq_set_type)(struct irq_data *data, unsigned int type);
>  
>  	const char * const *reset_sources;
>  	unsigned int num_reset_sources;
> @@ -1915,12 +1917,15 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
>  	.alloc = tegra_pmc_irq_alloc,
>  };
>  
> -static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
> +static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
>  {
>  	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
>  	unsigned int offset, bit;
>  	u32 value;
>  
> +	if (data->hwirq < 0)
> +		return 0;
> +

This isn't going to work: data->hwirq is unsigned long so it will never
be < 0. Also, why is this necessary? I see that it's there in
tegra_pmc_irq_set_wake(), but I fail to remember why that's there. When
is this ever invalid?

Thierry

>  	offset = data->hwirq / 32;
>  	bit = data->hwirq % 32;
>  
> @@ -1943,12 +1948,12 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
>  	return 0;
>  }
>  
> -static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type)
> +static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
>  {
>  	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
>  	u32 value;
>  
> -	if (data->hwirq == ULONG_MAX)
> +	if (data->hwirq < 0)
>  		return 0;
>  
>  	value = readl(pmc->wake + WAKE_AOWAKE_CNTRL(data->hwirq));
> @@ -1996,8 +2001,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
>  	pmc->irq.irq_unmask = irq_chip_unmask_parent;
>  	pmc->irq.irq_eoi = irq_chip_eoi_parent;
>  	pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent;
> -	pmc->irq.irq_set_type = tegra_pmc_irq_set_type;
> -	pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake;
> +	if (pmc->soc->pmc_irq_set_type)
> +		pmc->irq.irq_set_type = pmc->soc->pmc_irq_set_type;
> +	if (pmc->soc->pmc_irq_set_wake)
> +		pmc->irq.irq_set_wake = pmc->soc->pmc_irq_set_wake;
>  
>  	pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node,
>  					       &tegra_pmc_irq_domain_ops, pmc);
> @@ -2670,6 +2677,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
>  	.regs = &tegra186_pmc_regs,
>  	.init = NULL,
>  	.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
> +	.pmc_irq_set_wake = tegra186_pmc_irq_set_wake,
> +	.pmc_irq_set_type = tegra186_pmc_irq_set_type,
>  	.reset_sources = tegra186_reset_sources,
>  	.num_reset_sources = ARRAY_SIZE(tegra186_reset_sources),
>  	.reset_levels = tegra186_reset_levels,
> @@ -2748,6 +2757,8 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
>  	.regs = &tegra186_pmc_regs,
>  	.init = NULL,
>  	.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
> +	.pmc_irq_set_wake = tegra186_pmc_irq_set_wake,
> +	.pmc_irq_set_type = tegra186_pmc_irq_set_type,
>  	.num_wake_events = ARRAY_SIZE(tegra194_wake_events),
>  	.wake_events = tegra194_wake_events,
>  };
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 09/12] soc/tegra: pmc: add pmc wake support for tegra210
  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
  0 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:01 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 7031 bytes --]

On Tue, May 21, 2019 at 04:31:20PM -0700, Sowjanya Komatineni wrote:
> This patch implements PMC wakeup sequence for Tegra210 and defines
> common used wake events of RTC alarm and power key.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/soc/tegra/pmc.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 120 insertions(+)
> 
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index f77ce4b827e3..5e68e1de1780 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -57,6 +57,7 @@
>  #include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h>
>  #include <dt-bindings/gpio/tegra186-gpio.h>
>  #include <dt-bindings/gpio/tegra194-gpio.h>
> +#include <dt-bindings/gpio/tegra-gpio.h>
>  
>  #define PMC_CNTRL			0x0
>  #define  PMC_CNTRL_INTR_POLARITY	BIT(17) /* inverts INTR polarity */
> @@ -66,6 +67,12 @@
>  #define  PMC_CNTRL_SYSCLK_OE		BIT(11) /* system clock enable */
>  #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
>  #define  PMC_CNTRL_MAIN_RST		BIT(4)
> +#define  PMC_CNTRL_LATCH_WAKEUPS	BIT(5)
> +
> +#define PMC_WAKE_MASK			0x0c
> +#define PMC_WAKE_LEVEL			0x10
> +#define PMC_WAKE_STATUS			0x14
> +#define PMC_SW_WAKE_STATUS		0x18
>  
>  #define DPD_SAMPLE			0x020
>  #define  DPD_SAMPLE_ENABLE		BIT(0)
> @@ -96,6 +103,11 @@
>  
>  #define PMC_SCRATCH41			0x140
>  
> +#define PMC_WAKE2_MASK			0x160
> +#define PMC_WAKE2_LEVEL			0x164
> +#define PMC_WAKE2_STATUS		0x168
> +#define PMC_SW_WAKE2_STATUS		0x16c
> +
>  #define PMC_SENSOR_CTRL			0x1b0
>  #define  PMC_SENSOR_CTRL_SCRATCH_WRITE	BIT(2)
>  #define  PMC_SENSOR_CTRL_ENABLE_RST	BIT(1)
> @@ -1917,6 +1929,65 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = {
>  	.alloc = tegra_pmc_irq_alloc,
>  };
>  
> +static inline void clear_pmc_sw_wake_status(void)
> +{
> +	tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS);
> +	if (tegra_get_chip_id() != TEGRA20)

Please don't use tegra_get_chip_id(). We already have pmc->soc, so it
should be easy enough to add a flag to identify Tegra generations that
have this register. Another alternative would be to fill in all the wake
events that the generation supports and then iterate over that to find
out what the maximum wake event ID is and use that to determine which
registers are valid. I'm assuming Tegra20 supports less than 32 wake
events, hence why it doesn't need PMC_SW_WAKE_STATUS.

> +		tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS);
> +}
> +
> +static inline void clear_pmc_wake_status(void)
> +{
> +	u32 reg;

u32 value for consistency with the rest of the driver.

> +
> +	reg = tegra_pmc_readl(pmc, PMC_WAKE_STATUS);
> +	if (reg)
> +		tegra_pmc_writel(pmc, reg, PMC_WAKE_STATUS);

Is there any harm in writing 0 to these registers? Not that it matters
much, but the code here looks a little odd.

> +	if (tegra_get_chip_id() != TEGRA20) {
> +		reg = tegra_pmc_readl(pmc, PMC_WAKE2_STATUS);
> +		if (reg)
> +			tegra_pmc_writel(pmc, reg, PMC_WAKE2_STATUS);
> +	}
> +}
> +
> +static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
> +{
> +	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
> +	unsigned int offset, bit;
> +	u32 pmc_wake_mask_reg;
> +	u32 value;
> +
> +	if (data->hwirq < 0)
> +		return 0;

Again, is this really necessary?

> +
> +	offset = data->hwirq / 32;
> +	bit = data->hwirq % 32;
> +
> +	clear_pmc_sw_wake_status();
> +
> +	/* enable PMC wake */
> +	value = tegra_pmc_readl(pmc, PMC_CNTRL);
> +	value |= PMC_CNTRL_LATCH_WAKEUPS;
> +	tegra_pmc_writel(pmc, value, PMC_CNTRL);
> +	usleep_range(110, 120);
> +
> +	value &= ~PMC_CNTRL_LATCH_WAKEUPS;
> +	tegra_pmc_writel(pmc, value, PMC_CNTRL);
> +	usleep_range(110, 120);
> +
> +	clear_pmc_wake_status();

Could you add a couple more comments describing what this does. Maybe
also inline the clear_pmc_{sw_,}wake_status() functions into this since
they are only used here.

> +
> +	pmc_wake_mask_reg = (offset) ? PMC_WAKE2_MASK : PMC_WAKE_MASK;

I think it'd be clearer to write this as:

	if (data->hwirq > 32)
		offset = PMC_WAKE2_MASK;
	else
		offset = PMC_WAKE_MASK;

> +	value = tegra_pmc_readl(pmc, pmc_wake_mask_reg);
> +	if (on)
> +		value |= 1 << bit;
> +	else
> +		value &= ~(1 << bit);
> +	tegra_pmc_writel(pmc, value, pmc_wake_mask_reg);
> +
> +	return 0;
> +}
> +
>  static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
>  {
>  	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
> @@ -1948,6 +2019,46 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
>  	return 0;
>  }
>  
> +static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
> +{
> +	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
> +	unsigned int offset, bit;
> +	u32 pmc_wake_lvl_reg;
> +	u32 value;
> +
> +	if (data->hwirq < 0)
> +		return 0;
> +
> +	offset = data->hwirq / 32;
> +	bit = data->hwirq % 32;
> +
> +	pmc_wake_lvl_reg = (offset) ? PMC_WAKE2_LEVEL : PMC_WAKE_LEVEL;

Same comment as for the PMC_WAKE*_MASK registers.

Thierry

> +	value = tegra_pmc_readl(pmc, pmc_wake_lvl_reg);
> +
> +	switch (type) {
> +	case IRQ_TYPE_EDGE_RISING:
> +	case IRQ_TYPE_LEVEL_HIGH:
> +		value |= 1 << bit;
> +		break;
> +
> +	case IRQ_TYPE_EDGE_FALLING:
> +	case IRQ_TYPE_LEVEL_LOW:
> +		value &= ~(1 << bit);
> +		break;
> +
> +	case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
> +		value ^= 1 << bit;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	tegra_pmc_writel(pmc, value, pmc_wake_lvl_reg);
> +
> +	return 0;
> +}
> +
>  static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
>  {
>  	struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data);
> @@ -2535,6 +2646,11 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = {
>  	TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC)
>  };
>  
> +static const struct tegra_wake_event tegra210_wake_events[] = {
> +	TEGRA_WAKE_GPIO("power", 24, 0, 189), /*TEGRA_GPIO(X, 5)*/
> +	TEGRA_WAKE_IRQ("rtc", 16, 2),
> +};
> +
>  static const struct tegra_pmc_soc tegra210_pmc_soc = {
>  	.num_powergates = ARRAY_SIZE(tegra210_powergates),
>  	.powergates = tegra210_powergates,
> @@ -2552,10 +2668,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
>  	.regs = &tegra20_pmc_regs,
>  	.init = tegra20_pmc_init,
>  	.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
> +	.pmc_irq_set_wake = tegra210_pmc_irq_set_wake,
> +	.pmc_irq_set_type = tegra210_pmc_irq_set_type,
>  	.reset_sources = tegra210_reset_sources,
>  	.num_reset_sources = ARRAY_SIZE(tegra210_reset_sources),
>  	.reset_levels = NULL,
>  	.num_reset_levels = 0,
> +	.num_wake_events = ARRAY_SIZE(tegra210_wake_events),
> +	.wake_events = tegra210_wake_events,
>  };
>  
>  #define TEGRA186_IO_PAD_TABLE(_pad)					     \
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 08/12] soc/tegra: pmc: allow support for more tegra wake models
  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
  1 sibling, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:02 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1370 bytes --]

On Tue, May 21, 2019 at 04:31:19PM -0700, Sowjanya Komatineni wrote:
> This patch allows to create separate irq_set_wake and irq_set_type
> implementations for different tegra designs PMC that has different
> wake models which require difference wake registers and different
> programming sequence.
> 
> AOWAKE model support is available for Tegra186 and Tegra194 only
> and it resides within PMC and supports tiered wake architecture.
> 
> Tegra210 and prior tegra designs uses PMC directly to receive wake
> events and coordinate the wake sequence.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/soc/tegra/pmc.c | 21 ++++++++++++++++-----
>  1 file changed, 16 insertions(+), 5 deletions(-)

One more thing...

> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index 5648e5c09ef5..f77ce4b827e3 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -235,6 +235,8 @@ struct tegra_pmc_soc {
>  	void (*setup_irq_polarity)(struct tegra_pmc *pmc,
>  				   struct device_node *np,
>  				   bool invert);
> +	int (*pmc_irq_set_wake)(struct irq_data *data, unsigned int on);
> +	int (*pmc_irq_set_type)(struct irq_data *data, unsigned int type);

... drop the pmc_ prefix here. These are part of a struct tegra_pmc_soc
structure, so that pmc_ is redundant.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO
  2019-05-21 23:31 ` [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO Sowjanya Komatineni
@ 2019-05-22 13:24   ` Thierry Reding
  2019-05-25 20:39     ` Sowjanya Komatineni
  0 siblings, 1 reply; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:24 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 6881 bytes --]

On Tue, May 21, 2019 at 04:31:21PM -0700, Sowjanya Komatineni wrote:
> 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;

Why is this needed?

>  }
>  
>  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;

You can put the above two lines onto a single line.

> +	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;

Can this ever fail?

> +
> +	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);

What if num_irqs is different from 1? I'm not exactly sure what to pass
as &spec, but likely we'd have to create an array of struct irq_fwspec
and pass that along. It seems like some drivers catch that case and
refuse to work rather than pass potentially rubbish information along.
See for example drivers/irqchip/irq-meson-gpio.c.

> +}
> +
> +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;

This doesn't seem right. What about tegra_gpio_irq_set_wake()? If it's
no longer needed, just remove it. But then, what about the extra logic
in that function that causes the interrupts to be enabled during
suspend? Is that no longer necessary? Maybe that's no longer needed on
Tegra210, but what about other Tegra generations?

Thierry

>  #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
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 11/12] soc/tegra: pmc: configure tegra deep sleep control settings
  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
  0 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:28 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4115 bytes --]

On Tue, May 21, 2019 at 04:31:22PM -0700, Sowjanya Komatineni wrote:
> Tegra210 and prior Tegra chips have power request signal polarity,
> deep sleep entry and wake related timings which are platform specific
> that should be configured before entering into deep sleep.
> 
> Below are the timings specific configurations for deep sleep and wake.
> - Core rail power-on stabilization timer
> - OSC clock stabilization timer after SOC rail power is stabilized.
> - Core power off time is the minimum wake delay to keep the system
> in deep sleep state irrespective of any quick wake event.
> 
> These values depends on the discharge time of regulators and turn OFF
> time of the PMIC to allow the complete system to finish entering into
> deep sleep state.
> 
> These values vary based on the platform design and are specified
> through the device tree.
> 
> This patch has implementation to configure these configurations which
> are must to have for deep sleep state.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |  7 +++++++
>  drivers/soc/tegra/pmc.c                        | 18 ++++++++++++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
> index 4dcd0d36189a..7ac5e55a30aa 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
> @@ -266,6 +266,13 @@
>  
>  	pmc@7000e400 {
>  		nvidia,invert-interrupt;
> +		nvidia,suspend-mode = <0>;
> +		nvidia,cpu-pwr-good-time = <0>;
> +		nvidia,cpu-pwr-off-time = <0>;
> +		nvidia,core-pwr-good-time = <4587 3876>;
> +		nvidia,core-pwr-off-time = <39065>;
> +		nvidia,core-power-req-active-high;
> +		nvidia,sys-clock-req-active-high;
>  	};
>  
>  	/* eMMC */

This hunk looks like maybe it belongs in the last patch?

Thierry

> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index 5e68e1de1780..8d225962d136 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -66,6 +66,7 @@
>  #define  PMC_CNTRL_SIDE_EFFECT_LP0	BIT(14) /* LP0 when CPU pwr gated */
>  #define  PMC_CNTRL_SYSCLK_OE		BIT(11) /* system clock enable */
>  #define  PMC_CNTRL_SYSCLK_POLARITY	BIT(10) /* sys clk polarity */
> +#define  PMC_CNTRL_PWRREQ_POLARITY	BIT(8)
>  #define  PMC_CNTRL_MAIN_RST		BIT(4)
>  #define  PMC_CNTRL_LATCH_WAKEUPS	BIT(5)
>  
> @@ -98,6 +99,8 @@
>  
>  #define PMC_CPUPWRGOOD_TIMER		0xc8
>  #define PMC_CPUPWROFF_TIMER		0xcc
> +#define PMC_COREPWRGOOD_TIMER		0x3c
> +#define PMC_COREPWROFF_TIMER		0xe0
>  
>  #define PMC_PWR_DET_VALUE		0xe4
>  
> @@ -2293,6 +2296,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = {
>  static void tegra20_pmc_init(struct tegra_pmc *pmc)
>  {
>  	u32 value;
> +	unsigned long osc, pmu, off;
>  
>  	/* Always enable CPU power request */
>  	value = tegra_pmc_readl(pmc, PMC_CNTRL);
> @@ -2306,6 +2310,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
>  	else
>  		value |= PMC_CNTRL_SYSCLK_POLARITY;
>  
> +	if (pmc->corereq_high)
> +		value &= ~PMC_CNTRL_PWRREQ_POLARITY;
> +	else
> +		value |= PMC_CNTRL_PWRREQ_POLARITY;
> +
>  	/* configure the output polarity while the request is tristated */
>  	tegra_pmc_writel(pmc, value, PMC_CNTRL);
>  
> @@ -2313,6 +2322,15 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc)
>  	value = tegra_pmc_readl(pmc, PMC_CNTRL);
>  	value |= PMC_CNTRL_SYSCLK_OE;
>  	tegra_pmc_writel(pmc, value, PMC_CNTRL);
> +
> +	osc = DIV_ROUND_UP_ULL(pmc->core_osc_time * 8192, 1000000);
> +	pmu = DIV_ROUND_UP_ULL(pmc->core_pmu_time * 32768, 1000000);
> +	off = DIV_ROUND_UP_ULL(pmc->core_off_time * 32768, 1000000);
> +	if (osc && pmu)
> +		tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff),
> +				 PMC_COREPWRGOOD_TIMER);
> +	if (off)
> +		tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER);
>  }
>  
>  static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 03/12] clk: tegra: save and restore PLLs state for system
  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
  0 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:31 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 788 bytes --]

On Tue, May 21, 2019 at 04:31:14PM -0700, Sowjanya Komatineni wrote:
> This patch has implementation of saving and restoring PLL's state to
> support system suspend and resume operations.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
> ---
>  drivers/clk/tegra/clk-divider.c |  19 ++++
>  drivers/clk/tegra/clk-pll-out.c |  25 +++++
>  drivers/clk/tegra/clk-pll.c     | 220 ++++++++++++++++++++++++++++++++++++----
>  drivers/clk/tegra/clk.h         |  14 +++
>  4 files changed, 258 insertions(+), 20 deletions(-)

When you resend the series, can you Cc Peter De Schrijver for the clock
patches. I'm slightly concerned about the size of these changes and I'm
not very familiar with the clock implementation so I'd like Peter to
have a look.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 00/12] LP0 entry and exit support for Tegra210
  2019-05-21 23:31 [PATCH V1 00/12] LP0 entry and exit support for Tegra210 Sowjanya Komatineni
                   ` (11 preceding siblings ...)
  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 ` Thierry Reding
  12 siblings, 0 replies; 26+ messages in thread
From: Thierry Reding @ 2019-05-22 13:33 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3103 bytes --]

On Tue, May 21, 2019 at 04:31:11PM -0700, Sowjanya Komatineni wrote:
> This patch series includes Tegra210 deepsleep or LP0 support with
> deep sleep exit through RTC alarm wake and power button wake events.

There doesn't seem to be support for waking up from a power button
press. Is that in a patch that you didn't send out yet, or are you still
working on that?

The recipient list on this is very trimmed. I'm not sure if this was on
purpose. I'm fine if you want to keep it that way until things settle
down a bit, but eventually the subsystem maintainers will need to see
these, so make sure to Cc them when you think the patches are ready.

Thierry

> 
> This series also includes save and restore of PLLs, clocks, OSC contexts
> for basic LP0 exit.
> 
> This patch series is doesn't support for 100% suspend/resume to fully
> functional state and we are working on some more drivers suspend and
> resume implementations.
> 
> Sowjanya Komatineni (12):
>   irqchip: tegra: do not disable COP IRQ during suspend
>   pinctrl: tegra: add suspend and resume support
>   clk: tegra: save and restore PLLs state for system
>   clk: tegra: add support for peripheral clock suspend and resume
>   clk: tegra: add support for OSC clock resume
>   clk: tegra: add suspend resume support for DFLL clock
>   clk: tegra: support for Tegra210 clocks suspend-resume
>   soc/tegra: pmc: allow support for more tegra wake models
>   soc/tegra: pmc: add pmc wake support for tegra210
>   gpio: tegra: implement wake event support for Tegra210 and prior GPIO
>   soc/tegra: pmc: configure tegra deep sleep control settings
>   arm64: tegra: enable wake from deep sleep on RTC alarm.
> 
>  arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |   7 +
>  arch/arm64/boot/dts/nvidia/tegra210.dtsi       |   5 +-
>  drivers/clk/tegra/clk-dfll.c                   |  82 ++++++
>  drivers/clk/tegra/clk-dfll.h                   |   2 +
>  drivers/clk/tegra/clk-divider.c                |  19 ++
>  drivers/clk/tegra/clk-pll-out.c                |  25 ++
>  drivers/clk/tegra/clk-pll.c                    | 220 ++++++++++++--
>  drivers/clk/tegra/clk-tegra-fixed.c            |  15 +
>  drivers/clk/tegra/clk-tegra210.c               | 382 +++++++++++++++++++++++++
>  drivers/clk/tegra/clk.c                        |  74 ++++-
>  drivers/clk/tegra/clk.h                        |  18 ++
>  drivers/gpio/gpio-tegra.c                      | 109 ++++++-
>  drivers/irqchip/irq-tegra.c                    |  10 +-
>  drivers/pinctrl/tegra/pinctrl-tegra.c          |  68 ++++-
>  drivers/pinctrl/tegra/pinctrl-tegra.h          |   3 +
>  drivers/pinctrl/tegra/pinctrl-tegra114.c       |   1 +
>  drivers/pinctrl/tegra/pinctrl-tegra124.c       |   1 +
>  drivers/pinctrl/tegra/pinctrl-tegra20.c        |   1 +
>  drivers/pinctrl/tegra/pinctrl-tegra210.c       |   1 +
>  drivers/pinctrl/tegra/pinctrl-tegra30.c        |   1 +
>  drivers/soc/tegra/pmc.c                        | 159 +++++++++-
>  21 files changed, 1167 insertions(+), 36 deletions(-)
> 
> -- 
> 2.7.4
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO
  2019-05-22 13:24   ` Thierry Reding
@ 2019-05-25 20:39     ` Sowjanya Komatineni
  0 siblings, 0 replies; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-25 20:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: jonathanh, jckuo, talho, josephl, linux-tegra, linux-kernel


On 5/22/19 6:24 AM, Thierry Reding wrote:
> On Tue, May 21, 2019 at 04:31:21PM -0700, Sowjanya Komatineni wrote:
>> 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;
> Why is this needed?

Invoking GPIO irq_domain parent's set_type to configure PMC wake level 
based on the type for those GPIO's

that are wake-able thru specified PMC wake events.

>
>>   }
>>   
>>   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;
> You can put the above two lines onto a single line.
>
Will fix in next version
>> +	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;
> Can this ever fail?
Will remove in next version of updated series..
>
>> +
>> +	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);
> What if num_irqs is different from 1? I'm not exactly sure what to pass
> as &spec, but likely we'd have to create an array of struct irq_fwspec
> and pass that along. It seems like some drivers catch that case and
> refuse to work rather than pass potentially rubbish information along.
> See for example drivers/irqchip/irq-meson-gpio.c.
Will fix in next version
>> +}
>> +
>> +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;
> This doesn't seem right. What about tegra_gpio_irq_set_wake()? If it's
> no longer needed, just remove it. But then, what about the extra logic
> in that function that causes the interrupts to be enabled during
> suspend? Is that no longer necessary? Maybe that's no longer needed on
> Tegra210, but what about other Tegra generations?
>
> Thierry
>
Missed this. Will fix in next version
>>   #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
>>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 00/12] LP0 entry and exit support for Tegra210
  2019-05-23  1:29 ` Sowjanya Komatineni
@ 2019-05-28  9:28   ` Billy Laws
  0 siblings, 0 replies; 26+ messages in thread
From: Billy Laws @ 2019-05-28  9:28 UTC (permalink / raw)
  To: Sowjanya Komatineni
  Cc: thierry.reding, jonathanh, Laxman Dewangan, broonie,
	natechancellor, linux-tegra, linux-kernel, linux-spi

Hi,
By the looks of this you configure wake events but the pmc driver
still only configures lp1, which is the same as linux 4 tegra 4.4+
which also doesn't support lp0, if its selected in dts it'll just
change it to LP1/SC7 (both have same code path and neither set any lp0
stuff trm says). What confuses me further is that l4t and this
ptachset both configure wake events, which I think should only work in
LP0 and not LP1 and yet neither have lp0 entry code for t210. I also
looked at ATF, whis sets the flags to wake the cpu on interrupts,
which would suggest that it only supports lp1 (in deep sleep I dont
think them flags would work). Am I missing some important detail here?
Two other things, will you be adding full lp0 in later patchets and
will they use the bpmp or not?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH V1 00/12] LP0 entry and exit support for Tegra210
  2019-05-23  1:28 Sowjanya Komatineni
@ 2019-05-23  1:29 ` Sowjanya Komatineni
  2019-05-28  9:28   ` Billy Laws
  0 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-23  1:29 UTC (permalink / raw)
  To: thierry.reding, jonathanh, ldewangan, broonie, natechancellor
  Cc: linux-tegra, linux-kernel, linux-spi

HI All

Sorry. Please ignore this as it was sent out accidentally

thanks

sowjanya

On 5/22/19 6:28 PM, Sowjanya Komatineni wrote:
> This patch series includes Tegra210 deepsleep or LP0 support with
> deep sleep exit through RTC alarm wake and power button wake events.
>
> This series also includes save and restore of PLLs, clocks, OSC contexts
> for basic LP0 exit.
>
> This patch series is doesn't support for 100% suspend/resume to fully
> functional state and we are working on some more drivers suspend and
> resume implementations.
>
> Sowjanya Komatineni (12):
>    irqchip: tegra: do not disable COP IRQ during suspend
>    pinctrl: tegra: add suspend and resume support
>    clk: tegra: save and restore PLLs state for system
>    clk: tegra: add support for peripheral clock suspend and resume
>    clk: tegra: add support for OSC clock resume
>    clk: tegra: add suspend resume support for DFLL clock
>    clk: tegra: support for Tegra210 clocks suspend-resume
>    soc/tegra: pmc: allow support for more tegra wake models
>    soc/tegra: pmc: add pmc wake support for tegra210
>    gpio: tegra: implement wake event support for Tegra210 and prior GPIO
>    soc/tegra: pmc: configure tegra deep sleep control settings
>    arm64: tegra: enable wake from deep sleep on RTC alarm.
>
>   arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |   7 +
>   arch/arm64/boot/dts/nvidia/tegra210.dtsi       |   5 +-
>   drivers/clk/tegra/clk-dfll.c                   |  82 ++++++
>   drivers/clk/tegra/clk-dfll.h                   |   2 +
>   drivers/clk/tegra/clk-divider.c                |  19 ++
>   drivers/clk/tegra/clk-pll-out.c                |  25 ++
>   drivers/clk/tegra/clk-pll.c                    | 220 ++++++++++++--
>   drivers/clk/tegra/clk-tegra-fixed.c            |  15 +
>   drivers/clk/tegra/clk-tegra210.c               | 382 +++++++++++++++++++++++++
>   drivers/clk/tegra/clk.c                        |  74 ++++-
>   drivers/clk/tegra/clk.h                        |  18 ++
>   drivers/gpio/gpio-tegra.c                      | 109 ++++++-
>   drivers/irqchip/irq-tegra.c                    |  10 +-
>   drivers/pinctrl/tegra/pinctrl-tegra.c          |  68 ++++-
>   drivers/pinctrl/tegra/pinctrl-tegra.h          |   3 +
>   drivers/pinctrl/tegra/pinctrl-tegra114.c       |   1 +
>   drivers/pinctrl/tegra/pinctrl-tegra124.c       |   1 +
>   drivers/pinctrl/tegra/pinctrl-tegra20.c        |   1 +
>   drivers/pinctrl/tegra/pinctrl-tegra210.c       |   1 +
>   drivers/pinctrl/tegra/pinctrl-tegra30.c        |   1 +
>   drivers/soc/tegra/pmc.c                        | 159 +++++++++-
>   21 files changed, 1167 insertions(+), 36 deletions(-)
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH V1 00/12] LP0 entry and exit support for Tegra210
@ 2019-05-23  1:28 Sowjanya Komatineni
  2019-05-23  1:29 ` Sowjanya Komatineni
  0 siblings, 1 reply; 26+ messages in thread
From: Sowjanya Komatineni @ 2019-05-23  1:28 UTC (permalink / raw)
  To: thierry.reding, jonathanh, ldewangan, broonie, natechancellor
  Cc: linux-tegra, linux-kernel, linux-spi, Sowjanya Komatineni

This patch series includes Tegra210 deepsleep or LP0 support with
deep sleep exit through RTC alarm wake and power button wake events.

This series also includes save and restore of PLLs, clocks, OSC contexts
for basic LP0 exit.

This patch series is doesn't support for 100% suspend/resume to fully
functional state and we are working on some more drivers suspend and
resume implementations.

Sowjanya Komatineni (12):
  irqchip: tegra: do not disable COP IRQ during suspend
  pinctrl: tegra: add suspend and resume support
  clk: tegra: save and restore PLLs state for system
  clk: tegra: add support for peripheral clock suspend and resume
  clk: tegra: add support for OSC clock resume
  clk: tegra: add suspend resume support for DFLL clock
  clk: tegra: support for Tegra210 clocks suspend-resume
  soc/tegra: pmc: allow support for more tegra wake models
  soc/tegra: pmc: add pmc wake support for tegra210
  gpio: tegra: implement wake event support for Tegra210 and prior GPIO
  soc/tegra: pmc: configure tegra deep sleep control settings
  arm64: tegra: enable wake from deep sleep on RTC alarm.

 arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi |   7 +
 arch/arm64/boot/dts/nvidia/tegra210.dtsi       |   5 +-
 drivers/clk/tegra/clk-dfll.c                   |  82 ++++++
 drivers/clk/tegra/clk-dfll.h                   |   2 +
 drivers/clk/tegra/clk-divider.c                |  19 ++
 drivers/clk/tegra/clk-pll-out.c                |  25 ++
 drivers/clk/tegra/clk-pll.c                    | 220 ++++++++++++--
 drivers/clk/tegra/clk-tegra-fixed.c            |  15 +
 drivers/clk/tegra/clk-tegra210.c               | 382 +++++++++++++++++++++++++
 drivers/clk/tegra/clk.c                        |  74 ++++-
 drivers/clk/tegra/clk.h                        |  18 ++
 drivers/gpio/gpio-tegra.c                      | 109 ++++++-
 drivers/irqchip/irq-tegra.c                    |  10 +-
 drivers/pinctrl/tegra/pinctrl-tegra.c          |  68 ++++-
 drivers/pinctrl/tegra/pinctrl-tegra.h          |   3 +
 drivers/pinctrl/tegra/pinctrl-tegra114.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra124.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra20.c        |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra210.c       |   1 +
 drivers/pinctrl/tegra/pinctrl-tegra30.c        |   1 +
 drivers/soc/tegra/pmc.c                        | 159 +++++++++-
 21 files changed, 1167 insertions(+), 36 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2019-05-28  9:29 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH V1 10/12] gpio: tegra: implement wake event support for Tegra210 and prior GPIO Sowjanya Komatineni
2019-05-22 13:24   ` 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
2019-05-23  1:28 Sowjanya Komatineni
2019-05-23  1:29 ` Sowjanya Komatineni
2019-05-28  9:28   ` Billy Laws

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).