Unverified Commit 3a086236 authored by Yixun Lan's avatar Yixun Lan
Browse files

clk: spacemit: ccu_pll: add plla type clock

Introduce a new clock PLLA for SpacemiT's K3 SoC which has a different
register layout comparing to previous PPL type. And, It is configured
by swcr1, swcr3 and swcr2 BIT[15:8].

Link: https://lore.kernel.org/r/20260108-k3-clk-v5-3-42a11b74ad58@gentoo.org


Signed-off-by: default avatarYixun Lan <dlan@gentoo.org>
parent ace73b7e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ struct ccu_common {
		/* For PLL */
		struct {
			u32 reg_swcr1;
			u32 reg_swcr2;
			u32 reg_swcr3;
		};
	};
+118 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
#define PLL_SWCR3_EN		((u32)BIT(31))
#define PLL_SWCR3_MASK		GENMASK(30, 0)

#define PLLA_SWCR2_EN		((u32)BIT(16))
#define PLLA_SWCR2_MASK		GENMASK(15, 8)

static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_pll *pll,
							       unsigned long rate)
{
@@ -148,6 +151,110 @@ static int ccu_pll_init(struct clk_hw *hw)
	return 0;
}

static const struct ccu_pll_rate_tbl *ccu_plla_lookup_matched_entry(struct ccu_pll *pll)
{
	struct ccu_pll_config *config = &pll->config;
	const struct ccu_pll_rate_tbl *entry;
	u32 i, swcr1, swcr2, swcr3;

	swcr1 = ccu_read(&pll->common, swcr1);
	swcr2 = ccu_read(&pll->common, swcr2);
	swcr2 &= PLLA_SWCR2_MASK;
	swcr3 = ccu_read(&pll->common, swcr3);

	for (i = 0; i < config->tbl_num; i++) {
		entry = &config->rate_tbl[i];

		if (swcr1 == entry->swcr1 &&
		    swcr2 == entry->swcr2 &&
		    swcr3 == entry->swcr3)
			return entry;
	}

	return NULL;
}

static void ccu_plla_update_param(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *entry)
{
	struct ccu_common *common = &pll->common;

	regmap_write(common->regmap, common->reg_swcr1, entry->swcr1);
	regmap_write(common->regmap, common->reg_swcr3, entry->swcr3);
	ccu_update(common, swcr2, PLLA_SWCR2_MASK, entry->swcr2);
}

static int ccu_plla_is_enabled(struct clk_hw *hw)
{
	struct ccu_common *common = hw_to_ccu_common(hw);

	return ccu_read(common, swcr2) & PLLA_SWCR2_EN;
}

static int ccu_plla_enable(struct clk_hw *hw)
{
	struct ccu_pll *pll = hw_to_ccu_pll(hw);
	struct ccu_common *common = &pll->common;
	unsigned int tmp;

	ccu_update(common, swcr2, PLLA_SWCR2_EN, PLLA_SWCR2_EN);

	/* check lock status */
	return regmap_read_poll_timeout_atomic(common->lock_regmap,
					       pll->config.reg_lock,
					       tmp,
					       tmp & pll->config.mask_lock,
					       PLL_DELAY_US, PLL_TIMEOUT_US);
}

static void ccu_plla_disable(struct clk_hw *hw)
{
	struct ccu_common *common = hw_to_ccu_common(hw);

	ccu_update(common, swcr2, PLLA_SWCR2_EN, 0);
}

/*
 * PLLAs must be gated before changing rate, which is ensured by
 * flag CLK_SET_RATE_GATE.
 */
static int ccu_plla_set_rate(struct clk_hw *hw, unsigned long rate,
			     unsigned long parent_rate)
{
	struct ccu_pll *pll = hw_to_ccu_pll(hw);
	const struct ccu_pll_rate_tbl *entry;

	entry = ccu_pll_lookup_best_rate(pll, rate);
	ccu_plla_update_param(pll, entry);

	return 0;
}

static unsigned long ccu_plla_recalc_rate(struct clk_hw *hw,
					  unsigned long parent_rate)
{
	struct ccu_pll *pll = hw_to_ccu_pll(hw);
	const struct ccu_pll_rate_tbl *entry;

	entry = ccu_plla_lookup_matched_entry(pll);

	WARN_ON_ONCE(!entry);

	return entry ? entry->rate : 0;
}

static int ccu_plla_init(struct clk_hw *hw)
{
	struct ccu_pll *pll = hw_to_ccu_pll(hw);

	if (ccu_plla_lookup_matched_entry(pll))
		return 0;

	ccu_plla_disable(hw);
	ccu_plla_update_param(pll, &pll->config.rate_tbl[0]);

	return 0;
}

const struct clk_ops spacemit_ccu_pll_ops = {
	.init		= ccu_pll_init,
	.enable		= ccu_pll_enable,
@@ -158,3 +265,14 @@ const struct clk_ops spacemit_ccu_pll_ops = {
	.is_enabled	= ccu_pll_is_enabled,
};
EXPORT_SYMBOL_NS_GPL(spacemit_ccu_pll_ops, "CLK_SPACEMIT");

const struct clk_ops spacemit_ccu_plla_ops = {
	.init		= ccu_plla_init,
	.enable		= ccu_plla_enable,
	.disable	= ccu_plla_disable,
	.set_rate	= ccu_plla_set_rate,
	.recalc_rate	= ccu_plla_recalc_rate,
	.determine_rate	= ccu_pll_determine_rate,
	.is_enabled	= ccu_plla_is_enabled,
};
EXPORT_SYMBOL_NS_GPL(spacemit_ccu_plla_ops, "CLK_SPACEMIT");
+47 −10
Original line number Diff line number Diff line
@@ -16,14 +16,31 @@
 * configuration.
 *
 * @rate:	PLL rate
 * @swcr1:	Register value of PLLX_SW1_CTRL (PLLx_SWCR1).
 * @swcr3:	Register value of the PLLx_SW3_CTRL's lowest 31 bits of
 *		PLLx_SW3_CTRL (PLLx_SWCR3). This highest bit is for enabling
 *		the PLL and not contained in this field.
 * @swcr1:	Value of register PLLx_SW1_CTRL.
 * @swcr2:	Value of register PLLAx_SW2_CTRL.
 * @swcr3:	value of register PLLx_SW3_CTRL.
 *
 * See below tables for the register used in PPL/PPLA clocks
 *
 * Regular PLL type
 *  | Enable | swcr3 | PLLx_SW3_CTRL - BIT[31]    |
 *  -----------------------------------------------
 *  | Config | swcr1 | PLLx_SW1_CTRL - BIT[31:0]  |
 *  |        | swcr2 | Not used                   |
 *  |        | swcr3 | PLLx_SW3_CTRL - BIT[30:0]  |
 *
 * Special PLL type A
 *  | Enable | swcr2 | PLLAx_SW2_CTRL - BIT[16]   |
 *  -----------------------------------------------
 *  | Config | swcr1 | PLLAx_SW1_CTRL - BIT[31:0] |
 *  |        | swcr2 | PLLAx_SW2_CTRL - BIT[15:8] |
 *  |        | swcr3 | PLLAx_SW3_CTRL - BIT[31:0] |
 *
 */
struct ccu_pll_rate_tbl {
	unsigned long rate;
	u32 swcr1;
	u32 swcr2;
	u32 swcr3;
};

@@ -41,6 +58,14 @@ struct ccu_pll_config {
		.swcr3	= _swcr3,						\
	}

#define CCU_PLLA_RATE(_rate, _swcr1, _swcr2, _swcr3) \
	{									\
		.rate	= _rate,						\
		.swcr1	= _swcr1,						\
		.swcr2	= _swcr2,						\
		.swcr3	= _swcr3,						\
	}

struct ccu_pll {
	struct ccu_common	common;
	struct ccu_pll_config	config;
@@ -54,26 +79,37 @@ struct ccu_pll {
		.mask_lock	= (_mask_lock),					\
	}

#define CCU_PLL_HWINIT(_name, _flags)						\
#define CCU_PLL_COMMON_HWINIT(_name, _ops, _flags)				\
	(&(struct clk_init_data) {						\
		.name		= #_name,					\
		.ops		= &spacemit_ccu_pll_ops,			\
		.ops		= _ops,						\
		.parent_data	= &(struct clk_parent_data) { .index = 0 },	\
		.num_parents	= 1,						\
		.flags		= _flags,					\
	})

#define CCU_PLL_DEFINE(_name, _table, _reg_swcr1, _reg_swcr3, _reg_lock,	\
		       _mask_lock, _flags)					\
#define CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,	\
		       _reg_lock, _mask_lock, _ops, _flags)			\
static struct ccu_pll _name = {							\
	.config	= CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock),		\
	.common = {								\
		.reg_swcr1	= _reg_swcr1,					\
		.reg_swcr2	= _reg_swcr2,					\
		.reg_swcr3	= _reg_swcr3,					\
		.hw.init	= CCU_PLL_HWINIT(_name, _flags)			\
		.hw.init	= CCU_PLL_COMMON_HWINIT(_name, _ops, _flags)	\
	}									\
}

#define CCU_PLL_DEFINE(_name, _table, _reg_swcr1, _reg_swcr3, _reg_lock,	\
		       _mask_lock, _flags)					\
	CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, 0, _reg_swcr3,		\
		       _reg_lock, _mask_lock, &spacemit_ccu_pll_ops, _flags)

#define CCU_PLLA_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,	\
		       _reg_lock, _mask_lock, _flags)				\
	CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,	\
		       _reg_lock, _mask_lock, &spacemit_ccu_plla_ops, _flags)

static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw)
{
	struct ccu_common *common = hw_to_ccu_common(hw);
@@ -82,5 +118,6 @@ static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw)
}

extern const struct clk_ops spacemit_ccu_pll_ops;
extern const struct clk_ops spacemit_ccu_plla_ops;

#endif