File Index
Files and directories
Sign in
FILE

pll.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/zynq/pll.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Zynq PLL driver
  4 *
  5 *  Copyright (C) 2013 Xilinx
  6 *
  7 *  Sören Brinkmann <soren.brinkmann@xilinx.com>
  8 */
  9#include <linux/clk/zynq.h>
 10#include <linux/clk-provider.h>
 11#include <linux/slab.h>
 12#include <linux/io.h>
 13
 14/**
 15 * struct zynq_pll - pll clock
 16 * @hw:		Handle between common and hardware-specific interfaces
 17 * @pll_ctrl:	PLL control register
 18 * @pll_status:	PLL status register
 19 * @lock:	Register lock
 20 * @lockbit:	Indicates the associated PLL_LOCKED bit in the PLL status
 21 *		register.
 22 */
 23struct zynq_pll {
 24	struct clk_hw	hw;
 25	void __iomem	*pll_ctrl;
 26	void __iomem	*pll_status;
 27	spinlock_t	*lock;
 28	u8		lockbit;
 29};
 30#define to_zynq_pll(_hw)	container_of(_hw, struct zynq_pll, hw)
 31
 32/* Register bitfield defines */
 33#define PLLCTRL_FBDIV_MASK	0x7f000
 34#define PLLCTRL_FBDIV_SHIFT	12
 35#define PLLCTRL_BPQUAL_MASK	(1 << 3)
 36#define PLLCTRL_PWRDWN_MASK	2
 37#define PLLCTRL_PWRDWN_SHIFT	1
 38#define PLLCTRL_RESET_MASK	1
 39#define PLLCTRL_RESET_SHIFT	0
 40
 41#define PLL_FBDIV_MIN	13
 42#define PLL_FBDIV_MAX	66
 43
 44/**
 45 * zynq_pll_round_rate() - Round a clock frequency
 46 * @hw:		Handle between common and hardware-specific interfaces
 47 * @rate:	Desired clock frequency
 48 * @prate:	Clock frequency of parent clock
 49 * Return:	frequency closest to @rate the hardware can generate.
 50 */
 51static int zynq_pll_determine_rate(struct clk_hw *hw,
 52				   struct clk_rate_request *req)
 53{
 54	u32 fbdiv;
 55
 56	fbdiv = DIV_ROUND_CLOSEST(req->rate, req->best_parent_rate);
 57	if (fbdiv < PLL_FBDIV_MIN)
 58		fbdiv = PLL_FBDIV_MIN;
 59	else if (fbdiv > PLL_FBDIV_MAX)
 60		fbdiv = PLL_FBDIV_MAX;
 61
 62	req->rate = req->best_parent_rate * fbdiv;
 63
 64	return 0;
 65}
 66
 67/**
 68 * zynq_pll_recalc_rate() - Recalculate clock frequency
 69 * @hw:			Handle between common and hardware-specific interfaces
 70 * @parent_rate:	Clock frequency of parent clock
 71 * Return:		current clock frequency.
 72 */
 73static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,
 74		unsigned long parent_rate)
 75{
 76	struct zynq_pll *clk = to_zynq_pll(hw);
 77	u32 fbdiv;
 78
 79	/*
 80	 * makes probably sense to redundantly save fbdiv in the struct
 81	 * zynq_pll to save the IO access.
 82	 */
 83	fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>
 84			PLLCTRL_FBDIV_SHIFT;
 85
 86	return parent_rate * fbdiv;
 87}
 88
 89/**
 90 * zynq_pll_is_enabled - Check if a clock is enabled
 91 * @hw:		Handle between common and hardware-specific interfaces
 92 * Return:	1 if the clock is enabled, 0 otherwise.
 93 *
 94 * Not sure this is a good idea, but since disabled means bypassed for
 95 * this clock implementation we say we are always enabled.
 96 */
 97static int zynq_pll_is_enabled(struct clk_hw *hw)
 98{
 99	unsigned long flags = 0;
100	u32 reg;
101	struct zynq_pll *clk = to_zynq_pll(hw);
102
103	spin_lock_irqsave(clk->lock, flags);
104
105	reg = readl(clk->pll_ctrl);
106
107	spin_unlock_irqrestore(clk->lock, flags);
108
109	return !(reg & (PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK));
110}
111
112/**
113 * zynq_pll_enable - Enable clock
114 * @hw:		Handle between common and hardware-specific interfaces
115 * Return: 0 on success
116 */
117static int zynq_pll_enable(struct clk_hw *hw)
118{
119	unsigned long flags = 0;
120	u32 reg;
121	struct zynq_pll *clk = to_zynq_pll(hw);
122
123	if (zynq_pll_is_enabled(hw))
124		return 0;
125
126	pr_info("PLL: enable\n");
127
128	/* Power up PLL and wait for lock */
129	spin_lock_irqsave(clk->lock, flags);
130
131	reg = readl(clk->pll_ctrl);
132	reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);
133	writel(reg, clk->pll_ctrl);
134	while (!(readl(clk->pll_status) & (1 << clk->lockbit)))
135		;
136
137	spin_unlock_irqrestore(clk->lock, flags);
138
139	return 0;
140}
141
142/**
143 * zynq_pll_disable - Disable clock
144 * @hw:		Handle between common and hardware-specific interfaces
145 * Returns 0 on success
146 */
147static void zynq_pll_disable(struct clk_hw *hw)
148{
149	unsigned long flags = 0;
150	u32 reg;
151	struct zynq_pll *clk = to_zynq_pll(hw);
152
153	if (!zynq_pll_is_enabled(hw))
154		return;
155
156	pr_info("PLL: shutdown\n");
157
158	/* shut down PLL */
159	spin_lock_irqsave(clk->lock, flags);
160
161	reg = readl(clk->pll_ctrl);
162	reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;
163	writel(reg, clk->pll_ctrl);
164
165	spin_unlock_irqrestore(clk->lock, flags);
166}
167
168static const struct clk_ops zynq_pll_ops = {
169	.enable = zynq_pll_enable,
170	.disable = zynq_pll_disable,
171	.is_enabled = zynq_pll_is_enabled,
172	.determine_rate = zynq_pll_determine_rate,
173	.recalc_rate = zynq_pll_recalc_rate
174};
175
176/**
177 * clk_register_zynq_pll() - Register PLL with the clock framework
178 * @name:	PLL name
179 * @parent:	Parent clock name
180 * @pll_ctrl:	Pointer to PLL control register
181 * @pll_status:	Pointer to PLL status register
182 * @lock_index:	Bit index to this PLL's lock status bit in @pll_status
183 * @lock:	Register lock
184 * Return:	handle to the registered clock.
185 */
186struct clk *clk_register_zynq_pll(const char *name, const char *parent,
187		void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index,
188		spinlock_t *lock)
189{
190	struct zynq_pll *pll;
191	struct clk *clk;
192	u32 reg;
193	const char *parent_arr[1] = {parent};
194	unsigned long flags = 0;
195	struct clk_init_data initd = {
196		.name = name,
197		.parent_names = parent_arr,
198		.ops = &zynq_pll_ops,
199		.num_parents = 1,
200		.flags = 0
201	};
202
203	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
204	if (!pll)
205		return ERR_PTR(-ENOMEM);
206
207	/* Populate the struct */
208	pll->hw.init = &initd;
209	pll->pll_ctrl = pll_ctrl;
210	pll->pll_status = pll_status;
211	pll->lockbit = lock_index;
212	pll->lock = lock;
213
214	spin_lock_irqsave(pll->lock, flags);
215
216	reg = readl(pll->pll_ctrl);
217	reg &= ~PLLCTRL_BPQUAL_MASK;
218	writel(reg, pll->pll_ctrl);
219
220	spin_unlock_irqrestore(pll->lock, flags);
221
222	clk = clk_register(NULL, &pll->hw);
223	if (WARN_ON(IS_ERR(clk)))
224		goto free_pll;
225
226	return clk;
227
228free_pll:
229	kfree(pll);
230
231	return clk;
232}
~/ Read-only file preview.