diff options
Diffstat (limited to 'drivers/clk/at91/clk-programmable.c')
-rw-r--r-- | drivers/clk/at91/clk-programmable.c | 53 |
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, }; |