brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · ff3fb8b Raw
361 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2023 Nuvoton Technology Corp.4 * Author: Chi-Fang Li <cfli0@nuvoton.com>5 */6 7#include <linux/bitfield.h>8#include <linux/clk-provider.h>9#include <linux/container_of.h>10#include <linux/device.h>11#include <linux/io.h>12#include <linux/kernel.h>13#include <linux/math64.h>14#include <linux/slab.h>15#include <linux/units.h>16#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>17 18#include "clk-ma35d1.h"19 20/* PLL frequency limits */21#define PLL_FREF_MAX_FREQ	(200 * HZ_PER_MHZ)22#define PLL_FREF_MIN_FREQ	(1 * HZ_PER_MHZ)23#define PLL_FREF_M_MAX_FREQ	(40 * HZ_PER_MHZ)24#define PLL_FREF_M_MIN_FREQ	(10 * HZ_PER_MHZ)25#define PLL_FCLK_MAX_FREQ	(2400 * HZ_PER_MHZ)26#define PLL_FCLK_MIN_FREQ	(600 * HZ_PER_MHZ)27#define PLL_FCLKO_MAX_FREQ	(2400 * HZ_PER_MHZ)28#define PLL_FCLKO_MIN_FREQ	(85700 * HZ_PER_KHZ)29#define PLL_SS_RATE		0x7730#define PLL_SLOPE		0x58CFA31 32#define REG_PLL_CTL0_OFFSET	0x033#define REG_PLL_CTL1_OFFSET	0x434#define REG_PLL_CTL2_OFFSET	0x835 36/* bit fields for REG_CLK_PLL0CTL0, which is SMIC PLL design */37#define SPLL0_CTL0_FBDIV	GENMASK(7, 0)38#define SPLL0_CTL0_INDIV	GENMASK(11, 8)39#define SPLL0_CTL0_OUTDIV	GENMASK(13, 12)40#define SPLL0_CTL0_PD		BIT(16)41#define SPLL0_CTL0_BP		BIT(17)42 43/* bit fields for REG_CLK_PLLxCTL0 ~ REG_CLK_PLLxCTL2, where x = 2 ~ 5 */44#define PLL_CTL0_FBDIV		GENMASK(10, 0)45#define PLL_CTL0_INDIV		GENMASK(17, 12)46#define PLL_CTL0_MODE		GENMASK(19, 18)47#define PLL_CTL0_SSRATE		GENMASK(30, 20)48#define PLL_CTL1_PD		BIT(0)49#define PLL_CTL1_BP		BIT(1)50#define PLL_CTL1_OUTDIV		GENMASK(6, 4)51#define PLL_CTL1_FRAC		GENMASK(31, 24)52#define PLL_CTL2_SLOPE		GENMASK(23, 0)53 54#define INDIV_MIN		155#define INDIV_MAX		6356#define FBDIV_MIN		1657#define FBDIV_MAX		204758#define FBDIV_FRAC_MIN		160059#define FBDIV_FRAC_MAX		20470060#define OUTDIV_MIN		161#define OUTDIV_MAX		762 63#define PLL_MODE_INT            064#define PLL_MODE_FRAC           165#define PLL_MODE_SS             266 67struct ma35d1_clk_pll {68	struct clk_hw hw;69	u32 id;70	u8 mode;71	void __iomem *ctl0_base;72	void __iomem *ctl1_base;73	void __iomem *ctl2_base;74};75 76static inline struct ma35d1_clk_pll *to_ma35d1_clk_pll(struct clk_hw *_hw)77{78	return container_of(_hw, struct ma35d1_clk_pll, hw);79}80 81static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,82					       unsigned long parent_rate)83{84	u32 m, n, p, outdiv;85	u64 pll_freq;86 87	if (pll0_ctl0 & SPLL0_CTL0_BP)88		return parent_rate;89 90	n = FIELD_GET(SPLL0_CTL0_FBDIV, pll0_ctl0);91	m = FIELD_GET(SPLL0_CTL0_INDIV, pll0_ctl0);92	p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);93	outdiv = 1 << p;94	pll_freq = (u64)parent_rate * n;95	div_u64(pll_freq, m * outdiv);96	return pll_freq;97}98 99static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long parent_rate)100{101	unsigned long pll_freq, x;102	u32 m, n, p;103 104	if (reg_ctl[1] & PLL_CTL1_BP)105		return parent_rate;106 107	n = FIELD_GET(PLL_CTL0_FBDIV, reg_ctl[0]);108	m = FIELD_GET(PLL_CTL0_INDIV, reg_ctl[0]);109	p = FIELD_GET(PLL_CTL1_OUTDIV, reg_ctl[1]);110 111	if (mode == PLL_MODE_INT) {112		pll_freq = (u64)parent_rate * n;113		div_u64(pll_freq, m * p);114	} else {115		x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);116		/* 2 decimal places floating to integer (ex. 1.23 to 123) */117		n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));118		pll_freq = div_u64(parent_rate * n, 100 * m * p);119	}120	return pll_freq;121}122 123static int ma35d1_pll_find_closest(struct ma35d1_clk_pll *pll, unsigned long rate,124				   unsigned long parent_rate, u32 *reg_ctl,125				   unsigned long *freq)126{127	unsigned long min_diff = ULONG_MAX;128	int fbdiv_min, fbdiv_max;129	int p, m, n;130 131	*freq = 0;132	if (rate < PLL_FCLKO_MIN_FREQ || rate > PLL_FCLKO_MAX_FREQ)133		return -EINVAL;134 135	if (pll->mode == PLL_MODE_INT) {136		fbdiv_min = FBDIV_MIN;137		fbdiv_max = FBDIV_MAX;138	} else {139		fbdiv_min = FBDIV_FRAC_MIN;140		fbdiv_max = FBDIV_FRAC_MAX;141	}142 143	for (m = INDIV_MIN; m <= INDIV_MAX; m++) {144		for (n = fbdiv_min; n <= fbdiv_max; n++) {145			for (p = OUTDIV_MIN; p <= OUTDIV_MAX; p++) {146				unsigned long tmp, fout, fclk, diff;147 148				tmp = div_u64(parent_rate, m);149				if (tmp < PLL_FREF_M_MIN_FREQ ||150				    tmp > PLL_FREF_M_MAX_FREQ)151					continue; /* constrain */152 153				fclk = div_u64(parent_rate * n, m);154				/* for 2 decimal places */155				if (pll->mode != PLL_MODE_INT)156					fclk = div_u64(fclk, 100);157 158				if (fclk < PLL_FCLK_MIN_FREQ ||159				    fclk > PLL_FCLK_MAX_FREQ)160					continue; /* constrain */161 162				fout = div_u64(fclk, p);163				if (fout < PLL_FCLKO_MIN_FREQ ||164				    fout > PLL_FCLKO_MAX_FREQ)165					continue; /* constrain */166 167				diff = abs(rate - fout);168				if (diff < min_diff) {169					reg_ctl[0] = FIELD_PREP(PLL_CTL0_INDIV, m) |170						     FIELD_PREP(PLL_CTL0_FBDIV, n);171					reg_ctl[1] = FIELD_PREP(PLL_CTL1_OUTDIV, p);172					*freq = fout;173					min_diff = diff;174					if (min_diff == 0)175						break;176				}177			}178		}179	}180	if (*freq == 0)181		return -EINVAL; /* cannot find even one valid setting */182	return 0;183}184 185static int ma35d1_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,186				   unsigned long parent_rate)187{188	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);189	u32 reg_ctl[3] = { 0 };190	unsigned long pll_freq;191	int ret;192 193	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)194		return -EINVAL;195 196	ret = ma35d1_pll_find_closest(pll, rate, parent_rate, reg_ctl, &pll_freq);197	if (ret != 0)198		return ret;199 200	switch (pll->mode) {201	case PLL_MODE_INT:202		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_INT);203		break;204	case PLL_MODE_FRAC:205		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_FRAC);206		break;207	case PLL_MODE_SS:208		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_SS) |209			      FIELD_PREP(PLL_CTL0_SSRATE, PLL_SS_RATE);210		reg_ctl[2] = FIELD_PREP(PLL_CTL2_SLOPE, PLL_SLOPE);211		break;212	}213	reg_ctl[1] |= PLL_CTL1_PD;214 215	writel_relaxed(reg_ctl[0], pll->ctl0_base);216	writel_relaxed(reg_ctl[1], pll->ctl1_base);217	writel_relaxed(reg_ctl[2], pll->ctl2_base);218	return 0;219}220 221static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)222{223	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);224	u32 reg_ctl[3];225	unsigned long pll_freq;226 227	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)228		return 0;229 230	switch (pll->id) {231	case CAPLL:232		reg_ctl[0] = readl_relaxed(pll->ctl0_base);233		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], parent_rate);234		return pll_freq;235	case DDRPLL:236	case APLL:237	case EPLL:238	case VPLL:239		reg_ctl[0] = readl_relaxed(pll->ctl0_base);240		reg_ctl[1] = readl_relaxed(pll->ctl1_base);241		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, parent_rate);242		return pll_freq;243	}244	return 0;245}246 247static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,248				      unsigned long *parent_rate)249{250	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);251	u32 reg_ctl[3] = { 0 };252	unsigned long pll_freq;253	long ret;254 255	if (*parent_rate < PLL_FREF_MIN_FREQ || *parent_rate > PLL_FREF_MAX_FREQ)256		return -EINVAL;257 258	ret = ma35d1_pll_find_closest(pll, rate, *parent_rate, reg_ctl, &pll_freq);259	if (ret < 0)260		return ret;261 262	switch (pll->id) {263	case CAPLL:264		reg_ctl[0] = readl_relaxed(pll->ctl0_base);265		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], *parent_rate);266		return pll_freq;267	case DDRPLL:268	case APLL:269	case EPLL:270	case VPLL:271		reg_ctl[0] = readl_relaxed(pll->ctl0_base);272		reg_ctl[1] = readl_relaxed(pll->ctl1_base);273		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, *parent_rate);274		return pll_freq;275	}276	return 0;277}278 279static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)280{281	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);282	u32 val = readl_relaxed(pll->ctl1_base);283 284	return !(val & PLL_CTL1_PD);285}286 287static int ma35d1_clk_pll_prepare(struct clk_hw *hw)288{289	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);290	u32 val;291 292	val = readl_relaxed(pll->ctl1_base);293	val &= ~PLL_CTL1_PD;294	writel_relaxed(val, pll->ctl1_base);295	return 0;296}297 298static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)299{300	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);301	u32 val;302 303	val = readl_relaxed(pll->ctl1_base);304	val |= PLL_CTL1_PD;305	writel_relaxed(val, pll->ctl1_base);306}307 308static const struct clk_ops ma35d1_clk_pll_ops = {309	.is_prepared = ma35d1_clk_pll_is_prepared,310	.prepare = ma35d1_clk_pll_prepare,311	.unprepare = ma35d1_clk_pll_unprepare,312	.set_rate = ma35d1_clk_pll_set_rate,313	.recalc_rate = ma35d1_clk_pll_recalc_rate,314	.round_rate = ma35d1_clk_pll_round_rate,315};316 317static const struct clk_ops ma35d1_clk_fixed_pll_ops = {318	.recalc_rate = ma35d1_clk_pll_recalc_rate,319	.round_rate = ma35d1_clk_pll_round_rate,320};321 322struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,323				  struct clk_hw *parent_hw, void __iomem *base)324{325	struct clk_parent_data pdata = { .index = 0 };326	struct clk_init_data init = {};327	struct ma35d1_clk_pll *pll;328	struct clk_hw *hw;329	int ret;330 331	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);332	if (!pll)333		return ERR_PTR(-ENOMEM);334 335	pll->id = id;336	pll->mode = u8mode;337	pll->ctl0_base = base + REG_PLL_CTL0_OFFSET;338	pll->ctl1_base = base + REG_PLL_CTL1_OFFSET;339	pll->ctl2_base = base + REG_PLL_CTL2_OFFSET;340 341	init.name = name;342	init.flags = 0;343	pdata.hw = parent_hw;344	init.parent_data = &pdata;345	init.num_parents = 1;346 347	if (id == CAPLL || id == DDRPLL)348		init.ops = &ma35d1_clk_fixed_pll_ops;349	else350		init.ops = &ma35d1_clk_pll_ops;351 352	pll->hw.init = &init;353	hw = &pll->hw;354 355	ret = devm_clk_hw_register(dev, hw);356	if (ret)357		return ERR_PTR(ret);358	return hw;359}360EXPORT_SYMBOL_GPL(ma35d1_reg_clk_pll);361