Several s4 clocks use power-of-two dividers. Add support for them.
Signed-off-by: Sean Anderson <[email protected]>
---
drivers/clk/meson/clk_meson.c | 49 ++++++++++++++++++++++++++---------
drivers/clk/meson/clk_meson.h | 44 +++++++++++++++----------------
2 files changed, 59 insertions(+), 34 deletions(-)
diff --git a/drivers/clk/meson/clk_meson.c b/drivers/clk/meson/clk_meson.c
index 29615fdae77..50c72276457 100644
--- a/drivers/clk/meson/clk_meson.c
+++ b/drivers/clk/meson/clk_meson.c
@@ -55,26 +55,49 @@ int meson_clk_disable(struct clk *clk)
return meson_set_gate(clk, false);
}
+static ulong meson_div_get_rate_common(struct clk *clk,
+ enum meson_clk_type type, u16 *n,
+ ulong *rate)
+{
+ struct meson_clk *priv = dev_get_priv(clk->dev);
+ const struct meson_clk_info *info;
+ struct clk parent;
+
+ info = meson_clk_get_info(clk, type);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ *n = GET_PARM_VALUE(priv, info->parm);
+
+ parent.dev = clk->dev;
+ parent.id = info->parents[0];
+ *rate = meson_clk_get_rate(&parent);
+ return 0;
+}
+
static ulong meson_div_get_rate(struct clk *clk)
{
- struct meson_clk *priv = dev_get_priv(clk->dev);
+ ulong rate, err;
u16 n;
- ulong rate;
- const struct meson_clk_info *info;
- struct clk parent;
- info = meson_clk_get_info(clk, MESON_CLK_DIV);
- if (IS_ERR(info))
- return PTR_ERR(info);
+ err = meson_div_get_rate_common(clk, MESON_CLK_DIV, &n, &rate);
+ if (err)
+ return err;
/* Actual divider value is (field value + 1), hence the increment */
- n = GET_PARM_VALUE(priv, info->parm) + 1;
+ return rate / (n + 1);
+}
- parent.dev = clk->dev;
- parent.id = info->parents[0];
- rate = meson_clk_get_rate(&parent);
+static ulong meson_div2_get_rate(struct clk *clk)
+{
+ ulong rate, err;
+ u16 n;
- return rate / n;
+ err = meson_div_get_rate_common(clk, MESON_CLK_DIV2, &n, &rate);
+ if (err)
+ return err;
+
+ return rate >> n;
}
int meson_clk_get_parent(struct clk *clk)
@@ -150,6 +173,8 @@ ulong meson_clk_get_rate(struct clk *clk)
return meson_clk_get_rate(&parent);
case MESON_CLK_DIV:
return meson_div_get_rate(clk);
+ case MESON_CLK_DIV2:
+ return meson_div2_get_rate(clk);
case MESON_CLK_FIXED_DIV:
parent.dev = clk->dev;
parent.id = meson_clk_get_parent(clk);
diff --git a/drivers/clk/meson/clk_meson.h b/drivers/clk/meson/clk_meson.h
index 86b272985ab..46ee5519410 100644
--- a/drivers/clk/meson/clk_meson.h
+++ b/drivers/clk/meson/clk_meson.h
@@ -61,6 +61,7 @@ struct meson_clk {
* @MESON_CLK_GATE: This clock is a gate
* @MESON_CLK_MUX: This clock is a multiplexer
* @MESON_CLK_DIV: This clock is a configurable divider
+ * @MESON_CLK_DIV2: This clock is a configurable power-of-two divider
* @MESON_CLK_FIXED_DIV: This clock is a (fractional) fixed-factor clock
* @MESON_CLK_EXTERNAL: This is an external clock from different clock provider
* @MESON_CLK_PLL: This is a PLL
@@ -70,6 +71,7 @@ enum meson_clk_type {
MESON_CLK_GATE,
MESON_CLK_MUX,
MESON_CLK_DIV,
+ MESON_CLK_DIV2,
MESON_CLK_FIXED_DIV,
MESON_CLK_EXTERNAL,
MESON_CLK_PLL,
@@ -122,18 +124,25 @@ struct meson_clk_data {
.type = MESON_CLK_MUX, \
})
+#define _CLK_REG(_type, _name, _reg, _shift, _width, _parent) \
+ (&(struct meson_clk_info){ \
+ .parents = (const unsigned int[]) { (_parent) }, \
+ .parm = &(struct parm) { \
+ .reg_off = (_reg), \
+ .shift = (_shift), \
+ .width = (_width), \
+ }, \
+ .name = (_name), \
+ .type = _type, \
+ })
+
/* A divider with an integral divisor */
-#define CLK_DIV(_name, _reg, _shift, _width, _parent) \
- (&(struct meson_clk_info){ \
- .parents = (const unsigned int[]) { (_parent) }, \
- .parm = &(struct parm) { \
- .reg_off = (_reg), \
- .shift = (_shift), \
- .width = (_width), \
- }, \
- .name = (_name), \
- .type = MESON_CLK_DIV, \
- })
+#define CLK_DIV(name, reg, shift, width, parent) \
+ _CLK_REG(MESON_CLK_DIV, name, reg, shift, width, parent)
+
+/* A divider with a power-of-two divisor */
+#define CLK_DIV2(name, reg, shift, width, parent) \
+ _CLK_REG(MESON_CLK_DIV2, name, reg, shift, width, parent)
/* A fixed divider */
#define CLK_DIV_FIXED_FULL(_name, _mult, _div, _parent)
\
@@ -156,17 +165,8 @@ struct meson_clk_data {
})
/* A clock gate */
-#define CLK_GATE(_name, _reg, _shift, _parent) \
- (&(struct meson_clk_info){ \
- .parents = (const unsigned int[]) { (_parent) }, \
- .parm = &(struct parm) { \
- .reg_off = (_reg), \
- .shift = (_shift), \
- .width = 1, \
- }, \
- .name = (_name), \
- .type = MESON_CLK_GATE, \
- })
+#define CLK_GATE(name, reg, shift, parent) \
+ _CLK_REG(MESON_CLK_GATE, name, reg, shift, 1, parent)
/* A PLL clock */
#define CLK_PLL(_name, _parent, ...) \
--
2.53.0