soc/tegra: Add support for 32 kHz blink clock
authorSowjanya Komatineni <skomatineni@nvidia.com>
Tue, 14 Jan 2020 07:24:15 +0000 (23:24 -0800)
committerThierry Reding <treding@nvidia.com>
Fri, 13 Mar 2020 09:53:06 +0000 (10:53 +0100)
Tegra PMC has blink control to output 32 kHz clock out to Tegra blink
pin. Blink pad DPD state and enable controls are part of Tegra PMC
register space.

Currently Tegra clock driver registers blink control by passing PMC
address and register offset to clk_register_gate which performs direct
PMC access during clk_ops and with this when PMC is in secure mode, any
access from non-secure world does not go through.

This patch adds blink control registration to the Tegra PMC driver using
PMC specific clock gate operations that use tegra_pmc_readl() and
tegra_pmc_writel() to support both secure mode and non-secure
mode PMC register access.

Tested-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/soc/tegra/pmc.c

index ecce915..f65aea6 100644 (file)
 #define  PMC_CNTRL_SYSCLK_OE           BIT(11) /* system clock enable */
 #define  PMC_CNTRL_SYSCLK_POLARITY     BIT(10) /* sys clk polarity */
 #define  PMC_CNTRL_PWRREQ_POLARITY     BIT(8)
+#define  PMC_CNTRL_BLINK_EN            7
 #define  PMC_CNTRL_MAIN_RST            BIT(4)
 
 #define PMC_WAKE_MASK                  0x0c
 #define PMC_WAKE_LEVEL                 0x10
 #define PMC_WAKE_STATUS                        0x14
 #define PMC_SW_WAKE_STATUS             0x18
+#define PMC_DPD_PADS_ORIDE             0x1c
+#define  PMC_DPD_PADS_ORIDE_BLINK      20
 
 #define DPD_SAMPLE                     0x020
 #define  DPD_SAMPLE_ENABLE             BIT(0)
@@ -80,6 +83,7 @@
 
 #define PWRGATE_STATUS                 0x38
 
+#define PMC_BLINK_TIMER                        0x40
 #define PMC_IMPL_E_33V_PWR             0x40
 
 #define PMC_PWR_DET                    0x48
@@ -171,6 +175,14 @@ struct pmc_clk {
 
 #define to_pmc_clk(_hw) container_of(_hw, struct pmc_clk, 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 *name;
        const char *const *parents;
@@ -321,6 +333,7 @@ struct tegra_pmc_soc {
 
        const struct pmc_clk_init_data *pmc_clks_data;
        unsigned int num_pmc_clks;
+       bool has_blink_output;
 };
 
 static const char * const tegra186_reset_sources[] = {
@@ -2334,6 +2347,60 @@ tegra_pmc_clk_out_register(struct tegra_pmc *pmc,
        return clk_register(NULL, &pmc_clk->hw);
 }
 
+static int pmc_clk_gate_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 int pmc_clk_gate_enable(struct clk_hw *hw)
+{
+       struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
+
+       pmc_clk_set_state(gate->offs, gate->shift, 1);
+
+       return 0;
+}
+
+static void pmc_clk_gate_disable(struct clk_hw *hw)
+{
+       struct pmc_clk_gate *gate = to_pmc_clk_gate(hw);
+
+       pmc_clk_set_state(gate->offs, gate->shift, 0);
+}
+
+static const struct clk_ops pmc_clk_gate_ops = {
+       .is_enabled = pmc_clk_gate_is_enabled,
+       .enable = pmc_clk_gate_enable,
+       .disable = pmc_clk_gate_disable,
+};
+
+static struct clk *
+tegra_pmc_clk_gate_register(struct tegra_pmc *pmc, const char *name,
+                           const char *parent_name, unsigned long offset,
+                           u32 shift)
+{
+       struct clk_init_data init;
+       struct pmc_clk_gate *gate;
+
+       gate = devm_kzalloc(pmc->dev, 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 = 0;
+
+       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)
 {
@@ -2343,6 +2410,8 @@ static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
        int i, err;
 
        num_clks = pmc->soc->num_pmc_clks;
+       if (pmc->soc->has_blink_output)
+               num_clks += 1;
 
        if (!num_clks)
                return;
@@ -2384,6 +2453,42 @@ static void tegra_pmc_clock_register(struct tegra_pmc *pmc,
                clk_data->clks[data->clk_id] = clk;
        }
 
+       if (pmc->soc->has_blink_output) {
+               tegra_pmc_writel(pmc, 0x0, PMC_BLINK_TIMER);
+               clk = tegra_pmc_clk_gate_register(pmc,
+                                                 "pmc_blink_override",
+                                                 "clk_32k",
+                                                 PMC_DPD_PADS_ORIDE,
+                                                 PMC_DPD_PADS_ORIDE_BLINK);
+               if (IS_ERR(clk)) {
+                       dev_warn(pmc->dev,
+                                "unable to register pmc_blink_override: %d\n",
+                                PTR_ERR_OR_ZERO(clk));
+                       return;
+               }
+
+               clk = tegra_pmc_clk_gate_register(pmc, "pmc_blink",
+                                                 "pmc_blink_override",
+                                                 PMC_CNTRL,
+                                                 PMC_CNTRL_BLINK_EN);
+               if (IS_ERR(clk)) {
+                       dev_warn(pmc->dev,
+                                "unable to register pmc_blink: %d\n",
+                                PTR_ERR_OR_ZERO(clk));
+                       return;
+               }
+
+               err = clk_register_clkdev(clk, "pmc_blink", NULL);
+               if (err) {
+                       dev_warn(pmc->dev,
+                                "unable to register pmc_blink lookup: %d\n",
+                                err);
+                       return;
+               }
+
+               clk_data->clks[TEGRA_PMC_CLK_BLINK] = clk;
+       }
+
        err = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
        if (err)
                dev_warn(pmc->dev, "failed to add pmc clock provider: %d\n",
@@ -2652,6 +2757,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
        .num_reset_levels = 0,
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
+       .has_blink_output = true,
 };
 
 static const char * const tegra30_powergates[] = {
@@ -2701,6 +2807,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
        .num_reset_levels = 0,
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
+       .has_blink_output = true,
 };
 
 static const char * const tegra114_powergates[] = {
@@ -2754,6 +2861,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
        .num_reset_levels = 0,
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
+       .has_blink_output = true,
 };
 
 static const char * const tegra124_powergates[] = {
@@ -2867,6 +2975,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
        .num_reset_levels = 0,
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
+       .has_blink_output = true,
 };
 
 static const char * const tegra210_powergates[] = {
@@ -2983,6 +3092,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
        .wake_events = tegra210_wake_events,
        .pmc_clks_data = tegra_pmc_clks_data,
        .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
+       .has_blink_output = true,
 };
 
 #define TEGRA186_IO_PAD_TABLE(_pad)                                         \
@@ -3114,6 +3224,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
        .wake_events = tegra186_wake_events,
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
+       .has_blink_output = false,
 };
 
 static const struct tegra_io_pad_soc tegra194_io_pads[] = {
@@ -3233,6 +3344,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
        .wake_events = tegra194_wake_events,
        .pmc_clks_data = NULL,
        .num_pmc_clks = 0,
+       .has_blink_output = false,
 };
 
 static const struct of_device_id tegra_pmc_match[] = {