summaryrefslogtreecommitdiffstats
path: root/drivers/clk/at91/clk-programmable.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/at91/clk-programmable.c')
-rw-r--r--drivers/clk/at91/clk-programmable.c53
1 files changed, 33 insertions, 20 deletions
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 857ede1ca9..26c36a882d 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -1,11 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
*/
#include <common.h>
@@ -23,8 +18,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 {
@@ -41,11 +35,18 @@ static unsigned long clk_programmable_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
struct clk_programmable *prog = to_clk_programmable(clk);
+ 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_set_parent(struct clk *clk, u8 index)
@@ -93,25 +94,29 @@ static int clk_programmable_set_rate(struct clk *clk, unsigned long rate,
struct clk_programmable *prog = to_clk_programmable(clk);
const struct clk_programmable_layout *layout = prog->layout;
unsigned long div = parent_rate / rate;
- unsigned int pckr;
int shift = 0;
- regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
-
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_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
- PROG_PRES_MASK << layout->pres_shift,
- shift << layout->pres_shift);
+ layout->pres_mask << layout->pres_shift,
+ shift << layout->pres_shift);
return 0;
}
@@ -123,7 +128,7 @@ static const struct clk_ops programmable_ops = {
.set_rate = clk_programmable_set_rate,
};
-struct clk *
+struct clk * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
@@ -157,23 +162,31 @@ at91_clk_register_programmable(struct regmap *regmap,
return ERR_PTR(ret);
}
+ pmc_register_pck(id);
+
return &prog->clk;
}
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,
};