brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · e5f8fb7 Raw
231 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Zynq PLL driver4 *5 *  Copyright (C) 2013 Xilinx6 *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 clock16 * @hw:		Handle between common and hardware-specific interfaces17 * @pll_ctrl:	PLL control register18 * @pll_status:	PLL status register19 * @lock:	Register lock20 * @lockbit:	Indicates the associated PLL_LOCKED bit in the PLL status21 *		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	0x7f00034#define PLLCTRL_FBDIV_SHIFT	1235#define PLLCTRL_BPQUAL_MASK	(1 << 3)36#define PLLCTRL_PWRDWN_MASK	237#define PLLCTRL_PWRDWN_SHIFT	138#define PLLCTRL_RESET_MASK	139#define PLLCTRL_RESET_SHIFT	040 41#define PLL_FBDIV_MIN	1342#define PLL_FBDIV_MAX	6643 44/**45 * zynq_pll_round_rate() - Round a clock frequency46 * @hw:		Handle between common and hardware-specific interfaces47 * @rate:	Desired clock frequency48 * @prate:	Clock frequency of parent clock49 * Return:	frequency closest to @rate the hardware can generate.50 */51static long zynq_pll_round_rate(struct clk_hw *hw, unsigned long rate,52		unsigned long *prate)53{54	u32 fbdiv;55 56	fbdiv = DIV_ROUND_CLOSEST(rate, *prate);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	return *prate * fbdiv;63}64 65/**66 * zynq_pll_recalc_rate() - Recalculate clock frequency67 * @hw:			Handle between common and hardware-specific interfaces68 * @parent_rate:	Clock frequency of parent clock69 * Return:		current clock frequency.70 */71static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw,72		unsigned long parent_rate)73{74	struct zynq_pll *clk = to_zynq_pll(hw);75	u32 fbdiv;76 77	/*78	 * makes probably sense to redundantly save fbdiv in the struct79	 * zynq_pll to save the IO access.80	 */81	fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >>82			PLLCTRL_FBDIV_SHIFT;83 84	return parent_rate * fbdiv;85}86 87/**88 * zynq_pll_is_enabled - Check if a clock is enabled89 * @hw:		Handle between common and hardware-specific interfaces90 * Return:	1 if the clock is enabled, 0 otherwise.91 *92 * Not sure this is a good idea, but since disabled means bypassed for93 * this clock implementation we say we are always enabled.94 */95static int zynq_pll_is_enabled(struct clk_hw *hw)96{97	unsigned long flags = 0;98	u32 reg;99	struct zynq_pll *clk = to_zynq_pll(hw);100 101	spin_lock_irqsave(clk->lock, flags);102 103	reg = readl(clk->pll_ctrl);104 105	spin_unlock_irqrestore(clk->lock, flags);106 107	return !(reg & (PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK));108}109 110/**111 * zynq_pll_enable - Enable clock112 * @hw:		Handle between common and hardware-specific interfaces113 * Return: 0 on success114 */115static int zynq_pll_enable(struct clk_hw *hw)116{117	unsigned long flags = 0;118	u32 reg;119	struct zynq_pll *clk = to_zynq_pll(hw);120 121	if (zynq_pll_is_enabled(hw))122		return 0;123 124	pr_info("PLL: enable\n");125 126	/* Power up PLL and wait for lock */127	spin_lock_irqsave(clk->lock, flags);128 129	reg = readl(clk->pll_ctrl);130	reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK);131	writel(reg, clk->pll_ctrl);132	while (!(readl(clk->pll_status) & (1 << clk->lockbit)))133		;134 135	spin_unlock_irqrestore(clk->lock, flags);136 137	return 0;138}139 140/**141 * zynq_pll_disable - Disable clock142 * @hw:		Handle between common and hardware-specific interfaces143 * Returns 0 on success144 */145static void zynq_pll_disable(struct clk_hw *hw)146{147	unsigned long flags = 0;148	u32 reg;149	struct zynq_pll *clk = to_zynq_pll(hw);150 151	if (!zynq_pll_is_enabled(hw))152		return;153 154	pr_info("PLL: shutdown\n");155 156	/* shut down PLL */157	spin_lock_irqsave(clk->lock, flags);158 159	reg = readl(clk->pll_ctrl);160	reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK;161	writel(reg, clk->pll_ctrl);162 163	spin_unlock_irqrestore(clk->lock, flags);164}165 166static const struct clk_ops zynq_pll_ops = {167	.enable = zynq_pll_enable,168	.disable = zynq_pll_disable,169	.is_enabled = zynq_pll_is_enabled,170	.round_rate = zynq_pll_round_rate,171	.recalc_rate = zynq_pll_recalc_rate172};173 174/**175 * clk_register_zynq_pll() - Register PLL with the clock framework176 * @name:	PLL name177 * @parent:	Parent clock name178 * @pll_ctrl:	Pointer to PLL control register179 * @pll_status:	Pointer to PLL status register180 * @lock_index:	Bit index to this PLL's lock status bit in @pll_status181 * @lock:	Register lock182 * Return:	handle to the registered clock.183 */184struct clk *clk_register_zynq_pll(const char *name, const char *parent,185		void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index,186		spinlock_t *lock)187{188	struct zynq_pll *pll;189	struct clk *clk;190	u32 reg;191	const char *parent_arr[1] = {parent};192	unsigned long flags = 0;193	struct clk_init_data initd = {194		.name = name,195		.parent_names = parent_arr,196		.ops = &zynq_pll_ops,197		.num_parents = 1,198		.flags = 0199	};200 201	pll = kmalloc(sizeof(*pll), GFP_KERNEL);202	if (!pll)203		return ERR_PTR(-ENOMEM);204 205	/* Populate the struct */206	pll->hw.init = &initd;207	pll->pll_ctrl = pll_ctrl;208	pll->pll_status = pll_status;209	pll->lockbit = lock_index;210	pll->lock = lock;211 212	spin_lock_irqsave(pll->lock, flags);213 214	reg = readl(pll->pll_ctrl);215	reg &= ~PLLCTRL_BPQUAL_MASK;216	writel(reg, pll->pll_ctrl);217 218	spin_unlock_irqrestore(pll->lock, flags);219 220	clk = clk_register(NULL, &pll->hw);221	if (WARN_ON(IS_ERR(clk)))222		goto free_pll;223 224	return clk;225 226free_pll:227	kfree(pll);228 229	return clk;230}231