This patch series includes Tegra210 deepsleep (SC7) support with RTC alarm
wake event.
This series also includes save and restore of PLLs, clocks, OSC contexts
for deepsleep exit to normal operation.
This patch series doesn't support 100% suspend/resume to allow fully
functional state upon resume and we are working on some more drivers suspend
and resume implementations.
[v8]: Changes between v7 & v8 are
- v7 feedback fixes
- moved clock enable/disable state and reset enable/disable state
restore from clk_save/restore_context back to clk-tegra210 driver
to do complete register value store/restore.
- Removed manual store/restore of peripheral parent context and added
API in clk core to retrieve clock parent index to use during
restoring parent on resume.
- Implemented Tegra recommended clock source programming sequence
during restore (CLK ENB ON followed by divider/source programming
followed by restoring CLK ENB ON/OFF state followed by restoring
RST state).
- Removed pinctrl suspend/resume patch from this series as pinctrl
suspend/resume patch from v7 got acked and merged.
- Added pinctrl patch-0001 to update pmx_writel for proper placement
of write barrier.
- Added pinctrl patch-0002 to insert write barrier after all pinctrl
register writes during pinctrl resume.
Note:
Below patch is also needed for SC7 support as GPIO restore need
to happen prior to pinctrl.
https://patchwork.kernel.org/patch/11012077/
[v7]: Changes between V6 & v7 are
- V6 feedback fixes
- Removed patch-0001 from V6 which keeps COP IRQ enabled. Looking
more into ATF FW, it loads SC7 entry FW into IRAM and sets the
COP reset vector to SC7 FW load address and resets COP. So, COP
IRQ can be cleared during suspend.
Note:
Below patch is also needed for SC7 support as GPIO restore need
to happen prior to pinctrl.
https://patchwork.kernel.org/patch/11012077/
[V6]: Changes between V5 & V6 are
- V5 feedback fixes
- DFLL suspend and resume moved to DFLL clock driver
- Add suspend and resume support for CPUFreq driver to explicitly
switch source to safe source of PLLP and disable DFLL clock.
- Fix to super clock driver to enable PLLP branch to CPU before
source switch to PLLP.
- Added save and restore support for super clock driver.
[V5]: Changes between V4 & V5 are
- V4 feedback fixes
[V4]: Changes between V3 & V4 are
- V3 feedback fixes
- Removed park bits clear for EMMC pads in pinctrl-tegra driver
function tegra_pinctrl_clear_parked_bits as based on V3 feedback
parked_bit is updated to parked_bitmask to use with DRV_PINGROUP
as well and thierry posted patch series for this.
- Implemented all peripheral clocks save and restore through their
corresponding clk_ops save_context and restore_context and removed
all direct registers store and restore in clk-tegra210 driver.
- Created separate patch for fence_delay update during PLLU init based
on V3 feedback.
- Added more comments in tegra210_clk_resume regarding dfll restore
sequence and its dependency on peripheral clocks restore.
[V3]: Changes between V2 & V3 are
- V2 feedback fixes
- GPIO restore should happen prior to Pinctrl restore to prevent
glitch on GPIO lines. So using resume_noirq for gpio tegra to allow
gpio resume prior to pinctrl resume.
- Implemented save_context and restore_context callbacks for clock
plls, pll outs and dividers in corresponding drivers.
Note: Peripheral clocks and clock enable and reset need to be in
Tegra210 clock suspend/resume as they need to be in proper sequence
w.r.t DFLL resume for restoring CPU clock.
- Removed gpio-tegra changes for hierarchical support to have PMC as
parent to GPIOs for GPIO wake event support. Thierry is working on
gpiolib for some cleanup before adding hierarchical support. So
holding on to GPIO wake support for now.
[V2] : V1 feedback fixes
Patch 0002: This version still using syscore. Thierry suggest not to
use syscore and waiting on suggestion from Linux Walleij for any better
way of storing current state of pins before suspend entry and restoring
them on resume at very early stage. So left this the same way as V1 and
will address once I get more feedback on this.
Also need to findout and implement proper way of forcing resume order
between pinctrl and gpio driver.
[V1]: Tegra210 SC7 entry and exit thru RTC wake and Power button GPIO wake
using hierarchical IRQ with PMC as parent to GPIO.
Sowjanya Komatineni (21):
pinctrl: tegra: Fix write barrier placement in pmx_writel
pinctrl: tegra: Add write barrier after all pinctrl register writes
clk: tegra: divider: Save and restore divider rate
clk: tegra: pllout: Save and restore pllout context
clk: tegra: pll: Save and restore pll context
clk: tegra: Support for OSC context save and restore
clk: Add API to get index of the clock parent
clk: tegra: periph: Add restore_context support
clk: tegra: clk-super: Fix to enable PLLP branches to CPU
clk: tegra: clk-super: Add restore-context support
clk: tegra: clk-dfll: Add suspend and resume support
cpufreq: tegra124: Add suspend and resume support
clk: tegra210: Use fence_udelay during PLLU init
clk: tegra210: Add suspend and resume support
soc/tegra: pmc: Allow to support more tegras wake
soc/tegra: pmc: Add pmc wake support for tegra210
arm64: tegra: Enable wake from deep sleep on RTC alarm
soc/tegra: pmc: Configure core power request polarity
soc/tegra: pmc: Configure deep sleep control settings
arm64: dts: tegra210-p2180: Jetson TX1 SC7 timings
arm64: dts: tegra210-p3450: Jetson Nano SC7 timings
arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi | 7 ++
arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 7 ++
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 5 +-
drivers/clk/clk.c | 17 +++
drivers/clk/tegra/clk-dfll.c | 56 +++++++++
drivers/clk/tegra/clk-dfll.h | 2 +
drivers/clk/tegra/clk-divider.c | 11 ++
drivers/clk/tegra/clk-periph.c | 18 +++
drivers/clk/tegra/clk-pll-out.c | 9 ++
drivers/clk/tegra/clk-pll.c | 88 +++++++++-----
drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++
drivers/clk/tegra/clk-super.c | 35 ++++++
drivers/clk/tegra/clk-tegra-fixed.c | 15 +++
drivers/clk/tegra/clk-tegra-super-gen4.c | 7 +-
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 1 +
drivers/clk/tegra/clk-tegra210.c | 114 ++++++++++++++++--
drivers/clk/tegra/clk.c | 78 +++++++++++++
drivers/clk/tegra/clk.h | 17 +++
drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++
drivers/pinctrl/tegra/pinctrl-tegra.c | 6 +-
drivers/soc/tegra/pmc.c | 129 ++++++++++++++++++++-
include/linux/clk-provider.h | 1 +
22 files changed, 646 insertions(+), 49 deletions(-)
--
2.7.4
This patch uses fence_udelay rather than udelay during PLLU
initialization to ensure writes to clock registers happens before
waiting for specified delay.
Acked-by: Thierry Reding <[email protected]>
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/clk/tegra/clk-tegra210.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 4721ee030d1c..998bf60b219a 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -2841,7 +2841,7 @@ static int tegra210_enable_pllu(void)
reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
reg &= ~BIT(pllu.params->iddq_bit_idx);
writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
- udelay(5);
+ fence_udelay(5, clk_base);
reg = readl_relaxed(clk_base + PLLU_BASE);
reg &= ~GENMASK(20, 0);
@@ -2849,7 +2849,7 @@ static int tegra210_enable_pllu(void)
reg |= fentry->n << 8;
reg |= fentry->p << 16;
writel(reg, clk_base + PLLU_BASE);
- udelay(1);
+ fence_udelay(1, clk_base);
reg |= PLL_ENABLE;
writel(reg, clk_base + PLLU_BASE);
@@ -2895,12 +2895,12 @@ static int tegra210_init_pllu(void)
reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
- udelay(1);
+ fence_udelay(1, clk_base);
reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
- udelay(1);
+ fence_udelay(1, clk_base);
reg = readl_relaxed(clk_base + PLLU_BASE);
reg &= ~PLLU_BASE_CLKENABLE_USB;
--
2.7.4
This patch implements restore_context for clk_super_mux and clk_super.
During system supend, core power goes off the and context of Tegra
CAR registers is lost.
So on system resume, context of super clock registers are restored
to have them in same state as before suspend.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index e2a1e95a8db7..74c9e913e41c 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -124,9 +124,18 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
return err;
}
+static void clk_super_mux_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ clk_super_set_parent(hw, parent_id);
+}
+
static const struct clk_ops tegra_clk_super_mux_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
+ .restore_context = clk_super_mux_restore_context,
};
static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -162,12 +171,24 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
return super->div_ops->set_rate(div_hw, rate, parent_rate);
}
+static void clk_super_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
+ struct clk_hw *div_hw = &super->frac_div.hw;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ super->div_ops->restore_context(div_hw);
+ clk_super_set_parent(hw, parent_id);
+}
+
const struct clk_ops tegra_clk_super_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
.set_rate = clk_super_set_rate,
.round_rate = clk_super_round_rate,
.recalc_rate = clk_super_recalc_rate,
+ .restore_context = clk_super_restore_context,
};
struct clk *tegra_clk_register_super_mux(const char *name,
--
2.7.4
This patch implements restore_context support for clk-periph and
clk-sdmmc-mux clock operations to restore clock parent and rates
on system resume.
During system suspend, core power goes off and looses the context
of the Tegra clock controller registers.
So on system resume, clocks parent and rate are restored back to
the context before suspend based on cached data.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index 58437da25156..c9d28cbadccc 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -3,6 +3,7 @@
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/export.h>
#include <linux/slab.h>
@@ -99,6 +100,20 @@ static void clk_periph_disable(struct clk_hw *hw)
gate_ops->disable(gate_hw);
}
+static void clk_periph_restore_context(struct clk_hw *hw)
+{
+ struct tegra_clk_periph *periph = to_clk_periph(hw);
+ const struct clk_ops *div_ops = periph->div_ops;
+ struct clk_hw *div_hw = &periph->divider.hw;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
+ div_ops->restore_context(div_hw);
+
+ clk_periph_set_parent(hw, parent_id);
+}
+
const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
@@ -108,6 +123,7 @@ const struct clk_ops tegra_clk_periph_ops = {
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
+ .restore_context = clk_periph_restore_context,
};
static const struct clk_ops tegra_clk_periph_nodiv_ops = {
@@ -116,6 +132,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
.disable = clk_periph_disable,
+ .restore_context = clk_periph_restore_context,
};
static const struct clk_ops tegra_clk_periph_no_gate_ops = {
@@ -124,6 +141,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.set_rate = clk_periph_set_rate,
+ .restore_context = clk_periph_restore_context,
};
static struct clk *_tegra_clk_register_periph(const char *name,
diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
index a5cd3e31dbae..8db48966b100 100644
--- a/drivers/clk/tegra/clk-sdmmc-mux.c
+++ b/drivers/clk/tegra/clk-sdmmc-mux.c
@@ -194,6 +194,17 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
gate_ops->disable(gate_hw);
}
+static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long rate = clk_hw_get_rate(hw);
+ int parent_id = clk_hw_get_parent_index(hw, parent);
+
+ clk_sdmmc_mux_set_parent(hw, parent_id);
+ clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
+}
+
static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
.get_parent = clk_sdmmc_mux_get_parent,
.set_parent = clk_sdmmc_mux_set_parent,
@@ -203,6 +214,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
.is_enabled = clk_sdmmc_mux_is_enabled,
.enable = clk_sdmmc_mux_enable,
.disable = clk_sdmmc_mux_disable,
+ .restore_context = clk_sdmmc_mux_restore_context,
};
struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
--
2.7.4
pmx_writel uses writel which inserts write barrier before the
register write rather.
This patch has fix to replace writel with writel_relaxed followed
by a write barrier to ensure write operation before the barrier
is completed for successful pinctrl change.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/pinctrl/tegra/pinctrl-tegra.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c
index e3a237534281..982ee634b3b1 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra.c
@@ -32,7 +32,9 @@ static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg)
{
- writel(val, pmx->regs[bank] + reg);
+ writel_relaxed(val, pmx->regs[bank] + reg);
+ /* make sure pinmux register write completed */
+ wmb();
}
static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
--
2.7.4
This patch adds an API clk_hw_get_parent_index to get index of the
clock parent to use during the clock restore operations on system
resume.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/clk/clk.c | 17 +++++++++++++++++
include/linux/clk-provider.h | 1 +
2 files changed, 18 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c0990703ce54..f26252e48f73 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1643,6 +1643,23 @@ static int clk_fetch_parent_index(struct clk_core *core,
return i;
}
+/**
+ * clk_hw_get_parent_index - return the index of parent clock
+ * @hw: clk_hw associated with the clk being consumed
+ * @parent_hw: clk_hw associated with the parent of clk
+ *
+ * Fetches and returns the index of parent clock.
+ * if hw or parent_hw is NULL, returns -EINVAL.
+ */
+int clk_hw_get_parent_index(struct clk_hw *hw, struct clk_hw *parent_hw)
+{
+ if (!hw || !parent_hw)
+ return -EINVAL;
+
+ return clk_fetch_parent_index(hw->core, parent_hw->core);
+}
+EXPORT_SYMBOL_GPL(clk_hw_get_parent_index);
+
/*
* Update the orphan status of @core and all its children.
*/
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 2ae7604783dd..477112946dd2 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -817,6 +817,7 @@ unsigned int clk_hw_get_num_parents(const struct clk_hw *hw);
struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw);
struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
unsigned int index);
+int clk_hw_get_parent_index(struct clk_hw *hw, struct clk_hw *parent_hw);
unsigned int __clk_get_enable_count(struct clk *clk);
unsigned long clk_hw_get_rate(const struct clk_hw *hw);
unsigned long __clk_get_flags(struct clk *clk);
--
2.7.4
This patch adds support for saving OSC clock frequency and the
drive-strength during OSC clock init and creates an API to restore
OSC control register value from the saved context.
This API is invoked by Tegra210 clock driver during system resume
to restore the OSC clock settings.
Acked-by: Thierry Reding <[email protected]>
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
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 8d91b2b191cf..7c6c8abfcde6 100644
--- a/drivers/clk/tegra/clk-tegra-fixed.c
+++ b/drivers/clk/tegra/clk-tegra-fixed.c
@@ -17,6 +17,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,
@@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks,
unsigned osc_idx;
val = readl_relaxed(clk_base + OSC_CTRL);
+ osc_ctrl_ctx = val & OSC_CTRL_MASK;
osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
if (osc_idx < num)
@@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks)
*dt_clk = clk;
}
}
+
+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);
+}
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index dc546292e030..8a9af45b6084 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -837,6 +837,7 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate);
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);
+void tegra_clk_osc_resume(void __iomem *clk_base);
/* Combined read fence with delay */
--
2.7.4
This patch updates device tree for RTC and PMC to allow system wake
from deep sleep on RTC alarm.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
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 659753118e96..30a7c48385a2 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -768,7 +768,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";
};
@@ -778,6 +779,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
This patch has a fix to enable PLLP branches to CPU before changing
the CPU cluster clock source to PLLP for Gen5 Super clock and
disables PLLP branches to CPU when not in use.
During system suspend entry and exit, CPU source will be switched
to PLLP and this needs PLLP branches to be enabled to CPU prior to
the switch.
On system resume, warmboot code enables PLLP branches to CPU and
powers up the CPU with PLLP clock source.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/clk/tegra/clk-super.c | 14 ++++++++++++++
drivers/clk/tegra/clk-tegra-super-gen4.c | 7 ++++++-
drivers/clk/tegra/clk.c | 14 ++++++++++++++
drivers/clk/tegra/clk.h | 5 +++++
4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index 39ef31b46df5..e2a1e95a8db7 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -28,6 +28,9 @@
#define super_state_to_src_shift(m, s) ((m->width * s))
#define super_state_to_src_mask(m) (((1 << m->width) - 1))
+#define CCLK_SRC_PLLP_OUT0 4
+#define CCLK_SRC_PLLP_OUT4 5
+
static u8 clk_super_get_parent(struct clk_hw *hw)
{
struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
@@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
if (index == mux->div2_index)
index = mux->pllx_index;
}
+
+ /* enable PLLP branches to CPU before selecting PLLP source */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
+ tegra_clk_set_pllp_out_cpu(true);
+
val &= ~((super_state_to_src_mask(mux)) << shift);
val |= (index & (super_state_to_src_mask(mux))) << shift;
writel_relaxed(val, mux->reg);
udelay(2);
+ /* disable PLLP branches to CPU if not used */
+ if ((mux->flags & TEGRA210_CPU_CLK) &&
+ index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
+ tegra_clk_set_pllp_out_cpu(false);
+
out:
if (mux->lock)
spin_unlock_irqrestore(mux->lock, flags);
diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
index cdfe7c9697e1..98538f79b0c4 100644
--- a/drivers/clk/tegra/clk-tegra-super-gen4.c
+++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
@@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
gen_info->num_cclk_g_parents,
CLK_SET_RATE_PARENT,
clk_base + CCLKG_BURST_POLICY,
- 0, 4, 8, 0, NULL);
+ TEGRA210_CPU_CLK, 4, 8, 0, NULL);
} else {
clk = tegra_clk_register_super_mux("cclk_g",
gen_info->cclk_g_parents,
@@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
if (dt_clk) {
if (gen_info->gen == gen5) {
+ /*
+ * TEGRA210_CPU_CLK flag is not needed for cclk_lp as cluster
+ * switching is not currently supported on Tegra210 and also
+ * cpu_lp is not used.
+ */
clk = tegra_clk_register_super_mux("cclk_lp",
gen_info->cclk_lp_parents,
gen_info->num_cclk_lp_parents,
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index 573e3c967ae1..eb08047fd02f 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -23,6 +23,7 @@
#define CLK_OUT_ENB_W 0x364
#define CLK_OUT_ENB_X 0x280
#define CLK_OUT_ENB_Y 0x298
+#define CLK_ENB_PLLP_OUT_CPU BIT(31)
#define CLK_OUT_ENB_SET_L 0x320
#define CLK_OUT_ENB_CLR_L 0x324
#define CLK_OUT_ENB_SET_H 0x328
@@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
}
}
+void tegra_clk_set_pllp_out_cpu(bool enable)
+{
+ u32 val;
+
+ val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
+ if (enable)
+ val |= CLK_ENB_PLLP_OUT_CPU;
+ else
+ val &= ~CLK_ENB_PLLP_OUT_CPU;
+
+ writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
+}
+
struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
{
clk_base = regs;
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 8a9af45b6084..560e2bcb3d7d 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -677,6 +677,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
* Flags:
* TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
* that this is LP cluster clock.
+ * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
+ * super mux parent using PLLP branches. To use PLLP branches to CPU, need
+ * to configure additional bit PLLP_OUT_CPU in the clock registers.
*/
struct tegra_clk_super_mux {
struct clk_hw hw;
@@ -693,6 +696,7 @@ struct tegra_clk_super_mux {
#define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
#define TEGRA_DIVIDER_2 BIT(0)
+#define TEGRA210_CPU_CLK BIT(1)
extern const struct clk_ops tegra_clk_super_ops;
struct clk *tegra_clk_register_super_mux(const char *name,
@@ -838,6 +842,7 @@ 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);
void tegra_clk_osc_resume(void __iomem *clk_base);
+void tegra_clk_set_pllp_out_cpu(bool enable);
/* Combined read fence with delay */
--
2.7.4
This patch has Jetson TX1 platform specific SC7 timing configuration
in device tree.
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
index 27723829d033..cb58f79deb48 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2180.dtsi
@@ -279,6 +279,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 */
--
2.7.4
This patch adds suspend and resume pm ops for cpufreq driver.
PLLP is the safe clock source for CPU during system suspend and
resume as PLLP rate is below the CPU Fmax at Vmin.
CPUFreq driver suspend switches the CPU clock source to PLLP and
disables the DFLL clock.
During system resume, warmboot code powers up the CPU with PLLP
clock source. So CPUFreq driver resume enabled DFLL clock and
switches CPU back to DFLL clock source.
Acked-by: Viresh Kumar <[email protected]>
Reviewed-by: Dmitry Osipenko <[email protected]>
Signed-off-by: Sowjanya Komatineni <[email protected]>
---
drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
index 4f0c637b3b49..e979a3370988 100644
--- a/drivers/cpufreq/tegra124-cpufreq.c
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
return ret;
}
+static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
+{
+ struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+ int err;
+
+ /*
+ * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
+ * use during suspend and resume. So, switch the CPU clock source
+ * to PLLP and disable DFLL.
+ */
+ err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to reparent to PLLP: %d\n", err);
+ return err;
+ }
+
+ /* disable DFLL clock */
+ clk_disable_unprepare(priv->dfll_clk);
+
+ return 0;
+}
+
+static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
+{
+ struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
+ int err;
+
+ /*
+ * Warmboot code powers up the CPU with PLLP clock source.
+ * Enable DFLL clock and switch CPU clock source back to DFLL.
+ */
+ err = clk_prepare_enable(priv->dfll_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
+ goto disable_cpufreq;
+ }
+
+ err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
+ if (err < 0) {
+ dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
+ goto disable_dfll;
+ }
+
+ return 0;
+
+disable_dfll:
+ clk_disable_unprepare(priv->dfll_clk);
+disable_cpufreq:
+ disable_cpufreq();
+
+ return err;
+}
+
+static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
+ tegra124_cpufreq_resume)
+};
+
static struct platform_driver tegra124_cpufreq_platdrv = {
.driver.name = "cpufreq-tegra124",
+ .driver.pm = &tegra124_cpufreq_pm_ops,
.probe = tegra124_cpufreq_probe,
};
--
2.7.4
09.08.2019 2:46, Sowjanya Komatineni пишет:
> pmx_writel uses writel which inserts write barrier before the
> register write rather.
>
> This patch has fix to replace writel with writel_relaxed followed
> by a write barrier to ensure write operation before the barrier
> is completed for successful pinctrl change.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/pinctrl/tegra/pinctrl-tegra.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c
> index e3a237534281..982ee634b3b1 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c
> @@ -32,7 +32,9 @@ static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
>
> static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg)
> {
> - writel(val, pmx->regs[bank] + reg);
> + writel_relaxed(val, pmx->regs[bank] + reg);
> + /* make sure pinmux register write completed */
> + wmb();
> }
>
> static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
>
But this only ensures that CPU have sent the write to the APB BUS and
not that the write actually taken into effect? I'm a bit paranoid when
it comes to a cross-domain synchronization things.
Any ways it looks better than it was before.
Reviewed-by: Dmitry Osipenko <[email protected]>
09.08.2019 2:46, Sowjanya Komatineni пишет:
> This patch adds an API clk_hw_get_parent_index to get index of the
> clock parent to use during the clock restore operations on system
> resume.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/clk.c | 17 +++++++++++++++++
> include/linux/clk-provider.h | 1 +
> 2 files changed, 18 insertions(+)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index c0990703ce54..f26252e48f73 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1643,6 +1643,23 @@ static int clk_fetch_parent_index(struct clk_core *core,
> return i;
> }
>
> +/**
> + * clk_hw_get_parent_index - return the index of parent clock
> + * @hw: clk_hw associated with the clk being consumed
> + * @parent_hw: clk_hw associated with the parent of clk
> + *
> + * Fetches and returns the index of parent clock.
> + * if hw or parent_hw is NULL, returns -EINVAL.
> + */
> +int clk_hw_get_parent_index(struct clk_hw *hw, struct clk_hw *parent_hw)
> +{
> + if (!hw || !parent_hw)
> + return -EINVAL;
> +
> + return clk_fetch_parent_index(hw->core, parent_hw->core);
> +}
> +EXPORT_SYMBOL_GPL(clk_hw_get_parent_index);
> +
> /*
> * Update the orphan status of @core and all its children.
> */
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 2ae7604783dd..477112946dd2 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -817,6 +817,7 @@ unsigned int clk_hw_get_num_parents(const struct clk_hw *hw);
> struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw);
> struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
> unsigned int index);
> +int clk_hw_get_parent_index(struct clk_hw *hw, struct clk_hw *parent_hw);
> unsigned int __clk_get_enable_count(struct clk *clk);
> unsigned long clk_hw_get_rate(const struct clk_hw *hw);
> unsigned long __clk_get_flags(struct clk *clk);
>
Reviewed-by: Dmitry Osipenko <[email protected]>
09.08.2019 2:46, Sowjanya Komatineni пишет:
> This patch implements restore_context support for clk-periph and
> clk-sdmmc-mux clock operations to restore clock parent and rates
> on system resume.
>
> During system suspend, core power goes off and looses the context
> of the Tegra clock controller registers.
>
> So on system resume, clocks parent and rate are restored back to
> the context before suspend based on cached data.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
> drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
> 2 files changed, 30 insertions(+)
>
> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
> index 58437da25156..c9d28cbadccc 100644
> --- a/drivers/clk/tegra/clk-periph.c
> +++ b/drivers/clk/tegra/clk-periph.c
> @@ -3,6 +3,7 @@
> * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
> */
>
> +#include <linux/clk.h>
> #include <linux/clk-provider.h>
> #include <linux/export.h>
> #include <linux/slab.h>
> @@ -99,6 +100,20 @@ static void clk_periph_disable(struct clk_hw *hw)
> gate_ops->disable(gate_hw);
> }
>
> +static void clk_periph_restore_context(struct clk_hw *hw)
> +{
> + struct tegra_clk_periph *periph = to_clk_periph(hw);
> + const struct clk_ops *div_ops = periph->div_ops;
> + struct clk_hw *div_hw = &periph->divider.hw;
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + int parent_id = clk_hw_get_parent_index(hw, parent);
> +
> + if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
> + div_ops->restore_context(div_hw);
> +
> + clk_periph_set_parent(hw, parent_id);
> +}
> +
> const struct clk_ops tegra_clk_periph_ops = {
> .get_parent = clk_periph_get_parent,
> .set_parent = clk_periph_set_parent,
> @@ -108,6 +123,7 @@ const struct clk_ops tegra_clk_periph_ops = {
> .is_enabled = clk_periph_is_enabled,
> .enable = clk_periph_enable,
> .disable = clk_periph_disable,
> + .restore_context = clk_periph_restore_context,
> };
>
> static const struct clk_ops tegra_clk_periph_nodiv_ops = {
> @@ -116,6 +132,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
> .is_enabled = clk_periph_is_enabled,
> .enable = clk_periph_enable,
> .disable = clk_periph_disable,
> + .restore_context = clk_periph_restore_context,
> };
>
> static const struct clk_ops tegra_clk_periph_no_gate_ops = {
> @@ -124,6 +141,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
> .recalc_rate = clk_periph_recalc_rate,
> .round_rate = clk_periph_round_rate,
> .set_rate = clk_periph_set_rate,
> + .restore_context = clk_periph_restore_context,
> };
>
> static struct clk *_tegra_clk_register_periph(const char *name,
> diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
> index a5cd3e31dbae..8db48966b100 100644
> --- a/drivers/clk/tegra/clk-sdmmc-mux.c
> +++ b/drivers/clk/tegra/clk-sdmmc-mux.c
> @@ -194,6 +194,17 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
> gate_ops->disable(gate_hw);
> }
>
> +static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
> +{
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + unsigned long parent_rate = clk_hw_get_rate(parent);
> + unsigned long rate = clk_hw_get_rate(hw);
> + int parent_id = clk_hw_get_parent_index(hw, parent);
> +
> + clk_sdmmc_mux_set_parent(hw, parent_id);
> + clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
For the periph clocks you're restoring rate at first and then the
parent, while here it's the other way around. I'm wondering if there is
any difference in practice and thus whether rate's divider need to be
set to a some safe value before switching to a new parent that has a
higher clock rate than the old parent.
> +}
> +
> static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
> .get_parent = clk_sdmmc_mux_get_parent,
> .set_parent = clk_sdmmc_mux_set_parent,
> @@ -203,6 +214,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
> .is_enabled = clk_sdmmc_mux_is_enabled,
> .enable = clk_sdmmc_mux_enable,
> .disable = clk_sdmmc_mux_disable,
> + .restore_context = clk_sdmmc_mux_restore_context,
> };
>
> struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
>
09.08.2019 2:46, Sowjanya Komatineni пишет:
> This patch has a fix to enable PLLP branches to CPU before changing
> the CPU cluster clock source to PLLP for Gen5 Super clock and
> disables PLLP branches to CPU when not in use.
>
> During system suspend entry and exit, CPU source will be switched
> to PLLP and this needs PLLP branches to be enabled to CPU prior to
> the switch.
>
> On system resume, warmboot code enables PLLP branches to CPU and
> powers up the CPU with PLLP clock source.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-super.c | 14 ++++++++++++++
> drivers/clk/tegra/clk-tegra-super-gen4.c | 7 ++++++-
> drivers/clk/tegra/clk.c | 14 ++++++++++++++
> drivers/clk/tegra/clk.h | 5 +++++
> 4 files changed, 39 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> index 39ef31b46df5..e2a1e95a8db7 100644
> --- a/drivers/clk/tegra/clk-super.c
> +++ b/drivers/clk/tegra/clk-super.c
> @@ -28,6 +28,9 @@
> #define super_state_to_src_shift(m, s) ((m->width * s))
> #define super_state_to_src_mask(m) (((1 << m->width) - 1))
>
> +#define CCLK_SRC_PLLP_OUT0 4
> +#define CCLK_SRC_PLLP_OUT4 5
> +
> static u8 clk_super_get_parent(struct clk_hw *hw)
> {
> struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
> @@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
> if (index == mux->div2_index)
> index = mux->pllx_index;
> }
> +
> + /* enable PLLP branches to CPU before selecting PLLP source */
> + if ((mux->flags & TEGRA210_CPU_CLK) &&
> + (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
> + tegra_clk_set_pllp_out_cpu(true);
> +
> val &= ~((super_state_to_src_mask(mux)) << shift);
> val |= (index & (super_state_to_src_mask(mux))) << shift;
>
> writel_relaxed(val, mux->reg);
> udelay(2);
>
> + /* disable PLLP branches to CPU if not used */
> + if ((mux->flags & TEGRA210_CPU_CLK) &&
> + index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
> + tegra_clk_set_pllp_out_cpu(false);
> +
> out:
> if (mux->lock)
> spin_unlock_irqrestore(mux->lock, flags);
> diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
> index cdfe7c9697e1..98538f79b0c4 100644
> --- a/drivers/clk/tegra/clk-tegra-super-gen4.c
> +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
> @@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
> gen_info->num_cclk_g_parents,
> CLK_SET_RATE_PARENT,
> clk_base + CCLKG_BURST_POLICY,
> - 0, 4, 8, 0, NULL);
> + TEGRA210_CPU_CLK, 4, 8, 0, NULL);
> } else {
> clk = tegra_clk_register_super_mux("cclk_g",
> gen_info->cclk_g_parents,
> @@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
> dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
> if (dt_clk) {
> if (gen_info->gen == gen5) {
> + /*
> + * TEGRA210_CPU_CLK flag is not needed for cclk_lp as cluster
> + * switching is not currently supported on Tegra210 and also
> + * cpu_lp is not used.
> + */
> clk = tegra_clk_register_super_mux("cclk_lp",
> gen_info->cclk_lp_parents,
> gen_info->num_cclk_lp_parents,
> diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
> index 573e3c967ae1..eb08047fd02f 100644
> --- a/drivers/clk/tegra/clk.c
> +++ b/drivers/clk/tegra/clk.c
> @@ -23,6 +23,7 @@
> #define CLK_OUT_ENB_W 0x364
> #define CLK_OUT_ENB_X 0x280
> #define CLK_OUT_ENB_Y 0x298
> +#define CLK_ENB_PLLP_OUT_CPU BIT(31)
> #define CLK_OUT_ENB_SET_L 0x320
> #define CLK_OUT_ENB_CLR_L 0x324
> #define CLK_OUT_ENB_SET_H 0x328
> @@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
> }
> }
>
> +void tegra_clk_set_pllp_out_cpu(bool enable)
> +{
> + u32 val;
> +
> + val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
> + if (enable)
> + val |= CLK_ENB_PLLP_OUT_CPU;
> + else
> + val &= ~CLK_ENB_PLLP_OUT_CPU;
> +
> + writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
> +}
> +
> struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
> {
> clk_base = regs;
> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
> index 8a9af45b6084..560e2bcb3d7d 100644
> --- a/drivers/clk/tegra/clk.h
> +++ b/drivers/clk/tegra/clk.h
> @@ -677,6 +677,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
> * Flags:
> * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
> * that this is LP cluster clock.
> + * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
> + * super mux parent using PLLP branches. To use PLLP branches to CPU, need
> + * to configure additional bit PLLP_OUT_CPU in the clock registers.
> */
> struct tegra_clk_super_mux {
> struct clk_hw hw;
> @@ -693,6 +696,7 @@ struct tegra_clk_super_mux {
> #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
>
> #define TEGRA_DIVIDER_2 BIT(0)
> +#define TEGRA210_CPU_CLK BIT(1)
>
> extern const struct clk_ops tegra_clk_super_ops;
> struct clk *tegra_clk_register_super_mux(const char *name,
> @@ -838,6 +842,7 @@ 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);
> void tegra_clk_osc_resume(void __iomem *clk_base);
> +void tegra_clk_set_pllp_out_cpu(bool enable);
>
>
> /* Combined read fence with delay */
>
Looks good to me.
Reviewed-by: Dmitry Osipenko <[email protected]>
09.08.2019 2:46, Sowjanya Komatineni пишет:
> This patch implements restore_context for clk_super_mux and clk_super.
>
> During system supend, core power goes off the and context of Tegra
> CAR registers is lost.
>
> So on system resume, context of super clock registers are restored
> to have them in same state as before suspend.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> index e2a1e95a8db7..74c9e913e41c 100644
> --- a/drivers/clk/tegra/clk-super.c
> +++ b/drivers/clk/tegra/clk-super.c
> @@ -124,9 +124,18 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
> return err;
> }
>
> +static void clk_super_mux_restore_context(struct clk_hw *hw)
> +{
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + int parent_id = clk_hw_get_parent_index(hw, parent);
> +
> + clk_super_set_parent(hw, parent_id);
All Super clocks have a divider, including the "MUX". Thus I'm wondering
if there is a chance that divider's configuration may differ on resume
from what it was on suspend.
> +}
> +
> static const struct clk_ops tegra_clk_super_mux_ops = {
> .get_parent = clk_super_get_parent,
> .set_parent = clk_super_set_parent,
> + .restore_context = clk_super_mux_restore_context,
> };
>
> static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> @@ -162,12 +171,24 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
> return super->div_ops->set_rate(div_hw, rate, parent_rate);
> }
>
> +static void clk_super_restore_context(struct clk_hw *hw)
> +{
> + struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> + struct clk_hw *div_hw = &super->frac_div.hw;
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + int parent_id = clk_hw_get_parent_index(hw, parent);
> +
> + super->div_ops->restore_context(div_hw);
> + clk_super_set_parent(hw, parent_id);
> +}
> +
> const struct clk_ops tegra_clk_super_ops = {
> .get_parent = clk_super_get_parent,
> .set_parent = clk_super_set_parent,
> .set_rate = clk_super_set_rate,
> .round_rate = clk_super_round_rate,
> .recalc_rate = clk_super_recalc_rate,
> + .restore_context = clk_super_restore_context,
> };
>
> struct clk *tegra_clk_register_super_mux(const char *name,
>
09.08.2019 14:55, Dmitry Osipenko пишет:
> 09.08.2019 2:46, Sowjanya Komatineni пишет:
>> This patch implements restore_context support for clk-periph and
>> clk-sdmmc-mux clock operations to restore clock parent and rates
>> on system resume.
>>
>> During system suspend, core power goes off and looses the context
>> of the Tegra clock controller registers.
>>
>> So on system resume, clocks parent and rate are restored back to
>> the context before suspend based on cached data.
>>
>> Signed-off-by: Sowjanya Komatineni <[email protected]>
>> ---
>> drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
>> drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
>> 2 files changed, 30 insertions(+)
>>
>> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
>> index 58437da25156..c9d28cbadccc 100644
>> --- a/drivers/clk/tegra/clk-periph.c
>> +++ b/drivers/clk/tegra/clk-periph.c
>> @@ -3,6 +3,7 @@
>> * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
>> */
>>
>> +#include <linux/clk.h>
>> #include <linux/clk-provider.h>
>> #include <linux/export.h>
>> #include <linux/slab.h>
>> @@ -99,6 +100,20 @@ static void clk_periph_disable(struct clk_hw *hw)
>> gate_ops->disable(gate_hw);
>> }
>>
>> +static void clk_periph_restore_context(struct clk_hw *hw)
>> +{
>> + struct tegra_clk_periph *periph = to_clk_periph(hw);
>> + const struct clk_ops *div_ops = periph->div_ops;
>> + struct clk_hw *div_hw = &periph->divider.hw;
>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>> +
>> + if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>> + div_ops->restore_context(div_hw);
>> +
>> + clk_periph_set_parent(hw, parent_id);
>> +}
>> +
>> const struct clk_ops tegra_clk_periph_ops = {
>> .get_parent = clk_periph_get_parent,
>> .set_parent = clk_periph_set_parent,
>> @@ -108,6 +123,7 @@ const struct clk_ops tegra_clk_periph_ops = {
>> .is_enabled = clk_periph_is_enabled,
>> .enable = clk_periph_enable,
>> .disable = clk_periph_disable,
>> + .restore_context = clk_periph_restore_context,
>> };
>>
>> static const struct clk_ops tegra_clk_periph_nodiv_ops = {
>> @@ -116,6 +132,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
>> .is_enabled = clk_periph_is_enabled,
>> .enable = clk_periph_enable,
>> .disable = clk_periph_disable,
>> + .restore_context = clk_periph_restore_context,
>> };
>>
>> static const struct clk_ops tegra_clk_periph_no_gate_ops = {
>> @@ -124,6 +141,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
>> .recalc_rate = clk_periph_recalc_rate,
>> .round_rate = clk_periph_round_rate,
>> .set_rate = clk_periph_set_rate,
>> + .restore_context = clk_periph_restore_context,
>> };
>>
>> static struct clk *_tegra_clk_register_periph(const char *name,
>> diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
>> index a5cd3e31dbae..8db48966b100 100644
>> --- a/drivers/clk/tegra/clk-sdmmc-mux.c
>> +++ b/drivers/clk/tegra/clk-sdmmc-mux.c
>> @@ -194,6 +194,17 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
>> gate_ops->disable(gate_hw);
>> }
>>
>> +static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
>> +{
>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>> + unsigned long parent_rate = clk_hw_get_rate(parent);
>> + unsigned long rate = clk_hw_get_rate(hw);
>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>> +
>> + clk_sdmmc_mux_set_parent(hw, parent_id);
>> + clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
>
> For the periph clocks you're restoring rate at first and then the
> parent, while here it's the other way around. I'm wondering if there is
> any difference in practice and thus whether rate's divider need to be
> set to a some safe value before switching to a new parent that has a
> higher clock rate than the old parent.
Although, I guess that all peripheral clocks should be disabled at this
point of resume. Correct?
>> +}
>> +
>> static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>> .get_parent = clk_sdmmc_mux_get_parent,
>> .set_parent = clk_sdmmc_mux_set_parent,
>> @@ -203,6 +214,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>> .is_enabled = clk_sdmmc_mux_is_enabled,
>> .enable = clk_sdmmc_mux_enable,
>> .disable = clk_sdmmc_mux_disable,
>> + .restore_context = clk_sdmmc_mux_restore_context,
>> };
>>
>> struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
>>
>
On 8/9/19 5:17 AM, Dmitry Osipenko wrote:
> 09.08.2019 2:46, Sowjanya Komatineni пишет:
>> This patch implements restore_context for clk_super_mux and clk_super.
>>
>> During system supend, core power goes off the and context of Tegra
>> CAR registers is lost.
>>
>> So on system resume, context of super clock registers are restored
>> to have them in same state as before suspend.
>>
>> Signed-off-by: Sowjanya Komatineni <[email protected]>
>> ---
>> drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
>> 1 file changed, 21 insertions(+)
>>
>> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
>> index e2a1e95a8db7..74c9e913e41c 100644
>> --- a/drivers/clk/tegra/clk-super.c
>> +++ b/drivers/clk/tegra/clk-super.c
>> @@ -124,9 +124,18 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
>> return err;
>> }
>>
>> +static void clk_super_mux_restore_context(struct clk_hw *hw)
>> +{
>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>> +
>> + clk_super_set_parent(hw, parent_id);
> All Super clocks have a divider, including the "MUX". Thus I'm wondering
> if there is a chance that divider's configuration may differ on resume
> from what it was on suspend.
tegra_clk_register_super_mux which uses tegra_clk_super_mux_ops doesn't
do divider rate programming.
I believe you are referring to sclk_divider, cclklp_divider,
cclkg_divider...
these are registered as clk_divider and are restored during clk_divider
resume.
>> +}
>> +
>> static const struct clk_ops tegra_clk_super_mux_ops = {
>> .get_parent = clk_super_get_parent,
>> .set_parent = clk_super_set_parent,
>> + .restore_context = clk_super_mux_restore_context,
>> };
>>
>> static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
>> @@ -162,12 +171,24 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate,
>> return super->div_ops->set_rate(div_hw, rate, parent_rate);
>> }
>>
>> +static void clk_super_restore_context(struct clk_hw *hw)
>> +{
>> + struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
>> + struct clk_hw *div_hw = &super->frac_div.hw;
>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>> +
>> + super->div_ops->restore_context(div_hw);
>> + clk_super_set_parent(hw, parent_id);
>> +}
>> +
>> const struct clk_ops tegra_clk_super_ops = {
>> .get_parent = clk_super_get_parent,
>> .set_parent = clk_super_set_parent,
>> .set_rate = clk_super_set_rate,
>> .round_rate = clk_super_round_rate,
>> .recalc_rate = clk_super_recalc_rate,
>> + .restore_context = clk_super_restore_context,
>> };
>>
>> struct clk *tegra_clk_register_super_mux(const char *name,
>>
On 8/9/19 5:20 AM, Dmitry Osipenko wrote:
> 09.08.2019 14:55, Dmitry Osipenko пишет:
>> 09.08.2019 2:46, Sowjanya Komatineni пишет:
>>> This patch implements restore_context support for clk-periph and
>>> clk-sdmmc-mux clock operations to restore clock parent and rates
>>> on system resume.
>>>
>>> During system suspend, core power goes off and looses the context
>>> of the Tegra clock controller registers.
>>>
>>> So on system resume, clocks parent and rate are restored back to
>>> the context before suspend based on cached data.
>>>
>>> Signed-off-by: Sowjanya Komatineni <[email protected]>
>>> ---
>>> drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
>>> drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
>>> 2 files changed, 30 insertions(+)
>>>
>>> diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
>>> index 58437da25156..c9d28cbadccc 100644
>>> --- a/drivers/clk/tegra/clk-periph.c
>>> +++ b/drivers/clk/tegra/clk-periph.c
>>> @@ -3,6 +3,7 @@
>>> * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
>>> */
>>>
>>> +#include <linux/clk.h>
>>> #include <linux/clk-provider.h>
>>> #include <linux/export.h>
>>> #include <linux/slab.h>
>>> @@ -99,6 +100,20 @@ static void clk_periph_disable(struct clk_hw *hw)
>>> gate_ops->disable(gate_hw);
>>> }
>>>
>>> +static void clk_periph_restore_context(struct clk_hw *hw)
>>> +{
>>> + struct tegra_clk_periph *periph = to_clk_periph(hw);
>>> + const struct clk_ops *div_ops = periph->div_ops;
>>> + struct clk_hw *div_hw = &periph->divider.hw;
>>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>>> +
>>> + if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
>>> + div_ops->restore_context(div_hw);
>>> +
>>> + clk_periph_set_parent(hw, parent_id);
>>> +}
>>> +
>>> const struct clk_ops tegra_clk_periph_ops = {
>>> .get_parent = clk_periph_get_parent,
>>> .set_parent = clk_periph_set_parent,
>>> @@ -108,6 +123,7 @@ const struct clk_ops tegra_clk_periph_ops = {
>>> .is_enabled = clk_periph_is_enabled,
>>> .enable = clk_periph_enable,
>>> .disable = clk_periph_disable,
>>> + .restore_context = clk_periph_restore_context,
>>> };
>>>
>>> static const struct clk_ops tegra_clk_periph_nodiv_ops = {
>>> @@ -116,6 +132,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = {
>>> .is_enabled = clk_periph_is_enabled,
>>> .enable = clk_periph_enable,
>>> .disable = clk_periph_disable,
>>> + .restore_context = clk_periph_restore_context,
>>> };
>>>
>>> static const struct clk_ops tegra_clk_periph_no_gate_ops = {
>>> @@ -124,6 +141,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
>>> .recalc_rate = clk_periph_recalc_rate,
>>> .round_rate = clk_periph_round_rate,
>>> .set_rate = clk_periph_set_rate,
>>> + .restore_context = clk_periph_restore_context,
>>> };
>>>
>>> static struct clk *_tegra_clk_register_periph(const char *name,
>>> diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c
>>> index a5cd3e31dbae..8db48966b100 100644
>>> --- a/drivers/clk/tegra/clk-sdmmc-mux.c
>>> +++ b/drivers/clk/tegra/clk-sdmmc-mux.c
>>> @@ -194,6 +194,17 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw)
>>> gate_ops->disable(gate_hw);
>>> }
>>>
>>> +static void clk_sdmmc_mux_restore_context(struct clk_hw *hw)
>>> +{
>>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>>> + unsigned long parent_rate = clk_hw_get_rate(parent);
>>> + unsigned long rate = clk_hw_get_rate(hw);
>>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>>> +
>>> + clk_sdmmc_mux_set_parent(hw, parent_id);
>>> + clk_sdmmc_mux_set_rate(hw, rate, parent_rate);
>> For the periph clocks you're restoring rate at first and then the
>> parent, while here it's the other way around. I'm wondering if there is
>> any difference in practice and thus whether rate's divider need to be
>> set to a some safe value before switching to a new parent that has a
>> higher clock rate than the old parent.
> Although, I guess that all peripheral clocks should be disabled at this
> point of resume. Correct?
by the time restore happens, peripheral clocks are enabled but held in
reset state.
For non-boot clocks, doing divider programming before parent source is
preferred.
For sdmmc clock, programming parent before divisor is allowed.
Also, clk_sdmmc_mux_set_rate gets parent MUX selection thru register
read so restoring parent prior to this will have right divisor based on
rate and parent.
>>> +}
>>> +
>>> static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>>> .get_parent = clk_sdmmc_mux_get_parent,
>>> .set_parent = clk_sdmmc_mux_set_parent,
>>> @@ -203,6 +214,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = {
>>> .is_enabled = clk_sdmmc_mux_is_enabled,
>>> .enable = clk_sdmmc_mux_enable,
>>> .disable = clk_sdmmc_mux_disable,
>>> + .restore_context = clk_sdmmc_mux_restore_context,
>>> };
>>>
>>> struct clk *tegra_clk_register_sdmmc_mux_div(const char *name,
>>>
09.08.2019 20:08, Sowjanya Komatineni пишет:
>
> On 8/9/19 5:17 AM, Dmitry Osipenko wrote:
>> 09.08.2019 2:46, Sowjanya Komatineni пишет:
>>> This patch implements restore_context for clk_super_mux and clk_super.
>>>
>>> During system supend, core power goes off the and context of Tegra
>>> CAR registers is lost.
>>>
>>> So on system resume, context of super clock registers are restored
>>> to have them in same state as before suspend.
>>>
>>> Signed-off-by: Sowjanya Komatineni <[email protected]>
>>> ---
>>> drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
>>> 1 file changed, 21 insertions(+)
>>>
>>> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
>>> index e2a1e95a8db7..74c9e913e41c 100644
>>> --- a/drivers/clk/tegra/clk-super.c
>>> +++ b/drivers/clk/tegra/clk-super.c
>>> @@ -124,9 +124,18 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
>>> return err;
>>> }
>>> +static void clk_super_mux_restore_context(struct clk_hw *hw)
>>> +{
>>> + struct clk_hw *parent = clk_hw_get_parent(hw);
>>> + int parent_id = clk_hw_get_parent_index(hw, parent);
>>> +
>>> + clk_super_set_parent(hw, parent_id);
>> All Super clocks have a divider, including the "MUX". Thus I'm wondering
>> if there is a chance that divider's configuration may differ on resume
>> from what it was on suspend.
>
> tegra_clk_register_super_mux which uses tegra_clk_super_mux_ops doesn't do divider rate
> programming.
>
> I believe you are referring to sclk_divider, cclklp_divider, cclkg_divider...
>
> these are registered as clk_divider and are restored during clk_divider resume.
Indeed, thanks for the clarification.
09.08.2019 2:46, Sowjanya Komatineni пишет:
> This patch uses fence_udelay rather than udelay during PLLU
> initialization to ensure writes to clock registers happens before
> waiting for specified delay.
>
> Acked-by: Thierry Reding <[email protected]>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-tegra210.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
> index 4721ee030d1c..998bf60b219a 100644
> --- a/drivers/clk/tegra/clk-tegra210.c
> +++ b/drivers/clk/tegra/clk-tegra210.c
> @@ -2841,7 +2841,7 @@ static int tegra210_enable_pllu(void)
> reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
> reg &= ~BIT(pllu.params->iddq_bit_idx);
> writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
> - udelay(5);
> + fence_udelay(5, clk_base);
>
> reg = readl_relaxed(clk_base + PLLU_BASE);
> reg &= ~GENMASK(20, 0);
> @@ -2849,7 +2849,7 @@ static int tegra210_enable_pllu(void)
> reg |= fentry->n << 8;
> reg |= fentry->p << 16;
> writel(reg, clk_base + PLLU_BASE);
> - udelay(1);
> + fence_udelay(1, clk_base);
> reg |= PLL_ENABLE;
> writel(reg, clk_base + PLLU_BASE);
>
> @@ -2895,12 +2895,12 @@ static int tegra210_init_pllu(void)
> reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
> reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
> writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
> - udelay(1);
> + fence_udelay(1, clk_base);
>
> reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
> reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
> writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
> - udelay(1);
> + fence_udelay(1, clk_base);
>
> reg = readl_relaxed(clk_base + PLLU_BASE);
> reg &= ~PLLU_BASE_CLKENABLE_USB;
>
The clk_base corresponds to the RESET controller's part of Clock-and-Reset hardware, is it
okay to read-back the RST register and not the clock for the fencing?
On 8/11/19 11:02 AM, Dmitry Osipenko wrote:
> 09.08.2019 2:46, Sowjanya Komatineni пишет:
>> This patch uses fence_udelay rather than udelay during PLLU
>> initialization to ensure writes to clock registers happens before
>> waiting for specified delay.
>>
>> Acked-by: Thierry Reding <[email protected]>
>> Signed-off-by: Sowjanya Komatineni <[email protected]>
>> ---
>> drivers/clk/tegra/clk-tegra210.c | 8 ++++----
>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
>> index 4721ee030d1c..998bf60b219a 100644
>> --- a/drivers/clk/tegra/clk-tegra210.c
>> +++ b/drivers/clk/tegra/clk-tegra210.c
>> @@ -2841,7 +2841,7 @@ static int tegra210_enable_pllu(void)
>> reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]);
>> reg &= ~BIT(pllu.params->iddq_bit_idx);
>> writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]);
>> - udelay(5);
>> + fence_udelay(5, clk_base);
>>
>> reg = readl_relaxed(clk_base + PLLU_BASE);
>> reg &= ~GENMASK(20, 0);
>> @@ -2849,7 +2849,7 @@ static int tegra210_enable_pllu(void)
>> reg |= fentry->n << 8;
>> reg |= fentry->p << 16;
>> writel(reg, clk_base + PLLU_BASE);
>> - udelay(1);
>> + fence_udelay(1, clk_base);
>> reg |= PLL_ENABLE;
>> writel(reg, clk_base + PLLU_BASE);
>>
>> @@ -2895,12 +2895,12 @@ static int tegra210_init_pllu(void)
>> reg = readl_relaxed(clk_base + XUSB_PLL_CFG0);
>> reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK;
>> writel_relaxed(reg, clk_base + XUSB_PLL_CFG0);
>> - udelay(1);
>> + fence_udelay(1, clk_base);
>>
>> reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0);
>> reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE;
>> writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0);
>> - udelay(1);
>> + fence_udelay(1, clk_base);
>>
>> reg = readl_relaxed(clk_base + PLLU_BASE);
>> reg &= ~PLLU_BASE_CLKENABLE_USB;
>>
> The clk_base corresponds to the RESET controller's part of Clock-and-Reset hardware, is it
> okay to read-back the RST register and not the clock for the fencing?
Yes as both reset and clocks are all in same CAR
On Thu, Aug 08, 2019 at 04:46:40PM -0700, Sowjanya Komatineni wrote:
> pmx_writel uses writel which inserts write barrier before the
> register write rather.
>
> This patch has fix to replace writel with writel_relaxed followed
> by a write barrier to ensure write operation before the barrier
> is completed for successful pinctrl change.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/pinctrl/tegra/pinctrl-tegra.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
Acked-by: Thierry Reding <[email protected]>
On Thu, Aug 08, 2019 at 04:46:46PM -0700, Sowjanya Komatineni wrote:
> This patch adds an API clk_hw_get_parent_index to get index of the
> clock parent to use during the clock restore operations on system
> resume.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/clk.c | 17 +++++++++++++++++
> include/linux/clk-provider.h | 1 +
> 2 files changed, 18 insertions(+)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index c0990703ce54..f26252e48f73 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1643,6 +1643,23 @@ static int clk_fetch_parent_index(struct clk_core *core,
> return i;
> }
>
> +/**
> + * clk_hw_get_parent_index - return the index of parent clock
> + * @hw: clk_hw associated with the clk being consumed
> + * @parent_hw: clk_hw associated with the parent of clk
> + *
> + * Fetches and returns the index of parent clock.
> + * if hw or parent_hw is NULL, returns -EINVAL.
"If" because it's at the beginning of a sentence. You may also want to
turn this into a "Return:" section as described in:
Documentation/doc-guide/kernel-doc.rst
That said, other functions in this file don't use that construct either,
so I suppose this is fine as-is, for consistency.
So with the capitalization of "If" fixed, this is:
Reviewed-by: Thierry Reding <[email protected]>
On Thu, Aug 08, 2019 at 04:46:47PM -0700, Sowjanya Komatineni wrote:
> This patch implements restore_context support for clk-periph and
> clk-sdmmc-mux clock operations to restore clock parent and rates
> on system resume.
>
> During system suspend, core power goes off and looses the context
> of the Tegra clock controller registers.
>
> So on system resume, clocks parent and rate are restored back to
> the context before suspend based on cached data.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-periph.c | 18 ++++++++++++++++++
> drivers/clk/tegra/clk-sdmmc-mux.c | 12 ++++++++++++
> 2 files changed, 30 insertions(+)
Acked-by: Thierry Reding <[email protected]>
On Thu, Aug 08, 2019 at 04:46:48PM -0700, Sowjanya Komatineni wrote:
> This patch has a fix to enable PLLP branches to CPU before changing
> the CPU cluster clock source to PLLP for Gen5 Super clock and
> disables PLLP branches to CPU when not in use.
>
> During system suspend entry and exit, CPU source will be switched
> to PLLP and this needs PLLP branches to be enabled to CPU prior to
> the switch.
>
> On system resume, warmboot code enables PLLP branches to CPU and
> powers up the CPU with PLLP clock source.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-super.c | 14 ++++++++++++++
> drivers/clk/tegra/clk-tegra-super-gen4.c | 7 ++++++-
> drivers/clk/tegra/clk.c | 14 ++++++++++++++
> drivers/clk/tegra/clk.h | 5 +++++
> 4 files changed, 39 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> index 39ef31b46df5..e2a1e95a8db7 100644
> --- a/drivers/clk/tegra/clk-super.c
> +++ b/drivers/clk/tegra/clk-super.c
> @@ -28,6 +28,9 @@
> #define super_state_to_src_shift(m, s) ((m->width * s))
> #define super_state_to_src_mask(m) (((1 << m->width) - 1))
>
> +#define CCLK_SRC_PLLP_OUT0 4
> +#define CCLK_SRC_PLLP_OUT4 5
> +
> static u8 clk_super_get_parent(struct clk_hw *hw)
> {
> struct tegra_clk_super_mux *mux = to_clk_super_mux(hw);
> @@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index)
> if (index == mux->div2_index)
> index = mux->pllx_index;
> }
> +
> + /* enable PLLP branches to CPU before selecting PLLP source */
> + if ((mux->flags & TEGRA210_CPU_CLK) &&
> + (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4))
> + tegra_clk_set_pllp_out_cpu(true);
> +
> val &= ~((super_state_to_src_mask(mux)) << shift);
> val |= (index & (super_state_to_src_mask(mux))) << shift;
>
> writel_relaxed(val, mux->reg);
> udelay(2);
>
> + /* disable PLLP branches to CPU if not used */
> + if ((mux->flags & TEGRA210_CPU_CLK) &&
> + index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4)
> + tegra_clk_set_pllp_out_cpu(false);
> +
> out:
> if (mux->lock)
> spin_unlock_irqrestore(mux->lock, flags);
> diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c
> index cdfe7c9697e1..98538f79b0c4 100644
> --- a/drivers/clk/tegra/clk-tegra-super-gen4.c
> +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c
> @@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
> gen_info->num_cclk_g_parents,
> CLK_SET_RATE_PARENT,
> clk_base + CCLKG_BURST_POLICY,
> - 0, 4, 8, 0, NULL);
> + TEGRA210_CPU_CLK, 4, 8, 0, NULL);
> } else {
> clk = tegra_clk_register_super_mux("cclk_g",
> gen_info->cclk_g_parents,
> @@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base,
> dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks);
> if (dt_clk) {
> if (gen_info->gen == gen5) {
> + /*
> + * TEGRA210_CPU_CLK flag is not needed for cclk_lp as cluster
> + * switching is not currently supported on Tegra210 and also
> + * cpu_lp is not used.
> + */
Indentation looks odd here. If you want to comment the whole block, put
the comment above the "if (...) {". If you want to comment the contents
of the block, indent one level further so it aligns with the "clk = ..."
below.
Otherwise looks good, so with the indentation fixed:
Acked-by: Thierry Reding <[email protected]>
> clk = tegra_clk_register_super_mux("cclk_lp",
> gen_info->cclk_lp_parents,
> gen_info->num_cclk_lp_parents,
> diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
> index 573e3c967ae1..eb08047fd02f 100644
> --- a/drivers/clk/tegra/clk.c
> +++ b/drivers/clk/tegra/clk.c
> @@ -23,6 +23,7 @@
> #define CLK_OUT_ENB_W 0x364
> #define CLK_OUT_ENB_X 0x280
> #define CLK_OUT_ENB_Y 0x298
> +#define CLK_ENB_PLLP_OUT_CPU BIT(31)
> #define CLK_OUT_ENB_SET_L 0x320
> #define CLK_OUT_ENB_CLR_L 0x324
> #define CLK_OUT_ENB_SET_H 0x328
> @@ -199,6 +200,19 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid)
> }
> }
>
> +void tegra_clk_set_pllp_out_cpu(bool enable)
> +{
> + u32 val;
> +
> + val = readl_relaxed(clk_base + CLK_OUT_ENB_Y);
> + if (enable)
> + val |= CLK_ENB_PLLP_OUT_CPU;
> + else
> + val &= ~CLK_ENB_PLLP_OUT_CPU;
> +
> + writel_relaxed(val, clk_base + CLK_OUT_ENB_Y);
> +}
> +
> struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
> {
> clk_base = regs;
> diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
> index 8a9af45b6084..560e2bcb3d7d 100644
> --- a/drivers/clk/tegra/clk.h
> +++ b/drivers/clk/tegra/clk.h
> @@ -677,6 +677,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
> * Flags:
> * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates
> * that this is LP cluster clock.
> + * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5
> + * super mux parent using PLLP branches. To use PLLP branches to CPU, need
> + * to configure additional bit PLLP_OUT_CPU in the clock registers.
> */
> struct tegra_clk_super_mux {
> struct clk_hw hw;
> @@ -693,6 +696,7 @@ struct tegra_clk_super_mux {
> #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw)
>
> #define TEGRA_DIVIDER_2 BIT(0)
> +#define TEGRA210_CPU_CLK BIT(1)
>
> extern const struct clk_ops tegra_clk_super_ops;
> struct clk *tegra_clk_register_super_mux(const char *name,
> @@ -838,6 +842,7 @@ 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);
> void tegra_clk_osc_resume(void __iomem *clk_base);
> +void tegra_clk_set_pllp_out_cpu(bool enable);
>
>
> /* Combined read fence with delay */
> --
> 2.7.4
>
On Thu, Aug 08, 2019 at 04:46:49PM -0700, Sowjanya Komatineni wrote:
> This patch implements restore_context for clk_super_mux and clk_super.
>
> During system supend, core power goes off the and context of Tegra
> CAR registers is lost.
>
> So on system resume, context of super clock registers are restored
> to have them in same state as before suspend.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/clk/tegra/clk-super.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
Acked-by: Thierry Reding <[email protected]>
On Thu, Aug 08, 2019 at 04:46:51PM -0700, Sowjanya Komatineni wrote:
> This patch adds suspend and resume pm ops for cpufreq driver.
>
> PLLP is the safe clock source for CPU during system suspend and
> resume as PLLP rate is below the CPU Fmax at Vmin.
>
> CPUFreq driver suspend switches the CPU clock source to PLLP and
> disables the DFLL clock.
>
> During system resume, warmboot code powers up the CPU with PLLP
> clock source. So CPUFreq driver resume enabled DFLL clock and
> switches CPU back to DFLL clock source.
>
> Acked-by: Viresh Kumar <[email protected]>
> Reviewed-by: Dmitry Osipenko <[email protected]>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
> ---
> drivers/cpufreq/tegra124-cpufreq.c | 60 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 60 insertions(+)
>
> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> index 4f0c637b3b49..e979a3370988 100644
> --- a/drivers/cpufreq/tegra124-cpufreq.c
> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> @@ -6,6 +6,7 @@
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> #include <linux/clk.h>
> +#include <linux/cpufreq.h>
> #include <linux/err.h>
> #include <linux/init.h>
> #include <linux/kernel.h>
> @@ -128,8 +129,67 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> return ret;
> }
>
> +static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
> +{
> + struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
> + int err;
> +
> + /*
> + * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
> + * use during suspend and resume. So, switch the CPU clock source
> + * to PLLP and disable DFLL.
> + */
> + err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> + if (err < 0) {
> + dev_err(dev, "failed to reparent to PLLP: %d\n", err);
> + return err;
> + }
> +
> + /* disable DFLL clock */
> + clk_disable_unprepare(priv->dfll_clk);
This comment is superfluous since it doesn't explain anything that the
code below doesn't explain already.
Not sure who will end up merging this. If not me, then this is:
Acked-by: Thierry Reding <[email protected]>
On Fri, Aug 9, 2019 at 1:47 AM Sowjanya Komatineni
<[email protected]> wrote:
> pmx_writel uses writel which inserts write barrier before the
> register write rather.
>
> This patch has fix to replace writel with writel_relaxed followed
> by a write barrier to ensure write operation before the barrier
> is completed for successful pinctrl change.
>
> Signed-off-by: Sowjanya Komatineni <[email protected]>
Patch applied with the ACKs.
Yours,
Linus Walleij