diff options
Diffstat (limited to 'drivers/clk/at91/clk-utmi.c')
-rw-r--r-- | drivers/clk/at91/clk-utmi.c | 197 |
1 files changed, 142 insertions, 55 deletions
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index c40af34d0d..7d85e43024 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -1,22 +1,16 @@ +// 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 <of.h> #include <mfd/syscon.h> -#include <regmap.h> - +#include <linux/regmap.h> #include <soc/at91/atmel-sfr.h> +#include <linux/printk.h> #include "pmc.h" @@ -24,16 +18,16 @@ * The purpose of this clock is to generate a 480 MHz signal. A different * rate can't be configured. */ -#define UTMI_RATE 480000000 +#define UTMI_RATE 480000000 struct clk_utmi { - struct clk clk; - const char *parent; + struct clk_hw hw; struct regmap *regmap_pmc; struct regmap *regmap_sfr; + struct at91_clk_pms pms; }; -#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk) +#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) static inline bool clk_utmi_ready(struct regmap *regmap) { @@ -44,10 +38,10 @@ static inline bool clk_utmi_ready(struct regmap *regmap) return status & AT91_PMC_LOCKU; } -static int clk_utmi_enable(struct clk *clk) +static int clk_utmi_prepare(struct clk_hw *hw) { - struct clk *hw_parent; - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_hw *hw_parent; + struct clk_utmi *utmi = to_clk_utmi(hw); unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; unsigned int utmi_ref_clk_freq; @@ -58,8 +52,8 @@ static int clk_utmi_enable(struct clk *clk) * FREQ field of the SFR_UTMICKTRIM register to generate properly * the utmi clock. */ - hw_parent = clk_get_parent(clk); - parent_rate = clk_get_rate(hw_parent); + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); switch (parent_rate) { case 12000000: @@ -83,80 +77,173 @@ static int clk_utmi_enable(struct clk *clk) return -EINVAL; } - if (utmi->regmap_sfr) { - regmap_write_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, - AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); + regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, + AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); } else if (utmi_ref_clk_freq) { pr_err("UTMICK: sfr node required\n"); return -EINVAL; } - regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); while (!clk_utmi_ready(utmi->regmap_pmc)) - barrier(); + cpu_relax(); return 0; } -static int clk_utmi_is_enabled(struct clk *clk) +static int clk_utmi_is_prepared(struct clk_hw *hw) { - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_utmi *utmi = to_clk_utmi(hw); return clk_utmi_ready(utmi->regmap_pmc); } -static void clk_utmi_disable(struct clk *clk) +static void clk_utmi_unprepare(struct clk_hw *hw) { - struct clk_utmi *utmi = to_clk_utmi(clk); + struct clk_utmi *utmi = to_clk_utmi(hw); - regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, - AT91_PMC_UPLLEN, 0); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, + AT91_PMC_UPLLEN, 0); } -static unsigned long clk_utmi_recalc_rate(struct clk *clk, +static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - /* UTMI clk rate is fixed */ + /* UTMI clk rate is fixed. */ return UTMI_RATE; } static const struct clk_ops utmi_ops = { - .enable = clk_utmi_enable, - .disable = clk_utmi_disable, - .is_enabled = clk_utmi_is_enabled, + .enable = clk_utmi_prepare, + .disable = clk_utmi_unprepare, + .is_enabled = clk_utmi_is_prepared, .recalc_rate = clk_utmi_recalc_rate, }; -struct clk * __init -at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, - const char *name, const char *parent_name) +static struct clk_hw * __init +at91_clk_register_utmi_internal(struct regmap *regmap_pmc, + struct regmap *regmap_sfr, + const char *name, const char *parent_name, + const struct clk_ops *ops, unsigned long flags) { - int ret; struct clk_utmi *utmi; + struct clk_hw *hw; + struct clk_init_data init; + int ret; - utmi = xzalloc(sizeof(*utmi)); - - utmi->clk.name = name; - utmi->clk.ops = &utmi_ops; - - if (parent_name) { - utmi->parent = parent_name; - utmi->clk.parent_names = &utmi->parent; - utmi->clk.num_parents = 1; - } + utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return ERR_PTR(-ENOMEM); - /* utmi->clk.flags = CLK_SET_RATE_GATE; */ + init.name = name; + init.ops = ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = flags; + utmi->hw.init = &init; utmi->regmap_pmc = regmap_pmc; utmi->regmap_sfr = regmap_sfr; - ret = clk_register(&utmi->clk); + hw = &utmi->hw; + ret = clk_hw_register(NULL, &utmi->hw); if (ret) { kfree(utmi); - return ERR_PTR(ret); + hw = ERR_PTR(ret); } - return &utmi->clk; + return hw; +} + +struct clk_hw * __init +at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, + const char *name, const char *parent_name) +{ + return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name, + parent_name, &utmi_ops, CLK_SET_RATE_GATE); +} + +static int clk_utmi_sama7g5_prepare(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct clk_hw *hw_parent; + unsigned long parent_rate; + unsigned int val; + + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + switch (parent_rate) { + case 16000000: + val = 0; + break; + case 20000000: + val = 2; + break; + case 24000000: + val = 3; + break; + case 32000000: + val = 5; + break; + default: + pr_err("UTMICK: unsupported main_xtal rate\n"); + return -EINVAL; + } + + regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val); + + return 0; + +} + +static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw) +{ + struct clk_utmi *utmi = to_clk_utmi(hw); + struct clk_hw *hw_parent; + unsigned long parent_rate; + unsigned int val; + + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val); + switch (val & 0x7) { + case 0: + if (parent_rate == 16000000) + return 1; + break; + case 2: + if (parent_rate == 20000000) + return 1; + break; + case 3: + if (parent_rate == 24000000) + return 1; + break; + case 5: + if (parent_rate == 32000000) + return 1; + break; + default: + break; + } + + return 0; +} + +static const struct clk_ops sama7g5_utmi_ops = { + .enable = clk_utmi_sama7g5_prepare, + .is_enabled = clk_utmi_sama7g5_is_prepared, + .recalc_rate = clk_utmi_recalc_rate, +}; + +struct clk_hw * __init +at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name, + const char *parent_name) +{ + return at91_clk_register_utmi_internal(regmap_pmc, NULL, name, + parent_name, &sama7g5_utmi_ops, 0); } |