Re: [PATCH v4 06/19] soc: tegra: Add Tegra PMC clock registrations into PMC driver

From: Sowjanya Komatineni
Date: Wed Dec 18 2019 - 12:00:22 EST



On 12/18/19 8:52 AM, Dmitry Osipenko wrote:
18.12.2019 18:50, Sowjanya Komatineni ÐÐÑÐÑ:
On 12/18/19 7:46 AM, Sowjanya Komatineni wrote:
On 12/18/19 12:35 AM, Dmitry Osipenko wrote:
18.12.2019 11:30, Dmitry Osipenko ÐÐÑÐÑ:
17.12.2019 23:03, Sowjanya Komatineni ÐÐÑÐÑ:
Tegra PMC has clk_out_1, clk_out_2, and clk_out_3 clocks and currently
these PMC clocks are registered by Tegra clock driver with each
clock as
separate mux and gate clocks 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 PMC clocks registration to pmc driver with
PMC as
a clock provider and registers each clock as single clock.

clk_ops callback implementations for these clocks uses
tegra_pmc_readl and
tegra_pmc_writel which supports PMC programming in both secure mode
and
non-secure mode.

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

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index ea0e11a09c12..6d65194a6e71 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 */
@@ -100,6 +104,7 @@
 #define PMC_WAKE2_STATUS 0x168
 #define PMC_SW_WAKE2_STATUS 0x16c
 +#define PMC_CLK_OUT_CNTRL 0x1a8
 #define PMC_SENSOR_CTRL 0x1b0
 #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2)
 #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1)
@@ -155,6 +160,64 @@
 #define TEGRA_SMC_PMC_READ 0xaa
 #define TEGRA_SMC_PMC_WRITE 0xbb
 +struct pmc_clk {
+ÂÂÂ struct clk_hwÂÂÂ hw;
+ÂÂÂ unsigned longÂÂÂ offs;
+ÂÂÂ u32ÂÂÂÂÂÂÂ mux_mask;
+ÂÂÂ u32ÂÂÂÂÂÂÂ mux_shift;
+ÂÂÂ u32ÂÂÂÂÂÂÂ gate_shift;
+};
+
+#define to_pmc_clk(_hw) container_of(_hw, struct pmc_clk, hw)
+
+struct pmc_clk_init_data {
+ÂÂÂ char *name;
+ÂÂÂ const char *const *parents;
+ÂÂÂ int num_parents;
+ÂÂÂ int clk_id;
+ÂÂÂ u8 mux_shift;
+ÂÂÂ u8 gate_shift;
+};
+
+static const char * const clk_out1_parents[] = { "osc", "osc_div2",
+ÂÂÂ "osc_div4", "extern1",
+};
+
+static const char * const clk_out2_parents[] = { "osc", "osc_div2",
+ÂÂÂ "osc_div4", "extern2",
+};
+
+static const char * const clk_out3_parents[] = { "osc", "osc_div2",
+ÂÂÂ "osc_div4", "extern3",
+};
+
+static const struct pmc_clk_init_data tegra_pmc_clks_data[] = {
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .name = "clk_out_1",
+ÂÂÂÂÂÂÂ .parents = clk_out1_parents,
+ÂÂÂÂÂÂÂ .num_parents = ARRAY_SIZE(clk_out1_parents),
+ÂÂÂÂÂÂÂ .clk_id = TEGRA_PMC_CLK_OUT_1,
+ÂÂÂÂÂÂÂ .mux_shift = 6,
+ÂÂÂÂÂÂÂ .gate_shift = 2,
I'd replace these with a single .shift, given that mux_shift =
gate_shift + 4 for all clocks.

+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .name = "clk_out_2",
+ÂÂÂÂÂÂÂ .parents = clk_out2_parents,
+ÂÂÂÂÂÂÂ .num_parents = ARRAY_SIZE(clk_out2_parents),
+ÂÂÂÂÂÂÂ .clk_id = TEGRA_PMC_CLK_OUT_2,
+ÂÂÂÂÂÂÂ .mux_shift = 14,
+ÂÂÂÂÂÂÂ .gate_shift = 10,
+ÂÂÂ },
+ÂÂÂ {
+ÂÂÂÂÂÂÂ .name = "clk_out_3",
+ÂÂÂÂÂÂÂ .parents = clk_out3_parents,
+ÂÂÂÂÂÂÂ .num_parents = ARRAY_SIZE(clk_out3_parents),
+ÂÂÂÂÂÂÂ .clk_id = TEGRA_PMC_CLK_OUT_3,
+ÂÂÂÂÂÂÂ .mux_shift = 22,
+ÂÂÂÂÂÂÂ .gate_shift = 18,
+ÂÂÂ },
+};
+
 struct tegra_powergate {
ÂÂÂÂÂ struct generic_pm_domain genpd;
ÂÂÂÂÂ struct tegra_pmc *pmc;
@@ -254,6 +317,9 @@ struct tegra_pmc_soc {
ÂÂÂÂÂÂ */
ÂÂÂÂÂ const struct tegra_wake_event *wake_events;
ÂÂÂÂÂ unsigned int num_wake_events;
+
+ÂÂÂ const struct pmc_clk_init_data *pmc_clks_data;
+ÂÂÂ unsigned int num_pmc_clks;
 };
  static const char * const tegra186_reset_sources[] = {
@@ -2163,6 +2229,173 @@ 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 *clk = to_pmc_clk(hw);
+ÂÂÂ u32 val;
+
+ÂÂÂ val = tegra_pmc_readl(pmc, clk->offs) >> clk->mux_shift;
+ÂÂÂ val &= clk->mux_mask;
+
+ÂÂÂ return val;
+}
+
+static int pmc_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ÂÂÂ struct pmc_clk *clk = to_pmc_clk(hw);
+ÂÂÂ u32 val;
+
+ÂÂÂ val = tegra_pmc_readl(pmc, clk->offs);
+ÂÂÂ val &= ~(clk->mux_mask << clk->mux_shift);
+ÂÂÂ val |= index << clk->mux_shift;
+ÂÂÂ tegra_pmc_writel(pmc, val, clk->offs);
+ÂÂÂ pmc_clk_fence_udelay(clk->offs);
+
+ÂÂÂ return 0;
+}
+
+static int pmc_clk_is_enabled(struct clk_hw *hw)
+{
+ÂÂÂ struct pmc_clk *clk = to_pmc_clk(hw);
+
+ÂÂÂ return tegra_pmc_readl(pmc, clk->offs) & BIT(clk->gate_shift)
? 1 : 0;
+}
+
+static void pmc_clk_set_state(unsigned long offs, u32 shift, int
state)
+{
+ÂÂÂ u32 val;
+
+ÂÂÂ val = tegra_pmc_readl(pmc, offs);
+ÂÂÂ val = state ? (val | BIT(shift)) : (val & ~BIT(shift));
+ÂÂÂ tegra_pmc_writel(pmc, val, offs);
+ÂÂÂ pmc_clk_fence_udelay(offs);
+}
+
+static int pmc_clk_enable(struct clk_hw *hw)
+{
+ÂÂÂ struct pmc_clk *clk = to_pmc_clk(hw);
+
+ÂÂÂ pmc_clk_set_state(clk->offs, clk->gate_shift, 1);
+
+ÂÂÂ return 0;
+}
+
+static void pmc_clk_disable(struct clk_hw *hw)
+{
+ÂÂÂ struct pmc_clk *clk = to_pmc_clk(hw);
+
+ÂÂÂ pmc_clk_set_state(clk->offs, clk->gate_shift, 0);
+}
+
+static const struct clk_ops pmc_clk_ops = {
+ÂÂÂ .get_parent = pmc_clk_mux_get_parent,
+ÂÂÂ .set_parent = pmc_clk_mux_set_parent,
+ÂÂÂ .determine_rate = __clk_mux_determine_rate,
+ÂÂÂ .is_enabled = pmc_clk_is_enabled,
+ÂÂÂ .enable = pmc_clk_enable,
+ÂÂÂ .disable = pmc_clk_disable,
+};
+
+static struct clk *
+tegra_pmc_clk_out_register(const struct pmc_clk_init_data *data,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long offset)
+{
+ÂÂÂ struct clk_init_data init;
+ÂÂÂ struct pmc_clk *pmc_clk;
+
+ÂÂÂ pmc_clk = kzalloc(sizeof(*pmc_clk), GFP_KERNEL);
+ÂÂÂ if (!pmc_clk)
+ÂÂÂÂÂÂÂ return ERR_PTR(-ENOMEM);
+
+ÂÂÂ init.name = data->name;
+ÂÂÂ init.ops = &pmc_clk_ops;
+ÂÂÂ init.parent_names = data->parents;
+ÂÂÂ init.num_parents = data->num_parents;
+ÂÂÂ init.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT |
+ÂÂÂÂÂÂÂÂÂÂÂÂ CLK_SET_PARENT_GATE;
+
+ÂÂÂ pmc_clk->hw.init = &init;
+ÂÂÂ pmc_clk->offs = offset;
+ÂÂÂ pmc_clk->mux_mask = 3;
If mux_mask is a constant value, perhaps will be better to replace the
variable with a literal?

#define PMC_CLK_OUT_MUX_MASKÂÂÂ GENMASK(1, 0)
Maybe even:

#define PMC_CLK_OUT_MUX_MASK(c)ÂÂÂ GENMASK(c->shift + 1, c->shift)

+ÂÂÂ pmc_clk->mux_shift = data->mux_shift;
+ÂÂÂ pmc_clk->gate_shift = data->gate_shift;
+
+ÂÂÂ return clk_register(NULL, &pmc_clk->hw);
+}
+
+static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct device_node *np)
+{
+ÂÂÂ struct clk *clk;
+ÂÂÂ struct clk_onecell_data *clk_data;
+ÂÂÂ unsigned int num_clks;
+ÂÂÂ int i, err = -ENOMEM;
+
+ÂÂÂ num_clks = pmc->soc->num_pmc_clks;
+
+ÂÂÂ if (!num_clks)
+ÂÂÂÂÂÂÂ return;
+
+ÂÂÂ clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
+ÂÂÂ if (!clk_data)
+ÂÂÂÂÂÂÂ goto err_clk;
What about devm_kmalloc, devm_kcalloc?

+ÂÂÂ clk_data->clks = kcalloc(TEGRA_PMC_CLK_MAX,
sizeof(*clk_data->clks),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂ if (!clk_data->clks)
+ÂÂÂÂÂÂÂ goto free_clkdata;
+
+ÂÂÂ clk_data->clk_num = TEGRA_PMC_CLK_MAX;
+
+ÂÂÂ for (i = 0; i < TEGRA_PMC_CLK_MAX; i++)
+ÂÂÂÂÂÂÂ clk_data->clks[i] = ERR_PTR(-ENOENT);
+
+ÂÂÂ for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
+ÂÂÂÂÂÂÂ const struct pmc_clk_init_data *data;
+
+ÂÂÂÂÂÂÂ data = pmc->soc->pmc_clks_data + i;
+
+ÂÂÂÂÂÂÂ clk = tegra_pmc_clk_out_register(data,
PMC_CLK_OUT_CNTRL);> +ÂÂÂÂÂÂÂ if (IS_ERR(clk)) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(pmc->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "unable to register %s\n", data->name);
+ÂÂÂÂÂÂÂÂÂÂÂ err = PTR_ERR(clk);
Error codes in a message could be useful.
Added error code at end of clock register along with WARN message to
have it common to show warning with error code for all errors
including kmalloc and kcalloc.

Sure, With devm_kmalloc and devm_kcalloc, will move error code along
with message and do return right at point of errors.
There is no need to warn about memory allocations because there will be
warning anyway, unless __GFP_NOWARN is used.

Earlier I had WARN message at each of errors during clock registers except during malloc/calloc.

When you suggested to add additional WARN message at the end of clock register in earlier feedback, thought you were suggesting to warn about kmalloc/kcalloc failures.

Probably I misunderstood your suggestion.

Anyways now with moving to devm malloc/calloc, will use dev_warn to show warning along with error code right at the point of error.