From 61598093bbdd283a7edc367d900f223070ead8d2 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:43:03 +0000 Subject: [PATCH] add ax88772C AX88772C_eeprom_tools --- kernel/drivers/clk/ingenic/cgu.c | 228 ++++++++++++++++++++++++++++++--------------------------- 1 files changed, 120 insertions(+), 108 deletions(-) diff --git a/kernel/drivers/clk/ingenic/cgu.c b/kernel/drivers/clk/ingenic/cgu.c index b40160e..2b9bb7d 100644 --- a/kernel/drivers/clk/ingenic/cgu.c +++ b/kernel/drivers/clk/ingenic/cgu.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Ingenic SoC CGU driver * * Copyright (c) 2013-2015 Imagination Technologies * Author: Paul Burton <paul.burton@mips.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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/bitops.h> @@ -20,14 +11,24 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> #include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/math64.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/time.h> + #include "cgu.h" #define MHZ (1000 * 1000) + +static inline const struct ingenic_cgu_clk_info * +to_clk_info(struct ingenic_clk *clk) +{ + return &clk->cgu->clock_info[clk->idx]; +} /** * ingenic_cgu_gate_get() - get the value of clock gate register bit @@ -79,21 +80,17 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; const struct ingenic_cgu_pll_info *pll_info; unsigned m, n, od_enc, od; - bool bypass, enable; - unsigned long flags; + bool bypass; u32 ctl; - clk_info = &cgu->clock_info[ingenic_clk->idx]; BUG_ON(clk_info->type != CGU_CLK_PLL); pll_info = &clk_info->pll; - spin_lock_irqsave(&cgu->lock, flags); ctl = readl(cgu->base + pll_info->reg); - spin_unlock_irqrestore(&cgu->lock, flags); m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); m += pll_info->m_offset; @@ -101,9 +98,11 @@ n += pll_info->n_offset; od_enc = ctl >> pll_info->od_shift; od_enc &= GENMASK(pll_info->od_bits - 1, 0); + + ctl = readl(cgu->base + pll_info->bypass_reg); + bypass = !pll_info->no_bypass_bit && !!(ctl & BIT(pll_info->bypass_bit)); - enable = !!(ctl & BIT(pll_info->enable_bit)); if (bypass) return parent_rate; @@ -115,7 +114,8 @@ BUG_ON(od == pll_info->od_max); od++; - return div_u64((u64)parent_rate * m, n * od); + return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, + n * od); } static unsigned long @@ -148,19 +148,8 @@ if (pod) *pod = od; - return div_u64((u64)parent_rate * m, n * od); -} - -static inline const struct ingenic_cgu_clk_info *to_clk_info( - struct ingenic_clk *ingenic_clk) -{ - struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; - BUG_ON(clk_info->type != CGU_CLK_PLL); - - return clk_info; + return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, + n * od); } static long @@ -173,6 +162,16 @@ return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); } +static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu, + const struct ingenic_cgu_pll_info *pll_info) +{ + u32 ctl; + + return readl_poll_timeout(cgu->base + pll_info->reg, ctl, + ctl & BIT(pll_info->stable_bit), + 0, 100 * USEC_PER_MSEC); +} + static int ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, unsigned long parent_rate) @@ -183,6 +182,7 @@ const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; unsigned long rate, flags; unsigned int m, n, od; + int ret = 0; u32 ctl; rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, @@ -204,9 +204,14 @@ ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; writel(ctl, cgu->base + pll_info->reg); + + /* If the PLL is enabled, verify that it's stable */ + if (ctl & BIT(pll_info->enable_bit)) + ret = ingenic_pll_check_stable(cgu, pll_info); + spin_unlock_irqrestore(&cgu->lock, flags); - return 0; + return ret; } static int ingenic_pll_enable(struct clk_hw *hw) @@ -215,33 +220,27 @@ struct ingenic_cgu *cgu = ingenic_clk->cgu; const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; - const unsigned int timeout = 100; unsigned long flags; - unsigned int i; + int ret; u32 ctl; spin_lock_irqsave(&cgu->lock, flags); - ctl = readl(cgu->base + pll_info->reg); + ctl = readl(cgu->base + pll_info->bypass_reg); ctl &= ~BIT(pll_info->bypass_bit); + + writel(ctl, cgu->base + pll_info->bypass_reg); + + ctl = readl(cgu->base + pll_info->reg); + ctl |= BIT(pll_info->enable_bit); writel(ctl, cgu->base + pll_info->reg); - /* wait for the PLL to stabilise */ - for (i = 0; i < timeout; i++) { - ctl = readl(cgu->base + pll_info->reg); - if (ctl & BIT(pll_info->stable_bit)) - break; - mdelay(1); - } - + ret = ingenic_pll_check_stable(cgu, pll_info); spin_unlock_irqrestore(&cgu->lock, flags); - if (i == timeout) - return -EBUSY; - - return 0; + return ret; } static void ingenic_pll_disable(struct clk_hw *hw) @@ -268,12 +267,9 @@ struct ingenic_cgu *cgu = ingenic_clk->cgu; const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; - unsigned long flags; u32 ctl; - spin_lock_irqsave(&cgu->lock, flags); ctl = readl(cgu->base + pll_info->reg); - spin_unlock_irqrestore(&cgu->lock, flags); return !!(ctl & BIT(pll_info->enable_bit)); } @@ -295,12 +291,10 @@ static u8 ingenic_clk_get_parent(struct clk_hw *hw) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; u32 reg; u8 i, hw_idx, idx = 0; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_MUX) { reg = readl(cgu->base + clk_info->mux.reg); @@ -323,13 +317,11 @@ static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; unsigned long flags; u8 curr_idx, hw_idx, num_poss; u32 reg, mask; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_MUX) { /* @@ -373,19 +365,20 @@ ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; unsigned long rate = parent_rate; u32 div_reg, div; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_DIV) { div_reg = readl(cgu->base + clk_info->div.reg); div = (div_reg >> clk_info->div.shift) & GENMASK(clk_info->div.bits - 1, 0); - div += 1; - div *= clk_info->div.div; + + if (clk_info->div.div_table) + div = clk_info->div.div_table[div]; + else + div = (div + 1) * clk_info->div.div; rate /= div; } else if (clk_info->type & CGU_CLK_FIXDIV) { @@ -395,25 +388,52 @@ return rate; } +static unsigned int +ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, + unsigned int div) +{ + unsigned int i, best_i = 0, best = (unsigned int)-1; + + for (i = 0; i < (1 << clk_info->div.bits) + && clk_info->div.div_table[i]; i++) { + if (clk_info->div.div_table[i] >= div && + clk_info->div.div_table[i] < best) { + best = clk_info->div.div_table[i]; + best_i = i; + + if (div == best) + break; + } + } + + return best_i; +} + static unsigned ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, unsigned long parent_rate, unsigned long req_rate) { - unsigned div; + unsigned int div, hw_div; /* calculate the divide */ div = DIV_ROUND_UP(parent_rate, req_rate); - /* and impose hardware constraints */ - div = min_t(unsigned, div, 1 << clk_info->div.bits); - div = max_t(unsigned, div, 1); + if (clk_info->div.div_table) { + hw_div = ingenic_clk_calc_hw_div(clk_info, div); + + return clk_info->div.div_table[hw_div]; + } + + /* Impose hardware constraints */ + div = clamp_t(unsigned int, div, clk_info->div.div, + clk_info->div.div << clk_info->div.bits); /* * If the divider value itself must be divided before being written to * the divider register, we must ensure we don't have any bits set that * would be lost as a result of doing so. */ - div /= clk_info->div.div; + div = DIV_ROUND_UP(div, clk_info->div.div); div *= clk_info->div.div; return div; @@ -424,18 +444,27 @@ unsigned long *parent_rate) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); - struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); unsigned int div = 1; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_DIV) div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); else if (clk_info->type & CGU_CLK_FIXDIV) div = clk_info->fixdiv.div; + else if (clk_hw_can_set_rate_parent(hw)) + *parent_rate = req_rate; return DIV_ROUND_UP(*parent_rate, div); +} + +static inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu, + const struct ingenic_cgu_clk_info *clk_info) +{ + u32 reg; + + return readl_poll_timeout(cgu->base + clk_info->div.reg, reg, + !(reg & BIT(clk_info->div.busy_bit)), + 0, 100 * USEC_PER_MSEC); } static int @@ -443,15 +472,12 @@ unsigned long parent_rate) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; - const unsigned timeout = 100; unsigned long rate, flags; - unsigned div, i; + unsigned int hw_div, div; u32 reg, mask; int ret = 0; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_DIV) { div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); @@ -460,13 +486,18 @@ if (rate != req_rate) return -EINVAL; + if (clk_info->div.div_table) + hw_div = ingenic_clk_calc_hw_div(clk_info, div); + else + hw_div = ((div / clk_info->div.div) - 1); + spin_lock_irqsave(&cgu->lock, flags); reg = readl(cgu->base + clk_info->div.reg); /* update the divide */ mask = GENMASK(clk_info->div.bits - 1, 0); reg &= ~(mask << clk_info->div.shift); - reg |= ((div / clk_info->div.div) - 1) << clk_info->div.shift; + reg |= hw_div << clk_info->div.shift; /* clear the stop bit */ if (clk_info->div.stop_bit != -1) @@ -480,16 +511,8 @@ writel(reg, cgu->base + clk_info->div.reg); /* wait for the change to take effect */ - if (clk_info->div.busy_bit != -1) { - for (i = 0; i < timeout; i++) { - reg = readl(cgu->base + clk_info->div.reg); - if (!(reg & BIT(clk_info->div.busy_bit))) - break; - mdelay(1); - } - if (i == timeout) - ret = -EBUSY; - } + if (clk_info->div.busy_bit != -1) + ret = ingenic_clk_check_stable(cgu, clk_info); spin_unlock_irqrestore(&cgu->lock, flags); return ret; @@ -501,11 +524,9 @@ static int ingenic_clk_enable(struct clk_hw *hw) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; unsigned long flags; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_GATE) { /* ungate the clock */ @@ -523,11 +544,9 @@ static void ingenic_clk_disable(struct clk_hw *hw) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; unsigned long flags; - - clk_info = &cgu->clock_info[ingenic_clk->idx]; if (clk_info->type & CGU_CLK_GATE) { /* gate the clock */ @@ -540,18 +559,12 @@ static int ingenic_clk_is_enabled(struct clk_hw *hw) { struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); + const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); struct ingenic_cgu *cgu = ingenic_clk->cgu; - const struct ingenic_cgu_clk_info *clk_info; - unsigned long flags; int enabled = 1; - clk_info = &cgu->clock_info[ingenic_clk->idx]; - - if (clk_info->type & CGU_CLK_GATE) { - spin_lock_irqsave(&cgu->lock, flags); + if (clk_info->type & CGU_CLK_GATE) enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate); - spin_unlock_irqrestore(&cgu->lock, flags); - } return enabled; } @@ -624,6 +637,13 @@ caps = clk_info->type; + if (caps & CGU_CLK_DIV) { + caps &= ~CGU_CLK_DIV; + } else if (!(caps & CGU_CLK_CUSTOM)) { + /* pass rate changes to the parent clock */ + clk_init.flags |= CLK_SET_RATE_PARENT; + } + if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { clk_init.num_parents = 0; @@ -663,7 +683,6 @@ } } else if (caps & CGU_CLK_PLL) { clk_init.ops = &ingenic_pll_ops; - clk_init.flags |= CLK_SET_RATE_GATE; caps &= ~CGU_CLK_PLL; @@ -684,13 +703,6 @@ clk_init.flags |= CLK_SET_PARENT_GATE; caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); - } - - if (caps & CGU_CLK_DIV) { - caps &= ~CGU_CLK_DIV; - } else { - /* pass rate changes to the parent clock */ - clk_init.flags |= CLK_SET_RATE_PARENT; } if (caps) { -- Gitblit v1.6.2