Add a clock specific flag to switch register accesses to big endian, to allow runtime configuration of big endian divider clocks. Signed-off-by: Jonas Gorski <jonas.gorski@xxxxxxxxx> --- V1 -> V2: * switch from global to local flag drivers/clk/clk-divider.c | 26 ++++++++++++++++++++++---- include/linux/clk-provider.h | 4 ++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index e5a17265cfaf..ff791a7a5e9e 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -25,6 +25,24 @@ * parent - fixed parent. No clk_set_parent support */ +static inline u32 clk_div_readl(struct clk_divider *divider) +{ + if (divider->flags & CLK_DIVIDER_BIG_ENDIAN) + return ioread32be(divider->reg); + else + return clk_readl(divider->reg); +} + +static inline void clk_div_writel(struct clk_divider *divider, u32 val) +{ + if (divider->flags & CLK_DIVIDER_BIG_ENDIAN) + iowrite32be(val, divider->reg); + else + clk_writel(val, divider->reg); +} + +#define div_mask(width) ((1 << (width)) - 1) + static unsigned int _get_table_maxdiv(const struct clk_div_table *table, u8 width) { @@ -135,7 +153,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, struct clk_divider *divider = to_clk_divider(hw); unsigned int val; - val = clk_readl(divider->reg) >> divider->shift; + val = clk_div_readl(divider) >> divider->shift; val &= clk_div_mask(divider->width); return divider_recalc_rate(hw, parent_rate, val, divider->table, @@ -370,7 +388,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, if (divider->flags & CLK_DIVIDER_READ_ONLY) { u32 val; - val = clk_readl(divider->reg) >> divider->shift; + val = clk_div_readl(divider->reg) >> divider->shift; val &= clk_div_mask(divider->width); return divider_ro_round_rate(hw, rate, prate, divider->table, @@ -420,11 +438,11 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { val = clk_div_mask(divider->width) << (divider->shift + 16); } else { - val = clk_readl(divider->reg); + val = clk_div_readl(divider->reg); val &= ~(clk_div_mask(divider->width) << divider->shift); } val |= (u32)value << divider->shift; - clk_writel(val, divider->reg); + clk_div_writel(divider, val); if (divider->lock) spin_unlock_irqrestore(divider->lock, flags); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index db21437c77e2..7117b8cc0c0c 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -416,6 +416,9 @@ struct clk_div_table { * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED * except when the value read from the register is zero, the divisor is * 2^width of the field. + * CLK_DIVIDER_BIG_ENDIAN - By default little endian register accesses are used + * for the divider register. Setting this flag makes the register accesses + * big endian. */ struct clk_divider { struct clk_hw hw; @@ -437,6 +440,7 @@ struct clk_divider { #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) #define CLK_DIVIDER_READ_ONLY BIT(5) #define CLK_DIVIDER_MAX_AT_ZERO BIT(6) +#define CLK_DIVIDER_BIG_ENDIAN BIT(7) extern const struct clk_ops clk_divider_ops; extern const struct clk_ops clk_divider_ro_ops; -- 2.13.2