]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/clk/tegra/clk-tegra210.c
clk: tegra: Add Tegra210 special resets
[mirror_ubuntu-bionic-kernel.git] / drivers / clk / tegra / clk-tegra210.c
index 1a8121abd4275b8524b747f61dbd116ee4e71958..6f29125ec4391460f61a1fa3b1b9f037ff3909be 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/export.h>
 #include <linux/clk/tegra.h>
 #include <dt-bindings/clock/tegra210-car.h>
+#include <dt-bindings/reset/tegra210-car.h>
 #include <linux/iopoll.h>
 
 #include "clk.h"
 #define CLK_M_DIVISOR_SHIFT 2
 #define CLK_M_DIVISOR_MASK 0x3
 
+#define RST_DFLL_DVCO 0x2f4
+#define DVFS_DFLL_RESET_SHIFT 0
+
+#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8
+#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac
+
 /*
  * SDM fractional divisor is 16-bit 2's complement signed number within
  * (-2^12 ... 2^12-1) range. Represented in PLL data structure as unsigned
@@ -2982,6 +2989,81 @@ static void __init tegra210_clock_apply_init_table(void)
        tegra_init_from_table(init_table, clks, TEGRA210_CLK_CLK_MAX);
 }
 
+/**
+ * tegra210_car_barrier - wait for pending writes to the CAR to complete
+ *
+ * Wait for any outstanding writes to the CAR MMIO space from this CPU
+ * to complete before continuing execution.  No return value.
+ */
+static void tegra210_car_barrier(void)
+{
+       readl_relaxed(clk_base + RST_DFLL_DVCO);
+}
+
+/**
+ * tegra210_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset
+ *
+ * Assert the reset line of the DFLL's DVCO.  No return value.
+ */
+static void tegra210_clock_assert_dfll_dvco_reset(void)
+{
+       u32 v;
+
+       v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+       v |= (1 << DVFS_DFLL_RESET_SHIFT);
+       writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+       tegra210_car_barrier();
+}
+
+/**
+ * tegra210_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset
+ *
+ * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to
+ * operate.  No return value.
+ */
+static void tegra210_clock_deassert_dfll_dvco_reset(void)
+{
+       u32 v;
+
+       v = readl_relaxed(clk_base + RST_DFLL_DVCO);
+       v &= ~(1 << DVFS_DFLL_RESET_SHIFT);
+       writel_relaxed(v, clk_base + RST_DFLL_DVCO);
+       tegra210_car_barrier();
+}
+
+static int tegra210_reset_assert(unsigned long id)
+{
+       if (id == TEGRA210_RST_DFLL_DVCO)
+               tegra210_clock_assert_dfll_dvco_reset();
+       else if (id == TEGRA210_RST_ADSP)
+               writel(GENMASK(26, 21) | BIT(7),
+                       clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_SET);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int tegra210_reset_deassert(unsigned long id)
+{
+       if (id == TEGRA210_RST_DFLL_DVCO)
+               tegra210_clock_deassert_dfll_dvco_reset();
+       else if (id == TEGRA210_RST_ADSP) {
+               writel(BIT(21), clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_CLR);
+               /*
+                * Considering adsp cpu clock (min: 12.5MHZ, max: 1GHz)
+                * a delay of 5us ensures that it's at least
+                * 6 * adsp_cpu_cycle_period long.
+                */
+               udelay(5);
+               writel(GENMASK(26, 22) | BIT(7),
+                       clk_base + CLK_RST_CONTROLLER_RST_DEV_Y_CLR);
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
 /**
  * tegra210_clock_init - Tegra210-specific clock initialization
  * @np: struct device_node * of the DT node for the SoC CAR IP block
@@ -3046,6 +3128,9 @@ static void __init tegra210_clock_init(struct device_node *np)
 
        tegra_super_clk_gen5_init(clk_base, pmc_base, tegra210_clks,
                                  &pll_x_params);
+       tegra_init_special_resets(2, tegra210_reset_assert,
+                                 tegra210_reset_deassert);
+
        tegra_add_of_provider(np);
        tegra_register_devclks(devclks, ARRAY_SIZE(devclks));