2019-11-27 05:01:21

by Sowjanya Komatineni

[permalink] [raw]
Subject: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

Tegra210 and prior 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 <[email protected]>
---
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 ea0e11a09c12..a353f6d0a832 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,91 @@
#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;
+};
+
+#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;
+};
+
+#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_index;
+ int init_state;
+};
+
+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",
+};
+
+static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
+ {
+ .mux_name = "clk_out_1_mux",
+ .gate_name = "clk_out_1",
+ .parents = clk_out1_parents,
+ .num_parents = ARRAY_SIZE(clk_out1_parents),
+ .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
+ .gate_id = TEGRA_PMC_CLK_OUT_1,
+ .dev_name = "extern1",
+ .mux_shift = 6,
+ .gate_shift = 2,
+ .init_parent_index = 3,
+ .init_state = 1,
+ },
+ {
+ .mux_name = "clk_out_2_mux",
+ .gate_name = "clk_out_2",
+ .parents = clk_out2_parents,
+ .num_parents = ARRAY_SIZE(clk_out2_parents),
+ .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
+ .gate_id = TEGRA_PMC_CLK_OUT_2,
+ .dev_name = "extern2",
+ .mux_shift = 14,
+ .gate_shift = 10,
+ .init_parent_index = 0,
+ .init_state = 0,
+ },
+ {
+ .mux_name = "clk_out_3_mux",
+ .gate_name = "clk_out_3",
+ .parents = clk_out3_parents,
+ .num_parents = ARRAY_SIZE(clk_out3_parents),
+ .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
+ .gate_id = TEGRA_PMC_CLK_OUT_3,
+ .dev_name = "extern3",
+ .mux_shift = 22,
+ .gate_shift = 18,
+ .init_parent_index = 0,
+ .init_state = 0,
+ },
+};
+
struct tegra_powergate {
struct generic_pm_domain genpd;
struct tegra_pmc *pmc;
@@ -254,6 +344,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[] = {
@@ -2163,6 +2256,228 @@ 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;
+
+ 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);
+
+ 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,
+ unsigned long offset, u32 shift, u32 mask)
+{
+ struct clk_init_data init;
+ struct pmc_clk_mux *mux;
+
+ 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;
+
+ 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;
+
+ 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);
+}
+
+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, unsigned long offset,
+ u32 shift)
+{
+ struct clk_init_data init;
+ struct pmc_clk_gate *gate;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ 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;
+
+ return clk_register(NULL, &gate->hw);
+}
+
+static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
+ struct device_node *np)
+{
+ struct clk *clkmux, *clk, *parent;
+ struct clk_onecell_data *clk_data;
+ unsigned int num_clks;
+ int i, ret;
+
+ /* each pmc clock output has a mux and a gate */
+ num_clks = pmc->soc->num_pmc_clks * 2;
+
+ if (!num_clks)
+ return;
+
+ clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ 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 = num_clks;
+
+ for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
+ struct pmc_clk_init_data *data;
+
+ data = pmc->soc->pmc_clks_data + i;
+
+ clkmux = tegra_pmc_clk_mux_register(data->mux_name,
+ data->parents,
+ data->num_parents,
+ CLK_SET_RATE_NO_REPARENT |
+ CLK_SET_RATE_PARENT,
+ PMC_CLK_OUT_CNTRL,
+ data->mux_shift, 3);
+ if (IS_ERR(clkmux))
+ goto free_clks;
+
+ clk_data->clks[data->mux_id] = clkmux;
+
+ clk = tegra_pmc_clk_gate_register(data->gate_name,
+ data->mux_name,
+ CLK_SET_RATE_PARENT,
+ PMC_CLK_OUT_CNTRL,
+ data->gate_shift);
+ if (IS_ERR(clk))
+ goto free_clks;
+
+ clk_data->clks[data->gate_id] = clk;
+
+ ret = clk_set_parent(clk, clkmux);
+ if (ret < 0) {
+ pr_err("failed to set parent of %s to %s\n",
+ __func__, __clk_get_name(clk),
+ __clk_get_name(clkmux));
+ }
+
+ clk_register_clkdev(clk, data->dev_name, data->gate_name);
+
+ /* configure initial clock parent and state */
+ parent = clk_get_sys(data->gate_name,
+ data->parents[data->init_parent_index]);
+ if (!IS_ERR(parent)) {
+ ret = clk_set_parent(clkmux, parent);
+ if (ret < 0) {
+ pr_err("failed to set parent of %s to %s\n",
+ __func__, __clk_get_name(clkmux),
+ __clk_get_name(parent));
+ WARN_ON(1);
+ }
+ }
+
+ if (data->init_state) {
+ if (clk_prepare_enable(clk)) {
+ pr_err("failed to enable %s\n", __func__,
+ __clk_get_name(clk));
+ WARN_ON(1);
+ }
+ }
+ }
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+
+ return;
+
+free_clks:
+ kfree(clk_data->clks);
+free_clkdata:
+ kfree(clk_data);
+}
+
static int tegra_pmc_probe(struct platform_device *pdev)
{
void __iomem *base;
@@ -2281,6 +2596,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
pmc->base = base;
mutex_unlock(&pmc->powergates_lock);

+ tegra_pmc_clock_register(pmc, pdev->dev.of_node);
platform_set_drvdata(pdev, pmc);

return 0;
@@ -2422,6 +2738,8 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
.num_reset_sources = 0,
.reset_levels = NULL,
.num_reset_levels = 0,
+ .pmc_clks_data = NULL,
+ .num_pmc_clks = 0,
};

static const char * const tegra30_powergates[] = {
@@ -2469,6 +2787,8 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
.reset_levels = NULL,
.num_reset_levels = 0,
+ .pmc_clks_data = tegra_pmc_clks_data,
+ .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
};

static const char * const tegra114_powergates[] = {
@@ -2520,6 +2840,8 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
.reset_levels = NULL,
.num_reset_levels = 0,
+ .pmc_clks_data = tegra_pmc_clks_data,
+ .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
};

static const char * const tegra124_powergates[] = {
@@ -2631,6 +2953,8 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
.num_reset_sources = ARRAY_SIZE(tegra30_reset_sources),
.reset_levels = NULL,
.num_reset_levels = 0,
+ .pmc_clks_data = tegra_pmc_clks_data,
+ .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
};

static const char * const tegra210_powergates[] = {
@@ -2745,6 +3069,8 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.num_reset_levels = 0,
.num_wake_events = ARRAY_SIZE(tegra210_wake_events),
.wake_events = tegra210_wake_events,
+ .pmc_clks_data = tegra_pmc_clks_data,
+ .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
};

#define TEGRA186_IO_PAD_TABLE(_pad) \
@@ -2874,6 +3200,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra186_wake_events),
.wake_events = tegra186_wake_events,
+ .pmc_clks_data = NULL,
+ .num_pmc_clks = 0,
};

static const struct tegra_io_pad_soc tegra194_io_pads[] = {
@@ -2991,6 +3319,8 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra194_wake_events),
.wake_events = tegra194_wake_events,
+ .pmc_clks_data = NULL,
+ .num_pmc_clks = 0,
};

static const struct of_device_id tegra_pmc_match[] = {
--
2.7.4


2019-11-27 14:34:28

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

27.11.2019 07:59, Sowjanya Komatineni пишет:
> Tegra210 and prior 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 <[email protected]>
> ---
> 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 ea0e11a09c12..a353f6d0a832 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,91 @@
> #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;
> +};
> +
> +#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;
> +};
> +
> +#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_index;
> + int init_state;
> +};
> +
> +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",
> +};
> +
> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
> + {
> + .mux_name = "clk_out_1_mux",
> + .gate_name = "clk_out_1",
> + .parents = clk_out1_parents,
> + .num_parents = ARRAY_SIZE(clk_out1_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_1,
> + .dev_name = "extern1",
> + .mux_shift = 6,
> + .gate_shift = 2,
> + .init_parent_index = 3,
> + .init_state = 1,
> + },
> + {
> + .mux_name = "clk_out_2_mux",
> + .gate_name = "clk_out_2",
> + .parents = clk_out2_parents,
> + .num_parents = ARRAY_SIZE(clk_out2_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_2,
> + .dev_name = "extern2",
> + .mux_shift = 14,
> + .gate_shift = 10,
> + .init_parent_index = 0,
> + .init_state = 0,
> + },
> + {
> + .mux_name = "clk_out_3_mux",
> + .gate_name = "clk_out_3",
> + .parents = clk_out3_parents,
> + .num_parents = ARRAY_SIZE(clk_out3_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_3,
> + .dev_name = "extern3",
> + .mux_shift = 22,
> + .gate_shift = 18,
> + .init_parent_index = 0,
> + .init_state = 0,
> + },
> +};
> +
> struct tegra_powergate {
> struct generic_pm_domain genpd;
> struct tegra_pmc *pmc;
> @@ -254,6 +344,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[] = {
> @@ -2163,6 +2256,228 @@ 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;
> +
> + 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);
> +
> + 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,
> + unsigned long offset, u32 shift, u32 mask)
> +{
> + struct clk_init_data init;
> + struct pmc_clk_mux *mux;
> +
> + 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;
> +
> + 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;
> +
> + 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);
> +}
> +
> +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, unsigned long offset,
> + u32 shift)
> +{
> + struct clk_init_data init;
> + struct pmc_clk_gate *gate;
> +
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> + if (!gate)
> + return ERR_PTR(-ENOMEM);
> +
> + 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;
> +
> + return clk_register(NULL, &gate->hw);
> +}
> +
> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
> + struct device_node *np)
> +{
> + struct clk *clkmux, *clk, *parent;
> + struct clk_onecell_data *clk_data;
> + unsigned int num_clks;
> + int i, ret;
> +
> + /* each pmc clock output has a mux and a gate */
> + num_clks = pmc->soc->num_pmc_clks * 2;
> +
> + if (!num_clks)
> + return;
> +
> + clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
> + if (!clk_data)
> + return;
> +
> + 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 = num_clks;
> +
> + for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
> + struct pmc_clk_init_data *data;
> +
> + data = pmc->soc->pmc_clks_data + i;
> +
> + clkmux = tegra_pmc_clk_mux_register(data->mux_name,
> + data->parents,
> + data->num_parents,
> + CLK_SET_RATE_NO_REPARENT |
> + CLK_SET_RATE_PARENT,
> + PMC_CLK_OUT_CNTRL,
> + data->mux_shift, 3);
> + if (IS_ERR(clkmux))
> + goto free_clks;
> +
> + clk_data->clks[data->mux_id] = clkmux;
> +
> + clk = tegra_pmc_clk_gate_register(data->gate_name,
> + data->mux_name,
> + CLK_SET_RATE_PARENT,
> + PMC_CLK_OUT_CNTRL,
> + data->gate_shift);
> + if (IS_ERR(clk))
> + goto free_clks;
> +
> + clk_data->clks[data->gate_id] = clk;
> +
> + ret = clk_set_parent(clk, clkmux);
> + if (ret < 0) {
> + pr_err("failed to set parent of %s to %s\n",
> + __func__, __clk_get_name(clk),
> + __clk_get_name(clkmux));
> + }
> +
> + clk_register_clkdev(clk, data->dev_name, data->gate_name);
> +
> + /* configure initial clock parent and state */
> + parent = clk_get_sys(data->gate_name,
> + data->parents[data->init_parent_index]);
> + if (!IS_ERR(parent)) {
> + ret = clk_set_parent(clkmux, parent);
> + if (ret < 0) {
> + pr_err("failed to set parent of %s to %s\n",
> + __func__, __clk_get_name(clkmux),
> + __clk_get_name(parent));
> + WARN_ON(1);
> + }
> + }
> +
> + if (data->init_state) {
> + if (clk_prepare_enable(clk)) {
> + pr_err("failed to enable %s\n", __func__,
> + __clk_get_name(clk));
> + WARN_ON(1);
> + }
> + }
> + }

In file included from ./include/linux/printk.h:7,
from ./include/linux/kernel.h:15,
from ./include/linux/clk.h:13,
from drivers/soc/tegra/pmc.c:15:
drivers/soc/tegra/pmc.c: In function ‘tegra_pmc_clock_register’:
./include/linux/kern_levels.h:5:18: warning: too many arguments for
format [-Wformat-extra-args]
5 | #define KERN_SOH "\001" /* ASCII Start Of Header */
| ^~~~~~
./include/linux/kern_levels.h:11:18: note: in expansion of macro ‘KERN_SOH’
11 | #define KERN_ERR KERN_SOH "3" /* error conditions */
| ^~~~~~~~
./include/linux/printk.h:304:9: note: in expansion of macro ‘KERN_ERR’
304 | printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~
drivers/soc/tegra/pmc.c:2451:4: note: in expansion of macro ‘pr_err’
2451 | pr_err("failed to set parent of %s to %s\n",
| ^~~~~~
./include/linux/kern_levels.h:5:18: warning: too many arguments for
format [-Wformat-extra-args]
5 | #define KERN_SOH "\001" /* ASCII Start Of Header */
| ^~~~~~
./include/linux/kern_levels.h:11:18: note: in expansion of macro ‘KERN_SOH’
11 | #define KERN_ERR KERN_SOH "3" /* error conditions */
| ^~~~~~~~
./include/linux/printk.h:304:9: note: in expansion of macro ‘KERN_ERR’
304 | printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~
drivers/soc/tegra/pmc.c:2464:5: note: in expansion of macro ‘pr_err’
2464 | pr_err("failed to set parent of %s to %s\n",
| ^~~~~~
./include/linux/kern_levels.h:5:18: warning: too many arguments for
format [-Wformat-extra-args]
5 | #define KERN_SOH "\001" /* ASCII Start Of Header */
| ^~~~~~
./include/linux/kern_levels.h:11:18: note: in expansion of macro ‘KERN_SOH’
11 | #define KERN_ERR KERN_SOH "3" /* error conditions */
| ^~~~~~~~
./include/linux/printk.h:304:9: note: in expansion of macro ‘KERN_ERR’
304 | printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~
drivers/soc/tegra/pmc.c:2473:5: note: in expansion of macro ‘pr_err’
2473 | pr_err("failed to enable %s\n", __func__,
|

2019-11-27 14:36:03

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

27.11.2019 07:59, Sowjanya Komatineni пишет:
> Tegra210 and prior 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 <[email protected]>
> ---
> 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 ea0e11a09c12..a353f6d0a832 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,91 @@
> #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;
> +};
> +
> +#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;
> +};
> +
> +#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_index;
> + int init_state;
> +};
> +
> +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",
> +};
> +
> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {

const?

[snip]

2019-11-27 15:19:28

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

27.11.2019 07:59, Sowjanya Komatineni пишет:
> Tegra210 and prior 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 <[email protected]>
> ---
> 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 ea0e11a09c12..a353f6d0a832 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,91 @@
> #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;
> +};
> +
> +#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;
> +};
> +
> +#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_index;
> + int init_state;
> +};
> +
> +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",
> +};
> +
> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
> + {
> + .mux_name = "clk_out_1_mux",
> + .gate_name = "clk_out_1",
> + .parents = clk_out1_parents,
> + .num_parents = ARRAY_SIZE(clk_out1_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_1,
> + .dev_name = "extern1",
> + .mux_shift = 6,
> + .gate_shift = 2,
> + .init_parent_index = 3,
> + .init_state = 1,
> + },
> + {
> + .mux_name = "clk_out_2_mux",
> + .gate_name = "clk_out_2",
> + .parents = clk_out2_parents,
> + .num_parents = ARRAY_SIZE(clk_out2_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_2,
> + .dev_name = "extern2",
> + .mux_shift = 14,
> + .gate_shift = 10,
> + .init_parent_index = 0,
> + .init_state = 0,
> + },
> + {
> + .mux_name = "clk_out_3_mux",
> + .gate_name = "clk_out_3",
> + .parents = clk_out3_parents,
> + .num_parents = ARRAY_SIZE(clk_out3_parents),
> + .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
> + .gate_id = TEGRA_PMC_CLK_OUT_3,
> + .dev_name = "extern3",
> + .mux_shift = 22,
> + .gate_shift = 18,
> + .init_parent_index = 0,
> + .init_state = 0,
> + },
> +};
> +
> struct tegra_powergate {
> struct generic_pm_domain genpd;
> struct tegra_pmc *pmc;
> @@ -254,6 +344,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[] = {
> @@ -2163,6 +2256,228 @@ 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;
> +
> + 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);
> +
> + 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,
> + unsigned long offset, u32 shift, u32 mask)
> +{
> + struct clk_init_data init;
> + struct pmc_clk_mux *mux;
> +
> + 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;
> +
> + 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;
> +
> + 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);
> +}
> +
> +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, unsigned long offset,
> + u32 shift)
> +{
> + struct clk_init_data init;
> + struct pmc_clk_gate *gate;
> +
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> + if (!gate)
> + return ERR_PTR(-ENOMEM);
> +
> + 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;
> +
> + return clk_register(NULL, &gate->hw);
> +}
> +
> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
> + struct device_node *np)
> +{
> + struct clk *clkmux, *clk, *parent;
> + struct clk_onecell_data *clk_data;
> + unsigned int num_clks;
> + int i, ret;
> +
> + /* each pmc clock output has a mux and a gate */
> + num_clks = pmc->soc->num_pmc_clks * 2;
> +
> + if (!num_clks)
> + return;
> +
> + clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
> + if (!clk_data)
> + return;
> +
> + 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 = num_clks;
> +
> + for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
> + struct pmc_clk_init_data *data;
> +
> + data = pmc->soc->pmc_clks_data + i;
> +
> + clkmux = tegra_pmc_clk_mux_register(data->mux_name,
> + data->parents,
> + data->num_parents,
> + CLK_SET_RATE_NO_REPARENT |
> + CLK_SET_RATE_PARENT,
> + PMC_CLK_OUT_CNTRL,
> + data->mux_shift, 3);
> + if (IS_ERR(clkmux))
> + goto free_clks;
> +
> + clk_data->clks[data->mux_id] = clkmux;
> +
> + clk = tegra_pmc_clk_gate_register(data->gate_name,
> + data->mux_name,
> + CLK_SET_RATE_PARENT,
> + PMC_CLK_OUT_CNTRL,
> + data->gate_shift);
> + if (IS_ERR(clk))
> + goto free_clks;
> +
> + clk_data->clks[data->gate_id] = clk;
> +
> + ret = clk_set_parent(clk, clkmux);
> + if (ret < 0) {
> + pr_err("failed to set parent of %s to %s\n",
> + __func__, __clk_get_name(clk),
> + __clk_get_name(clkmux));
> + }
> +
> + clk_register_clkdev(clk, data->dev_name, data->gate_name);
> +
> + /* configure initial clock parent and state */
> + parent = clk_get_sys(data->gate_name,
> + data->parents[data->init_parent_index]);
> + if (!IS_ERR(parent)) {
> + ret = clk_set_parent(clkmux, parent);
> + if (ret < 0) {
> + pr_err("failed to set parent of %s to %s\n",
> + __func__, __clk_get_name(clkmux),
> + __clk_get_name(parent));
> + WARN_ON(1);
> + }
> + }
> +
> + if (data->init_state) {
> + if (clk_prepare_enable(clk)) {
> + pr_err("failed to enable %s\n", __func__,
> + __clk_get_name(clk));
> + WARN_ON(1);

Should be a bit better to move the WARN_ON to the end of errors handling
in order to catch all possible errors:

@@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
tegra_pmc *pmc,
return;

free_clks:
+ WARN_ON(1);
kfree(clk_data->clks);
free_clkdata:
kfree(clk_data);

2019-11-27 22:59:19

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>> Tegra210 and prior 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 <[email protected]>
>> ---
>> 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 ea0e11a09c12..a353f6d0a832 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,91 @@
>> #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;
>> +};
>> +
>> +#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;
>> +};
>> +
>> +#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_index;
>> + int init_state;
>> +};
>> +
>> +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",
>> +};
>> +
>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>> + {
>> + .mux_name = "clk_out_1_mux",
>> + .gate_name = "clk_out_1",
>> + .parents = clk_out1_parents,
>> + .num_parents = ARRAY_SIZE(clk_out1_parents),
>> + .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>> + .gate_id = TEGRA_PMC_CLK_OUT_1,
>> + .dev_name = "extern1",
>> + .mux_shift = 6,
>> + .gate_shift = 2,
>> + .init_parent_index = 3,
>> + .init_state = 1,
>> + },
>> + {
>> + .mux_name = "clk_out_2_mux",
>> + .gate_name = "clk_out_2",
>> + .parents = clk_out2_parents,
>> + .num_parents = ARRAY_SIZE(clk_out2_parents),
>> + .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>> + .gate_id = TEGRA_PMC_CLK_OUT_2,
>> + .dev_name = "extern2",
>> + .mux_shift = 14,
>> + .gate_shift = 10,
>> + .init_parent_index = 0,
>> + .init_state = 0,
>> + },
>> + {
>> + .mux_name = "clk_out_3_mux",
>> + .gate_name = "clk_out_3",
>> + .parents = clk_out3_parents,
>> + .num_parents = ARRAY_SIZE(clk_out3_parents),
>> + .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>> + .gate_id = TEGRA_PMC_CLK_OUT_3,
>> + .dev_name = "extern3",
>> + .mux_shift = 22,
>> + .gate_shift = 18,
>> + .init_parent_index = 0,
>> + .init_state = 0,
>> + },
>> +};
>> +
>> struct tegra_powergate {
>> struct generic_pm_domain genpd;
>> struct tegra_pmc *pmc;
>> @@ -254,6 +344,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[] = {
>> @@ -2163,6 +2256,228 @@ 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;
>> +
>> + 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);
>> +
>> + 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,
>> + unsigned long offset, u32 shift, u32 mask)
>> +{
>> + struct clk_init_data init;
>> + struct pmc_clk_mux *mux;
>> +
>> + 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;
>> +
>> + 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;
>> +
>> + 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);
>> +}
>> +
>> +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, unsigned long offset,
>> + u32 shift)
>> +{
>> + struct clk_init_data init;
>> + struct pmc_clk_gate *gate;
>> +
>> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>> + if (!gate)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + 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;
>> +
>> + return clk_register(NULL, &gate->hw);
>> +}
>> +
>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>> + struct device_node *np)
>> +{
>> + struct clk *clkmux, *clk, *parent;
>> + struct clk_onecell_data *clk_data;
>> + unsigned int num_clks;
>> + int i, ret;
>> +
>> + /* each pmc clock output has a mux and a gate */
>> + num_clks = pmc->soc->num_pmc_clks * 2;
>> +
>> + if (!num_clks)
>> + return;
>> +
>> + clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>> + if (!clk_data)
>> + return;
>> +
>> + 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 = num_clks;
>> +
>> + for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>> + struct pmc_clk_init_data *data;
>> +
>> + data = pmc->soc->pmc_clks_data + i;
>> +
>> + clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>> + data->parents,
>> + data->num_parents,
>> + CLK_SET_RATE_NO_REPARENT |
>> + CLK_SET_RATE_PARENT,
>> + PMC_CLK_OUT_CNTRL,
>> + data->mux_shift, 3);
>> + if (IS_ERR(clkmux))
>> + goto free_clks;
>> +
>> + clk_data->clks[data->mux_id] = clkmux;
>> +
>> + clk = tegra_pmc_clk_gate_register(data->gate_name,
>> + data->mux_name,
>> + CLK_SET_RATE_PARENT,
>> + PMC_CLK_OUT_CNTRL,
>> + data->gate_shift);
>> + if (IS_ERR(clk))
>> + goto free_clks;
>> +
>> + clk_data->clks[data->gate_id] = clk;
>> +
>> + ret = clk_set_parent(clk, clkmux);
>> + if (ret < 0) {
>> + pr_err("failed to set parent of %s to %s\n",
>> + __func__, __clk_get_name(clk),
>> + __clk_get_name(clkmux));
>> + }
>> +
>> + clk_register_clkdev(clk, data->dev_name, data->gate_name);
>> +
>> + /* configure initial clock parent and state */
>> + parent = clk_get_sys(data->gate_name,
>> + data->parents[data->init_parent_index]);
>> + if (!IS_ERR(parent)) {
>> + ret = clk_set_parent(clkmux, parent);
>> + if (ret < 0) {
>> + pr_err("failed to set parent of %s to %s\n",
>> + __func__, __clk_get_name(clkmux),
>> + __clk_get_name(parent));
>> + WARN_ON(1);
>> + }
>> + }
>> +
>> + if (data->init_state) {
>> + if (clk_prepare_enable(clk)) {
>> + pr_err("failed to enable %s\n", __func__,
>> + __clk_get_name(clk));
>> + WARN_ON(1);
> Should be a bit better to move the WARN_ON to the end of errors handling
> in order to catch all possible errors:
>
> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
> tegra_pmc *pmc,
> return;
>
> free_clks:
> + WARN_ON(1);
> kfree(clk_data->clks);
> free_clkdata:
> kfree(clk_data);

Reason I had WARN_ON right during clk_set_parent failure is to have the
loop continue for subsequence pmc clocks registration instead of
terminating all pmc clocks registration.

2019-11-28 13:27:06

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

28.11.2019 01:57, Sowjanya Komatineni пишет:
>
> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>> Tegra210 and prior 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 <[email protected]>
>>> ---
>>>   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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>   #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;
>>> +};
>>> +
>>> +#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;
>>> +};
>>> +
>>> +#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_index;
>>> +    int init_state;
>>> +};
>>> +
>>> +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",
>>> +};
>>> +
>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>> +    {
>>> +        .mux_name = "clk_out_1_mux",
>>> +        .gate_name = "clk_out_1",
>>> +        .parents = clk_out1_parents,
>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>> +        .dev_name = "extern1",
>>> +        .mux_shift = 6,
>>> +        .gate_shift = 2,
>>> +        .init_parent_index = 3,
>>> +        .init_state = 1,
>>> +    },
>>> +    {
>>> +        .mux_name = "clk_out_2_mux",
>>> +        .gate_name = "clk_out_2",
>>> +        .parents = clk_out2_parents,
>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>> +        .dev_name = "extern2",
>>> +        .mux_shift = 14,
>>> +        .gate_shift = 10,
>>> +        .init_parent_index = 0,
>>> +        .init_state = 0,
>>> +    },
>>> +    {
>>> +        .mux_name = "clk_out_3_mux",
>>> +        .gate_name = "clk_out_3",
>>> +        .parents = clk_out3_parents,
>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>> +        .dev_name = "extern3",
>>> +        .mux_shift = 22,
>>> +        .gate_shift = 18,
>>> +        .init_parent_index = 0,
>>> +        .init_state = 0,
>>> +    },
>>> +};
>>> +
>>>   struct tegra_powergate {
>>>       struct generic_pm_domain genpd;
>>>       struct tegra_pmc *pmc;
>>> @@ -254,6 +344,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[] = {
>>> @@ -2163,6 +2256,228 @@ 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;
>>> +
>>> +    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);
>>> +
>>> +    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,
>>> +               unsigned long offset, u32 shift, u32 mask)
>>> +{
>>> +    struct clk_init_data init;
>>> +    struct pmc_clk_mux *mux;
>>> +
>>> +    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;
>>> +
>>> +    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;
>>> +
>>> +    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);
>>> +}
>>> +
>>> +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, unsigned long offset,
>>> +                u32 shift)
>>> +{
>>> +    struct clk_init_data init;
>>> +    struct pmc_clk_gate *gate;
>>> +
>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>> +    if (!gate)
>>> +        return ERR_PTR(-ENOMEM);
>>> +
>>> +    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;
>>> +
>>> +    return clk_register(NULL, &gate->hw);
>>> +}
>>> +
>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>> +                     struct device_node *np)
>>> +{
>>> +    struct clk *clkmux, *clk, *parent;
>>> +    struct clk_onecell_data *clk_data;
>>> +    unsigned int num_clks;
>>> +    int i, ret;
>>> +
>>> +    /* each pmc clock output has a mux and a gate */
>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>> +
>>> +    if (!num_clks)
>>> +        return;
>>> +
>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>> +    if (!clk_data)
>>> +        return;
>>> +
>>> +    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 = num_clks;
>>> +
>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>> +        struct pmc_clk_init_data *data;
>>> +
>>> +        data = pmc->soc->pmc_clks_data + i;
>>> +
>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>> +                            data->parents,
>>> +                            data->num_parents,
>>> +                            CLK_SET_RATE_NO_REPARENT |
>>> +                            CLK_SET_RATE_PARENT,
>>> +                            PMC_CLK_OUT_CNTRL,
>>> +                            data->mux_shift, 3);
>>> +        if (IS_ERR(clkmux))
>>> +            goto free_clks;
>>> +
>>> +        clk_data->clks[data->mux_id] = clkmux;
>>> +
>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>> +                          data->mux_name,
>>> +                          CLK_SET_RATE_PARENT,
>>> +                          PMC_CLK_OUT_CNTRL,
>>> +                          data->gate_shift);
>>> +        if (IS_ERR(clk))
>>> +            goto free_clks;
>>> +
>>> +        clk_data->clks[data->gate_id] = clk;
>>> +
>>> +        ret = clk_set_parent(clk, clkmux);
>>> +        if (ret < 0) {
>>> +            pr_err("failed to set parent of %s to %s\n",
>>> +                   __func__, __clk_get_name(clk),
>>> +                   __clk_get_name(clkmux));
>>> +        }
>>> +
>>> +        clk_register_clkdev(clk, data->dev_name, data->gate_name);
>>> +
>>> +        /* configure initial clock parent and state */
>>> +        parent = clk_get_sys(data->gate_name,
>>> +                     data->parents[data->init_parent_index]);
>>> +        if (!IS_ERR(parent)) {
>>> +            ret = clk_set_parent(clkmux, parent);
>>> +            if (ret < 0) {
>>> +                pr_err("failed to set parent of %s to %s\n",
>>> +                       __func__, __clk_get_name(clkmux),
>>> +                       __clk_get_name(parent));
>>> +                WARN_ON(1);
>>> +            }
>>> +        }
>>> +
>>> +        if (data->init_state) {
>>> +            if (clk_prepare_enable(clk)) {
>>> +                pr_err("failed to enable %s\n", __func__,
>>> +                       __clk_get_name(clk));
>>> +                WARN_ON(1);

Alternatively you could write it like this:

err = clk_prepare_enable(clk);

WARN_ON(err, "failed to enable %s: %d\n",
__clk_get_name(clk), err);

>> Should be a bit better to move the WARN_ON to the end of errors handling
>> in order to catch all possible errors:
>>
>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>> tegra_pmc *pmc,
>>          return;
>>
>>   free_clks:
>> +       WARN_ON(1);
>>          kfree(clk_data->clks);
>>   free_clkdata:
>>          kfree(clk_data);
>
> Reason I had WARN_ON right during clk_set_parent failure is to have the
> loop continue for subsequence pmc clocks registration instead of
> terminating all pmc clocks registration.

Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least (IMO).

2019-12-02 20:11:54

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>> Tegra210 and prior 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 <[email protected]>
>>>> ---
>>>>   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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>   #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;
>>>> +};
>>>> +
>>>> +#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;
>>>> +};
>>>> +
>>>> +#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_index;
>>>> +    int init_state;
>>>> +};
>>>> +
>>>> +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",
>>>> +};
>>>> +
>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>> +    {
>>>> +        .mux_name = "clk_out_1_mux",
>>>> +        .gate_name = "clk_out_1",
>>>> +        .parents = clk_out1_parents,
>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>> +        .dev_name = "extern1",
>>>> +        .mux_shift = 6,
>>>> +        .gate_shift = 2,
>>>> +        .init_parent_index = 3,
>>>> +        .init_state = 1,
>>>> +    },
>>>> +    {
>>>> +        .mux_name = "clk_out_2_mux",
>>>> +        .gate_name = "clk_out_2",
>>>> +        .parents = clk_out2_parents,
>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>> +        .dev_name = "extern2",
>>>> +        .mux_shift = 14,
>>>> +        .gate_shift = 10,
>>>> +        .init_parent_index = 0,
>>>> +        .init_state = 0,
>>>> +    },
>>>> +    {
>>>> +        .mux_name = "clk_out_3_mux",
>>>> +        .gate_name = "clk_out_3",
>>>> +        .parents = clk_out3_parents,
>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>> +        .dev_name = "extern3",
>>>> +        .mux_shift = 22,
>>>> +        .gate_shift = 18,
>>>> +        .init_parent_index = 0,
>>>> +        .init_state = 0,
>>>> +    },
>>>> +};
>>>> +
>>>>   struct tegra_powergate {
>>>>       struct generic_pm_domain genpd;
>>>>       struct tegra_pmc *pmc;
>>>> @@ -254,6 +344,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[] = {
>>>> @@ -2163,6 +2256,228 @@ 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;
>>>> +
>>>> +    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);
>>>> +
>>>> +    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,
>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>> +{
>>>> +    struct clk_init_data init;
>>>> +    struct pmc_clk_mux *mux;
>>>> +
>>>> +    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;
>>>> +
>>>> +    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;
>>>> +
>>>> +    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);
>>>> +}
>>>> +
>>>> +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, unsigned long offset,
>>>> +                u32 shift)
>>>> +{
>>>> +    struct clk_init_data init;
>>>> +    struct pmc_clk_gate *gate;
>>>> +
>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>> +    if (!gate)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    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;
>>>> +
>>>> +    return clk_register(NULL, &gate->hw);
>>>> +}
>>>> +
>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>> +                     struct device_node *np)
>>>> +{
>>>> +    struct clk *clkmux, *clk, *parent;
>>>> +    struct clk_onecell_data *clk_data;
>>>> +    unsigned int num_clks;
>>>> +    int i, ret;
>>>> +
>>>> +    /* each pmc clock output has a mux and a gate */
>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>> +
>>>> +    if (!num_clks)
>>>> +        return;
>>>> +
>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>> +    if (!clk_data)
>>>> +        return;
>>>> +
>>>> +    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 = num_clks;
>>>> +
>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>> +        struct pmc_clk_init_data *data;
>>>> +
>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>> +
>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>> +                            data->parents,
>>>> +                            data->num_parents,
>>>> +                            CLK_SET_RATE_NO_REPARENT |
>>>> +                            CLK_SET_RATE_PARENT,
>>>> +                            PMC_CLK_OUT_CNTRL,
>>>> +                            data->mux_shift, 3);
>>>> +        if (IS_ERR(clkmux))
>>>> +            goto free_clks;
>>>> +
>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>> +
>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>> +                          data->mux_name,
>>>> +                          CLK_SET_RATE_PARENT,
>>>> +                          PMC_CLK_OUT_CNTRL,
>>>> +                          data->gate_shift);
>>>> +        if (IS_ERR(clk))
>>>> +            goto free_clks;
>>>> +
>>>> +        clk_data->clks[data->gate_id] = clk;
>>>> +
>>>> +        ret = clk_set_parent(clk, clkmux);
>>>> +        if (ret < 0) {
>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>> +                   __func__, __clk_get_name(clk),
>>>> +                   __clk_get_name(clkmux));
>>>> +        }
>>>> +
>>>> +        clk_register_clkdev(clk, data->dev_name, data->gate_name);
>>>> +
>>>> +        /* configure initial clock parent and state */
>>>> +        parent = clk_get_sys(data->gate_name,
>>>> +                     data->parents[data->init_parent_index]);
>>>> +        if (!IS_ERR(parent)) {
>>>> +            ret = clk_set_parent(clkmux, parent);
>>>> +            if (ret < 0) {
>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>> +                       __func__, __clk_get_name(clkmux),
>>>> +                       __clk_get_name(parent));
>>>> +                WARN_ON(1);
>>>> +            }
>>>> +        }
>>>> +
>>>> +        if (data->init_state) {
>>>> +            if (clk_prepare_enable(clk)) {
>>>> +                pr_err("failed to enable %s\n", __func__,
>>>> +                       __clk_get_name(clk));
>>>> +                WARN_ON(1);
> Alternatively you could write it like this:
>
> err = clk_prepare_enable(clk);
>
> WARN_ON(err, "failed to enable %s: %d\n",
> __clk_get_name(clk), err);
>
>>> Should be a bit better to move the WARN_ON to the end of errors handling
>>> in order to catch all possible errors:
>>>
>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>> tegra_pmc *pmc,
>>>          return;
>>>
>>>   free_clks:
>>> +       WARN_ON(1);
>>>          kfree(clk_data->clks);
>>>   free_clkdata:
>>>          kfree(clk_data);
>> Reason I had WARN_ON right during clk_set_parent failure is to have the
>> loop continue for subsequence pmc clocks registration instead of
>> terminating all pmc clocks registration.
> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least (IMO).

Hi Dmitry, Just want to be clear on the above comment. Are you
suggesting to add additional WARN_ON at the end?

Thought WARN_ON right during corresponding clock failure with warn
message showing clock names will be clear and also other clocks still
should be registered.

To add additional WARN_ON at the end need to track status of each clock
and use that to as warn condition.

2019-12-02 21:54:52

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

02.12.2019 23:09, Sowjanya Komatineni пишет:
>
> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>> Tegra210 and prior 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 <[email protected]>
>>>>> ---
>>>>>    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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>    #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;
>>>>> +};
>>>>> +
>>>>> +#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;
>>>>> +};
>>>>> +
>>>>> +#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_index;
>>>>> +    int init_state;
>>>>> +};
>>>>> +
>>>>> +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",
>>>>> +};
>>>>> +
>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>> +    {
>>>>> +        .mux_name = "clk_out_1_mux",
>>>>> +        .gate_name = "clk_out_1",
>>>>> +        .parents = clk_out1_parents,
>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>> +        .dev_name = "extern1",
>>>>> +        .mux_shift = 6,
>>>>> +        .gate_shift = 2,
>>>>> +        .init_parent_index = 3,
>>>>> +        .init_state = 1,
>>>>> +    },
>>>>> +    {
>>>>> +        .mux_name = "clk_out_2_mux",
>>>>> +        .gate_name = "clk_out_2",
>>>>> +        .parents = clk_out2_parents,
>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>> +        .dev_name = "extern2",
>>>>> +        .mux_shift = 14,
>>>>> +        .gate_shift = 10,
>>>>> +        .init_parent_index = 0,
>>>>> +        .init_state = 0,
>>>>> +    },
>>>>> +    {
>>>>> +        .mux_name = "clk_out_3_mux",
>>>>> +        .gate_name = "clk_out_3",
>>>>> +        .parents = clk_out3_parents,
>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>> +        .dev_name = "extern3",
>>>>> +        .mux_shift = 22,
>>>>> +        .gate_shift = 18,
>>>>> +        .init_parent_index = 0,
>>>>> +        .init_state = 0,
>>>>> +    },
>>>>> +};
>>>>> +
>>>>>    struct tegra_powergate {
>>>>>        struct generic_pm_domain genpd;
>>>>>        struct tegra_pmc *pmc;
>>>>> @@ -254,6 +344,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[] = {
>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>> +
>>>>> +    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);
>>>>> +
>>>>> +    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,
>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>> +{
>>>>> +    struct clk_init_data init;
>>>>> +    struct pmc_clk_mux *mux;
>>>>> +
>>>>> +    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;
>>>>> +
>>>>> +    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;
>>>>> +
>>>>> +    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);
>>>>> +}
>>>>> +
>>>>> +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, unsigned long offset,
>>>>> +                u32 shift)
>>>>> +{
>>>>> +    struct clk_init_data init;
>>>>> +    struct pmc_clk_gate *gate;
>>>>> +
>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>> +    if (!gate)
>>>>> +        return ERR_PTR(-ENOMEM);
>>>>> +
>>>>> +    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;
>>>>> +
>>>>> +    return clk_register(NULL, &gate->hw);
>>>>> +}
>>>>> +
>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>> +                     struct device_node *np)
>>>>> +{
>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>> +    struct clk_onecell_data *clk_data;
>>>>> +    unsigned int num_clks;
>>>>> +    int i, ret;
>>>>> +
>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>> +
>>>>> +    if (!num_clks)
>>>>> +        return;
>>>>> +
>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>> +    if (!clk_data)
>>>>> +        return;
>>>>> +
>>>>> +    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 = num_clks;
>>>>> +
>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>> +        struct pmc_clk_init_data *data;
>>>>> +
>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>> +
>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>> +                            data->parents,
>>>>> +                            data->num_parents,
>>>>> +                            CLK_SET_RATE_NO_REPARENT |
>>>>> +                            CLK_SET_RATE_PARENT,
>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>> +                            data->mux_shift, 3);
>>>>> +        if (IS_ERR(clkmux))
>>>>> +            goto free_clks;
>>>>> +
>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>> +
>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>> +                          data->mux_name,
>>>>> +                          CLK_SET_RATE_PARENT,
>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>> +                          data->gate_shift);
>>>>> +        if (IS_ERR(clk))
>>>>> +            goto free_clks;
>>>>> +
>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>> +
>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>> +        if (ret < 0) {
>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>> +                   __func__, __clk_get_name(clk),
>>>>> +                   __clk_get_name(clkmux));
>>>>> +        }
>>>>> +
>>>>> +        clk_register_clkdev(clk, data->dev_name, data->gate_name);
>>>>> +
>>>>> +        /* configure initial clock parent and state */
>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>> +                     data->parents[data->init_parent_index]);

Couldn't the default parent be defined using "assigned clock" in a
device-tree? Please see "Assigned clock parents and rates" in the doc.

https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt

Then you could simply use of_clk_set_defaults(pmc->dev->of_node, true).

>>>>> +        if (!IS_ERR(parent)) {
>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>> +            if (ret < 0) {
>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>> +                       __clk_get_name(parent));
>>>>> +                WARN_ON(1);
>>>>> +            }
>>>>> +        }
>>>>> +
>>>>> +        if (data->init_state) {
>>>>> +            if (clk_prepare_enable(clk)) {
>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>> +                       __clk_get_name(clk));
>>>>> +                WARN_ON(1);
>> Alternatively you could write it like this:
>>
>>     err = clk_prepare_enable(clk);
>>
>>     WARN_ON(err, "failed to enable %s: %d\n",
>>         __clk_get_name(clk), err);
>>
>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>> handling
>>>> in order to catch all possible errors:
>>>>
>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>> tegra_pmc *pmc,
>>>>           return;
>>>>
>>>>    free_clks:
>>>> +       WARN_ON(1);
>>>>           kfree(clk_data->clks);
>>>>    free_clkdata:
>>>>           kfree(clk_data);
>>> Reason I had WARN_ON right during clk_set_parent failure is to have the
>>> loop continue for subsequence pmc clocks registration instead of
>>> terminating all pmc clocks registration.
>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least
>> (IMO).
> Hi Dmitry, Just want to be clear on the above comment. Are you
> suggesting to add additional WARN_ON at the end?

Yes, it was my suggestion.

> Thought WARN_ON right during corresponding clock failure with warn
> message showing clock names will be clear and also other clocks still
> should be registered.
>
> To add additional WARN_ON at the end need to track status of each clock
> and use that to as warn condition.

You could add a warning/error message to every point of failure.

Primarily, it is important not to miss a error. Secondarily, it is
important to make diagnostic message meaningful.

Realistically, I doubt that this chunk of code will ever fail once it is
known to work well. So it will be nice to have a more detailed
diagnostics (just in a case), but it shouldn't be a must.

2019-12-02 23:00:13

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>> ---
>>>>>>    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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>    #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;
>>>>>> +};
>>>>>> +
>>>>>> +#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;
>>>>>> +};
>>>>>> +
>>>>>> +#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_index;
>>>>>> +    int init_state;
>>>>>> +};
>>>>>> +
>>>>>> +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",
>>>>>> +};
>>>>>> +
>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>> +    {
>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>> +        .gate_name = "clk_out_1",
>>>>>> +        .parents = clk_out1_parents,
>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>> +        .dev_name = "extern1",
>>>>>> +        .mux_shift = 6,
>>>>>> +        .gate_shift = 2,
>>>>>> +        .init_parent_index = 3,
>>>>>> +        .init_state = 1,
>>>>>> +    },
>>>>>> +    {
>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>> +        .gate_name = "clk_out_2",
>>>>>> +        .parents = clk_out2_parents,
>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>> +        .dev_name = "extern2",
>>>>>> +        .mux_shift = 14,
>>>>>> +        .gate_shift = 10,
>>>>>> +        .init_parent_index = 0,
>>>>>> +        .init_state = 0,
>>>>>> +    },
>>>>>> +    {
>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>> +        .gate_name = "clk_out_3",
>>>>>> +        .parents = clk_out3_parents,
>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>> +        .dev_name = "extern3",
>>>>>> +        .mux_shift = 22,
>>>>>> +        .gate_shift = 18,
>>>>>> +        .init_parent_index = 0,
>>>>>> +        .init_state = 0,
>>>>>> +    },
>>>>>> +};
>>>>>> +
>>>>>>    struct tegra_powergate {
>>>>>>        struct generic_pm_domain genpd;
>>>>>>        struct tegra_pmc *pmc;
>>>>>> @@ -254,6 +344,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[] = {
>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>> +
>>>>>> +    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);
>>>>>> +
>>>>>> +    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,
>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>> +{
>>>>>> +    struct clk_init_data init;
>>>>>> +    struct pmc_clk_mux *mux;
>>>>>> +
>>>>>> +    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;
>>>>>> +
>>>>>> +    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;
>>>>>> +
>>>>>> +    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);
>>>>>> +}
>>>>>> +
>>>>>> +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, unsigned long offset,
>>>>>> +                u32 shift)
>>>>>> +{
>>>>>> +    struct clk_init_data init;
>>>>>> +    struct pmc_clk_gate *gate;
>>>>>> +
>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>> +    if (!gate)
>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>> +
>>>>>> +    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;
>>>>>> +
>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>> +}
>>>>>> +
>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>> +                     struct device_node *np)
>>>>>> +{
>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>> +    unsigned int num_clks;
>>>>>> +    int i, ret;
>>>>>> +
>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>> +
>>>>>> +    if (!num_clks)
>>>>>> +        return;
>>>>>> +
>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>> +    if (!clk_data)
>>>>>> +        return;
>>>>>> +
>>>>>> +    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 = num_clks;
>>>>>> +
>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>> +        struct pmc_clk_init_data *data;
>>>>>> +
>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>> +
>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>> +                            data->parents,
>>>>>> +                            data->num_parents,
>>>>>> +                            CLK_SET_RATE_NO_REPARENT |
>>>>>> +                            CLK_SET_RATE_PARENT,
>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>> +                            data->mux_shift, 3);
>>>>>> +        if (IS_ERR(clkmux))
>>>>>> +            goto free_clks;
>>>>>> +
>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>> +
>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>> +                          data->mux_name,
>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>> +                          data->gate_shift);
>>>>>> +        if (IS_ERR(clk))
>>>>>> +            goto free_clks;
>>>>>> +
>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>> +
>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>> +        if (ret < 0) {
>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>> +                   __clk_get_name(clkmux));
>>>>>> +        }
>>>>>> +
>>>>>> +        clk_register_clkdev(clk, data->dev_name, data->gate_name);
>>>>>> +
>>>>>> +        /* configure initial clock parent and state */
>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>> +                     data->parents[data->init_parent_index]);
> Couldn't the default parent be defined using "assigned clock" in a
> device-tree? Please see "Assigned clock parents and rates" in the doc.
>
> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>
> Then you could simply use of_clk_set_defaults(pmc->dev->of_node, true).

Yes, of_clk_add_provider() does of_clk_set_defaults which sets based on
assigned parents and clock rates.

This need device tree to specify assigned clock parent properties. Will
update device tree and remove init parent from the driver.

>
>>>>>> +        if (!IS_ERR(parent)) {
>>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>>> +            if (ret < 0) {
>>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>>> +                       __clk_get_name(parent));
>>>>>> +                WARN_ON(1);
>>>>>> +            }
>>>>>> +        }
>>>>>> +
>>>>>> +        if (data->init_state) {
>>>>>> +            if (clk_prepare_enable(clk)) {
>>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>>> +                       __clk_get_name(clk));
>>>>>> +                WARN_ON(1);
>>> Alternatively you could write it like this:
>>>
>>>     err = clk_prepare_enable(clk);
>>>
>>>     WARN_ON(err, "failed to enable %s: %d\n",
>>>         __clk_get_name(clk), err);
>>>
>>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>>> handling
>>>>> in order to catch all possible errors:
>>>>>
>>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>>> tegra_pmc *pmc,
>>>>>           return;
>>>>>
>>>>>    free_clks:
>>>>> +       WARN_ON(1);
>>>>>           kfree(clk_data->clks);
>>>>>    free_clkdata:
>>>>>           kfree(clk_data);
>>>> Reason I had WARN_ON right during clk_set_parent failure is to have the
>>>> loop continue for subsequence pmc clocks registration instead of
>>>> terminating all pmc clocks registration.
>>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least
>>> (IMO).
>> Hi Dmitry, Just want to be clear on the above comment. Are you
>> suggesting to add additional WARN_ON at the end?
> Yes, it was my suggestion.
>
>> Thought WARN_ON right during corresponding clock failure with warn
>> message showing clock names will be clear and also other clocks still
>> should be registered.
>>
>> To add additional WARN_ON at the end need to track status of each clock
>> and use that to as warn condition.
> You could add a warning/error message to every point of failure.
>
> Primarily, it is important not to miss a error. Secondarily, it is
> important to make diagnostic message meaningful.
>
> Realistically, I doubt that this chunk of code will ever fail once it is
> known to work well. So it will be nice to have a more detailed
> diagnostics (just in a case), but it shouldn't be a must.

OK, Will add additional WARN message "failed registering PMC clocks" at
the end.

2019-12-02 23:12:24

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>
> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>> ---
>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>     #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;
>>>>>>> +};
>>>>>>> +
>>>>>>> +#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;
>>>>>>> +};
>>>>>>> +
>>>>>>> +#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_index;
>>>>>>> +    int init_state;
>>>>>>> +};
>>>>>>> +
>>>>>>> +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",
>>>>>>> +};
>>>>>>> +
>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>> +    {
>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>> +        .parents = clk_out1_parents,
>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>> +        .dev_name = "extern1",
>>>>>>> +        .mux_shift = 6,
>>>>>>> +        .gate_shift = 2,
>>>>>>> +        .init_parent_index = 3,
>>>>>>> +        .init_state = 1,
>>>>>>> +    },
>>>>>>> +    {
>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>> +        .parents = clk_out2_parents,
>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>> +        .dev_name = "extern2",
>>>>>>> +        .mux_shift = 14,
>>>>>>> +        .gate_shift = 10,
>>>>>>> +        .init_parent_index = 0,
>>>>>>> +        .init_state = 0,
>>>>>>> +    },
>>>>>>> +    {
>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>> +        .parents = clk_out3_parents,
>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>> +        .dev_name = "extern3",
>>>>>>> +        .mux_shift = 22,
>>>>>>> +        .gate_shift = 18,
>>>>>>> +        .init_parent_index = 0,
>>>>>>> +        .init_state = 0,
>>>>>>> +    },
>>>>>>> +};
>>>>>>> +
>>>>>>>     struct tegra_powergate {
>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>         struct tegra_pmc *pmc;
>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>> +
>>>>>>> +    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);
>>>>>>> +
>>>>>>> +    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,
>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>> +{
>>>>>>> +    struct clk_init_data init;
>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>> +
>>>>>>> +    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;
>>>>>>> +
>>>>>>> +    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;
>>>>>>> +
>>>>>>> +    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);
>>>>>>> +}
>>>>>>> +
>>>>>>> +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, unsigned long offset,
>>>>>>> +                u32 shift)
>>>>>>> +{
>>>>>>> +    struct clk_init_data init;
>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>> +
>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>> +    if (!gate)
>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>> +
>>>>>>> +    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;
>>>>>>> +
>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>> +                     struct device_node *np)
>>>>>>> +{
>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>> +    unsigned int num_clks;
>>>>>>> +    int i, ret;
>>>>>>> +
>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>> +
>>>>>>> +    if (!num_clks)
>>>>>>> +        return;
>>>>>>> +
>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>> +    if (!clk_data)
>>>>>>> +        return;
>>>>>>> +
>>>>>>> +    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 = num_clks;
>>>>>>> +
>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>> +
>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>> +
>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>> +                            data->parents,
>>>>>>> +                            data->num_parents,
>>>>>>> +                            CLK_SET_RATE_NO_REPARENT |
>>>>>>> +                            CLK_SET_RATE_PARENT,
>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>> +                            data->mux_shift, 3);
>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>> +            goto free_clks;
>>>>>>> +
>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>> +
>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>> +                          data->mux_name,
>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>> +                          data->gate_shift);
>>>>>>> +        if (IS_ERR(clk))
>>>>>>> +            goto free_clks;
>>>>>>> +
>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>> +
>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>> +        if (ret < 0) {
>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        clk_register_clkdev(clk, data->dev_name, data->gate_name);
>>>>>>> +
>>>>>>> +        /* configure initial clock parent and state */
>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>> + data->parents[data->init_parent_index]);
>> Couldn't the default parent be defined using "assigned clock" in a
>> device-tree? Please see "Assigned clock parents and rates" in the doc.
>>
>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>
>>
>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node, true).
>
> Yes, of_clk_add_provider() does of_clk_set_defaults which sets based
> on assigned parents and clock rates.
>
> This need device tree to specify assigned clock parent properties.
> Will update device tree and remove init parent from the driver.
>
assigned-clock properties should be set in consumer node of these clocks
and currently these clocks are not used yet.

So will just remove init parent from driver and when these clocks are
used device tree can be updated in corresponding consumer node with
these properties.

>>
>>>>>>> +        if (!IS_ERR(parent)) {
>>>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>>>> +            if (ret < 0) {
>>>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>>>> +                       __clk_get_name(parent));
>>>>>>> +                WARN_ON(1);
>>>>>>> +            }
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (data->init_state) {
>>>>>>> +            if (clk_prepare_enable(clk)) {
>>>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>>>> +                       __clk_get_name(clk));
>>>>>>> +                WARN_ON(1);
>>>> Alternatively you could write it like this:
>>>>
>>>>      err = clk_prepare_enable(clk);
>>>>
>>>>      WARN_ON(err, "failed to enable %s: %d\n",
>>>>          __clk_get_name(clk), err);
>>>>
>>>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>>>> handling
>>>>>> in order to catch all possible errors:
>>>>>>
>>>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>>>> tegra_pmc *pmc,
>>>>>>            return;
>>>>>>
>>>>>>     free_clks:
>>>>>> +       WARN_ON(1);
>>>>>>            kfree(clk_data->clks);
>>>>>>     free_clkdata:
>>>>>>            kfree(clk_data);
>>>>> Reason I had WARN_ON right during clk_set_parent failure is to
>>>>> have the
>>>>> loop continue for subsequence pmc clocks registration instead of
>>>>> terminating all pmc clocks registration.
>>>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least
>>>> (IMO).
>>> Hi Dmitry, Just want to be clear on the above comment. Are you
>>> suggesting to add additional WARN_ON at the end?
>> Yes, it was my suggestion.
>>
>>> Thought WARN_ON right during corresponding clock failure with warn
>>> message showing clock names will be clear and also other clocks still
>>> should be registered.
>>>
>>> To add additional WARN_ON at the end need to track status of each clock
>>> and use that to as warn condition.
>> You could add a warning/error message to every point of failure.
>>
>> Primarily, it is important not to miss a error. Secondarily, it is
>> important to make diagnostic message meaningful.
>>
>> Realistically, I doubt that this chunk of code will ever fail once it is
>> known to work well. So it will be nice to have a more detailed
>> diagnostics (just in a case), but it shouldn't be a must.
>
> OK, Will add additional WARN message "failed registering PMC clocks"
> at the end.
>

2019-12-02 23:17:52

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>
> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>
>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>> ---
>>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>     #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;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +#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;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +#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_index;
>>>>>>>> +    int init_state;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +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",
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>> +    {
>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>> +        .dev_name = "extern1",
>>>>>>>> +        .mux_shift = 6,
>>>>>>>> +        .gate_shift = 2,
>>>>>>>> +        .init_parent_index = 3,
>>>>>>>> +        .init_state = 1,
>>>>>>>> +    },
>>>>>>>> +    {
>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>> +        .dev_name = "extern2",
>>>>>>>> +        .mux_shift = 14,
>>>>>>>> +        .gate_shift = 10,
>>>>>>>> +        .init_parent_index = 0,
>>>>>>>> +        .init_state = 0,
>>>>>>>> +    },
>>>>>>>> +    {
>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>> +        .dev_name = "extern3",
>>>>>>>> +        .mux_shift = 22,
>>>>>>>> +        .gate_shift = 18,
>>>>>>>> +        .init_parent_index = 0,
>>>>>>>> +        .init_state = 0,
>>>>>>>> +    },
>>>>>>>> +};
>>>>>>>> +
>>>>>>>>     struct tegra_powergate {
>>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>>         struct tegra_pmc *pmc;
>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>> +
>>>>>>>> +    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);
>>>>>>>> +
>>>>>>>> +    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,
>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>> +{
>>>>>>>> +    struct clk_init_data init;
>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>> +
>>>>>>>> +    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;
>>>>>>>> +
>>>>>>>> +    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;
>>>>>>>> +
>>>>>>>> +    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);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +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, unsigned long offset,
>>>>>>>> +                u32 shift)
>>>>>>>> +{
>>>>>>>> +    struct clk_init_data init;
>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>> +
>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>> +    if (!gate)
>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>> +
>>>>>>>> +    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;
>>>>>>>> +
>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>> +                     struct device_node *np)
>>>>>>>> +{
>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>> +    unsigned int num_clks;
>>>>>>>> +    int i, ret;
>>>>>>>> +
>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>> +
>>>>>>>> +    if (!num_clks)
>>>>>>>> +        return;
>>>>>>>> +
>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>> +    if (!clk_data)
>>>>>>>> +        return;
>>>>>>>> +
>>>>>>>> +    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 = num_clks;
>>>>>>>> +
>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>> +
>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>> +
>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>> +                            data->parents,
>>>>>>>> +                            data->num_parents,
>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>> +                            CLK_SET_RATE_PARENT,
>>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>>> +                            data->mux_shift, 3);
>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>> +            goto free_clks;
>>>>>>>> +
>>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>>> +
>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>> +                          data->mux_name,
>>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>>> +                          data->gate_shift);
>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>> +            goto free_clks;
>>>>>>>> +
>>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>>> +
>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>> +        if (ret < 0) {
>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>> data->gate_name);
>>>>>>>> +
>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>> + data->parents[data->init_parent_index]);
>>> Couldn't the default parent be defined using "assigned clock" in a
>>> device-tree? Please see "Assigned clock parents and rates" in the doc.
>>>
>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>
>>>
>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node, true).
>>
>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets based
>> on assigned parents and clock rates.
>>
>> This need device tree to specify assigned clock parent properties.
>> Will update device tree and remove init parent from the driver.
>>
> assigned-clock properties should be set in consumer node of these
> clocks and currently these clocks are not used yet.
>
> So will just remove init parent from driver and when these clocks are
> used device tree can be updated in corresponding consumer node with
> these properties.
>
How about default ON/OFF init state for the clocks? I see assigned-clock
properties for parent and rate only.

But based on existing clock-tegra-pmc driver, I see clk_out_1 is default
enabled with extern1 parent for T30 thru T210 platforms.

Peter/Thierry, What was the reason we enable clk_out_1 right from the
clock registration?

>>>
>>>>>>>> +        if (!IS_ERR(parent)) {
>>>>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>>>>> +            if (ret < 0) {
>>>>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>>>>> +                       __clk_get_name(parent));
>>>>>>>> +                WARN_ON(1);
>>>>>>>> +            }
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>> +        if (data->init_state) {
>>>>>>>> +            if (clk_prepare_enable(clk)) {
>>>>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>>>>> +                       __clk_get_name(clk));
>>>>>>>> +                WARN_ON(1);
>>>>> Alternatively you could write it like this:
>>>>>
>>>>>      err = clk_prepare_enable(clk);
>>>>>
>>>>>      WARN_ON(err, "failed to enable %s: %d\n",
>>>>>          __clk_get_name(clk), err);
>>>>>
>>>>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>>>>> handling
>>>>>>> in order to catch all possible errors:
>>>>>>>
>>>>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>>>>> tegra_pmc *pmc,
>>>>>>>            return;
>>>>>>>
>>>>>>>     free_clks:
>>>>>>> +       WARN_ON(1);
>>>>>>>            kfree(clk_data->clks);
>>>>>>>     free_clkdata:
>>>>>>>            kfree(clk_data);
>>>>>> Reason I had WARN_ON right during clk_set_parent failure is to
>>>>>> have the
>>>>>> loop continue for subsequence pmc clocks registration instead of
>>>>>> terminating all pmc clocks registration.
>>>>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the least
>>>>> (IMO).
>>>> Hi Dmitry, Just want to be clear on the above comment. Are you
>>>> suggesting to add additional WARN_ON at the end?
>>> Yes, it was my suggestion.
>>>
>>>> Thought WARN_ON right during corresponding clock failure with warn
>>>> message showing clock names will be clear and also other clocks still
>>>> should be registered.
>>>>
>>>> To add additional WARN_ON at the end need to track status of each
>>>> clock
>>>> and use that to as warn condition.
>>> You could add a warning/error message to every point of failure.
>>>
>>> Primarily, it is important not to miss a error. Secondarily, it is
>>> important to make diagnostic message meaningful.
>>>
>>> Realistically, I doubt that this chunk of code will ever fail once
>>> it is
>>> known to work well. So it will be nice to have a more detailed
>>> diagnostics (just in a case), but it shouldn't be a must.
>>
>> OK, Will add additional WARN message "failed registering PMC clocks"
>> at the end.
>>

2019-12-03 00:08:33

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>
> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>
>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>
>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>> ---
>>>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>     #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;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +#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;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +#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_index;
>>>>>>>>> +    int init_state;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +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",
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>> +    {
>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>> +        .init_state = 1,
>>>>>>>>> +    },
>>>>>>>>> +    {
>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>> +        .init_state = 0,
>>>>>>>>> +    },
>>>>>>>>> +    {
>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>> +        .init_state = 0,
>>>>>>>>> +    },
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>>     struct tegra_powergate {
>>>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>>>         struct tegra_pmc *pmc;
>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>> +
>>>>>>>>> +    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);
>>>>>>>>> +
>>>>>>>>> +    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,
>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>> +{
>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>> +
>>>>>>>>> +    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;
>>>>>>>>> +
>>>>>>>>> +    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;
>>>>>>>>> +
>>>>>>>>> +    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);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +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, unsigned long offset,
>>>>>>>>> +                u32 shift)
>>>>>>>>> +{
>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>> +
>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>> +    if (!gate)
>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>> +
>>>>>>>>> +    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;
>>>>>>>>> +
>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>> +                     struct device_node *np)
>>>>>>>>> +{
>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>> +    int i, ret;
>>>>>>>>> +
>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>> +
>>>>>>>>> +    if (!num_clks)
>>>>>>>>> +        return;
>>>>>>>>> +
>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>> +    if (!clk_data)
>>>>>>>>> +        return;
>>>>>>>>> +
>>>>>>>>> +    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 = num_clks;
>>>>>>>>> +
>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>> +
>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>> +
>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>> +                            data->parents,
>>>>>>>>> +                            data->num_parents,
>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>> +                            CLK_SET_RATE_PARENT,
>>>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>>>> +                            data->mux_shift, 3);
>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>> +            goto free_clks;
>>>>>>>>> +
>>>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>> +
>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>> +                          data->mux_name,
>>>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>>>> +                          data->gate_shift);
>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>> +            goto free_clks;
>>>>>>>>> +
>>>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>>>> +
>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>> +        if (ret < 0) {
>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>>>> +        }
>>>>>>>>> +
>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>> data->gate_name);
>>>>>>>>> +
>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>> + data->parents[data->init_parent_index]);
>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>> device-tree? Please see "Assigned clock parents and rates" in the doc.
>>>>
>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>
>>>>
>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>> true).
>>>
>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets based
>>> on assigned parents and clock rates.
>>>
>>> This need device tree to specify assigned clock parent properties.
>>> Will update device tree and remove init parent from the driver.
>>>
>> assigned-clock properties should be set in consumer node of these
>> clocks and currently these clocks are not used yet.
>>
>> So will just remove init parent from driver and when these clocks are
>> used device tree can be updated in corresponding consumer node with
>> these properties.
>>
> How about default ON/OFF init state for the clocks? I see
> assigned-clock properties for parent and rate only.
>
> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
> default enabled with extern1 parent for T30 thru T210 platforms.
>
> Peter/Thierry, What was the reason we enable clk_out_1 right from the
> clock registration?
>
clk_out_1 is for audio and its not required to be enabled during the
boot and audio driver can enable/disable it.

same with blink 32khz which is used for WIFI. WIFI driver can
enable/disable during power up/down sequence and technically as per
design we dont need to have it always on right from the boot.

So can remove out clocks init states from driver once thierry also agree
on this.

>>>>
>>>>>>>>> +        if (!IS_ERR(parent)) {
>>>>>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>>>>>> +            if (ret < 0) {
>>>>>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>>>>>> +                       __clk_get_name(parent));
>>>>>>>>> +                WARN_ON(1);
>>>>>>>>> +            }
>>>>>>>>> +        }
>>>>>>>>> +
>>>>>>>>> +        if (data->init_state) {
>>>>>>>>> +            if (clk_prepare_enable(clk)) {
>>>>>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>>>>>> +                       __clk_get_name(clk));
>>>>>>>>> +                WARN_ON(1);
>>>>>> Alternatively you could write it like this:
>>>>>>
>>>>>>      err = clk_prepare_enable(clk);
>>>>>>
>>>>>>      WARN_ON(err, "failed to enable %s: %d\n",
>>>>>>          __clk_get_name(clk), err);
>>>>>>
>>>>>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>>>>>> handling
>>>>>>>> in order to catch all possible errors:
>>>>>>>>
>>>>>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>>>>>> tegra_pmc *pmc,
>>>>>>>>            return;
>>>>>>>>
>>>>>>>>     free_clks:
>>>>>>>> +       WARN_ON(1);
>>>>>>>>            kfree(clk_data->clks);
>>>>>>>>     free_clkdata:
>>>>>>>>            kfree(clk_data);
>>>>>>> Reason I had WARN_ON right during clk_set_parent failure is to
>>>>>>> have the
>>>>>>> loop continue for subsequence pmc clocks registration instead of
>>>>>>> terminating all pmc clocks registration.
>>>>>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the
>>>>>> least
>>>>>> (IMO).
>>>>> Hi Dmitry, Just want to be clear on the above comment. Are you
>>>>> suggesting to add additional WARN_ON at the end?
>>>> Yes, it was my suggestion.
>>>>
>>>>> Thought WARN_ON right during corresponding clock failure with warn
>>>>> message showing clock names will be clear and also other clocks still
>>>>> should be registered.
>>>>>
>>>>> To add additional WARN_ON at the end need to track status of each
>>>>> clock
>>>>> and use that to as warn condition.
>>>> You could add a warning/error message to every point of failure.
>>>>
>>>> Primarily, it is important not to miss a error. Secondarily, it is
>>>> important to make diagnostic message meaningful.
>>>>
>>>> Realistically, I doubt that this chunk of code will ever fail once
>>>> it is
>>>> known to work well. So it will be nice to have a more detailed
>>>> diagnostics (just in a case), but it shouldn't be a must.
>>>
>>> OK, Will add additional WARN message "failed registering PMC clocks"
>>> at the end.
>>>

2019-12-03 16:58:28

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>
> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>
>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>
>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>
>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>> ---
>>>>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>     #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;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +#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;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +#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_index;
>>>>>>>>>> +    int init_state;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +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",
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>> +    {
>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>> +    },
>>>>>>>>>> +    {
>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>> +    },
>>>>>>>>>> +    {
>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>> +    },
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>>     struct tegra_powergate {
>>>>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>>>>         struct tegra_pmc *pmc;
>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>> +
>>>>>>>>>> +    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);
>>>>>>>>>> +
>>>>>>>>>> +    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,
>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>> +{
>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>> +
>>>>>>>>>> +    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;
>>>>>>>>>> +
>>>>>>>>>> +    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;
>>>>>>>>>> +
>>>>>>>>>> +    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);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>> +                u32 shift)
>>>>>>>>>> +{
>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>> +
>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>> +    if (!gate)
>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>> +
>>>>>>>>>> +    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;
>>>>>>>>>> +
>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>> +{
>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>> +    int i, ret;
>>>>>>>>>> +
>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>> +
>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>> +        return;
>>>>>>>>>> +
>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>> +        return;
>>>>>>>>>> +
>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>> +
>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>> +
>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>> +
>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>> +                            data->parents,
>>>>>>>>>> + data->num_parents,
>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>>>>> +                            data->mux_shift, 3);
>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>> +            goto free_clks;
>>>>>>>>>> +
>>>>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>> +
>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>> +                          data->mux_name,
>>>>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>>>>> +                          data->gate_shift);
>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>> +            goto free_clks;
>>>>>>>>>> +
>>>>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>>>>> +
>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>>>>> +        }
>>>>>>>>>> +
>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>> data->gate_name);
>>>>>>>>>> +
>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>> doc.
>>>>>
>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>
>>>>>
>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>> true).
>>>>
>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>> based on assigned parents and clock rates.
>>>>
>>>> This need device tree to specify assigned clock parent properties.
>>>> Will update device tree and remove init parent from the driver.
>>>>
>>> assigned-clock properties should be set in consumer node of these
>>> clocks and currently these clocks are not used yet.
>>>
>>> So will just remove init parent from driver and when these clocks
>>> are used device tree can be updated in corresponding consumer node
>>> with these properties.
>>>
>> How about default ON/OFF init state for the clocks? I see
>> assigned-clock properties for parent and rate only.
>>
>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>> default enabled with extern1 parent for T30 thru T210 platforms.
>>
>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>> clock registration?
>>
> clk_out_1 is for audio and its not required to be enabled during the
> boot and audio driver can enable/disable it.
>
> same with blink 32khz which is used for WIFI. WIFI driver can
> enable/disable during power up/down sequence and technically as per
> design we dont need to have it always on right from the boot.
>
> So can remove out clocks init states from driver once thierry also
> agree on this.
>
Hi Dmitry,

Looking at audio driver, it doesn't take care of mclk which is from
clk_out_1 and expects mclk to be always on.

So probably we should have this init state enables in pmc driver for
32Khz and clk_out's to not break existing functionality.

Regarding using assigned-clock properties for init parent and removing
init parent from driver, it also needs consumer node in device tree to
be updated to specify assigned-clock properties for default/init parent.

This breaks device tree ABI as prior Tegra210 supports audio driver.


>>>>>>>>>> +        if (!IS_ERR(parent)) {
>>>>>>>>>> +            ret = clk_set_parent(clkmux, parent);
>>>>>>>>>> +            if (ret < 0) {
>>>>>>>>>> +                pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>> +                       __func__, __clk_get_name(clkmux),
>>>>>>>>>> +                       __clk_get_name(parent));
>>>>>>>>>> +                WARN_ON(1);
>>>>>>>>>> +            }
>>>>>>>>>> +        }
>>>>>>>>>> +
>>>>>>>>>> +        if (data->init_state) {
>>>>>>>>>> +            if (clk_prepare_enable(clk)) {
>>>>>>>>>> +                pr_err("failed to enable %s\n", __func__,
>>>>>>>>>> +                       __clk_get_name(clk));
>>>>>>>>>> +                WARN_ON(1);
>>>>>>> Alternatively you could write it like this:
>>>>>>>
>>>>>>>      err = clk_prepare_enable(clk);
>>>>>>>
>>>>>>>      WARN_ON(err, "failed to enable %s: %d\n",
>>>>>>>          __clk_get_name(clk), err);
>>>>>>>
>>>>>>>>> Should be a bit better to move the WARN_ON to the end of errors
>>>>>>>>> handling
>>>>>>>>> in order to catch all possible errors:
>>>>>>>>>
>>>>>>>>> @@ -2510,6 +2510,7 @@ static void tegra_pmc_clock_register(struct
>>>>>>>>> tegra_pmc *pmc,
>>>>>>>>>            return;
>>>>>>>>>
>>>>>>>>>     free_clks:
>>>>>>>>> +       WARN_ON(1);
>>>>>>>>>            kfree(clk_data->clks);
>>>>>>>>>     free_clkdata:
>>>>>>>>>            kfree(clk_data);
>>>>>>>> Reason I had WARN_ON right during clk_set_parent failure is to
>>>>>>>> have the
>>>>>>>> loop continue for subsequence pmc clocks registration instead of
>>>>>>>> terminating all pmc clocks registration.
>>>>>>> Ah, okay. Nevertheless this WARN_ON in the end shouldn't be the
>>>>>>> least
>>>>>>> (IMO).
>>>>>> Hi Dmitry, Just want to be clear on the above comment. Are you
>>>>>> suggesting to add additional WARN_ON at the end?
>>>>> Yes, it was my suggestion.
>>>>>
>>>>>> Thought WARN_ON right during corresponding clock failure with warn
>>>>>> message showing clock names will be clear and also other clocks
>>>>>> still
>>>>>> should be registered.
>>>>>>
>>>>>> To add additional WARN_ON at the end need to track status of each
>>>>>> clock
>>>>>> and use that to as warn condition.
>>>>> You could add a warning/error message to every point of failure.
>>>>>
>>>>> Primarily, it is important not to miss a error. Secondarily, it is
>>>>> important to make diagnostic message meaningful.
>>>>>
>>>>> Realistically, I doubt that this chunk of code will ever fail once
>>>>> it is
>>>>> known to work well. So it will be nice to have a more detailed
>>>>> diagnostics (just in a case), but it shouldn't be a must.
>>>>
>>>> OK, Will add additional WARN message "failed registering PMC
>>>> clocks" at the end.
>>>>

2019-12-04 13:40:06

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

03.12.2019 19:45, Sowjanya Komatineni пишет:
>
> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>
>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>
>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>
>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>
>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>> ---
>>>>>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>     #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;
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +#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;
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +#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_index;
>>>>>>>>>>> +    int init_state;
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +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",
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>> +    {
>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>> +    },
>>>>>>>>>>> +    {
>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>> +    },
>>>>>>>>>>> +    {
>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>> +    },
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>>     struct tegra_powergate {
>>>>>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>>>>>         struct tegra_pmc *pmc;
>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>> +
>>>>>>>>>>> +    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);
>>>>>>>>>>> +
>>>>>>>>>>> +    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,
>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>> +
>>>>>>>>>>> +    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;
>>>>>>>>>>> +
>>>>>>>>>>> +    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;
>>>>>>>>>>> +
>>>>>>>>>>> +    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);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>> +
>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>> +
>>>>>>>>>>> +    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;
>>>>>>>>>>> +
>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>> +{
>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>> +
>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>> +
>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>> +        return;
>>>>>>>>>>> +
>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>> +        return;
>>>>>>>>>>> +
>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>> +
>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>> +
>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>> +
>>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>> +                            data->parents,
>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>>>>>> +                            data->mux_shift, 3);
>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>> +
>>>>>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>> +
>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>> +                          data->mux_name,
>>>>>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>>>>>> +                          data->gate_shift);
>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>> +
>>>>>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>> +
>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>>>>>> +        }
>>>>>>>>>>> +
>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>> data->gate_name);
>>>>>>>>>>> +
>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>>> doc.
>>>>>>
>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>
>>>>>>
>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>> true).
>>>>>
>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>> based on assigned parents and clock rates.
>>>>>
>>>>> This need device tree to specify assigned clock parent properties.
>>>>> Will update device tree and remove init parent from the driver.
>>>>>
>>>> assigned-clock properties should be set in consumer node of these
>>>> clocks and currently these clocks are not used yet.
>>>>
>>>> So will just remove init parent from driver and when these clocks
>>>> are used device tree can be updated in corresponding consumer node
>>>> with these properties.
>>>>
>>> How about default ON/OFF init state for the clocks? I see
>>> assigned-clock properties for parent and rate only.
>>>
>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>
>>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>>> clock registration?
>>>
>> clk_out_1 is for audio and its not required to be enabled during the
>> boot and audio driver can enable/disable it.
>>
>> same with blink 32khz which is used for WIFI. WIFI driver can
>> enable/disable during power up/down sequence and technically as per
>> design we dont need to have it always on right from the boot.
>>
>> So can remove out clocks init states from driver once thierry also
>> agree on this.
>>
> Hi Dmitry,
>
> Looking at audio driver, it doesn't take care of mclk which is from
> clk_out_1 and expects mclk to be always on.
>
> So probably we should have this init state enables in pmc driver for
> 32Khz and clk_out's to not break existing functionality.

Hello Sowjanya,

IIUC, it's a bug in the device-trees and sound's MCLK actually should be
set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the case,
then the device-trees need to be fixed.

> Regarding using assigned-clock properties for init parent and removing
> init parent from driver, it also needs consumer node in device tree to
> be updated to specify assigned-clock properties for default/init parent.
>
> This breaks device tree ABI as prior Tegra210 supports audio driver.

So it's the sound node which should have had the assigned clocks in
device-tree in order to define route for the audio MCLK clock from CaR
to PMC.

Given that the audio clocks configuration is the same for all of the
currently supported boards, I think it will be better to remove the
entire audio clocks initialization from the clk drivers and move it all
to the audio driver. It could be something like this:

int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev)
{
...

if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) &&
tegra_get_chip_id() > TEGRA20) {
clk_extern1 = clk_get(dev, "extern1");
...
clk_set_parent(clk_extern1, clk_pll_a_out0);
...
clk_put(data->clk_cdev1);

data->clk_cdev1 = clk_out_1;
}
...

So now the old device-trees will cointinue to work and new could have
the assigned-clocks and set MCLK to CLK_OUT_1.

[snip]

2019-12-04 16:03:16

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>> ---
>>>>>>>>>>>>     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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>     #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;
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>> +#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;
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>> +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",
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>> +    {
>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>> +    },
>>>>>>>>>>>> +    {
>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>> +    },
>>>>>>>>>>>> +    {
>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>> +    },
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>>     struct tegra_powergate {
>>>>>>>>>>>>         struct generic_pm_domain genpd;
>>>>>>>>>>>>         struct tegra_pmc *pmc;
>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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,
>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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);
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>> +        return;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>> +        return;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>> +                            data->parents,
>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>> +                            PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>> +                            data->mux_shift, 3);
>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>> +                          data->mux_name,
>>>>>>>>>>>> +                          CLK_SET_RATE_PARENT,
>>>>>>>>>>>> +                          PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>> +                          data->gate_shift);
>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>> +
>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>> +                   __clk_get_name(clkmux));
>>>>>>>>>>>> +        }
>>>>>>>>>>>> +
>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>> +
>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>>>> doc.
>>>>>>>
>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>
>>>>>>>
>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>> true).
>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>> based on assigned parents and clock rates.
>>>>>>
>>>>>> This need device tree to specify assigned clock parent properties.
>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>
>>>>> assigned-clock properties should be set in consumer node of these
>>>>> clocks and currently these clocks are not used yet.
>>>>>
>>>>> So will just remove init parent from driver and when these clocks
>>>>> are used device tree can be updated in corresponding consumer node
>>>>> with these properties.
>>>>>
>>>> How about default ON/OFF init state for the clocks? I see
>>>> assigned-clock properties for parent and rate only.
>>>>
>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>
>>>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>>>> clock registration?
>>>>
>>> clk_out_1 is for audio and its not required to be enabled during the
>>> boot and audio driver can enable/disable it.
>>>
>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>> enable/disable during power up/down sequence and technically as per
>>> design we dont need to have it always on right from the boot.
>>>
>>> So can remove out clocks init states from driver once thierry also
>>> agree on this.
>>>
>> Hi Dmitry,
>>
>> Looking at audio driver, it doesn't take care of mclk which is from
>> clk_out_1 and expects mclk to be always on.
>>
>> So probably we should have this init state enables in pmc driver for
>> 32Khz and clk_out's to not break existing functionality.
> Hello Sowjanya,
>
> IIUC, it's a bug in the device-trees and sound's MCLK actually should be
> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the case,
> then the device-trees need to be fixed.

OK, will have this in v3

>> Regarding using assigned-clock properties for init parent and removing
>> init parent from driver, it also needs consumer node in device tree to
>> be updated to specify assigned-clock properties for default/init parent.
>>
>> This breaks device tree ABI as prior Tegra210 supports audio driver.
> So it's the sound node which should have had the assigned clocks in
> device-tree in order to define route for the audio MCLK clock from CaR
> to PMC.
>
> Given that the audio clocks configuration is the same for all of the
> currently supported boards, I think it will be better to remove the
> entire audio clocks initialization from the clk drivers and move it all
> to the audio driver. It could be something like this:
>
> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
> struct device *dev)
> {
> ...
>
> if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) &&
> tegra_get_chip_id() > TEGRA20) {
> clk_extern1 = clk_get(dev, "extern1");
> ...
> clk_set_parent(clk_extern1, clk_pll_a_out0);
> ...
> clk_put(data->clk_cdev1);
>
> data->clk_cdev1 = clk_out_1;
> }
> ...
>
> So now the old device-trees will cointinue to work and new could have
> the assigned-clocks and set MCLK to CLK_OUT_1.
>
> [snip]

Will update clk-tegra, pmc and audio driver to move init state
configuration for extern1 and clk_out_1 into audio driver and will use
assigned clock properties in device tree.

2019-12-04 20:17:05

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/4/19 8:02 AM, Sowjanya Komatineni wrote:
>
> On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
>> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>>      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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>>      #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;
>>>>>>>>>>>>> +};
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +#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;
>>>>>>>>>>>>> +};
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>>> +};
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +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",
>>>>>>>>>>>>> +};
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>>> +    {
>>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>>> +    },
>>>>>>>>>>>>> +    {
>>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>> +    },
>>>>>>>>>>>>> +    {
>>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>> +    },
>>>>>>>>>>>>> +};
>>>>>>>>>>>>> +
>>>>>>>>>>>>>      struct tegra_powergate {
>>>>>>>>>>>>>          struct generic_pm_domain genpd;
>>>>>>>>>>>>>          struct tegra_pmc *pmc;
>>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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,
>>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>>> + data->parents,
>>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>> + data->mux_shift, 3);
>>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> + clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>>> + data->mux_name,
>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>> + data->gate_shift);
>>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> + clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>>> + __clk_get_name(clkmux));
>>>>>>>>>>>>> +        }
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>>>>> doc.
>>>>>>>>
>>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>>> true).
>>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>>> based on assigned parents and clock rates.
>>>>>>>
>>>>>>> This need device tree to specify assigned clock parent properties.
>>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>>
>>>>>> assigned-clock properties should be set in consumer node of these
>>>>>> clocks and currently these clocks are not used yet.
>>>>>>
>>>>>> So will just remove init parent from driver and when these clocks
>>>>>> are used device tree can be updated in corresponding consumer node
>>>>>> with these properties.
>>>>>>
>>>>> How about default ON/OFF init state for the clocks? I see
>>>>> assigned-clock properties for parent and rate only.
>>>>>
>>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>>
>>>>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>>>>> clock registration?
>>>>>
>>>> clk_out_1 is for audio and its not required to be enabled during the
>>>> boot and audio driver can enable/disable it.
>>>>
>>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>>> enable/disable during power up/down sequence and technically as per
>>>> design we dont need to have it always on right from the boot.
>>>>
>>>> So can remove out clocks init states from driver once thierry also
>>>> agree on this.
>>>>
>>> Hi Dmitry,
>>>
>>> Looking at audio driver, it doesn't take care of mclk which is from
>>> clk_out_1 and expects mclk to be always on.
>>>
>>> So probably we should have this init state enables in pmc driver for
>>> 32Khz and clk_out's to not break existing functionality.
>> Hello Sowjanya,
>>
>> IIUC, it's a bug in the device-trees and sound's MCLK actually should be
>> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the case,
>> then the device-trees need to be fixed.
>
> OK, will have this in v3

Actually if we change mclk to use clk_out_1, then it will break with old
device trees.

Currently clk_out_1 parent is set to extern1 as init state by the clock
driver and device tree for sound node is using extern1 as mclk works and
extern1 itself has enable and disable. But yes implementation wise, its
incorrect as mclk should really be clk_out_1.

Now, with moving clk_out_1 to tegra PMC, pmc is the clock provider and
if we now change the device tree to use CLK_OUT_1 as mclk then it will
break old device tree.

>
>>> Regarding using assigned-clock properties for init parent and removing
>>> init parent from driver, it also needs consumer node in device tree to
>>> be updated to specify assigned-clock properties for default/init
>>> parent.
>>>
>>> This breaks device tree ABI as prior Tegra210 supports audio driver.
>> So it's the sound node which should have had the assigned clocks in
>> device-tree in order to define route for the audio MCLK clock from CaR
>> to PMC.
>>
>> Given that the audio clocks configuration is the same for all of the
>> currently supported boards, I think it will be better to remove the
>> entire audio clocks initialization from the clk drivers and move it all
>> to the audio driver. It could be something like this:
>>
>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>               struct device *dev)
>> {
>>     ...
>>
>>     if (!of_find_property(dev->of_node, "assigned-clock-parents",
>> NULL) &&
>>         tegra_get_chip_id() > TEGRA20) {
>>         clk_extern1 = clk_get(dev, "extern1");
>>         ...
>>         clk_set_parent(clk_extern1, clk_pll_a_out0);
>>         ...
>>         clk_put(data->clk_cdev1);
>>
>>         data->clk_cdev1 = clk_out_1;
>>     }
>>     ...
>>
>> So now the old device-trees will cointinue to work and new could have
>> the assigned-clocks and set MCLK to CLK_OUT_1.
>>
>> [snip]
>
> Will update clk-tegra, pmc and audio driver to move init state
> configuration for extern1 and clk_out_1 into audio driver and will use
> assigned clock properties in device tree.
>

2019-12-04 20:30:18

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

04.12.2019 23:08, Sowjanya Komatineni пишет:
>
> On 12/4/19 8:02 AM, Sowjanya Komatineni wrote:
>>
>> On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
>>> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>>>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>      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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>>>      #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;
>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +#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;
>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +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",
>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>      struct tegra_powergate {
>>>>>>>>>>>>>>          struct generic_pm_domain genpd;
>>>>>>>>>>>>>>          struct tegra_pmc *pmc;
>>>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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,
>>>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>>>> + data->parents,
>>>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>> + data->mux_shift, 3);
>>>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> + clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>>>> + data->mux_name,
>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>> + data->gate_shift);
>>>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> + clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>>>> + __clk_get_name(clkmux));
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>>>>>> doc.
>>>>>>>>>
>>>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>>>> true).
>>>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>>>> based on assigned parents and clock rates.
>>>>>>>>
>>>>>>>> This need device tree to specify assigned clock parent properties.
>>>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>>>
>>>>>>> assigned-clock properties should be set in consumer node of these
>>>>>>> clocks and currently these clocks are not used yet.
>>>>>>>
>>>>>>> So will just remove init parent from driver and when these clocks
>>>>>>> are used device tree can be updated in corresponding consumer node
>>>>>>> with these properties.
>>>>>>>
>>>>>> How about default ON/OFF init state for the clocks? I see
>>>>>> assigned-clock properties for parent and rate only.
>>>>>>
>>>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>>>
>>>>>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>>>>>> clock registration?
>>>>>>
>>>>> clk_out_1 is for audio and its not required to be enabled during the
>>>>> boot and audio driver can enable/disable it.
>>>>>
>>>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>>>> enable/disable during power up/down sequence and technically as per
>>>>> design we dont need to have it always on right from the boot.
>>>>>
>>>>> So can remove out clocks init states from driver once thierry also
>>>>> agree on this.
>>>>>
>>>> Hi Dmitry,
>>>>
>>>> Looking at audio driver, it doesn't take care of mclk which is from
>>>> clk_out_1 and expects mclk to be always on.
>>>>
>>>> So probably we should have this init state enables in pmc driver for
>>>> 32Khz and clk_out's to not break existing functionality.
>>> Hello Sowjanya,
>>>
>>> IIUC, it's a bug in the device-trees and sound's MCLK actually should be
>>> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the case,
>>> then the device-trees need to be fixed.
>>
>> OK, will have this in v3
>
> Actually if we change mclk to use clk_out_1, then it will break with old
> device trees.
>
> Currently clk_out_1 parent is set to extern1 as init state by the clock
> driver and device tree for sound node is using extern1 as mclk works and
> extern1 itself has enable and disable. But yes implementation wise, its
> incorrect as mclk should really be clk_out_1.
>
> Now, with moving clk_out_1 to tegra PMC, pmc is the clock provider and
> if we now change the device tree to use CLK_OUT_1 as mclk then it will
> break old device tree.

There souldn't be any problem with enabling CLK_OUT_1 by the audio
driver itself (in the code) if an old device-tree is detected.

clk_out_1 = clk_get(dev, "clk_out_1");
clk_set_parent(clk_out_1, clk_extern1);
...
data->clk_cdev1 = clk_out_1;

Maybe it will be even better to compare clocks for detection of an older DT:

if (tegra_get_chip_id() > TEGRA20 &&
clk_is_match(data->clk_cdev1, clk_extern1)) {
...

>>>> Regarding using assigned-clock properties for init parent and removing
>>>> init parent from driver, it also needs consumer node in device tree to
>>>> be updated to specify assigned-clock properties for default/init
>>>> parent.
>>>>
>>>> This breaks device tree ABI as prior Tegra210 supports audio driver.
>>> So it's the sound node which should have had the assigned clocks in
>>> device-tree in order to define route for the audio MCLK clock from CaR
>>> to PMC.
>>>
>>> Given that the audio clocks configuration is the same for all of the
>>> currently supported boards, I think it will be better to remove the
>>> entire audio clocks initialization from the clk drivers and move it all
>>> to the audio driver. It could be something like this:
>>>
>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>               struct device *dev)
>>> {
>>>     ...
>>>
>>>     if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>> NULL) &&
>>>         tegra_get_chip_id() > TEGRA20) {
>>>         clk_extern1 = clk_get(dev, "extern1");
>>>         ...
>>>         clk_set_parent(clk_extern1, clk_pll_a_out0);
>>>         ...
>>>         clk_put(data->clk_cdev1);
>>>
>>>         data->clk_cdev1 = clk_out_1;
>>>     }
>>>     ...
>>>
>>> So now the old device-trees will cointinue to work and new could have
>>> the assigned-clocks and set MCLK to CLK_OUT_1.
>>>
>>> [snip]
>>
>> Will update clk-tegra, pmc and audio driver to move init state
>> configuration for extern1 and clk_out_1 into audio driver and will use
>> assigned clock properties in device tree.
>>

2019-12-04 20:34:25

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/4/19 12:28 PM, Dmitry Osipenko wrote:
> 04.12.2019 23:08, Sowjanya Komatineni пишет:
>> On 12/4/19 8:02 AM, Sowjanya Komatineni wrote:
>>> On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
>>>> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>>>>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>>>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>      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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>>>>      #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;
>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +#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;
>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +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",
>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>      struct tegra_powergate {
>>>>>>>>>>>>>>>          struct generic_pm_domain genpd;
>>>>>>>>>>>>>>>          struct tegra_pmc *pmc;
>>>>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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,
>>>>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
>>>>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        clkmux = tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>>>>> + data->parents,
>>>>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>> + data->mux_shift, 3);
>>>>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> + clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>>>>> + data->mux_name,
>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>> + data->gate_shift);
>>>>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> + clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>>>>> + __clk_get_name(clkmux));
>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>>>>> Couldn't the default parent be defined using "assigned clock" in a
>>>>>>>>>> device-tree? Please see "Assigned clock parents and rates" in the
>>>>>>>>>> doc.
>>>>>>>>>>
>>>>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>>>>> true).
>>>>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>>>>> based on assigned parents and clock rates.
>>>>>>>>>
>>>>>>>>> This need device tree to specify assigned clock parent properties.
>>>>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>>>>
>>>>>>>> assigned-clock properties should be set in consumer node of these
>>>>>>>> clocks and currently these clocks are not used yet.
>>>>>>>>
>>>>>>>> So will just remove init parent from driver and when these clocks
>>>>>>>> are used device tree can be updated in corresponding consumer node
>>>>>>>> with these properties.
>>>>>>>>
>>>>>>> How about default ON/OFF init state for the clocks? I see
>>>>>>> assigned-clock properties for parent and rate only.
>>>>>>>
>>>>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>>>>
>>>>>>> Peter/Thierry, What was the reason we enable clk_out_1 right from the
>>>>>>> clock registration?
>>>>>>>
>>>>>> clk_out_1 is for audio and its not required to be enabled during the
>>>>>> boot and audio driver can enable/disable it.
>>>>>>
>>>>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>>>>> enable/disable during power up/down sequence and technically as per
>>>>>> design we dont need to have it always on right from the boot.
>>>>>>
>>>>>> So can remove out clocks init states from driver once thierry also
>>>>>> agree on this.
>>>>>>
>>>>> Hi Dmitry,
>>>>>
>>>>> Looking at audio driver, it doesn't take care of mclk which is from
>>>>> clk_out_1 and expects mclk to be always on.
>>>>>
>>>>> So probably we should have this init state enables in pmc driver for
>>>>> 32Khz and clk_out's to not break existing functionality.
>>>> Hello Sowjanya,
>>>>
>>>> IIUC, it's a bug in the device-trees and sound's MCLK actually should be
>>>> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the case,
>>>> then the device-trees need to be fixed.
>>> OK, will have this in v3
>> Actually if we change mclk to use clk_out_1, then it will break with old
>> device trees.
>>
>> Currently clk_out_1 parent is set to extern1 as init state by the clock
>> driver and device tree for sound node is using extern1 as mclk works and
>> extern1 itself has enable and disable. But yes implementation wise, its
>> incorrect as mclk should really be clk_out_1.
>>
>> Now, with moving clk_out_1 to tegra PMC, pmc is the clock provider and
>> if we now change the device tree to use CLK_OUT_1 as mclk then it will
>> break old device tree.
> There souldn't be any problem with enabling CLK_OUT_1 by the audio
> driver itself (in the code) if an old device-tree is detected.
>
> clk_out_1 = clk_get(dev, "clk_out_1");
> clk_set_parent(clk_out_1, clk_extern1);
> ...
> data->clk_cdev1 = clk_out_1;
>
> Maybe it will be even better to compare clocks for detection of an older DT:
>
> if (tegra_get_chip_id() > TEGRA20 &&
> clk_is_match(data->clk_cdev1, clk_extern1)) {
> ...
>
we can do parent init in audio driver which is not an issue.

But I was saying we can keep extern1 as mclk in device tree instead of
changing to clk_out_1 from pmc as it will break old device tree becasue
we moved clk_out_1 from clock driver to pmc driver.

once we do init parent in audio driver, we still can use extern1 as mclk
in device tree.

mclk -> clk_out_1 -> extern1 -> plla_out0 -> plla

with init of clk_out_1 parent to extern1, mclk can use extern1 handle
for rate/enable/disable clk operations

>>>>> Regarding using assigned-clock properties for init parent and removing
>>>>> init parent from driver, it also needs consumer node in device tree to
>>>>> be updated to specify assigned-clock properties for default/init
>>>>> parent.
>>>>>
>>>>> This breaks device tree ABI as prior Tegra210 supports audio driver.
>>>> So it's the sound node which should have had the assigned clocks in
>>>> device-tree in order to define route for the audio MCLK clock from CaR
>>>> to PMC.
>>>>
>>>> Given that the audio clocks configuration is the same for all of the
>>>> currently supported boards, I think it will be better to remove the
>>>> entire audio clocks initialization from the clk drivers and move it all
>>>> to the audio driver. It could be something like this:
>>>>
>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>>               struct device *dev)
>>>> {
>>>>     ...
>>>>
>>>>     if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>> NULL) &&
>>>>         tegra_get_chip_id() > TEGRA20) {
>>>>         clk_extern1 = clk_get(dev, "extern1");
>>>>         ...
>>>>         clk_set_parent(clk_extern1, clk_pll_a_out0);
>>>>         ...
>>>>         clk_put(data->clk_cdev1);
>>>>
>>>>         data->clk_cdev1 = clk_out_1;
>>>>     }
>>>>     ...
>>>>
>>>> So now the old device-trees will cointinue to work and new could have
>>>> the assigned-clocks and set MCLK to CLK_OUT_1.
>>>>
>>>> [snip]
>>> Will update clk-tegra, pmc and audio driver to move init state
>>> configuration for extern1 and clk_out_1 into audio driver and will use
>>> assigned clock properties in device tree.
>>>

2019-12-04 21:13:44

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver

04.12.2019 23:33, Sowjanya Komatineni пишет:
>
> On 12/4/19 12:28 PM, Dmitry Osipenko wrote:
>> 04.12.2019 23:08, Sowjanya Komatineni пишет:
>>> On 12/4/19 8:02 AM, Sowjanya Komatineni wrote:
>>>> On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
>>>>> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>>>>>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>>>>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>       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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>>>>>       #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;
>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +#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;
>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +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",
>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>       struct tegra_powergate {
>>>>>>>>>>>>>>>>           struct generic_pm_domain genpd;
>>>>>>>>>>>>>>>>           struct tegra_pmc *pmc;
>>>>>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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,
>>>>>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc
>>>>>>>>>>>>>>>> *pmc,
>>>>>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        clkmux =
>>>>>>>>>>>>>>>> tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>>>>>> + data->parents,
>>>>>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>>> + data->mux_shift, 3);
>>>>>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> + clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>>>>>> + data->mux_name,
>>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>>> + data->gate_shift);
>>>>>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> + clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>>>>>> + __clk_get_name(clkmux));
>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>>>>>> Couldn't the default parent be defined using "assigned clock"
>>>>>>>>>>> in a
>>>>>>>>>>> device-tree? Please see "Assigned clock parents and rates" in
>>>>>>>>>>> the
>>>>>>>>>>> doc.
>>>>>>>>>>>
>>>>>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>>>>>> true).
>>>>>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>>>>>> based on assigned parents and clock rates.
>>>>>>>>>>
>>>>>>>>>> This need device tree to specify assigned clock parent
>>>>>>>>>> properties.
>>>>>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>>>>>
>>>>>>>>> assigned-clock properties should be set in consumer node of these
>>>>>>>>> clocks and currently these clocks are not used yet.
>>>>>>>>>
>>>>>>>>> So will just remove init parent from driver and when these clocks
>>>>>>>>> are used device tree can be updated in corresponding consumer node
>>>>>>>>> with these properties.
>>>>>>>>>
>>>>>>>> How about default ON/OFF init state for the clocks? I see
>>>>>>>> assigned-clock properties for parent and rate only.
>>>>>>>>
>>>>>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>>>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>>>>>
>>>>>>>> Peter/Thierry, What was the reason we enable clk_out_1 right
>>>>>>>> from the
>>>>>>>> clock registration?
>>>>>>>>
>>>>>>> clk_out_1 is for audio and its not required to be enabled during the
>>>>>>> boot and audio driver can enable/disable it.
>>>>>>>
>>>>>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>>>>>> enable/disable during power up/down sequence and technically as per
>>>>>>> design we dont need to have it always on right from the boot.
>>>>>>>
>>>>>>> So can remove out clocks init states from driver once thierry also
>>>>>>> agree on this.
>>>>>>>
>>>>>> Hi Dmitry,
>>>>>>
>>>>>> Looking at audio driver, it doesn't take care of mclk which is from
>>>>>> clk_out_1 and expects mclk to be always on.
>>>>>>
>>>>>> So probably we should have this init state enables in pmc driver for
>>>>>> 32Khz and clk_out's to not break existing functionality.
>>>>> Hello Sowjanya,
>>>>>
>>>>> IIUC, it's a bug in the device-trees and sound's MCLK actually
>>>>> should be
>>>>> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the
>>>>> case,
>>>>> then the device-trees need to be fixed.
>>>> OK, will have this in v3
>>> Actually if we change mclk to use clk_out_1, then it will break with old
>>> device trees.
>>>
>>> Currently clk_out_1 parent is set to extern1 as init state by the clock
>>> driver and device tree for sound node is using extern1 as mclk works and
>>> extern1 itself has enable and disable. But yes implementation wise, its
>>> incorrect as mclk should really be clk_out_1.
>>>
>>> Now, with moving clk_out_1 to tegra PMC, pmc is the clock provider and
>>> if we now change the device tree to use CLK_OUT_1 as mclk then it will
>>> break old device tree.
>> There souldn't be any problem with enabling CLK_OUT_1 by the audio
>> driver itself (in the code) if an old device-tree is detected.
>>
>>     clk_out_1 = clk_get(dev, "clk_out_1");
>>     clk_set_parent(clk_out_1, clk_extern1);
>>     ...
>>     data->clk_cdev1 = clk_out_1;
>>
>> Maybe it will be even better to compare clocks for detection of an
>> older DT:
>>
>>     if (tegra_get_chip_id() > TEGRA20 &&
>>         clk_is_match(data->clk_cdev1, clk_extern1)) {
>>     ...
>>
> we can do parent init in audio driver which is not an issue.
>
> But I was saying we can keep extern1 as mclk in device tree instead of
> changing to clk_out_1 from pmc as it will break old device tree becasue
> we moved clk_out_1 from clock driver to pmc driver.
>
> once we do init parent in audio driver, we still can use extern1 as mclk
> in device tree.
>
> mclk -> clk_out_1 -> extern1 -> plla_out0 -> plla
>
> with init of clk_out_1 parent to extern1, mclk can use extern1 handle
> for rate/enable/disable clk operations

It should be okay to fallback to extern1 for a backwards compatibility
of the new DT with older kernels.

@@ -192,8 +192,14 @@ int tegra_asoc_utils_init(struct
tegra_asoc_utils_data *data,
data->clk_cdev1 = clk_get(dev, "mclk");
if (IS_ERR(data->clk_cdev1)) {
dev_err(data->dev, "Can't retrieve clk cdev1\n");
- ret = PTR_ERR(data->clk_cdev1);
- goto err_put_pll_a_out0;
+
+ data->clk_cdev1 = clk_get_sys("clk_out_1", "extern1");
+ if (IS_ERR(data->clk_cdev1)) {
+ ret = PTR_ERR(data->clk_cdev1);
+ goto err_put_pll_a_out0;
+ }
+
+ dev_err(data->dev, "Falling back to extern1\n");
}

ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);

This change should be a standalone patch and it should be requsted for
backporting into stable kernels.

>>>>>> Regarding using assigned-clock properties for init parent and
>>>>>> removing
>>>>>> init parent from driver, it also needs consumer node in device
>>>>>> tree to
>>>>>> be updated to specify assigned-clock properties for default/init
>>>>>> parent.
>>>>>>
>>>>>> This breaks device tree ABI as prior Tegra210 supports audio driver.
>>>>> So it's the sound node which should have had the assigned clocks in
>>>>> device-tree in order to define route for the audio MCLK clock from CaR
>>>>> to PMC.
>>>>>
>>>>> Given that the audio clocks configuration is the same for all of the
>>>>> currently supported boards, I think it will be better to remove the
>>>>> entire audio clocks initialization from the clk drivers and move it
>>>>> all
>>>>> to the audio driver. It could be something like this:
>>>>>
>>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>>>                struct device *dev)
>>>>> {
>>>>>      ...
>>>>>
>>>>>      if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>>> NULL) &&
>>>>>          tegra_get_chip_id() > TEGRA20) {
>>>>>          clk_extern1 = clk_get(dev, "extern1");
>>>>>          ...
>>>>>          clk_set_parent(clk_extern1, clk_pll_a_out0);
>>>>>          ...
>>>>>          clk_put(data->clk_cdev1);
>>>>>
>>>>>          data->clk_cdev1 = clk_out_1;
>>>>>      }
>>>>>      ...
>>>>>
>>>>> So now the old device-trees will cointinue to work and new could have
>>>>> the assigned-clocks and set MCLK to CLK_OUT_1.
>>>>>
>>>>> [snip]
>>>> Will update clk-tegra, pmc and audio driver to move init state
>>>> configuration for extern1 and clk_out_1 into audio driver and will use
>>>> assigned clock properties in device tree.
>>>>

2019-12-04 21:28:35

by Sowjanya Komatineni

[permalink] [raw]
Subject: Re: [PATCH v2 02/11] soc: tegra: Add Tegra PMC clock registrations into PMC driver


On 12/4/19 1:12 PM, Dmitry Osipenko wrote:
> 04.12.2019 23:33, Sowjanya Komatineni пишет:
>> On 12/4/19 12:28 PM, Dmitry Osipenko wrote:
>>> 04.12.2019 23:08, Sowjanya Komatineni пишет:
>>>> On 12/4/19 8:02 AM, Sowjanya Komatineni wrote:
>>>>> On 12/4/19 5:39 AM, Dmitry Osipenko wrote:
>>>>>> 03.12.2019 19:45, Sowjanya Komatineni пишет:
>>>>>>> On 12/2/19 4:07 PM, Sowjanya Komatineni wrote:
>>>>>>>> On 12/2/19 3:14 PM, Sowjanya Komatineni wrote:
>>>>>>>>> On 12/2/19 3:10 PM, Sowjanya Komatineni wrote:
>>>>>>>>>> On 12/2/19 2:58 PM, Sowjanya Komatineni wrote:
>>>>>>>>>>> On 12/2/19 1:50 PM, Dmitry Osipenko wrote:
>>>>>>>>>>>> 02.12.2019 23:09, Sowjanya Komatineni пишет:
>>>>>>>>>>>>> On 11/28/19 5:25 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>> 28.11.2019 01:57, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>> On 11/27/19 7:14 AM, Dmitry Osipenko wrote:
>>>>>>>>>>>>>>>> 27.11.2019 07:59, Sowjanya Komatineni пишет:
>>>>>>>>>>>>>>>>> Tegra210 and prior 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 <[email protected]>
>>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>>       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 ea0e11a09c12..a353f6d0a832 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,91 @@
>>>>>>>>>>>>>>>>>       #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;
>>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +#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;
>>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +#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_index;
>>>>>>>>>>>>>>>>> +    int init_state;
>>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +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",
>>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +static struct pmc_clk_init_data tegra_pmc_clks_data[] = {
>>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_1_mux",
>>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_1",
>>>>>>>>>>>>>>>>> +        .parents = clk_out1_parents,
>>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out1_parents),
>>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_1_MUX,
>>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_1,
>>>>>>>>>>>>>>>>> +        .dev_name = "extern1",
>>>>>>>>>>>>>>>>> +        .mux_shift = 6,
>>>>>>>>>>>>>>>>> +        .gate_shift = 2,
>>>>>>>>>>>>>>>>> +        .init_parent_index = 3,
>>>>>>>>>>>>>>>>> +        .init_state = 1,
>>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_2_mux",
>>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_2",
>>>>>>>>>>>>>>>>> +        .parents = clk_out2_parents,
>>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out2_parents),
>>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_2_MUX,
>>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_2,
>>>>>>>>>>>>>>>>> +        .dev_name = "extern2",
>>>>>>>>>>>>>>>>> +        .mux_shift = 14,
>>>>>>>>>>>>>>>>> +        .gate_shift = 10,
>>>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>>> +    {
>>>>>>>>>>>>>>>>> +        .mux_name = "clk_out_3_mux",
>>>>>>>>>>>>>>>>> +        .gate_name = "clk_out_3",
>>>>>>>>>>>>>>>>> +        .parents = clk_out3_parents,
>>>>>>>>>>>>>>>>> +        .num_parents = ARRAY_SIZE(clk_out3_parents),
>>>>>>>>>>>>>>>>> +        .mux_id = TEGRA_PMC_CLK_OUT_3_MUX,
>>>>>>>>>>>>>>>>> +        .gate_id = TEGRA_PMC_CLK_OUT_3,
>>>>>>>>>>>>>>>>> +        .dev_name = "extern3",
>>>>>>>>>>>>>>>>> +        .mux_shift = 22,
>>>>>>>>>>>>>>>>> +        .gate_shift = 18,
>>>>>>>>>>>>>>>>> +        .init_parent_index = 0,
>>>>>>>>>>>>>>>>> +        .init_state = 0,
>>>>>>>>>>>>>>>>> +    },
>>>>>>>>>>>>>>>>> +};
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>       struct tegra_powergate {
>>>>>>>>>>>>>>>>>           struct generic_pm_domain genpd;
>>>>>>>>>>>>>>>>>           struct tegra_pmc *pmc;
>>>>>>>>>>>>>>>>> @@ -254,6 +344,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[] = {
>>>>>>>>>>>>>>>>> @@ -2163,6 +2256,228 @@ 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;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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,
>>>>>>>>>>>>>>>>> +               unsigned long offset, u32 shift, u32 mask)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>>>> +    struct pmc_clk_mux *mux;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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);
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +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, unsigned long offset,
>>>>>>>>>>>>>>>>> +                u32 shift)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct clk_init_data init;
>>>>>>>>>>>>>>>>> +    struct pmc_clk_gate *gate;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>>>>>>>>>>>>>>>>> +    if (!gate)
>>>>>>>>>>>>>>>>> +        return ERR_PTR(-ENOMEM);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    return clk_register(NULL, &gate->hw);
>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +static void tegra_pmc_clock_register(struct tegra_pmc
>>>>>>>>>>>>>>>>> *pmc,
>>>>>>>>>>>>>>>>> +                     struct device_node *np)
>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>> +    struct clk *clkmux, *clk, *parent;
>>>>>>>>>>>>>>>>> +    struct clk_onecell_data *clk_data;
>>>>>>>>>>>>>>>>> +    unsigned int num_clks;
>>>>>>>>>>>>>>>>> +    int i, ret;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    /* each pmc clock output has a mux and a gate */
>>>>>>>>>>>>>>>>> +    num_clks = pmc->soc->num_pmc_clks * 2;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    if (!num_clks)
>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    clk_data = kmalloc(sizeof(*clk_data), GFP_KERNEL);
>>>>>>>>>>>>>>>>> +    if (!clk_data)
>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    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 = num_clks;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +    for (i = 0; i < pmc->soc->num_pmc_clks; i++) {
>>>>>>>>>>>>>>>>> +        struct pmc_clk_init_data *data;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        data = pmc->soc->pmc_clks_data + i;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        clkmux =
>>>>>>>>>>>>>>>>> tegra_pmc_clk_mux_register(data->mux_name,
>>>>>>>>>>>>>>>>> + data->parents,
>>>>>>>>>>>>>>>>> + data->num_parents,
>>>>>>>>>>>>>>>>> + CLK_SET_RATE_NO_REPARENT |
>>>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>>>> + data->mux_shift, 3);
>>>>>>>>>>>>>>>>> +        if (IS_ERR(clkmux))
>>>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> + clk_data->clks[data->mux_id] = clkmux;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        clk = tegra_pmc_clk_gate_register(data->gate_name,
>>>>>>>>>>>>>>>>> + data->mux_name,
>>>>>>>>>>>>>>>>> + CLK_SET_RATE_PARENT,
>>>>>>>>>>>>>>>>> + PMC_CLK_OUT_CNTRL,
>>>>>>>>>>>>>>>>> + data->gate_shift);
>>>>>>>>>>>>>>>>> +        if (IS_ERR(clk))
>>>>>>>>>>>>>>>>> +            goto free_clks;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> + clk_data->clks[data->gate_id] = clk;
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        ret = clk_set_parent(clk, clkmux);
>>>>>>>>>>>>>>>>> +        if (ret < 0) {
>>>>>>>>>>>>>>>>> +            pr_err("failed to set parent of %s to %s\n",
>>>>>>>>>>>>>>>>> +                   __func__, __clk_get_name(clk),
>>>>>>>>>>>>>>>>> + __clk_get_name(clkmux));
>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        clk_register_clkdev(clk, data->dev_name,
>>>>>>>>>>>>>>>>> data->gate_name);
>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>> +        /* configure initial clock parent and state */
>>>>>>>>>>>>>>>>> +        parent = clk_get_sys(data->gate_name,
>>>>>>>>>>>>>>>>> + data->parents[data->init_parent_index]);
>>>>>>>>>>>> Couldn't the default parent be defined using "assigned clock"
>>>>>>>>>>>> in a
>>>>>>>>>>>> device-tree? Please see "Assigned clock parents and rates" in
>>>>>>>>>>>> the
>>>>>>>>>>>> doc.
>>>>>>>>>>>>
>>>>>>>>>>>> https://www.kernel.org/doc/Documentation/devicetree/bindings/clock/clock-bindings.txt
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Then you could simply use of_clk_set_defaults(pmc->dev->of_node,
>>>>>>>>>>>> true).
>>>>>>>>>>> Yes, of_clk_add_provider() does of_clk_set_defaults which sets
>>>>>>>>>>> based on assigned parents and clock rates.
>>>>>>>>>>>
>>>>>>>>>>> This need device tree to specify assigned clock parent
>>>>>>>>>>> properties.
>>>>>>>>>>> Will update device tree and remove init parent from the driver.
>>>>>>>>>>>
>>>>>>>>>> assigned-clock properties should be set in consumer node of these
>>>>>>>>>> clocks and currently these clocks are not used yet.
>>>>>>>>>>
>>>>>>>>>> So will just remove init parent from driver and when these clocks
>>>>>>>>>> are used device tree can be updated in corresponding consumer node
>>>>>>>>>> with these properties.
>>>>>>>>>>
>>>>>>>>> How about default ON/OFF init state for the clocks? I see
>>>>>>>>> assigned-clock properties for parent and rate only.
>>>>>>>>>
>>>>>>>>> But based on existing clock-tegra-pmc driver, I see clk_out_1 is
>>>>>>>>> default enabled with extern1 parent for T30 thru T210 platforms.
>>>>>>>>>
>>>>>>>>> Peter/Thierry, What was the reason we enable clk_out_1 right
>>>>>>>>> from the
>>>>>>>>> clock registration?
>>>>>>>>>
>>>>>>>> clk_out_1 is for audio and its not required to be enabled during the
>>>>>>>> boot and audio driver can enable/disable it.
>>>>>>>>
>>>>>>>> same with blink 32khz which is used for WIFI. WIFI driver can
>>>>>>>> enable/disable during power up/down sequence and technically as per
>>>>>>>> design we dont need to have it always on right from the boot.
>>>>>>>>
>>>>>>>> So can remove out clocks init states from driver once thierry also
>>>>>>>> agree on this.
>>>>>>>>
>>>>>>> Hi Dmitry,
>>>>>>>
>>>>>>> Looking at audio driver, it doesn't take care of mclk which is from
>>>>>>> clk_out_1 and expects mclk to be always on.
>>>>>>>
>>>>>>> So probably we should have this init state enables in pmc driver for
>>>>>>> 32Khz and clk_out's to not break existing functionality.
>>>>>> Hello Sowjanya,
>>>>>>
>>>>>> IIUC, it's a bug in the device-trees and sound's MCLK actually
>>>>>> should be
>>>>>> set to CLK_OUT_1 of PMC instead of CaR's EXTPERIPH1. If that's the
>>>>>> case,
>>>>>> then the device-trees need to be fixed.
>>>>> OK, will have this in v3
>>>> Actually if we change mclk to use clk_out_1, then it will break with old
>>>> device trees.
>>>>
>>>> Currently clk_out_1 parent is set to extern1 as init state by the clock
>>>> driver and device tree for sound node is using extern1 as mclk works and
>>>> extern1 itself has enable and disable. But yes implementation wise, its
>>>> incorrect as mclk should really be clk_out_1.
>>>>
>>>> Now, with moving clk_out_1 to tegra PMC, pmc is the clock provider and
>>>> if we now change the device tree to use CLK_OUT_1 as mclk then it will
>>>> break old device tree.
>>> There souldn't be any problem with enabling CLK_OUT_1 by the audio
>>> driver itself (in the code) if an old device-tree is detected.
>>>
>>>     clk_out_1 = clk_get(dev, "clk_out_1");
>>>     clk_set_parent(clk_out_1, clk_extern1);
>>>     ...
>>>     data->clk_cdev1 = clk_out_1;
>>>
>>> Maybe it will be even better to compare clocks for detection of an
>>> older DT:
>>>
>>>     if (tegra_get_chip_id() > TEGRA20 &&
>>>         clk_is_match(data->clk_cdev1, clk_extern1)) {
>>>     ...
>>>
>> we can do parent init in audio driver which is not an issue.
>>
>> But I was saying we can keep extern1 as mclk in device tree instead of
>> changing to clk_out_1 from pmc as it will break old device tree becasue
>> we moved clk_out_1 from clock driver to pmc driver.
>>
>> once we do init parent in audio driver, we still can use extern1 as mclk
>> in device tree.
>>
>> mclk -> clk_out_1 -> extern1 -> plla_out0 -> plla
>>
>> with init of clk_out_1 parent to extern1, mclk can use extern1 handle
>> for rate/enable/disable clk operations
> It should be okay to fallback to extern1 for a backwards compatibility
> of the new DT with older kernels.
>
> @@ -192,8 +192,14 @@ int tegra_asoc_utils_init(struct
> tegra_asoc_utils_data *data,
> data->clk_cdev1 = clk_get(dev, "mclk");
> if (IS_ERR(data->clk_cdev1)) {
> dev_err(data->dev, "Can't retrieve clk cdev1\n");
> - ret = PTR_ERR(data->clk_cdev1);
> - goto err_put_pll_a_out0;
> +
> + data->clk_cdev1 = clk_get_sys("clk_out_1", "extern1");
> + if (IS_ERR(data->clk_cdev1)) {
> + ret = PTR_ERR(data->clk_cdev1);
> + goto err_put_pll_a_out0;
> + }
> +
> + dev_err(data->dev, "Falling back to extern1\n");
> }
>
> ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
>
> This change should be a standalone patch and it should be requsted for
> backporting into stable kernels.

ok will add fallback. yes will request this patch for backporting.

Thanks dmitry.

>>>>>>> Regarding using assigned-clock properties for init parent and
>>>>>>> removing
>>>>>>> init parent from driver, it also needs consumer node in device
>>>>>>> tree to
>>>>>>> be updated to specify assigned-clock properties for default/init
>>>>>>> parent.
>>>>>>>
>>>>>>> This breaks device tree ABI as prior Tegra210 supports audio driver.
>>>>>> So it's the sound node which should have had the assigned clocks in
>>>>>> device-tree in order to define route for the audio MCLK clock from CaR
>>>>>> to PMC.
>>>>>>
>>>>>> Given that the audio clocks configuration is the same for all of the
>>>>>> currently supported boards, I think it will be better to remove the
>>>>>> entire audio clocks initialization from the clk drivers and move it
>>>>>> all
>>>>>> to the audio driver. It could be something like this:
>>>>>>
>>>>>> int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
>>>>>>                struct device *dev)
>>>>>> {
>>>>>>      ...
>>>>>>
>>>>>>      if (!of_find_property(dev->of_node, "assigned-clock-parents",
>>>>>> NULL) &&
>>>>>>          tegra_get_chip_id() > TEGRA20) {
>>>>>>          clk_extern1 = clk_get(dev, "extern1");
>>>>>>          ...
>>>>>>          clk_set_parent(clk_extern1, clk_pll_a_out0);
>>>>>>          ...
>>>>>>          clk_put(data->clk_cdev1);
>>>>>>
>>>>>>          data->clk_cdev1 = clk_out_1;
>>>>>>      }
>>>>>>      ...
>>>>>>
>>>>>> So now the old device-trees will cointinue to work and new could have
>>>>>> the assigned-clocks and set MCLK to CLK_OUT_1.
>>>>>>
>>>>>> [snip]
>>>>> Will update clk-tegra, pmc and audio driver to move init state
>>>>> configuration for extern1 and clk_out_1 into audio driver and will use
>>>>> assigned clock properties in device tree.
>>>>>