162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
762306a36Sopenharmony_ci *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Baikal-T1 CCU Dividers interface driver
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-div: " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/printk.h>
1662306a36Sopenharmony_ci#include <linux/bits.h>
1762306a36Sopenharmony_ci#include <linux/bitfield.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/clk-provider.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/spinlock.h>
2262306a36Sopenharmony_ci#include <linux/regmap.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/time64.h>
2562306a36Sopenharmony_ci#include <linux/debugfs.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "ccu-div.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define CCU_DIV_CTL			0x00
3062306a36Sopenharmony_ci#define CCU_DIV_CTL_EN			BIT(0)
3162306a36Sopenharmony_ci#define CCU_DIV_CTL_RST			BIT(1)
3262306a36Sopenharmony_ci#define CCU_DIV_CTL_SET_CLKDIV		BIT(2)
3362306a36Sopenharmony_ci#define CCU_DIV_CTL_CLKDIV_FLD		4
3462306a36Sopenharmony_ci#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
3562306a36Sopenharmony_ci	GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
3662306a36Sopenharmony_ci#define CCU_DIV_CTL_LOCK_SHIFTED	BIT(27)
3762306a36Sopenharmony_ci#define CCU_DIV_CTL_GATE_REF_BUF	BIT(28)
3862306a36Sopenharmony_ci#define CCU_DIV_CTL_LOCK_NORMAL		BIT(31)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define CCU_DIV_LOCK_CHECK_RETRIES	50
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define CCU_DIV_CLKDIV_MIN		0
4362306a36Sopenharmony_ci#define CCU_DIV_CLKDIV_MAX(_mask) \
4462306a36Sopenharmony_ci	((_mask) >> CCU_DIV_CTL_CLKDIV_FLD)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Use the next two methods until there are generic field setter and
4862306a36Sopenharmony_ci * getter available with non-constant mask support.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic inline u32 ccu_div_get(u32 mask, u32 val)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return (val & mask) >> CCU_DIV_CTL_CLKDIV_FLD;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic inline u32 ccu_div_prep(u32 mask, u32 val)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return (val << CCU_DIV_CTL_CLKDIV_FLD) & mask;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic inline unsigned long ccu_div_lock_delay_ns(unsigned long ref_clk,
6162306a36Sopenharmony_ci						  unsigned long div)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	u64 ns = 4ULL * (div ?: 1) * NSEC_PER_SEC;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	do_div(ns, ref_clk);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return ns;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline unsigned long ccu_div_calc_freq(unsigned long ref_clk,
7162306a36Sopenharmony_ci					      unsigned long div)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return ref_clk / (div ?: 1);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int ccu_div_var_update_clkdiv(struct ccu_div *div,
7762306a36Sopenharmony_ci				     unsigned long parent_rate,
7862306a36Sopenharmony_ci				     unsigned long divider)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned long nd;
8162306a36Sopenharmony_ci	u32 val = 0;
8262306a36Sopenharmony_ci	u32 lock;
8362306a36Sopenharmony_ci	int count;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	nd = ccu_div_lock_delay_ns(parent_rate, divider);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (div->features & CCU_DIV_LOCK_SHIFTED)
8862306a36Sopenharmony_ci		lock = CCU_DIV_CTL_LOCK_SHIFTED;
8962306a36Sopenharmony_ci	else
9062306a36Sopenharmony_ci		lock = CCU_DIV_CTL_LOCK_NORMAL;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
9362306a36Sopenharmony_ci			   CCU_DIV_CTL_SET_CLKDIV, CCU_DIV_CTL_SET_CLKDIV);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Until there is nsec-version of readl_poll_timeout() is available
9762306a36Sopenharmony_ci	 * we have to implement the next polling loop.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	count = CCU_DIV_LOCK_CHECK_RETRIES;
10062306a36Sopenharmony_ci	do {
10162306a36Sopenharmony_ci		ndelay(nd);
10262306a36Sopenharmony_ci		regmap_read(div->sys_regs, div->reg_ctl, &val);
10362306a36Sopenharmony_ci		if (val & lock)
10462306a36Sopenharmony_ci			return 0;
10562306a36Sopenharmony_ci	} while (--count);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return -ETIMEDOUT;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int ccu_div_var_enable(struct clk_hw *hw)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
11362306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
11462306a36Sopenharmony_ci	unsigned long flags;
11562306a36Sopenharmony_ci	u32 val = 0;
11662306a36Sopenharmony_ci	int ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (!parent_hw) {
11962306a36Sopenharmony_ci		pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
12062306a36Sopenharmony_ci		return -EINVAL;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
12462306a36Sopenharmony_ci	if (val & CCU_DIV_CTL_EN)
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
12862306a36Sopenharmony_ci	ret = ccu_div_var_update_clkdiv(div, clk_hw_get_rate(parent_hw),
12962306a36Sopenharmony_ci					ccu_div_get(div->mask, val));
13062306a36Sopenharmony_ci	if (!ret)
13162306a36Sopenharmony_ci		regmap_update_bits(div->sys_regs, div->reg_ctl,
13262306a36Sopenharmony_ci				   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
13362306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
13462306a36Sopenharmony_ci	if (ret)
13562306a36Sopenharmony_ci		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return ret;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int ccu_div_gate_enable(struct clk_hw *hw)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
14362306a36Sopenharmony_ci	unsigned long flags;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
14662306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
14762306a36Sopenharmony_ci			   CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
14862306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void ccu_div_gate_disable(struct clk_hw *hw)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
15662306a36Sopenharmony_ci	unsigned long flags;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
15962306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0);
16062306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int ccu_div_gate_is_enabled(struct clk_hw *hw)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
16662306a36Sopenharmony_ci	u32 val = 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return !!(val & CCU_DIV_CTL_EN);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int ccu_div_buf_enable(struct clk_hw *hw)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
17662306a36Sopenharmony_ci	unsigned long flags;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
17962306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
18062306a36Sopenharmony_ci			   CCU_DIV_CTL_GATE_REF_BUF, 0);
18162306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void ccu_div_buf_disable(struct clk_hw *hw)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
18962306a36Sopenharmony_ci	unsigned long flags;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
19262306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
19362306a36Sopenharmony_ci			   CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
19462306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int ccu_div_buf_is_enabled(struct clk_hw *hw)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
20062306a36Sopenharmony_ci	u32 val = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return !(val & CCU_DIV_CTL_GATE_REF_BUF);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
20862306a36Sopenharmony_ci					     unsigned long parent_rate)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
21162306a36Sopenharmony_ci	unsigned long divider;
21262306a36Sopenharmony_ci	u32 val = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &val);
21562306a36Sopenharmony_ci	divider = ccu_div_get(div->mask, val);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return ccu_div_calc_freq(parent_rate, divider);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic inline unsigned long ccu_div_var_calc_divider(unsigned long rate,
22162306a36Sopenharmony_ci						     unsigned long parent_rate,
22262306a36Sopenharmony_ci						     unsigned int mask)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	unsigned long divider;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	divider = parent_rate / rate;
22762306a36Sopenharmony_ci	return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN,
22862306a36Sopenharmony_ci		       CCU_DIV_CLKDIV_MAX(mask));
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic long ccu_div_var_round_rate(struct clk_hw *hw, unsigned long rate,
23262306a36Sopenharmony_ci				   unsigned long *parent_rate)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
23562306a36Sopenharmony_ci	unsigned long divider;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return ccu_div_calc_freq(*parent_rate, divider);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * This method is used for the clock divider blocks, which support the
24462306a36Sopenharmony_ci * on-the-fly rate change. So due to lacking the EN bit functionality
24562306a36Sopenharmony_ci * they can't be gated before the rate adjustment.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic int ccu_div_var_set_rate_slow(struct clk_hw *hw, unsigned long rate,
24862306a36Sopenharmony_ci				     unsigned long parent_rate)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
25162306a36Sopenharmony_ci	unsigned long flags, divider;
25262306a36Sopenharmony_ci	u32 val;
25362306a36Sopenharmony_ci	int ret;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
25662306a36Sopenharmony_ci	if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) {
25762306a36Sopenharmony_ci		divider = 0;
25862306a36Sopenharmony_ci	} else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) {
25962306a36Sopenharmony_ci		if (divider == 1 || divider == 2)
26062306a36Sopenharmony_ci			divider = 0;
26162306a36Sopenharmony_ci		else if (divider == 3)
26262306a36Sopenharmony_ci			divider = 4;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	val = ccu_div_prep(div->mask, divider);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
26862306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val);
26962306a36Sopenharmony_ci	ret = ccu_div_var_update_clkdiv(div, parent_rate, divider);
27062306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
27162306a36Sopenharmony_ci	if (ret)
27262306a36Sopenharmony_ci		pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return ret;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * This method is used for the clock divider blocks, which don't support
27962306a36Sopenharmony_ci * the on-the-fly rate change.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic int ccu_div_var_set_rate_fast(struct clk_hw *hw, unsigned long rate,
28262306a36Sopenharmony_ci				     unsigned long parent_rate)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
28562306a36Sopenharmony_ci	unsigned long flags, divider;
28662306a36Sopenharmony_ci	u32 val;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
28962306a36Sopenharmony_ci	val = ccu_div_prep(div->mask, divider);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * Also disable the clock divider block if it was enabled by default
29362306a36Sopenharmony_ci	 * or by the bootloader.
29462306a36Sopenharmony_ci	 */
29562306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
29662306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
29762306a36Sopenharmony_ci			   div->mask | CCU_DIV_CTL_EN, val);
29862306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw,
30462306a36Sopenharmony_ci					       unsigned long parent_rate)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return ccu_div_calc_freq(parent_rate, div->divider);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic long ccu_div_fixed_round_rate(struct clk_hw *hw, unsigned long rate,
31262306a36Sopenharmony_ci				     unsigned long *parent_rate)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return ccu_div_calc_freq(*parent_rate, div->divider);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
32062306a36Sopenharmony_ci				  unsigned long parent_rate)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistruct ccu_div_dbgfs_bit {
32862306a36Sopenharmony_ci	struct ccu_div *div;
32962306a36Sopenharmony_ci	const char *name;
33062306a36Sopenharmony_ci	u32 mask;
33162306a36Sopenharmony_ci};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#define CCU_DIV_DBGFS_BIT_ATTR(_name, _mask) {	\
33462306a36Sopenharmony_ci		.name = _name,			\
33562306a36Sopenharmony_ci		.mask = _mask			\
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
33962306a36Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
34062306a36Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
34162306a36Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
34262306a36Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),
34362306a36Sopenharmony_ci	CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
34462306a36Sopenharmony_ci};
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci#define CCU_DIV_DBGFS_BIT_NUM	ARRAY_SIZE(ccu_div_bits)
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/*
34962306a36Sopenharmony_ci * It can be dangerous to change the Divider settings behind clock framework
35062306a36Sopenharmony_ci * back, therefore we don't provide any kernel config based compile time option
35162306a36Sopenharmony_ci * for this feature to enable.
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_ci#undef CCU_DIV_ALLOW_WRITE_DEBUGFS
35462306a36Sopenharmony_ci#ifdef CCU_DIV_ALLOW_WRITE_DEBUGFS
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int ccu_div_dbgfs_bit_set(void *priv, u64 val)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	const struct ccu_div_dbgfs_bit *bit = priv;
35962306a36Sopenharmony_ci	struct ccu_div *div = bit->div;
36062306a36Sopenharmony_ci	unsigned long flags;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
36362306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl,
36462306a36Sopenharmony_ci			   bit->mask, val ? bit->mask : 0);
36562306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int ccu_div_dbgfs_var_clkdiv_set(void *priv, u64 val)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct ccu_div *div = priv;
37362306a36Sopenharmony_ci	unsigned long flags;
37462306a36Sopenharmony_ci	u32 data;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	val = clamp_t(u64, val, CCU_DIV_CLKDIV_MIN,
37762306a36Sopenharmony_ci		      CCU_DIV_CLKDIV_MAX(div->mask));
37862306a36Sopenharmony_ci	data = ccu_div_prep(div->mask, val);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	spin_lock_irqsave(&div->lock, flags);
38162306a36Sopenharmony_ci	regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data);
38262306a36Sopenharmony_ci	spin_unlock_irqrestore(&div->lock, flags);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci#define ccu_div_dbgfs_mode		0644
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci#else /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci#define ccu_div_dbgfs_bit_set		NULL
39262306a36Sopenharmony_ci#define ccu_div_dbgfs_var_clkdiv_set	NULL
39362306a36Sopenharmony_ci#define ccu_div_dbgfs_mode		0444
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#endif /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int ccu_div_dbgfs_bit_get(void *priv, u64 *val)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	const struct ccu_div_dbgfs_bit *bit = priv;
40062306a36Sopenharmony_ci	struct ccu_div *div = bit->div;
40162306a36Sopenharmony_ci	u32 data = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &data);
40462306a36Sopenharmony_ci	*val = !!(data & bit->mask);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_bit_fops,
40962306a36Sopenharmony_ci	ccu_div_dbgfs_bit_get, ccu_div_dbgfs_bit_set, "%llu\n");
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int ccu_div_dbgfs_var_clkdiv_get(void *priv, u64 *val)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct ccu_div *div = priv;
41462306a36Sopenharmony_ci	u32 data = 0;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	regmap_read(div->sys_regs, div->reg_ctl, &data);
41762306a36Sopenharmony_ci	*val = ccu_div_get(div->mask, data);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_var_clkdiv_fops,
42262306a36Sopenharmony_ci	ccu_div_dbgfs_var_clkdiv_get, ccu_div_dbgfs_var_clkdiv_set, "%llu\n");
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int ccu_div_dbgfs_fixed_clkdiv_get(void *priv, u64 *val)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct ccu_div *div = priv;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	*val = div->divider;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_fixed_clkdiv_fops,
43362306a36Sopenharmony_ci	ccu_div_dbgfs_fixed_clkdiv_get, NULL, "%llu\n");
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
43862306a36Sopenharmony_ci	struct ccu_div_dbgfs_bit *bits;
43962306a36Sopenharmony_ci	int didx, bidx, num = 2;
44062306a36Sopenharmony_ci	const char *name;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	num += !!(div->flags & CLK_SET_RATE_GATE) +
44362306a36Sopenharmony_ci		!!(div->features & CCU_DIV_RESET_DOMAIN);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	bits = kcalloc(num, sizeof(*bits), GFP_KERNEL);
44662306a36Sopenharmony_ci	if (!bits)
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	for (didx = 0, bidx = 0; bidx < CCU_DIV_DBGFS_BIT_NUM; ++bidx) {
45062306a36Sopenharmony_ci		name = ccu_div_bits[bidx].name;
45162306a36Sopenharmony_ci		if (!(div->flags & CLK_SET_RATE_GATE) &&
45262306a36Sopenharmony_ci		    !strcmp("div_en", name)) {
45362306a36Sopenharmony_ci			continue;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		if (!(div->features & CCU_DIV_RESET_DOMAIN) &&
45762306a36Sopenharmony_ci		    !strcmp("div_rst", name)) {
45862306a36Sopenharmony_ci			continue;
45962306a36Sopenharmony_ci		}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		if (!strcmp("div_buf", name))
46262306a36Sopenharmony_ci			continue;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		bits[didx] = ccu_div_bits[bidx];
46562306a36Sopenharmony_ci		bits[didx].div = div;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (div->features & CCU_DIV_LOCK_SHIFTED &&
46862306a36Sopenharmony_ci		    !strcmp("div_lock", name)) {
46962306a36Sopenharmony_ci			bits[didx].mask = CCU_DIV_CTL_LOCK_SHIFTED;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		debugfs_create_file_unsafe(bits[didx].name, ccu_div_dbgfs_mode,
47362306a36Sopenharmony_ci					   dentry, &bits[didx],
47462306a36Sopenharmony_ci					   &ccu_div_dbgfs_bit_fops);
47562306a36Sopenharmony_ci		++didx;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", ccu_div_dbgfs_mode, dentry,
47962306a36Sopenharmony_ci				   div, &ccu_div_dbgfs_var_clkdiv_fops);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
48562306a36Sopenharmony_ci	struct ccu_div_dbgfs_bit *bit;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	bit = kmalloc(sizeof(*bit), GFP_KERNEL);
48862306a36Sopenharmony_ci	if (!bit)
48962306a36Sopenharmony_ci		return;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	*bit = ccu_div_bits[0];
49262306a36Sopenharmony_ci	bit->div = div;
49362306a36Sopenharmony_ci	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
49462306a36Sopenharmony_ci				   &ccu_div_dbgfs_bit_fops);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
49762306a36Sopenharmony_ci				   &ccu_div_dbgfs_fixed_clkdiv_fops);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
50362306a36Sopenharmony_ci	struct ccu_div_dbgfs_bit *bit;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	bit = kmalloc(sizeof(*bit), GFP_KERNEL);
50662306a36Sopenharmony_ci	if (!bit)
50762306a36Sopenharmony_ci		return;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	*bit = ccu_div_bits[3];
51062306a36Sopenharmony_ci	bit->div = div;
51162306a36Sopenharmony_ci	debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
51262306a36Sopenharmony_ci				   &ccu_div_dbgfs_bit_fops);
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct ccu_div *div = to_ccu_div(hw);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
52062306a36Sopenharmony_ci				   &ccu_div_dbgfs_fixed_clkdiv_fops);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci#else /* !CONFIG_DEBUG_FS */
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci#define ccu_div_var_debug_init NULL
52662306a36Sopenharmony_ci#define ccu_div_gate_debug_init NULL
52762306a36Sopenharmony_ci#define ccu_div_buf_debug_init NULL
52862306a36Sopenharmony_ci#define ccu_div_fixed_debug_init NULL
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci#endif /* !CONFIG_DEBUG_FS */
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic const struct clk_ops ccu_div_var_gate_to_set_ops = {
53362306a36Sopenharmony_ci	.enable = ccu_div_var_enable,
53462306a36Sopenharmony_ci	.disable = ccu_div_gate_disable,
53562306a36Sopenharmony_ci	.is_enabled = ccu_div_gate_is_enabled,
53662306a36Sopenharmony_ci	.recalc_rate = ccu_div_var_recalc_rate,
53762306a36Sopenharmony_ci	.round_rate = ccu_div_var_round_rate,
53862306a36Sopenharmony_ci	.set_rate = ccu_div_var_set_rate_fast,
53962306a36Sopenharmony_ci	.debug_init = ccu_div_var_debug_init
54062306a36Sopenharmony_ci};
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic const struct clk_ops ccu_div_var_nogate_ops = {
54362306a36Sopenharmony_ci	.recalc_rate = ccu_div_var_recalc_rate,
54462306a36Sopenharmony_ci	.round_rate = ccu_div_var_round_rate,
54562306a36Sopenharmony_ci	.set_rate = ccu_div_var_set_rate_slow,
54662306a36Sopenharmony_ci	.debug_init = ccu_div_var_debug_init
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic const struct clk_ops ccu_div_gate_ops = {
55062306a36Sopenharmony_ci	.enable = ccu_div_gate_enable,
55162306a36Sopenharmony_ci	.disable = ccu_div_gate_disable,
55262306a36Sopenharmony_ci	.is_enabled = ccu_div_gate_is_enabled,
55362306a36Sopenharmony_ci	.recalc_rate = ccu_div_fixed_recalc_rate,
55462306a36Sopenharmony_ci	.round_rate = ccu_div_fixed_round_rate,
55562306a36Sopenharmony_ci	.set_rate = ccu_div_fixed_set_rate,
55662306a36Sopenharmony_ci	.debug_init = ccu_div_gate_debug_init
55762306a36Sopenharmony_ci};
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic const struct clk_ops ccu_div_buf_ops = {
56062306a36Sopenharmony_ci	.enable = ccu_div_buf_enable,
56162306a36Sopenharmony_ci	.disable = ccu_div_buf_disable,
56262306a36Sopenharmony_ci	.is_enabled = ccu_div_buf_is_enabled,
56362306a36Sopenharmony_ci	.debug_init = ccu_div_buf_debug_init
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic const struct clk_ops ccu_div_fixed_ops = {
56762306a36Sopenharmony_ci	.recalc_rate = ccu_div_fixed_recalc_rate,
56862306a36Sopenharmony_ci	.round_rate = ccu_div_fixed_round_rate,
56962306a36Sopenharmony_ci	.set_rate = ccu_div_fixed_set_rate,
57062306a36Sopenharmony_ci	.debug_init = ccu_div_fixed_debug_init
57162306a36Sopenharmony_ci};
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistruct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct clk_parent_data parent_data = { };
57662306a36Sopenharmony_ci	struct clk_init_data hw_init = { };
57762306a36Sopenharmony_ci	struct ccu_div *div;
57862306a36Sopenharmony_ci	int ret;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (!div_init)
58162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	div = kzalloc(sizeof(*div), GFP_KERNEL);
58462306a36Sopenharmony_ci	if (!div)
58562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/*
58862306a36Sopenharmony_ci	 * Note since Baikal-T1 System Controller registers are MMIO-backed
58962306a36Sopenharmony_ci	 * we won't check the regmap IO operations return status, because it
59062306a36Sopenharmony_ci	 * must be zero anyway.
59162306a36Sopenharmony_ci	 */
59262306a36Sopenharmony_ci	div->hw.init = &hw_init;
59362306a36Sopenharmony_ci	div->id = div_init->id;
59462306a36Sopenharmony_ci	div->reg_ctl = div_init->base + CCU_DIV_CTL;
59562306a36Sopenharmony_ci	div->sys_regs = div_init->sys_regs;
59662306a36Sopenharmony_ci	div->flags = div_init->flags;
59762306a36Sopenharmony_ci	div->features = div_init->features;
59862306a36Sopenharmony_ci	spin_lock_init(&div->lock);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	hw_init.name = div_init->name;
60162306a36Sopenharmony_ci	hw_init.flags = div_init->flags;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (div_init->type == CCU_DIV_VAR) {
60462306a36Sopenharmony_ci		if (hw_init.flags & CLK_SET_RATE_GATE)
60562306a36Sopenharmony_ci			hw_init.ops = &ccu_div_var_gate_to_set_ops;
60662306a36Sopenharmony_ci		else
60762306a36Sopenharmony_ci			hw_init.ops = &ccu_div_var_nogate_ops;
60862306a36Sopenharmony_ci		div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width);
60962306a36Sopenharmony_ci	} else if (div_init->type == CCU_DIV_GATE) {
61062306a36Sopenharmony_ci		hw_init.ops = &ccu_div_gate_ops;
61162306a36Sopenharmony_ci		div->divider = div_init->divider;
61262306a36Sopenharmony_ci	} else if (div_init->type == CCU_DIV_BUF) {
61362306a36Sopenharmony_ci		hw_init.ops = &ccu_div_buf_ops;
61462306a36Sopenharmony_ci	} else if (div_init->type == CCU_DIV_FIXED) {
61562306a36Sopenharmony_ci		hw_init.ops = &ccu_div_fixed_ops;
61662306a36Sopenharmony_ci		div->divider = div_init->divider;
61762306a36Sopenharmony_ci	} else {
61862306a36Sopenharmony_ci		ret = -EINVAL;
61962306a36Sopenharmony_ci		goto err_free_div;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (!div_init->parent_name) {
62362306a36Sopenharmony_ci		ret = -EINVAL;
62462306a36Sopenharmony_ci		goto err_free_div;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci	parent_data.fw_name = div_init->parent_name;
62762306a36Sopenharmony_ci	parent_data.name = div_init->parent_name;
62862306a36Sopenharmony_ci	hw_init.parent_data = &parent_data;
62962306a36Sopenharmony_ci	hw_init.num_parents = 1;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	ret = of_clk_hw_register(div_init->np, &div->hw);
63262306a36Sopenharmony_ci	if (ret)
63362306a36Sopenharmony_ci		goto err_free_div;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return div;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cierr_free_div:
63862306a36Sopenharmony_ci	kfree(div);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return ERR_PTR(ret);
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_civoid ccu_div_hw_unregister(struct ccu_div *div)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	clk_hw_unregister(&div->hw);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	kfree(div);
64862306a36Sopenharmony_ci}
649