Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45b06682 authored by Matthias Wieloch's avatar Matthias Wieloch Committed by Stephen Boyd
Browse files

clk: at91: fix programmable clock for sama5d2



The prescaler formula of the programmable clock has changed for sama5d2.
Update the driver accordingly.

Fixes: a2038077 ("clk: at91: add sama5d2 PMC driver")
Cc: <stable@vger.kernel.org> # v4.20+
Signed-off-by: default avatarNicolas Ferre <nicolas.ferre@microchip.com>
[nicolas.ferre@microchip.com: adapt the prescaler range,
		fix clk_programmable_recalc_rate, split patch]
Signed-off-by: default avatarMatthias Wieloch <matthias.wieloch@few-bauer.de>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: default avatarStephen Boyd <sboyd@kernel.org>
parent 9e98c678
Loading
Loading
Loading
Loading
+43 −14
Original line number Diff line number Diff line
@@ -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 {
@@ -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;

@@ -60,11 +68,19 @@ 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++) {
		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)
			continue;
@@ -137,16 +153,23 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
	if (!div)
		return -EINVAL;

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

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

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

	if (shift >= PROG_PRES_MASK)
		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;
@@ -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 −0
Original line number Diff line number Diff line
@@ -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;
+9 −1
Original line number Diff line number Diff line
@@ -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);
@@ -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;
	}