summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2013-11-05 17:33:17 -0700
committerStephen Warren <swarren@nvidia.com>2013-12-11 16:42:48 -0700
commit6d5b988e7dc56bb97c39bdcbc006fadcd6ca371b (patch)
tree4d5b526c30909e2b6ab88a5048fdcdffa56a48e9
parente04214683aca5cf7bbb53ee9d16373a8dbfa4259 (diff)
downloadlinux-6d5b988e7dc56bb97c39bdcbc006fadcd6ca371b.tar.bz2
clk: tegra: implement a reset driver
The Tegra CAR module implements both a clock and reset controller. So far, the driver exposes the clock feature via the common clock API and the reset feature using a custom API. This patch adds an implementation of the common reset framework API (include/linux/reset*.h). The legacy reset implementation will be removed once all drivers have been converted. Signed-off-by: Stephen Warren <swarren@nvidia.com> Reviewed-by: Thierry Reding <treding@nvidia.com> Acked-By: Peter De Schrijver <pdeschrijver@nvidia.com>
-rw-r--r--drivers/clk/tegra/clk-tegra114.c3
-rw-r--r--drivers/clk/tegra/clk-tegra124.c2
-rw-r--r--drivers/clk/tegra/clk-tegra20.c3
-rw-r--r--drivers/clk/tegra/clk-tegra30.c3
-rw-r--r--drivers/clk/tegra/clk.c50
-rw-r--r--drivers/clk/tegra/clk.h2
6 files changed, 57 insertions, 6 deletions
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index 29b912582e3d..90d9d25f2228 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -1460,7 +1460,8 @@ static void __init tegra114_clock_init(struct device_node *np)
return;
}
- clks = tegra_clk_init(TEGRA114_CLK_CLK_MAX, TEGRA114_CLK_PERIPH_BANKS);
+ clks = tegra_clk_init(clk_base, TEGRA114_CLK_CLK_MAX,
+ TEGRA114_CLK_PERIPH_BANKS);
if (!clks)
return;
diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c
index 0ef4485e9b0a..aff86b5bc745 100644
--- a/drivers/clk/tegra/clk-tegra124.c
+++ b/drivers/clk/tegra/clk-tegra124.c
@@ -1398,7 +1398,7 @@ static void __init tegra124_clock_init(struct device_node *np)
return;
}
- clks = tegra_clk_init(TEGRA124_CLK_CLK_MAX, 6);
+ clks = tegra_clk_init(clk_base, TEGRA124_CLK_CLK_MAX, 6);
if (!clks)
return;
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index b3b7204acfe7..5a6a60d9443a 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -1109,7 +1109,8 @@ static void __init tegra20_clock_init(struct device_node *np)
BUG();
}
- clks = tegra_clk_init(TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_PERIPH_BANKS);
+ clks = tegra_clk_init(clk_base, TEGRA20_CLK_CLK_MAX,
+ TEGRA20_CLK_PERIPH_BANKS);
if (!clks)
return;
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index dcb6843b3a89..2e47383418c8 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -1427,7 +1427,8 @@ static void __init tegra30_clock_init(struct device_node *np)
BUG();
}
- clks = tegra_clk_init(TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_PERIPH_BANKS);
+ clks = tegra_clk_init(clk_base, TEGRA30_CLK_CLK_MAX,
+ TEGRA30_CLK_PERIPH_BANKS);
if (!clks)
return;
diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c
index a12a5f5107ec..c0a7d7723510 100644
--- a/drivers/clk/tegra/clk.c
+++ b/drivers/clk/tegra/clk.c
@@ -18,6 +18,8 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/clk/tegra.h>
+#include <linux/reset-controller.h>
+#include <linux/tegra-soc.h>
#include "clk.h"
@@ -121,6 +123,35 @@ static struct tegra_clk_periph_regs periph_regs[] = {
},
};
+static void __iomem *clk_base;
+
+static int tegra_clk_rst_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ /*
+ * If peripheral is on the APB bus then we must read the APB bus to
+ * flush the write operation in apb bus. This will avoid peripheral
+ * access after disabling clock. Since the reset driver has no
+ * knowledge of which reset IDs represent which devices, simply do
+ * this all the time.
+ */
+ tegra_read_chipid();
+
+ writel_relaxed(BIT(id % 32),
+ clk_base + periph_regs[id / 32].rst_set_reg);
+
+ return 0;
+}
+
+static int tegra_clk_rst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ writel_relaxed(BIT(id % 32),
+ clk_base + periph_regs[id / 32].rst_clr_reg);
+
+ return 0;
+}
+
struct tegra_clk_periph_regs *get_reg_bank(int clkid)
{
int reg_bank = clkid / 32;
@@ -133,8 +164,10 @@ struct tegra_clk_periph_regs *get_reg_bank(int clkid)
}
}
-struct clk ** __init tegra_clk_init(int num, int banks)
+struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks)
{
+ clk_base = regs;
+
if (WARN_ON(banks > ARRAY_SIZE(periph_regs)))
return NULL;
@@ -203,6 +236,17 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
}
}
+static struct reset_control_ops rst_ops = {
+ .assert = tegra_clk_rst_assert,
+ .deassert = tegra_clk_rst_deassert,
+};
+
+static struct reset_controller_dev rst_ctlr = {
+ .ops = &rst_ops,
+ .owner = THIS_MODULE,
+ .of_reset_n_cells = 1,
+};
+
void __init tegra_add_of_provider(struct device_node *np)
{
int i;
@@ -220,6 +264,10 @@ void __init tegra_add_of_provider(struct device_node *np)
clk_data.clks = clks;
clk_data.clk_num = clk_num;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+ rst_ctlr.of_node = np;
+ rst_ctlr.nr_resets = clk_num * 32;
+ reset_controller_register(&rst_ctlr);
}
void __init tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index 40fb011233c0..07c62f997371 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -597,7 +597,7 @@ void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
struct clk *clks[], int clk_max);
struct tegra_clk_periph_regs *get_reg_bank(int clkid);
-struct clk **tegra_clk_init(int num, int periph_banks);
+struct clk **tegra_clk_init(void __iomem *clk_base, int num, int periph_banks);
struct clk **tegra_lookup_dt_id(int clk_id, struct tegra_clk *tegra_clk);