File Index
Files and directories
Sign in
FILE

ccu_pll.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/spacemit/ccu_pll.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2024 SpacemiT Technology Co. Ltd
  4 * Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
  5 */
  6
  7#include <linux/clk-provider.h>
  8#include <linux/math.h>
  9#include <linux/regmap.h>
 10
 11#include "ccu_common.h"
 12#include "ccu_pll.h"
 13
 14#define PLL_TIMEOUT_US		3000
 15#define PLL_DELAY_US		5
 16
 17#define PLL_SWCR3_EN		((u32)BIT(31))
 18#define PLL_SWCR3_MASK		GENMASK(30, 0)
 19
 20static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_pll *pll,
 21							       unsigned long rate)
 22{
 23	struct ccu_pll_config *config = &pll->config;
 24	const struct ccu_pll_rate_tbl *best_entry;
 25	unsigned long best_delta = ULONG_MAX;
 26	int i;
 27
 28	for (i = 0; i < config->tbl_num; i++) {
 29		const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
 30		unsigned long delta = abs_diff(entry->rate, rate);
 31
 32		if (delta < best_delta) {
 33			best_delta = delta;
 34			best_entry = entry;
 35		}
 36	}
 37
 38	return best_entry;
 39}
 40
 41static const struct ccu_pll_rate_tbl *ccu_pll_lookup_matched_entry(struct ccu_pll *pll)
 42{
 43	struct ccu_pll_config *config = &pll->config;
 44	u32 swcr1, swcr3;
 45	int i;
 46
 47	swcr1 = ccu_read(&pll->common, swcr1);
 48	swcr3 = ccu_read(&pll->common, swcr3);
 49	swcr3 &= PLL_SWCR3_MASK;
 50
 51	for (i = 0; i < config->tbl_num; i++) {
 52		const struct ccu_pll_rate_tbl *entry = &config->rate_tbl[i];
 53
 54		if (swcr1 == entry->swcr1 && swcr3 == entry->swcr3)
 55			return entry;
 56	}
 57
 58	return NULL;
 59}
 60
 61static void ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *entry)
 62{
 63	struct ccu_common *common = &pll->common;
 64
 65	regmap_write(common->regmap, common->reg_swcr1, entry->swcr1);
 66	ccu_update(common, swcr3, PLL_SWCR3_MASK, entry->swcr3);
 67}
 68
 69static int ccu_pll_is_enabled(struct clk_hw *hw)
 70{
 71	struct ccu_common *common = hw_to_ccu_common(hw);
 72
 73	return ccu_read(common, swcr3) & PLL_SWCR3_EN;
 74}
 75
 76static int ccu_pll_enable(struct clk_hw *hw)
 77{
 78	struct ccu_pll *pll = hw_to_ccu_pll(hw);
 79	struct ccu_common *common = &pll->common;
 80	unsigned int tmp;
 81
 82	ccu_update(common, swcr3, PLL_SWCR3_EN, PLL_SWCR3_EN);
 83
 84	/* check lock status */
 85	return regmap_read_poll_timeout_atomic(common->lock_regmap,
 86					       pll->config.reg_lock,
 87					       tmp,
 88					       tmp & pll->config.mask_lock,
 89					       PLL_DELAY_US, PLL_TIMEOUT_US);
 90}
 91
 92static void ccu_pll_disable(struct clk_hw *hw)
 93{
 94	struct ccu_common *common = hw_to_ccu_common(hw);
 95
 96	ccu_update(common, swcr3, PLL_SWCR3_EN, 0);
 97}
 98
 99/*
100 * PLLs must be gated before changing rate, which is ensured by
101 * flag CLK_SET_RATE_GATE.
102 */
103static int ccu_pll_set_rate(struct clk_hw *hw, unsigned long rate,
104			    unsigned long parent_rate)
105{
106	struct ccu_pll *pll = hw_to_ccu_pll(hw);
107	const struct ccu_pll_rate_tbl *entry;
108
109	entry = ccu_pll_lookup_best_rate(pll, rate);
110	ccu_pll_update_param(pll, entry);
111
112	return 0;
113}
114
115static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
116					 unsigned long parent_rate)
117{
118	struct ccu_pll *pll = hw_to_ccu_pll(hw);
119	const struct ccu_pll_rate_tbl *entry;
120
121	entry = ccu_pll_lookup_matched_entry(pll);
122
123	WARN_ON_ONCE(!entry);
124
125	return entry ? entry->rate : 0;
126}
127
128static int ccu_pll_determine_rate(struct clk_hw *hw,
129				  struct clk_rate_request *req)
130{
131	struct ccu_pll *pll = hw_to_ccu_pll(hw);
132
133	req->rate = ccu_pll_lookup_best_rate(pll, req->rate)->rate;
134
135	return 0;
136}
137
138static int ccu_pll_init(struct clk_hw *hw)
139{
140	struct ccu_pll *pll = hw_to_ccu_pll(hw);
141
142	if (ccu_pll_lookup_matched_entry(pll))
143		return 0;
144
145	ccu_pll_disable(hw);
146	ccu_pll_update_param(pll, &pll->config.rate_tbl[0]);
147
148	return 0;
149}
150
151const struct clk_ops spacemit_ccu_pll_ops = {
152	.init		= ccu_pll_init,
153	.enable		= ccu_pll_enable,
154	.disable	= ccu_pll_disable,
155	.set_rate	= ccu_pll_set_rate,
156	.recalc_rate	= ccu_pll_recalc_rate,
157	.determine_rate = ccu_pll_determine_rate,
158	.is_enabled	= ccu_pll_is_enabled,
159};
160EXPORT_SYMBOL_NS_GPL(spacemit_ccu_pll_ops, "CLK_SPACEMIT");
~/ Read-only file preview.