brintos

brintos / linux-shallow public Read only

0
0
Text · 16.1 KiB · 8d5fc71 Raw
649 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC4 *5 * Authors:6 *   Serge Semin <Sergey.Semin@baikalelectronics.ru>7 *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>8 *9 * Baikal-T1 CCU Dividers interface driver10 */11 12#define pr_fmt(fmt) "bt1-ccu-div: " fmt13 14#include <linux/kernel.h>15#include <linux/printk.h>16#include <linux/bits.h>17#include <linux/bitfield.h>18#include <linux/slab.h>19#include <linux/clk-provider.h>20#include <linux/of.h>21#include <linux/spinlock.h>22#include <linux/regmap.h>23#include <linux/delay.h>24#include <linux/time64.h>25#include <linux/debugfs.h>26 27#include "ccu-div.h"28 29#define CCU_DIV_CTL			0x0030#define CCU_DIV_CTL_EN			BIT(0)31#define CCU_DIV_CTL_RST			BIT(1)32#define CCU_DIV_CTL_SET_CLKDIV		BIT(2)33#define CCU_DIV_CTL_CLKDIV_FLD		434#define CCU_DIV_CTL_CLKDIV_MASK(_width) \35	GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)36#define CCU_DIV_CTL_LOCK_SHIFTED	BIT(27)37#define CCU_DIV_CTL_GATE_REF_BUF	BIT(28)38#define CCU_DIV_CTL_LOCK_NORMAL		BIT(31)39 40#define CCU_DIV_LOCK_CHECK_RETRIES	5041 42#define CCU_DIV_CLKDIV_MIN		043#define CCU_DIV_CLKDIV_MAX(_mask) \44	((_mask) >> CCU_DIV_CTL_CLKDIV_FLD)45 46/*47 * Use the next two methods until there are generic field setter and48 * getter available with non-constant mask support.49 */50static inline u32 ccu_div_get(u32 mask, u32 val)51{52	return (val & mask) >> CCU_DIV_CTL_CLKDIV_FLD;53}54 55static inline u32 ccu_div_prep(u32 mask, u32 val)56{57	return (val << CCU_DIV_CTL_CLKDIV_FLD) & mask;58}59 60static inline unsigned long ccu_div_lock_delay_ns(unsigned long ref_clk,61						  unsigned long div)62{63	u64 ns = 4ULL * (div ?: 1) * NSEC_PER_SEC;64 65	do_div(ns, ref_clk);66 67	return ns;68}69 70static inline unsigned long ccu_div_calc_freq(unsigned long ref_clk,71					      unsigned long div)72{73	return ref_clk / (div ?: 1);74}75 76static int ccu_div_var_update_clkdiv(struct ccu_div *div,77				     unsigned long parent_rate,78				     unsigned long divider)79{80	unsigned long nd;81	u32 val = 0;82	u32 lock;83	int count;84 85	nd = ccu_div_lock_delay_ns(parent_rate, divider);86 87	if (div->features & CCU_DIV_LOCK_SHIFTED)88		lock = CCU_DIV_CTL_LOCK_SHIFTED;89	else90		lock = CCU_DIV_CTL_LOCK_NORMAL;91 92	regmap_update_bits(div->sys_regs, div->reg_ctl,93			   CCU_DIV_CTL_SET_CLKDIV, CCU_DIV_CTL_SET_CLKDIV);94 95	/*96	 * Until there is nsec-version of readl_poll_timeout() is available97	 * we have to implement the next polling loop.98	 */99	count = CCU_DIV_LOCK_CHECK_RETRIES;100	do {101		ndelay(nd);102		regmap_read(div->sys_regs, div->reg_ctl, &val);103		if (val & lock)104			return 0;105	} while (--count);106 107	return -ETIMEDOUT;108}109 110static int ccu_div_var_enable(struct clk_hw *hw)111{112	struct clk_hw *parent_hw = clk_hw_get_parent(hw);113	struct ccu_div *div = to_ccu_div(hw);114	unsigned long flags;115	u32 val = 0;116	int ret;117 118	if (!parent_hw) {119		pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));120		return -EINVAL;121	}122 123	regmap_read(div->sys_regs, div->reg_ctl, &val);124	if (val & CCU_DIV_CTL_EN)125		return 0;126 127	spin_lock_irqsave(&div->lock, flags);128	ret = ccu_div_var_update_clkdiv(div, clk_hw_get_rate(parent_hw),129					ccu_div_get(div->mask, val));130	if (!ret)131		regmap_update_bits(div->sys_regs, div->reg_ctl,132				   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);133	spin_unlock_irqrestore(&div->lock, flags);134	if (ret)135		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));136 137	return ret;138}139 140static int ccu_div_gate_enable(struct clk_hw *hw)141{142	struct ccu_div *div = to_ccu_div(hw);143	unsigned long flags;144 145	spin_lock_irqsave(&div->lock, flags);146	regmap_update_bits(div->sys_regs, div->reg_ctl,147			   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);148	spin_unlock_irqrestore(&div->lock, flags);149 150	return 0;151}152 153static void ccu_div_gate_disable(struct clk_hw *hw)154{155	struct ccu_div *div = to_ccu_div(hw);156	unsigned long flags;157 158	spin_lock_irqsave(&div->lock, flags);159	regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0);160	spin_unlock_irqrestore(&div->lock, flags);161}162 163static int ccu_div_gate_is_enabled(struct clk_hw *hw)164{165	struct ccu_div *div = to_ccu_div(hw);166	u32 val = 0;167 168	regmap_read(div->sys_regs, div->reg_ctl, &val);169 170	return !!(val & CCU_DIV_CTL_EN);171}172 173static int ccu_div_buf_enable(struct clk_hw *hw)174{175	struct ccu_div *div = to_ccu_div(hw);176	unsigned long flags;177 178	spin_lock_irqsave(&div->lock, flags);179	regmap_update_bits(div->sys_regs, div->reg_ctl,180			   CCU_DIV_CTL_GATE_REF_BUF, 0);181	spin_unlock_irqrestore(&div->lock, flags);182 183	return 0;184}185 186static void ccu_div_buf_disable(struct clk_hw *hw)187{188	struct ccu_div *div = to_ccu_div(hw);189	unsigned long flags;190 191	spin_lock_irqsave(&div->lock, flags);192	regmap_update_bits(div->sys_regs, div->reg_ctl,193			   CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);194	spin_unlock_irqrestore(&div->lock, flags);195}196 197static int ccu_div_buf_is_enabled(struct clk_hw *hw)198{199	struct ccu_div *div = to_ccu_div(hw);200	u32 val = 0;201 202	regmap_read(div->sys_regs, div->reg_ctl, &val);203 204	return !(val & CCU_DIV_CTL_GATE_REF_BUF);205}206 207static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,208					     unsigned long parent_rate)209{210	struct ccu_div *div = to_ccu_div(hw);211	unsigned long divider;212	u32 val = 0;213 214	regmap_read(div->sys_regs, div->reg_ctl, &val);215	divider = ccu_div_get(div->mask, val);216 217	return ccu_div_calc_freq(parent_rate, divider);218}219 220static inline unsigned long ccu_div_var_calc_divider(unsigned long rate,221						     unsigned long parent_rate,222						     unsigned int mask)223{224	unsigned long divider;225 226	divider = parent_rate / rate;227	return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN,228		       CCU_DIV_CLKDIV_MAX(mask));229}230 231static long ccu_div_var_round_rate(struct clk_hw *hw, unsigned long rate,232				   unsigned long *parent_rate)233{234	struct ccu_div *div = to_ccu_div(hw);235	unsigned long divider;236 237	divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask);238 239	return ccu_div_calc_freq(*parent_rate, divider);240}241 242/*243 * This method is used for the clock divider blocks, which support the244 * on-the-fly rate change. So due to lacking the EN bit functionality245 * they can't be gated before the rate adjustment.246 */247static int ccu_div_var_set_rate_slow(struct clk_hw *hw, unsigned long rate,248				     unsigned long parent_rate)249{250	struct ccu_div *div = to_ccu_div(hw);251	unsigned long flags, divider;252	u32 val;253	int ret;254 255	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);256	if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) {257		divider = 0;258	} else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) {259		if (divider == 1 || divider == 2)260			divider = 0;261		else if (divider == 3)262			divider = 4;263	}264 265	val = ccu_div_prep(div->mask, divider);266 267	spin_lock_irqsave(&div->lock, flags);268	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val);269	ret = ccu_div_var_update_clkdiv(div, parent_rate, divider);270	spin_unlock_irqrestore(&div->lock, flags);271	if (ret)272		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));273 274	return ret;275}276 277/*278 * This method is used for the clock divider blocks, which don't support279 * the on-the-fly rate change.280 */281static int ccu_div_var_set_rate_fast(struct clk_hw *hw, unsigned long rate,282				     unsigned long parent_rate)283{284	struct ccu_div *div = to_ccu_div(hw);285	unsigned long flags, divider;286	u32 val;287 288	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);289	val = ccu_div_prep(div->mask, divider);290 291	/*292	 * Also disable the clock divider block if it was enabled by default293	 * or by the bootloader.294	 */295	spin_lock_irqsave(&div->lock, flags);296	regmap_update_bits(div->sys_regs, div->reg_ctl,297			   div->mask | CCU_DIV_CTL_EN, val);298	spin_unlock_irqrestore(&div->lock, flags);299 300	return 0;301}302 303static unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw,304					       unsigned long parent_rate)305{306	struct ccu_div *div = to_ccu_div(hw);307 308	return ccu_div_calc_freq(parent_rate, div->divider);309}310 311static long ccu_div_fixed_round_rate(struct clk_hw *hw, unsigned long rate,312				     unsigned long *parent_rate)313{314	struct ccu_div *div = to_ccu_div(hw);315 316	return ccu_div_calc_freq(*parent_rate, div->divider);317}318 319static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,320				  unsigned long parent_rate)321{322	return 0;323}324 325#ifdef CONFIG_DEBUG_FS326 327struct ccu_div_dbgfs_bit {328	struct ccu_div *div;329	const char *name;330	u32 mask;331};332 333#define CCU_DIV_DBGFS_BIT_ATTR(_name, _mask) {	\334		.name = _name,			\335		.mask = _mask			\336	}337 338static const struct ccu_div_dbgfs_bit ccu_div_bits[] = {339	CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),340	CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),341	CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),342	CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),343	CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)344};345 346#define CCU_DIV_DBGFS_BIT_NUM	ARRAY_SIZE(ccu_div_bits)347 348/*349 * It can be dangerous to change the Divider settings behind clock framework350 * back, therefore we don't provide any kernel config based compile time option351 * for this feature to enable.352 */353#undef CCU_DIV_ALLOW_WRITE_DEBUGFS354#ifdef CCU_DIV_ALLOW_WRITE_DEBUGFS355 356static int ccu_div_dbgfs_bit_set(void *priv, u64 val)357{358	const struct ccu_div_dbgfs_bit *bit = priv;359	struct ccu_div *div = bit->div;360	unsigned long flags;361 362	spin_lock_irqsave(&div->lock, flags);363	regmap_update_bits(div->sys_regs, div->reg_ctl,364			   bit->mask, val ? bit->mask : 0);365	spin_unlock_irqrestore(&div->lock, flags);366 367	return 0;368}369 370static int ccu_div_dbgfs_var_clkdiv_set(void *priv, u64 val)371{372	struct ccu_div *div = priv;373	unsigned long flags;374	u32 data;375 376	val = clamp_t(u64, val, CCU_DIV_CLKDIV_MIN,377		      CCU_DIV_CLKDIV_MAX(div->mask));378	data = ccu_div_prep(div->mask, val);379 380	spin_lock_irqsave(&div->lock, flags);381	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data);382	spin_unlock_irqrestore(&div->lock, flags);383 384	return 0;385}386 387#define ccu_div_dbgfs_mode		0644388 389#else /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */390 391#define ccu_div_dbgfs_bit_set		NULL392#define ccu_div_dbgfs_var_clkdiv_set	NULL393#define ccu_div_dbgfs_mode		0444394 395#endif /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */396 397static int ccu_div_dbgfs_bit_get(void *priv, u64 *val)398{399	const struct ccu_div_dbgfs_bit *bit = priv;400	struct ccu_div *div = bit->div;401	u32 data = 0;402 403	regmap_read(div->sys_regs, div->reg_ctl, &data);404	*val = !!(data & bit->mask);405 406	return 0;407}408DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_bit_fops,409	ccu_div_dbgfs_bit_get, ccu_div_dbgfs_bit_set, "%llu\n");410 411static int ccu_div_dbgfs_var_clkdiv_get(void *priv, u64 *val)412{413	struct ccu_div *div = priv;414	u32 data = 0;415 416	regmap_read(div->sys_regs, div->reg_ctl, &data);417	*val = ccu_div_get(div->mask, data);418 419	return 0;420}421DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_var_clkdiv_fops,422	ccu_div_dbgfs_var_clkdiv_get, ccu_div_dbgfs_var_clkdiv_set, "%llu\n");423 424static int ccu_div_dbgfs_fixed_clkdiv_get(void *priv, u64 *val)425{426	struct ccu_div *div = priv;427 428	*val = div->divider;429 430	return 0;431}432DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_fixed_clkdiv_fops,433	ccu_div_dbgfs_fixed_clkdiv_get, NULL, "%llu\n");434 435static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)436{437	struct ccu_div *div = to_ccu_div(hw);438	struct ccu_div_dbgfs_bit *bits;439	int didx, bidx, num = 2;440	const char *name;441 442	num += !!(div->flags & CLK_SET_RATE_GATE) +443		!!(div->features & CCU_DIV_RESET_DOMAIN);444 445	bits = kcalloc(num, sizeof(*bits), GFP_KERNEL);446	if (!bits)447		return;448 449	for (didx = 0, bidx = 0; bidx < CCU_DIV_DBGFS_BIT_NUM; ++bidx) {450		name = ccu_div_bits[bidx].name;451		if (!(div->flags & CLK_SET_RATE_GATE) &&452		    !strcmp("div_en", name)) {453			continue;454		}455 456		if (!(div->features & CCU_DIV_RESET_DOMAIN) &&457		    !strcmp("div_rst", name)) {458			continue;459		}460 461		if (!strcmp("div_buf", name))462			continue;463 464		bits[didx] = ccu_div_bits[bidx];465		bits[didx].div = div;466 467		if (div->features & CCU_DIV_LOCK_SHIFTED &&468		    !strcmp("div_lock", name)) {469			bits[didx].mask = CCU_DIV_CTL_LOCK_SHIFTED;470		}471 472		debugfs_create_file_unsafe(bits[didx].name, ccu_div_dbgfs_mode,473					   dentry, &bits[didx],474					   &ccu_div_dbgfs_bit_fops);475		++didx;476	}477 478	debugfs_create_file_unsafe("div_clkdiv", ccu_div_dbgfs_mode, dentry,479				   div, &ccu_div_dbgfs_var_clkdiv_fops);480}481 482static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)483{484	struct ccu_div *div = to_ccu_div(hw);485	struct ccu_div_dbgfs_bit *bit;486 487	bit = kmalloc(sizeof(*bit), GFP_KERNEL);488	if (!bit)489		return;490 491	*bit = ccu_div_bits[0];492	bit->div = div;493	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,494				   &ccu_div_dbgfs_bit_fops);495 496	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,497				   &ccu_div_dbgfs_fixed_clkdiv_fops);498}499 500static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)501{502	struct ccu_div *div = to_ccu_div(hw);503	struct ccu_div_dbgfs_bit *bit;504 505	bit = kmalloc(sizeof(*bit), GFP_KERNEL);506	if (!bit)507		return;508 509	*bit = ccu_div_bits[3];510	bit->div = div;511	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,512				   &ccu_div_dbgfs_bit_fops);513}514 515static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)516{517	struct ccu_div *div = to_ccu_div(hw);518 519	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,520				   &ccu_div_dbgfs_fixed_clkdiv_fops);521}522 523#else /* !CONFIG_DEBUG_FS */524 525#define ccu_div_var_debug_init NULL526#define ccu_div_gate_debug_init NULL527#define ccu_div_buf_debug_init NULL528#define ccu_div_fixed_debug_init NULL529 530#endif /* !CONFIG_DEBUG_FS */531 532static const struct clk_ops ccu_div_var_gate_to_set_ops = {533	.enable = ccu_div_var_enable,534	.disable = ccu_div_gate_disable,535	.is_enabled = ccu_div_gate_is_enabled,536	.recalc_rate = ccu_div_var_recalc_rate,537	.round_rate = ccu_div_var_round_rate,538	.set_rate = ccu_div_var_set_rate_fast,539	.debug_init = ccu_div_var_debug_init540};541 542static const struct clk_ops ccu_div_var_nogate_ops = {543	.recalc_rate = ccu_div_var_recalc_rate,544	.round_rate = ccu_div_var_round_rate,545	.set_rate = ccu_div_var_set_rate_slow,546	.debug_init = ccu_div_var_debug_init547};548 549static const struct clk_ops ccu_div_gate_ops = {550	.enable = ccu_div_gate_enable,551	.disable = ccu_div_gate_disable,552	.is_enabled = ccu_div_gate_is_enabled,553	.recalc_rate = ccu_div_fixed_recalc_rate,554	.round_rate = ccu_div_fixed_round_rate,555	.set_rate = ccu_div_fixed_set_rate,556	.debug_init = ccu_div_gate_debug_init557};558 559static const struct clk_ops ccu_div_buf_ops = {560	.enable = ccu_div_buf_enable,561	.disable = ccu_div_buf_disable,562	.is_enabled = ccu_div_buf_is_enabled,563	.debug_init = ccu_div_buf_debug_init564};565 566static const struct clk_ops ccu_div_fixed_ops = {567	.recalc_rate = ccu_div_fixed_recalc_rate,568	.round_rate = ccu_div_fixed_round_rate,569	.set_rate = ccu_div_fixed_set_rate,570	.debug_init = ccu_div_fixed_debug_init571};572 573struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)574{575	struct clk_parent_data parent_data = { };576	struct clk_init_data hw_init = { };577	struct ccu_div *div;578	int ret;579 580	if (!div_init)581		return ERR_PTR(-EINVAL);582 583	div = kzalloc(sizeof(*div), GFP_KERNEL);584	if (!div)585		return ERR_PTR(-ENOMEM);586 587	/*588	 * Note since Baikal-T1 System Controller registers are MMIO-backed589	 * we won't check the regmap IO operations return status, because it590	 * must be zero anyway.591	 */592	div->hw.init = &hw_init;593	div->id = div_init->id;594	div->reg_ctl = div_init->base + CCU_DIV_CTL;595	div->sys_regs = div_init->sys_regs;596	div->flags = div_init->flags;597	div->features = div_init->features;598	spin_lock_init(&div->lock);599 600	hw_init.name = div_init->name;601	hw_init.flags = div_init->flags;602 603	if (div_init->type == CCU_DIV_VAR) {604		if (hw_init.flags & CLK_SET_RATE_GATE)605			hw_init.ops = &ccu_div_var_gate_to_set_ops;606		else607			hw_init.ops = &ccu_div_var_nogate_ops;608		div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width);609	} else if (div_init->type == CCU_DIV_GATE) {610		hw_init.ops = &ccu_div_gate_ops;611		div->divider = div_init->divider;612	} else if (div_init->type == CCU_DIV_BUF) {613		hw_init.ops = &ccu_div_buf_ops;614	} else if (div_init->type == CCU_DIV_FIXED) {615		hw_init.ops = &ccu_div_fixed_ops;616		div->divider = div_init->divider;617	} else {618		ret = -EINVAL;619		goto err_free_div;620	}621 622	if (!div_init->parent_name) {623		ret = -EINVAL;624		goto err_free_div;625	}626	parent_data.fw_name = div_init->parent_name;627	parent_data.name = div_init->parent_name;628	hw_init.parent_data = &parent_data;629	hw_init.num_parents = 1;630 631	ret = of_clk_hw_register(div_init->np, &div->hw);632	if (ret)633		goto err_free_div;634 635	return div;636 637err_free_div:638	kfree(div);639 640	return ERR_PTR(ret);641}642 643void ccu_div_hw_unregister(struct ccu_div *div)644{645	clk_hw_unregister(&div->hw);646 647	kfree(div);648}649