Re: [PATCH v1 04/17] soc: tegra: Add Tegra PMC clock registrations into PMC driver

From: Sowjanya Komatineni
Date: Tue Nov 19 2019 - 15:29:58 EST



On 11/19/19 12:08 PM, Sowjanya Komatineni wrote:

On 11/19/19 11:33 AM, Dmitry Osipenko wrote:
19.11.2019 09:50, Sowjanya Komatineni ÐÐÑÐÑ:
Tegra PMC has clk_out_1, clk_out_2, clk_out_3 with mux and gate for
each of these clocks.

Currently these PMC clocks are registered by Tegra clock driver using
clk_register_mux and clk_register_gate by passing PMC base address
and register offsets and PMC programming for these clocks happens
through direct PMC access by the clock driver.

With this, when PMC is in secure mode any direct PMC access from the
non-secure world does not go through and these clocks will not be
functional.

This patch adds these clocks registration with PMC as a clock provider
for these clocks. clk_ops callback implementations for these clocks
uses tegra_pmc_readl and tegra_pmc_writel which supports PMC programming
in secure mode and non-secure mode.

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

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 7a5aab0b993b..790a6619ba32 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -13,6 +13,9 @@
  #include <linux/arm-smccc.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/clk-conf.h>
 #include <linux/clk/tegra.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
@@ -48,6 +51,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/soc/tegra-pmc.h>
  #define PMC_CNTRL 0x0
 #define PMC_CNTRL_INTR_POLARITY BIT(17) /* inverts INTR polarity */
@@ -108,6 +112,7 @@
 #define PMC_WAKE2_STATUS 0x168
 #define PMC_SW_WAKE2_STATUS 0x16c
 +#define PMC_CLK_OUT_CNTRL 0x1a8
 #define PMC_SATA_PWRGT 0x1ac
 #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5)
 #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4)
@@ -170,6 +175,78 @@
 #define TEGRA_SMC_PMC_READ 0xaa
 #define TEGRA_SMC_PMC_WRITE 0xbb
 +struct pmc_clk_mux {
+ÂÂÂ struct clk_hwÂÂÂ hw;
+ÂÂÂ unsigned longÂÂÂ offs;
+ÂÂÂ u32ÂÂÂÂÂÂÂ mask;
+ÂÂÂ u32ÂÂÂÂÂÂÂ shift;
+ÂÂÂ /* register lock */
+ÂÂÂ spinlock_tÂÂÂ *lock;
+};
+
+#define to_pmc_clk_mux(_hw) container_of(_hw, struct pmc_clk_mux, hw)
+
+struct pmc_clk_gate {
+ÂÂÂ struct clk_hwÂÂÂ hw;
+ÂÂÂ unsigned longÂÂÂ offs;
+ÂÂÂ u32ÂÂÂÂÂÂÂ shift;
+ÂÂÂ /* register lock */
+ÂÂÂ spinlock_tÂÂÂ *lock;
+};
+
+#define to_pmc_clk_gate(_hw) container_of(_hw, struct pmc_clk_gate, hw)
+
+struct pmc_clk_init_data {
+ÂÂÂ char *mux_name;
+ÂÂÂ char *gate_name;
+ÂÂÂ const char **parents;
+ÂÂÂ int num_parents;
+ÂÂÂ int mux_id;
+ÂÂÂ int gate_id;
+ÂÂÂ char *dev_name;
+ÂÂÂ u8 mux_shift;
+ÂÂÂ u8 gate_shift;
+ÂÂÂ u8 init_parent;
+ÂÂÂ int init_state;
+ÂÂÂ struct pmc_clk_mux mux;
+ÂÂÂ struct pmc_clk_gate gate;
+};
+
+#define PMC_CLK(_num, _mux_shift, _gate_shift, _init_parent, _init_state)\
+ÂÂÂ {\
+ÂÂÂÂÂÂÂ .mux_name = "clk_out_" #_num "_mux",\
+ÂÂÂÂÂÂÂ .gate_name = "clk_out_" #_num,\
+ÂÂÂÂÂÂÂ .parents = clk_out ##_num ##_parents,\
+ÂÂÂÂÂÂÂ .num_parents = ARRAY_SIZE(clk_out ##_num ##_parents),\
+ÂÂÂÂÂÂÂ .mux_id = TEGRA_PMC_CLK_OUT_ ##_num ##_MUX,\
+ÂÂÂÂÂÂÂ .gate_id = TEGRA_PMC_CLK_OUT_ ##_num,\
+ÂÂÂÂÂÂÂ .dev_name = "extern" #_num,\
+ÂÂÂÂÂÂÂ .mux_shift = _mux_shift,\
+ÂÂÂÂÂÂÂ .gate_shift = _gate_shift,\
+ÂÂÂÂÂÂÂ .init_parent = _init_parent,\
+ÂÂÂÂÂÂÂ .init_state = _init_state,\
+ÂÂÂ }
+
+static DEFINE_SPINLOCK(clk_out_lock);
+
+static const char *clk_out1_parents[] = { "clk_m", "clk_m_div2",
+ÂÂÂ "clk_m_div4", "extern1",
+};
+
+static const char *clk_out2_parents[] = { "clk_m", "clk_m_div2",
+ÂÂÂ "clk_m_div4", "extern2",
+};
+
+static const char *clk_out3_parents[] = { "clk_m", "clk_m_div2",
+ÂÂÂ "clk_m_div4", "extern3",
+};
Why these are unused?
They are used in PMC_CLK macro

+static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
+ÂÂÂ PMC_CLK(1, 6, 2, 3, 1),
+ÂÂÂ PMC_CLK(2, 14, 10, 0, 0),
+ÂÂÂ PMC_CLK(3, 22, 18, 0, 0),
+};
+
 struct tegra_powergate {
ÂÂÂÂÂ struct generic_pm_domain genpd;
ÂÂÂÂÂ struct tegra_pmc *pmc;
@@ -279,6 +356,9 @@ struct tegra_pmc_soc {
ÂÂÂÂÂÂ */
ÂÂÂÂÂ const struct tegra_wake_event *wake_events;
ÂÂÂÂÂ unsigned int num_wake_events;
+
+ÂÂÂ struct pmc_clk_init_data *pmc_clks_data;
+ÂÂÂ unsigned int num_pmc_clks;
 };
  static const char * const tegra186_reset_sources[] = {
@@ -2299,6 +2379,241 @@ static int tegra_pmc_clk_notify_cb(struct notifier_block *nb,
ÂÂÂÂÂ return NOTIFY_OK;
 }
 +static void pmc_clk_fence_udelay(u32 offset)
+{
+ÂÂÂ tegra_pmc_readl(pmc, offset);
+ÂÂÂ /* pmc clk propagation delay 2 us */
+ÂÂÂ udelay(2);
+}
+
+static u8 pmc_clk_mux_get_parent(struct clk_hw *hw)
+{
+ÂÂÂ struct pmc_clk_mux *mux = to_pmc_clk_mux(hw);
+ÂÂÂ int num_parents = clk_hw_get_num_parents(hw);
+ÂÂÂ u32 val;
+
+ÂÂÂ val = tegra_pmc_readl(pmc, mux->offs) >> mux->shift;
+ÂÂÂ val &= mux->mask;
+
+ÂÂÂ if (val >= num_parents)
+ÂÂÂÂÂÂÂ return -EINVAL;
+
+ÂÂÂ return val;
+}
+
+static int pmc_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ÂÂÂ struct pmc_clk_mux *mux = to_pmc_clk_mux(hw);
+ÂÂÂ u32 val;
+ÂÂÂ unsigned long flags = 0;
+
+ÂÂÂ spin_lock_irqsave(mux->lock, flags);
+
+ÂÂÂ val = tegra_pmc_readl(pmc, mux->offs);
+ÂÂÂ val &= ~(mux->mask << mux->shift);
+ÂÂÂ val |= index << mux->shift;
+ÂÂÂ tegra_pmc_writel(pmc, val, mux->offs);
+ÂÂÂ pmc_clk_fence_udelay(mux->offs);
+
+ÂÂÂ spin_unlock_irqrestore(mux->lock, flags);
+
+ÂÂÂ return 0;
+}
+
+static const struct clk_ops pmc_clk_mux_ops = {
+ÂÂÂ .get_parent = pmc_clk_mux_get_parent,
+ÂÂÂ .set_parent = pmc_clk_mux_set_parent,
+ÂÂÂ .determine_rate = __clk_mux_determine_rate,
+};
+
+static struct clk *
+tegra_pmc_clk_mux_register(const char *name, const char * const *parent_names,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ int num_parents, unsigned long flags,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct pmc_clk_mux *mux, unsigned long offset,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ u32 shift, u32 mask, spinlock_t *lock)
+{
+ÂÂÂ struct clk_init_data init;
+
+ÂÂÂ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ÂÂÂ if (!mux)
+ÂÂÂÂÂÂÂ return ERR_PTR(-ENOMEM);
+
+ÂÂÂ init.name = name;
+ÂÂÂ init.ops = &pmc_clk_mux_ops;
+ÂÂÂ init.parent_names = parent_names;
+ÂÂÂ init.num_parents = num_parents;
+ÂÂÂ init.flags = flags;
+
+ÂÂÂ mux->hw.init = &init;
+ÂÂÂ mux->offs = offset;
+ÂÂÂ mux->mask = mask;
+ÂÂÂ mux->shift = shift;
+ÂÂÂ mux->lock = lock;
+
+ÂÂÂ return clk_register(NULL, &mux->hw);
+}
+
+static int pmc_clk_is_enabled(struct clk_hw *hw)
+{
+ÂÂÂ struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
+
+ÂÂÂ return tegra_pmc_readl(pmc, gate->offs) & BIT(gate->shift) ? 1 : 0;
+}
+
+static void pmc_clk_set_state(struct clk_hw *hw, int state)
+{
+ÂÂÂ struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
+ÂÂÂ u32 val;
+ÂÂÂ unsigned long flags = 0;
+
+ÂÂÂ spin_lock_irqsave(gate->lock, flags);
+
+ÂÂÂ val = tegra_pmc_readl(pmc, gate->offs);
+ÂÂÂ val = state ? (val | BIT(gate->shift)) : (val & ~BIT(gate->shift));
+ÂÂÂ tegra_pmc_writel(pmc, val, gate->offs);
+ÂÂÂ pmc_clk_fence_udelay(gate->offs);
+
+ÂÂÂ spin_unlock_irqrestore(gate->lock, flags);
+}
+
+static int pmc_clk_enable(struct clk_hw *hw)
+{
+ÂÂÂ pmc_clk_set_state(hw, 1);
+
+ÂÂÂ return 0;
+}
+
+static void pmc_clk_disable(struct clk_hw *hw)
+{
+ÂÂÂ pmc_clk_set_state(hw, 0);
+}
+
+static const struct clk_ops pmc_clk_gate_ops = {
+ÂÂÂ .is_enabled = pmc_clk_is_enabled,
+ÂÂÂ .enable = pmc_clk_enable,
+ÂÂÂ .disable = pmc_clk_disable,
+};
+
+static struct clk *
+tegra_pmc_clk_gate_register(const char *name, const char *parent_name,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long flags, struct pmc_clk_gate *gate,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long offset, u32 shift, spinlock_t *lock)
+{
+ÂÂÂ struct clk_init_data init;
+
+ÂÂÂ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ÂÂÂ if (!gate)
+ÂÂÂÂÂÂÂ return ERR_PTR(-ENOMEM);
Why *gate is a function argument?
for storing corresponding gate register info to use for gate clk_ops

I had gate and mux as members of pmc_clk_init_data.

Actually we don't need that so will remove it and also passing argument in next version


+
+ÂÂÂ init.name = name;
+ÂÂÂ init.ops = &pmc_clk_gate_ops;
+ÂÂÂ init.parent_names = &parent_name;
+ÂÂÂ init.num_parents = 1;
+ÂÂÂ init.flags = flags;
+
+ÂÂÂ gate->hw.init = &init;
+ÂÂÂ gate->offs = offset;
+ÂÂÂ gate->shift = shift;
+ÂÂÂ gate->lock = lock;
+
+ÂÂÂ return clk_register(NULL, &gate->hw);
+}
[snip]