diff options
Diffstat (limited to 'drivers/clk/at91/clk-main.c')
-rw-r--r-- | drivers/clk/at91/clk-main.c | 302 |
1 files changed, 168 insertions, 134 deletions
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index abae35566c..a1dd327b56 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -1,62 +1,64 @@ +// 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 <linux/list.h> -#include <linux/clk.h> + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> +#include <clock.h> #include <mfd/syscon.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/printk.h> #include "pmc.h" #define SLOW_CLOCK_FREQ 32768 #define MAINF_DIV 16 -#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \ +#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \ SLOW_CLOCK_FREQ) -#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ) #define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT #define MOR_KEY_MASK (0xff << 16) +#define clk_main_parent_select(s) (((s) & \ + (AT91_PMC_MOSCEN | \ + AT91_PMC_OSCBYPASS)) ? 1 : 0) + struct clk_main_osc { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent; + struct at91_clk_pms pms; }; -#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk) +#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw) struct clk_main_rc_osc { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; unsigned long frequency; + unsigned long accuracy; + struct at91_clk_pms pms; }; -#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk) +#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw) struct clk_rm9200_main { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; - const char *parent; }; -#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk) +#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw) struct clk_sam9x5_main { - struct clk clk; + struct clk_hw hw; struct regmap *regmap; + struct at91_clk_pms pms; u8 parent; }; -#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk) +#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw) static inline bool clk_main_osc_ready(struct regmap *regmap) { @@ -67,9 +69,9 @@ static inline bool clk_main_osc_ready(struct regmap *regmap) return status & AT91_PMC_MOSCS; } -static int clk_main_osc_enable(struct clk *clk) +static int clk_main_osc_prepare(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp; @@ -85,14 +87,14 @@ static int clk_main_osc_enable(struct clk *clk) } while (!clk_main_osc_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static void clk_main_osc_disable(struct clk *clk) +static void clk_main_osc_unprepare(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp; @@ -107,9 +109,9 @@ static void clk_main_osc_disable(struct clk *clk) regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY); } -static int clk_main_osc_is_enabled(struct clk *clk) +static int clk_main_osc_is_prepared(struct clk_hw *hw) { - struct clk_main_osc *osc = to_clk_main_osc(clk); + struct clk_main_osc *osc = to_clk_main_osc(hw); struct regmap *regmap = osc->regmap; u32 tmp, status; @@ -119,49 +121,56 @@ static int clk_main_osc_is_enabled(struct clk *clk) regmap_read(regmap, AT91_PMC_SR, &status); - return (status & AT91_PMC_MOSCS) && (tmp & AT91_PMC_MOSCEN); + return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp); } static const struct clk_ops main_osc_ops = { - .enable = clk_main_osc_enable, - .disable = clk_main_osc_disable, - .is_enabled = clk_main_osc_is_enabled, + .enable = clk_main_osc_prepare, + .disable = clk_main_osc_unprepare, + .is_enabled = clk_main_osc_is_prepared, }; -struct clk * +struct clk_hw * __init at91_clk_register_main_osc(struct regmap *regmap, const char *name, const char *parent_name, bool bypass) { struct clk_main_osc *osc; + struct clk_init_data init; + struct clk_hw *hw; int ret; if (!name || !parent_name) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &main_osc_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_IGNORE_UNUSED; - osc->parent = parent_name; - osc->clk.name = name; - osc->clk.ops = &main_osc_ops; - osc->clk.parent_names = &osc->parent; - osc->clk.num_parents = 1; + osc->hw.init = &init; osc->regmap = regmap; if (bypass) - regmap_write_bits(regmap, - AT91_CKGR_MOR, MOR_KEY_MASK | - AT91_PMC_MOSCEN, - AT91_PMC_OSCBYPASS | AT91_PMC_KEY); + regmap_update_bits(regmap, + AT91_CKGR_MOR, MOR_KEY_MASK | + AT91_PMC_OSCBYPASS, + AT91_PMC_OSCBYPASS | AT91_PMC_KEY); - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, &osc->hw); if (ret) { - free(osc); - return ERR_PTR(ret); + kfree(osc); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } static bool clk_main_rc_osc_ready(struct regmap *regmap) @@ -170,31 +179,31 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap) regmap_read(regmap, AT91_PMC_SR, &status); - return status & AT91_PMC_MOSCRCS; + return !!(status & AT91_PMC_MOSCRCS); } -static int clk_main_rc_osc_enable(struct clk *clk) +static int clk_main_rc_osc_prepare(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor; regmap_read(regmap, AT91_CKGR_MOR, &mor); if (!(mor & AT91_PMC_MOSCRCEN)) - regmap_write_bits(regmap, AT91_CKGR_MOR, - MOR_KEY_MASK | AT91_PMC_MOSCRCEN, - AT91_PMC_MOSCRCEN | AT91_PMC_KEY); + regmap_update_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, + AT91_PMC_MOSCRCEN | AT91_PMC_KEY); while (!clk_main_rc_osc_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static void clk_main_rc_osc_disable(struct clk *clk) +static void clk_main_rc_osc_unprepare(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor; @@ -203,13 +212,13 @@ static void clk_main_rc_osc_disable(struct clk *clk) if (!(mor & AT91_PMC_MOSCRCEN)) return; - regmap_write_bits(regmap, AT91_CKGR_MOR, - MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); + regmap_update_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); } -static int clk_main_rc_osc_is_enabled(struct clk *clk) +static int clk_main_rc_osc_is_prepared(struct clk_hw *hw) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); struct regmap *regmap = osc->regmap; unsigned int mor, status; @@ -219,61 +228,71 @@ static int clk_main_rc_osc_is_enabled(struct clk *clk) return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS); } -static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk, +static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw); return osc->frequency; } static const struct clk_ops main_rc_osc_ops = { - .enable = clk_main_rc_osc_enable, - .disable = clk_main_rc_osc_disable, - .is_enabled = clk_main_rc_osc_is_enabled, + .enable = clk_main_rc_osc_prepare, + .disable = clk_main_rc_osc_unprepare, + .is_enabled = clk_main_rc_osc_is_prepared, .recalc_rate = clk_main_rc_osc_recalc_rate, }; -struct clk * +struct clk_hw * __init at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name, u32 frequency, u32 accuracy) { - int ret; struct clk_main_rc_osc *osc; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name || !frequency) return ERR_PTR(-EINVAL); - osc = xzalloc(sizeof(*osc)); + osc = kzalloc(sizeof(*osc), GFP_KERNEL); + if (!osc) + return ERR_PTR(-ENOMEM); - osc->clk.name = name; - osc->clk.ops = &main_rc_osc_ops; - osc->clk.parent_names = NULL; - osc->clk.num_parents = 0; + init.name = name; + init.ops = &main_rc_osc_ops; + init.parent_names = NULL; + init.num_parents = 0; + init.flags = CLK_IGNORE_UNUSED; + osc->hw.init = &init; osc->regmap = regmap; osc->frequency = frequency; + osc->accuracy = accuracy; - ret = clk_register(&osc->clk); + hw = &osc->hw; + ret = clk_hw_register(NULL, hw); if (ret) { kfree(osc); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &osc->clk; + return hw; } static int clk_main_probe_frequency(struct regmap *regmap) { + u64 start_time; unsigned int mcfr; - uint64_t start = get_time_ns(); + start_time = get_time_ns(); do { regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); if (mcfr & AT91_PMC_MAINRDY) return 0; - } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND)); + udelay(MAINF_LOOP_MIN_WAIT); + } while (!is_timeout(start_time, MAINFRDY_TIMEOUT * NSEC_PER_USEC)); return -ETIMEDOUT; } @@ -294,44 +313,46 @@ static unsigned long clk_main_recalc_rate(struct regmap *regmap, return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV; } -static int clk_rm9200_main_enable(struct clk *clk) +static int clk_rm9200_main_prepare(struct clk_hw *hw) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); return clk_main_probe_frequency(clkmain->regmap); } -static int clk_rm9200_main_is_enabled(struct clk *clk) +static int clk_rm9200_main_is_prepared(struct clk_hw *hw) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); unsigned int status; regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status); - return status & AT91_PMC_MAINRDY ? 1 : 0; + return !!(status & AT91_PMC_MAINRDY); } -static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk, +static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw); return clk_main_recalc_rate(clkmain->regmap, parent_rate); } static const struct clk_ops rm9200_main_ops = { - .enable = clk_rm9200_main_enable, - .is_enabled = clk_rm9200_main_is_enabled, + .enable = clk_rm9200_main_prepare, + .is_enabled = clk_rm9200_main_is_prepared, .recalc_rate = clk_rm9200_main_recalc_rate, }; -struct clk * +struct clk_hw * __init at91_clk_register_rm9200_main(struct regmap *regmap, const char *name, const char *parent_name) { - int ret; struct clk_rm9200_main *clkmain; + struct clk_init_data init; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -339,22 +360,27 @@ at91_clk_register_rm9200_main(struct regmap *regmap, if (!parent_name) return ERR_PTR(-EINVAL); - clkmain = xzalloc(sizeof(*clkmain)); + clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); + if (!clkmain) + return ERR_PTR(-ENOMEM); - clkmain->parent = parent_name; - clkmain->clk.name = name; - clkmain->clk.ops = &rm9200_main_ops; - clkmain->clk.parent_names = &clkmain->parent; - clkmain->clk.num_parents = 1; + init.name = name; + init.ops = &rm9200_main_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + clkmain->hw.init = &init; clkmain->regmap = regmap; - ret = clk_register(&clkmain->clk); + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); if (ret) { kfree(clkmain); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &clkmain->clk; + return hw; } static inline bool clk_sam9x5_main_ready(struct regmap *regmap) @@ -363,38 +389,38 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap) regmap_read(regmap, AT91_PMC_SR, &status); - return status & AT91_PMC_MOSCSELS ? 1 : 0; + return !!(status & AT91_PMC_MOSCSELS); } -static int clk_sam9x5_main_enable(struct clk *clk) +static int clk_sam9x5_main_prepare(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); struct regmap *regmap = clkmain->regmap; while (!clk_sam9x5_main_ready(regmap)) - barrier(); + cpu_relax(); return clk_main_probe_frequency(regmap); } -static int clk_sam9x5_main_is_enabled(struct clk *clk) +static int clk_sam9x5_main_is_prepared(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); return clk_sam9x5_main_ready(clkmain->regmap); } -static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk, +static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); return clk_main_recalc_rate(clkmain->regmap, parent_rate); } -static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index) +static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); struct regmap *regmap = clkmain->regmap; unsigned int tmp; @@ -402,47 +428,53 @@ static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index) return -EINVAL; regmap_read(regmap, AT91_CKGR_MOR, &tmp); - tmp &= ~MOR_KEY_MASK; if (index && !(tmp & AT91_PMC_MOSCSEL)) - regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL); + tmp = AT91_PMC_MOSCSEL; else if (!index && (tmp & AT91_PMC_MOSCSEL)) - regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL); + tmp = 0; + else + return 0; + + regmap_update_bits(regmap, AT91_CKGR_MOR, + AT91_PMC_MOSCSEL | MOR_KEY_MASK, + tmp | AT91_PMC_KEY); while (!clk_sam9x5_main_ready(regmap)) - barrier(); + cpu_relax(); return 0; } -static int clk_sam9x5_main_get_parent(struct clk *clk) +static int clk_sam9x5_main_get_parent(struct clk_hw *hw) { - struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw); unsigned int status; regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); - return status & AT91_PMC_MOSCEN ? 1 : 0; + return clk_main_parent_select(status); } static const struct clk_ops sam9x5_main_ops = { - .enable = clk_sam9x5_main_enable, - .is_enabled = clk_sam9x5_main_is_enabled, + .enable = clk_sam9x5_main_prepare, + .is_enabled = clk_sam9x5_main_is_prepared, .recalc_rate = clk_sam9x5_main_recalc_rate, .set_parent = clk_sam9x5_main_set_parent, .get_parent = clk_sam9x5_main_get_parent, }; -struct clk * +struct clk_hw * __init at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name, const char **parent_names, int num_parents) { - int ret; - unsigned int status; - size_t parents_array_size; struct clk_sam9x5_main *clkmain; + struct clk_init_data init; + unsigned int status; + struct clk_hw *hw; + int ret; if (!name) return ERR_PTR(-EINVAL); @@ -450,25 +482,27 @@ at91_clk_register_sam9x5_main(struct regmap *regmap, if (!parent_names || !num_parents) return ERR_PTR(-EINVAL); - clkmain = xzalloc(sizeof(*clkmain)); - - clkmain->clk.name = name; - clkmain->clk.ops = &sam9x5_main_ops; - parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]); - clkmain->clk.parent_names = xmemdup(parent_names, parents_array_size); - clkmain->clk.num_parents = num_parents; + clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL); + if (!clkmain) + return ERR_PTR(-ENOMEM); - /* init.flags = CLK_SET_PARENT_GATE; */ + init.name = name; + init.ops = &sam9x5_main_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_PARENT_GATE; + clkmain->hw.init = &init; clkmain->regmap = regmap; regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); - clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0; + clkmain->parent = clk_main_parent_select(status); - ret = clk_register(&clkmain->clk); + hw = &clkmain->hw; + ret = clk_hw_register(NULL, &clkmain->hw); if (ret) { kfree(clkmain); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &clkmain->clk; + return hw; } |