summaryrefslogtreecommitdiffstats
path: root/drivers/soc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/tegra/pmc.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index ecce91517af2..f65aea61149d 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -62,12 +62,15 @@
#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[] = {