diff options
Diffstat (limited to 'drivers/clk/at91/clk-programmable.c')
-rw-r--r-- | drivers/clk/at91/clk-programmable.c | 119 |
1 files changed, 69 insertions, 50 deletions
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 857ede1ca9..3bf13568f5 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -1,56 +1,55 @@ +// 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> -#include <clock.h> -#include <io.h> -#include <linux/list.h> -#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <of.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> #include "pmc.h" -#define PROG_SOURCE_MAX 5 #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 { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + u32 *mux_table; u8 id; const struct clk_programmable_layout *layout; - const char *parent_names[PROG_SOURCE_MAX]; + struct at91_clk_pms pms; }; -#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk) +#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw) -static unsigned long clk_programmable_recalc_rate(struct clk *clk, +static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_programmable *prog = to_clk_programmable(clk); + 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_set_parent(struct clk *clk, u8 index) +static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned int mask = layout->css_mask; unsigned int pckr = index; @@ -58,6 +57,9 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index) if (layout->have_slck_mck) mask |= AT91_PMC_CSSMCK_MCK; + if (prog->mux_table) + pckr = clk_mux_index_to_val(prog->mux_table, 0, index); + if (index > layout->css_mask) { if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) return -EINVAL; @@ -65,14 +67,14 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index) pckr |= AT91_PMC_CSSMCK_MCK; } - regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr); + regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr); return 0; } -static int clk_programmable_get_parent(struct clk *clk) +static int clk_programmable_get_parent(struct clk_hw *hw) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; unsigned int pckr; u8 ret; @@ -84,33 +86,40 @@ static int clk_programmable_get_parent(struct clk *clk) if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret) ret = PROG_MAX_RM9200_CSS + 1; + if (prog->mux_table) + ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret); + return ret; } -static int clk_programmable_set_rate(struct clk *clk, unsigned long rate, +static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct clk_programmable *prog = to_clk_programmable(clk); + struct clk_programmable *prog = to_clk_programmable(hw); 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, + regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), + layout->pres_mask << layout->pres_shift, shift << layout->pres_shift); return 0; @@ -123,13 +132,16 @@ static const struct clk_ops programmable_ops = { .set_rate = clk_programmable_set_rate, }; -struct clk * +struct clk_hw * __init at91_clk_register_programmable(struct regmap *regmap, const char *name, const char **parent_names, u8 num_parents, u8 id, - const struct clk_programmable_layout *layout) + const struct clk_programmable_layout *layout, + u32 *mux_table) { struct clk_programmable *prog; + struct clk_hw *hw; + struct clk_init_data init; int ret; if (id > PROG_ID_MAX) @@ -139,41 +151,48 @@ at91_clk_register_programmable(struct regmap *regmap, if (!prog) return ERR_PTR(-ENOMEM); - prog->clk.name = name; - prog->clk.ops = &programmable_ops; - memcpy(prog->parent_names, parent_names, - num_parents * sizeof(prog->parent_names[0])); - prog->clk.parent_names = &prog->parent_names[0]; - prog->clk.num_parents = num_parents; - /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + init.name = name; + init.ops = &programmable_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; prog->id = id; prog->layout = layout; + prog->hw.init = &init; prog->regmap = regmap; + prog->mux_table = mux_table; - ret = clk_register(&prog->clk); + hw = &prog->hw; + ret = clk_hw_register(NULL, &prog->hw); if (ret) { kfree(prog); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &prog->clk; + return hw; } 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, }; |