brintos

brintos / linux-shallow public Read only

0
0
Text · 13.8 KiB · 025b9df Raw
511 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Google, Inc.4 */5 6#define pr_fmt(fmt) "%s: " fmt, __func__7 8#include <linux/clk-provider.h>9#include <linux/io.h>10#include <linux/kernel.h>11#include <linux/printk.h>12#include <linux/slab.h>13 14#include "clk.h"15 16#define PLL_STATUS			0x017#define PLL_STATUS_LOCK			BIT(0)18 19#define PLL_CTRL1			0x420#define PLL_CTRL1_REFDIV_SHIFT		021#define PLL_CTRL1_REFDIV_MASK		0x3f22#define PLL_CTRL1_FBDIV_SHIFT		623#define PLL_CTRL1_FBDIV_MASK		0xfff24#define PLL_INT_CTRL1_POSTDIV1_SHIFT	1825#define PLL_INT_CTRL1_POSTDIV1_MASK	0x726#define PLL_INT_CTRL1_POSTDIV2_SHIFT	2127#define PLL_INT_CTRL1_POSTDIV2_MASK	0x728#define PLL_INT_CTRL1_PD		BIT(24)29#define PLL_INT_CTRL1_DSMPD		BIT(25)30#define PLL_INT_CTRL1_FOUTPOSTDIVPD	BIT(26)31#define PLL_INT_CTRL1_FOUTVCOPD		BIT(27)32 33#define PLL_CTRL2			0x834#define PLL_FRAC_CTRL2_FRAC_SHIFT	035#define PLL_FRAC_CTRL2_FRAC_MASK	0xffffff36#define PLL_FRAC_CTRL2_POSTDIV1_SHIFT	2437#define PLL_FRAC_CTRL2_POSTDIV1_MASK	0x738#define PLL_FRAC_CTRL2_POSTDIV2_SHIFT	2739#define PLL_FRAC_CTRL2_POSTDIV2_MASK	0x740#define PLL_INT_CTRL2_BYPASS		BIT(28)41 42#define PLL_CTRL3			0xc43#define PLL_FRAC_CTRL3_PD		BIT(0)44#define PLL_FRAC_CTRL3_DACPD		BIT(1)45#define PLL_FRAC_CTRL3_DSMPD		BIT(2)46#define PLL_FRAC_CTRL3_FOUTPOSTDIVPD	BIT(3)47#define PLL_FRAC_CTRL3_FOUT4PHASEPD	BIT(4)48#define PLL_FRAC_CTRL3_FOUTVCOPD	BIT(5)49 50#define PLL_CTRL4			0x1051#define PLL_FRAC_CTRL4_BYPASS		BIT(28)52 53#define MIN_PFD				9600000UL54#define MIN_VCO_LA			400000000UL55#define MAX_VCO_LA			1600000000UL56#define MIN_VCO_FRAC_INT		600000000UL57#define MAX_VCO_FRAC_INT		1600000000UL58#define MIN_VCO_FRAC_FRAC		600000000UL59#define MAX_VCO_FRAC_FRAC		2400000000UL60#define MIN_OUTPUT_LA			8000000UL61#define MAX_OUTPUT_LA			1600000000UL62#define MIN_OUTPUT_FRAC			12000000UL63#define MAX_OUTPUT_FRAC			1600000000UL64 65/* Fractional PLL operating modes */66enum pll_mode {67	PLL_MODE_FRAC,68	PLL_MODE_INT,69};70 71struct pistachio_clk_pll {72	struct clk_hw hw;73	void __iomem *base;74	struct pistachio_pll_rate_table *rates;75	unsigned int nr_rates;76};77 78static inline u32 pll_readl(struct pistachio_clk_pll *pll, u32 reg)79{80	return readl(pll->base + reg);81}82 83static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg)84{85	writel(val, pll->base + reg);86}87 88static inline void pll_lock(struct pistachio_clk_pll *pll)89{90	while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))91		cpu_relax();92}93 94static inline u64 do_div_round_closest(u64 dividend, u64 divisor)95{96	dividend += divisor / 2;97	return div64_u64(dividend, divisor);98}99 100static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)101{102	return container_of(hw, struct pistachio_clk_pll, hw);103}104 105static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw)106{107	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);108	u32 val;109 110	val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD;111	return val ? PLL_MODE_INT : PLL_MODE_FRAC;112}113 114static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode)115{116	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);117	u32 val;118 119	val = pll_readl(pll, PLL_CTRL3);120	if (mode == PLL_MODE_INT)121		val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD;122	else123		val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD);124 125	pll_writel(pll, val, PLL_CTRL3);126}127 128static struct pistachio_pll_rate_table *129pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,130	       unsigned long fout)131{132	unsigned int i;133 134	for (i = 0; i < pll->nr_rates; i++) {135		if (pll->rates[i].fref == fref && pll->rates[i].fout == fout)136			return &pll->rates[i];137	}138 139	return NULL;140}141 142static long pll_round_rate(struct clk_hw *hw, unsigned long rate,143			   unsigned long *parent_rate)144{145	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);146	unsigned int i;147 148	for (i = 0; i < pll->nr_rates; i++) {149		if (i > 0 && pll->rates[i].fref == *parent_rate &&150		    pll->rates[i].fout <= rate)151			return pll->rates[i - 1].fout;152	}153 154	return pll->rates[0].fout;155}156 157static int pll_gf40lp_frac_enable(struct clk_hw *hw)158{159	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);160	u32 val;161 162	val = pll_readl(pll, PLL_CTRL3);163	val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |164		 PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD);165	pll_writel(pll, val, PLL_CTRL3);166 167	val = pll_readl(pll, PLL_CTRL4);168	val &= ~PLL_FRAC_CTRL4_BYPASS;169	pll_writel(pll, val, PLL_CTRL4);170 171	pll_lock(pll);172 173	return 0;174}175 176static void pll_gf40lp_frac_disable(struct clk_hw *hw)177{178	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);179	u32 val;180 181	val = pll_readl(pll, PLL_CTRL3);182	val |= PLL_FRAC_CTRL3_PD;183	pll_writel(pll, val, PLL_CTRL3);184}185 186static int pll_gf40lp_frac_is_enabled(struct clk_hw *hw)187{188	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);189 190	return !(pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_PD);191}192 193static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,194				    unsigned long parent_rate)195{196	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);197	struct pistachio_pll_rate_table *params;198	int enabled = pll_gf40lp_frac_is_enabled(hw);199	u64 val, vco, old_postdiv1, old_postdiv2;200	const char *name = clk_hw_get_name(hw);201 202	if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)203		return -EINVAL;204 205	params = pll_get_params(pll, parent_rate, rate);206	if (!params || !params->refdiv)207		return -EINVAL;208 209	/* calculate vco */210	vco = params->fref;211	vco *= (params->fbdiv << 24) + params->frac;212	vco = div64_u64(vco, params->refdiv << 24);213 214	if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC)215		pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco,216			MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);217 218	val = div64_u64(params->fref, params->refdiv);219	if (val < MIN_PFD)220		pr_warn("%s: PFD %llu is too low (min %lu)\n",221			name, val, MIN_PFD);222	if (val > vco / 16)223		pr_warn("%s: PFD %llu is too high (max %llu)\n",224			name, val, vco / 16);225 226	val = pll_readl(pll, PLL_CTRL1);227	val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |228		 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT));229	val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |230		(params->fbdiv << PLL_CTRL1_FBDIV_SHIFT);231	pll_writel(pll, val, PLL_CTRL1);232 233	val = pll_readl(pll, PLL_CTRL2);234 235	old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &236		       PLL_FRAC_CTRL2_POSTDIV1_MASK;237	old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &238		       PLL_FRAC_CTRL2_POSTDIV2_MASK;239	if (enabled &&240	    (params->postdiv1 != old_postdiv1 ||241	     params->postdiv2 != old_postdiv2))242		pr_warn("%s: changing postdiv while PLL is enabled\n", name);243 244	if (params->postdiv2 > params->postdiv1)245		pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);246 247	val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) |248		 (PLL_FRAC_CTRL2_POSTDIV1_MASK <<249		  PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |250		 (PLL_FRAC_CTRL2_POSTDIV2_MASK <<251		  PLL_FRAC_CTRL2_POSTDIV2_SHIFT));252	val |= (params->frac << PLL_FRAC_CTRL2_FRAC_SHIFT) |253		(params->postdiv1 << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |254		(params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);255	pll_writel(pll, val, PLL_CTRL2);256 257	/* set operating mode */258	if (params->frac)259		pll_frac_set_mode(hw, PLL_MODE_FRAC);260	else261		pll_frac_set_mode(hw, PLL_MODE_INT);262 263	if (enabled)264		pll_lock(pll);265 266	return 0;267}268 269static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,270						 unsigned long parent_rate)271{272	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);273	u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate;274 275	val = pll_readl(pll, PLL_CTRL1);276	prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;277	fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;278 279	val = pll_readl(pll, PLL_CTRL2);280	postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &281		PLL_FRAC_CTRL2_POSTDIV1_MASK;282	postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &283		PLL_FRAC_CTRL2_POSTDIV2_MASK;284	frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;285 286	/* get operating mode (int/frac) and calculate rate accordingly */287	rate = parent_rate;288	if (pll_frac_get_mode(hw) == PLL_MODE_FRAC)289		rate *= (fbdiv << 24) + frac;290	else291		rate *= (fbdiv << 24);292 293	rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);294 295	return rate;296}297 298static const struct clk_ops pll_gf40lp_frac_ops = {299	.enable = pll_gf40lp_frac_enable,300	.disable = pll_gf40lp_frac_disable,301	.is_enabled = pll_gf40lp_frac_is_enabled,302	.recalc_rate = pll_gf40lp_frac_recalc_rate,303	.round_rate = pll_round_rate,304	.set_rate = pll_gf40lp_frac_set_rate,305};306 307static const struct clk_ops pll_gf40lp_frac_fixed_ops = {308	.enable = pll_gf40lp_frac_enable,309	.disable = pll_gf40lp_frac_disable,310	.is_enabled = pll_gf40lp_frac_is_enabled,311	.recalc_rate = pll_gf40lp_frac_recalc_rate,312};313 314static int pll_gf40lp_laint_enable(struct clk_hw *hw)315{316	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);317	u32 val;318 319	val = pll_readl(pll, PLL_CTRL1);320	val &= ~(PLL_INT_CTRL1_PD |321		 PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD);322	pll_writel(pll, val, PLL_CTRL1);323 324	val = pll_readl(pll, PLL_CTRL2);325	val &= ~PLL_INT_CTRL2_BYPASS;326	pll_writel(pll, val, PLL_CTRL2);327 328	pll_lock(pll);329 330	return 0;331}332 333static void pll_gf40lp_laint_disable(struct clk_hw *hw)334{335	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);336	u32 val;337 338	val = pll_readl(pll, PLL_CTRL1);339	val |= PLL_INT_CTRL1_PD;340	pll_writel(pll, val, PLL_CTRL1);341}342 343static int pll_gf40lp_laint_is_enabled(struct clk_hw *hw)344{345	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);346 347	return !(pll_readl(pll, PLL_CTRL1) & PLL_INT_CTRL1_PD);348}349 350static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,351				     unsigned long parent_rate)352{353	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);354	struct pistachio_pll_rate_table *params;355	int enabled = pll_gf40lp_laint_is_enabled(hw);356	u32 val, vco, old_postdiv1, old_postdiv2;357	const char *name = clk_hw_get_name(hw);358 359	if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA)360		return -EINVAL;361 362	params = pll_get_params(pll, parent_rate, rate);363	if (!params || !params->refdiv)364		return -EINVAL;365 366	vco = div_u64(params->fref * params->fbdiv, params->refdiv);367	if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)368		pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,369			MIN_VCO_LA, MAX_VCO_LA);370 371	val = div_u64(params->fref, params->refdiv);372	if (val < MIN_PFD)373		pr_warn("%s: PFD %u is too low (min %lu)\n",374			name, val, MIN_PFD);375	if (val > vco / 16)376		pr_warn("%s: PFD %u is too high (max %u)\n",377			name, val, vco / 16);378 379	val = pll_readl(pll, PLL_CTRL1);380 381	old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &382		       PLL_INT_CTRL1_POSTDIV1_MASK;383	old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &384		       PLL_INT_CTRL1_POSTDIV2_MASK;385	if (enabled &&386	    (params->postdiv1 != old_postdiv1 ||387	     params->postdiv2 != old_postdiv2))388		pr_warn("%s: changing postdiv while PLL is enabled\n", name);389 390	if (params->postdiv2 > params->postdiv1)391		pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);392 393	val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |394		 (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) |395		 (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |396		 (PLL_INT_CTRL1_POSTDIV2_MASK << PLL_INT_CTRL1_POSTDIV2_SHIFT));397	val |= (params->refdiv << PLL_CTRL1_REFDIV_SHIFT) |398		(params->fbdiv << PLL_CTRL1_FBDIV_SHIFT) |399		(params->postdiv1 << PLL_INT_CTRL1_POSTDIV1_SHIFT) |400		(params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT);401	pll_writel(pll, val, PLL_CTRL1);402 403	if (enabled)404		pll_lock(pll);405 406	return 0;407}408 409static unsigned long pll_gf40lp_laint_recalc_rate(struct clk_hw *hw,410						  unsigned long parent_rate)411{412	struct pistachio_clk_pll *pll = to_pistachio_pll(hw);413	u32 val, prediv, fbdiv, postdiv1, postdiv2;414	u64 rate = parent_rate;415 416	val = pll_readl(pll, PLL_CTRL1);417	prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;418	fbdiv = (val >> PLL_CTRL1_FBDIV_SHIFT) & PLL_CTRL1_FBDIV_MASK;419	postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &420		PLL_INT_CTRL1_POSTDIV1_MASK;421	postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &422		PLL_INT_CTRL1_POSTDIV2_MASK;423 424	rate *= fbdiv;425	rate = do_div_round_closest(rate, prediv * postdiv1 * postdiv2);426 427	return rate;428}429 430static const struct clk_ops pll_gf40lp_laint_ops = {431	.enable = pll_gf40lp_laint_enable,432	.disable = pll_gf40lp_laint_disable,433	.is_enabled = pll_gf40lp_laint_is_enabled,434	.recalc_rate = pll_gf40lp_laint_recalc_rate,435	.round_rate = pll_round_rate,436	.set_rate = pll_gf40lp_laint_set_rate,437};438 439static const struct clk_ops pll_gf40lp_laint_fixed_ops = {440	.enable = pll_gf40lp_laint_enable,441	.disable = pll_gf40lp_laint_disable,442	.is_enabled = pll_gf40lp_laint_is_enabled,443	.recalc_rate = pll_gf40lp_laint_recalc_rate,444};445 446static struct clk *pll_register(const char *name, const char *parent_name,447				unsigned long flags, void __iomem *base,448				enum pistachio_pll_type type,449				struct pistachio_pll_rate_table *rates,450				unsigned int nr_rates)451{452	struct pistachio_clk_pll *pll;453	struct clk_init_data init;454	struct clk *clk;455 456	pll = kzalloc(sizeof(*pll), GFP_KERNEL);457	if (!pll)458		return ERR_PTR(-ENOMEM);459 460	init.name = name;461	init.flags = flags | CLK_GET_RATE_NOCACHE;462	init.parent_names = &parent_name;463	init.num_parents = 1;464 465	switch (type) {466	case PLL_GF40LP_FRAC:467		if (rates)468			init.ops = &pll_gf40lp_frac_ops;469		else470			init.ops = &pll_gf40lp_frac_fixed_ops;471		break;472	case PLL_GF40LP_LAINT:473		if (rates)474			init.ops = &pll_gf40lp_laint_ops;475		else476			init.ops = &pll_gf40lp_laint_fixed_ops;477		break;478	default:479		pr_err("Unrecognized PLL type %u\n", type);480		kfree(pll);481		return ERR_PTR(-EINVAL);482	}483 484	pll->hw.init = &init;485	pll->base = base;486	pll->rates = rates;487	pll->nr_rates = nr_rates;488 489	clk = clk_register(NULL, &pll->hw);490	if (IS_ERR(clk))491		kfree(pll);492 493	return clk;494}495 496void pistachio_clk_register_pll(struct pistachio_clk_provider *p,497				struct pistachio_pll *pll,498				unsigned int num)499{500	struct clk *clk;501	unsigned int i;502 503	for (i = 0; i < num; i++) {504		clk = pll_register(pll[i].name, pll[i].parent,505				   0, p->base + pll[i].reg_base,506				   pll[i].type, pll[i].rates,507				   pll[i].nr_rates);508		p->clk_data.clks[pll[i].id] = clk;509	}510}511