Skip to content

Commit

Permalink
clk-divider: Fix READ_ONLY when divider > 1
Browse files Browse the repository at this point in the history
Commit 79c6ab5 (clk: divider: add CLK_DIVIDER_READ_ONLY flag) in
v3.16 introduced the CLK_DIVIDER_READ_ONLY flag which caused the
recalc_rate() and round_rate() clock callbacks to be omitted.

However using this flag has the unfortunate side effect of causing the
clock recalculation code when a clock rate change is attempted to always
treat it as a pass-through clock, i.e. with a fixed divide of 1, which
may not be the case. Child clock rates are then recalculated using the
wrong parent rate.

Therefore instead of dropping the recalc_rate() and round_rate()
callbacks, alter clk_divider_bestdiv() to always report the current
divider as the best divider so that it is never altered.

For me the read only clock was the system clock, which divided the PLL
rate by 2, from which both the UART and the SPI clocks were divided.
Initial setting of the UART rate set it correctly, but when the SPI
clock was set, the other child clocks were miscalculated. The UART clock
was recalculated using the PLL rate as the parent rate, resulting in a
UART new_rate of double what it should be, and a UART which spewed forth
garbage when the rate changes were propagated.

Signed-off-by: James Hogan <[email protected]>
Cc: Thomas Abraham <[email protected]>
Cc: Tomasz Figa <[email protected]>
Cc: Max Schwarz <[email protected]>
Cc: <[email protected]> # v3.16+
Acked-by: Haojian Zhuang <[email protected]>
Signed-off-by: Michael Turquette <[email protected]>
  • Loading branch information
James Hogan authored and Michael Turquette committed Nov 17, 2014
1 parent 9a6cb70 commit e6d5e7d
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 13 deletions.
18 changes: 9 additions & 9 deletions drivers/clk/clk-divider.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!rate)
rate = 1;

/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
bestdiv = readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider);
bestdiv = _get_div(divider, bestdiv);
return bestdiv;
}

maxdiv = _get_maxdiv(divider);

if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
Expand Down Expand Up @@ -361,11 +369,6 @@ const struct clk_ops clk_divider_ops = {
};
EXPORT_SYMBOL_GPL(clk_divider_ops);

const struct clk_ops clk_divider_ro_ops = {
.recalc_rate = clk_divider_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_divider_ro_ops);

static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
Expand All @@ -391,10 +394,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
}

init.name = name;
if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
init.ops = &clk_divider_ro_ops;
else
init.ops = &clk_divider_ops;
init.ops = &clk_divider_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
Expand Down
4 changes: 1 addition & 3 deletions drivers/clk/rockchip/clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
div->width = div_width;
div->lock = lock;
div->table = div_table;
div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
? &clk_divider_ro_ops
: &clk_divider_ops;
div_ops = &clk_divider_ops;
}

clk = clk_register_composite(NULL, name, parent_names, num_parents,
Expand Down
1 change: 0 additions & 1 deletion include/linux/clk-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,6 @@ struct clk_divider {
#define CLK_DIVIDER_READ_ONLY BIT(5)

extern const struct clk_ops clk_divider_ops;
extern const struct clk_ops clk_divider_ro_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
Expand Down

0 comments on commit e6d5e7d

Please sign in to comment.