Skip to content

Commit

Permalink
clk: at91: fix programmable clock for sama5d2
Browse files Browse the repository at this point in the history
The prescaler formula of the programmable clock has changed for sama5d2.
Update the driver accordingly.

Fixes: a203807 ("clk: at91: add sama5d2 PMC driver")
Cc: <stable@vger.kernel.org> # v4.20+
Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
[nicolas.ferre@microchip.com: adapt the prescaler range,
		fix clk_programmable_recalc_rate, split patch]
Signed-off-by: Matthias Wieloch <matthias.wieloch@few-bauer.de>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
  • Loading branch information
Matthias Wieloch authored and Stephen Boyd committed Mar 18, 2019
1 parent 9e98c67 commit 45b0668
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 15 deletions.
57 changes: 43 additions & 14 deletions drivers/clk/at91/clk-programmable.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
#define PROG_ID_MAX 7

#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
#define PROG_PRES_MASK 0x7
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
#define PROG_MAX_RM9200_CSS 3

struct clk_programmable {
Expand All @@ -37,20 +36,29 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int pckr;
unsigned long rate;

regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);

return parent_rate >> PROG_PRES(prog->layout, pckr);
if (layout->is_pres_direct)
rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
else
rate = parent_rate >> PROG_PRES(layout, pckr);

return rate;
}

static int clk_programmable_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
struct clk_hw *parent;
long best_rate = -EINVAL;
unsigned long parent_rate;
unsigned long tmp_rate;
unsigned long tmp_rate = 0;
int shift;
int i;

Expand All @@ -60,10 +68,18 @@ static int clk_programmable_determine_rate(struct clk_hw *hw,
continue;

parent_rate = clk_hw_get_rate(parent);
for (shift = 0; shift < PROG_PRES_MASK; shift++) {
tmp_rate = parent_rate >> shift;
if (tmp_rate <= req->rate)
break;
if (layout->is_pres_direct) {
for (shift = 0; shift <= layout->pres_mask; shift++) {
tmp_rate = parent_rate / (shift + 1);
if (tmp_rate <= req->rate)
break;
}
} else {
for (shift = 0; shift < layout->pres_mask; shift++) {
tmp_rate = parent_rate >> shift;
if (tmp_rate <= req->rate)
break;
}
}

if (tmp_rate > req->rate)
Expand Down Expand Up @@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
if (!div)
return -EINVAL;

shift = fls(div) - 1;
if (layout->is_pres_direct) {
shift = div - 1;

if (div != (1 << shift))
return -EINVAL;
if (shift > layout->pres_mask)
return -EINVAL;
} else {
shift = fls(div) - 1;

if (shift >= PROG_PRES_MASK)
return -EINVAL;
if (div != (1 << shift))
return -EINVAL;

if (shift >= layout->pres_mask)
return -EINVAL;
}

regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
PROG_PRES_MASK << layout->pres_shift,
layout->pres_mask << layout->pres_shift,
shift << layout->pres_shift);

return 0;
Expand Down Expand Up @@ -202,19 +225,25 @@ at91_clk_register_programmable(struct regmap *regmap,
}

const struct clk_programmable_layout at91rm9200_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 0,
.is_pres_direct = 0,
};

const struct clk_programmable_layout at91sam9g45_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 1,
.is_pres_direct = 0,
};

const struct clk_programmable_layout at91sam9x5_programmable_layout = {
.pres_mask = 0x7,
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
.is_pres_direct = 0,
};
2 changes: 2 additions & 0 deletions drivers/clk/at91/pmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ struct clk_pll_characteristics {
};

struct clk_programmable_layout {
u8 pres_mask;
u8 pres_shift;
u8 css_mask;
u8 have_slck_mck;
u8 is_pres_direct;
};

extern const struct clk_programmable_layout at91rm9200_programmable_layout;
Expand Down
10 changes: 9 additions & 1 deletion drivers/clk/at91/sama5d2.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ static const struct {
.pll = true },
};

static const struct clk_programmable_layout sama5d2_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
.is_pres_direct = 1,
};

static void __init sama5d2_pmc_setup(struct device_node *np)
{
struct clk_range range = CLK_RANGE(0, 0);
Expand Down Expand Up @@ -249,7 +257,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
&at91sam9x5_programmable_layout);
&sama5d2_programmable_layout);
if (IS_ERR(hw))
goto err_free;
}
Expand Down

0 comments on commit 45b0668

Please sign in to comment.