summaryrefslogtreecommitdiff
path: root/drivers/clk/ingenic/cgu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/ingenic/cgu.c')
-rw-r--r--drivers/clk/ingenic/cgu.c94
1 files changed, 78 insertions, 16 deletions
diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c
index ab393637f7b0..56a712c9075f 100644
--- a/drivers/clk/ingenic/cgu.c
+++ b/drivers/clk/ingenic/cgu.c
@@ -100,15 +100,13 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
n += pll_info->n_offset;
od_enc = ctl >> pll_info->od_shift;
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
- bypass = !!(ctl & BIT(pll_info->bypass_bit));
+ bypass = !pll_info->no_bypass_bit &&
+ !!(ctl & BIT(pll_info->bypass_bit));
enable = !!(ctl & BIT(pll_info->enable_bit));
if (bypass)
return parent_rate;
- if (!enable)
- return 0;
-
for (od = 0; od < pll_info->od_max; od++) {
if (pll_info->od_encoding[od] == od_enc)
break;
@@ -152,17 +150,25 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
return div_u64((u64)parent_rate * m, n * od);
}
-static long
-ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
- unsigned long *prate)
+static inline const struct ingenic_cgu_clk_info *to_clk_info(
+ struct ingenic_clk *ingenic_clk)
{
- struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
clk_info = &cgu->clock_info[ingenic_clk->idx];
BUG_ON(clk_info->type != CGU_CLK_PLL);
+ return clk_info;
+}
+
+static long
+ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
+ unsigned long *prate)
+{
+ struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+ const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
+
return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL);
}
@@ -170,19 +176,14 @@ static int
ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long parent_rate)
{
- const unsigned timeout = 100;
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
- const struct ingenic_cgu_clk_info *clk_info;
- const struct ingenic_cgu_pll_info *pll_info;
+ const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
+ const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
unsigned long rate, flags;
- unsigned m, n, od, i;
+ unsigned int m, n, od;
u32 ctl;
- clk_info = &cgu->clock_info[ingenic_clk->idx];
- BUG_ON(clk_info->type != CGU_CLK_PLL);
- pll_info = &clk_info->pll;
-
rate = ingenic_pll_calc(clk_info, req_rate, parent_rate,
&m, &n, &od);
if (rate != req_rate)
@@ -201,6 +202,26 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
+ writel(ctl, cgu->base + pll_info->reg);
+ spin_unlock_irqrestore(&cgu->lock, flags);
+
+ return 0;
+}
+
+static int ingenic_pll_enable(struct clk_hw *hw)
+{
+ struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+ struct ingenic_cgu *cgu = ingenic_clk->cgu;
+ const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
+ const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
+ const unsigned int timeout = 100;
+ unsigned long flags;
+ unsigned int i;
+ u32 ctl;
+
+ spin_lock_irqsave(&cgu->lock, flags);
+ ctl = readl(cgu->base + pll_info->reg);
+
ctl &= ~BIT(pll_info->bypass_bit);
ctl |= BIT(pll_info->enable_bit);
@@ -222,10 +243,48 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
return 0;
}
+static void ingenic_pll_disable(struct clk_hw *hw)
+{
+ struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+ struct ingenic_cgu *cgu = ingenic_clk->cgu;
+ const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
+ const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
+ unsigned long flags;
+ u32 ctl;
+
+ spin_lock_irqsave(&cgu->lock, flags);
+ ctl = readl(cgu->base + pll_info->reg);
+
+ ctl &= ~BIT(pll_info->enable_bit);
+
+ writel(ctl, cgu->base + pll_info->reg);
+ spin_unlock_irqrestore(&cgu->lock, flags);
+}
+
+static int ingenic_pll_is_enabled(struct clk_hw *hw)
+{
+ struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
+ struct ingenic_cgu *cgu = ingenic_clk->cgu;
+ const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
+ const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
+ unsigned long flags;
+ u32 ctl;
+
+ spin_lock_irqsave(&cgu->lock, flags);
+ ctl = readl(cgu->base + pll_info->reg);
+ spin_unlock_irqrestore(&cgu->lock, flags);
+
+ return !!(ctl & BIT(pll_info->enable_bit));
+}
+
static const struct clk_ops ingenic_pll_ops = {
.recalc_rate = ingenic_pll_recalc_rate,
.round_rate = ingenic_pll_round_rate,
.set_rate = ingenic_pll_set_rate,
+
+ .enable = ingenic_pll_enable,
+ .disable = ingenic_pll_disable,
+ .is_enabled = ingenic_pll_is_enabled,
};
/*
@@ -328,6 +387,8 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
div *= clk_info->div.div;
rate /= div;
+ } else if (clk_info->type & CGU_CLK_FIXDIV) {
+ rate /= clk_info->fixdiv.div;
}
return rate;
@@ -598,6 +659,7 @@ static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx)
}
} else if (caps & CGU_CLK_PLL) {
clk_init.ops = &ingenic_pll_ops;
+ clk_init.flags |= CLK_SET_RATE_GATE;
caps &= ~CGU_CLK_PLL;