brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 705352a Raw
254 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018, The Linux Foundation. All rights reserved.3 4#include <linux/kernel.h>5#include <linux/export.h>6#include <linux/regmap.h>7#include <linux/delay.h>8#include <linux/err.h>9#include <linux/clk-provider.h>10#include <linux/spinlock.h>11 12#include "clk-regmap.h"13#include "clk-hfpll.h"14 15#define PLL_OUTCTRL	BIT(0)16#define PLL_BYPASSNL	BIT(1)17#define PLL_RESET_N	BIT(2)18 19/* Initialize a HFPLL at a given rate and enable it. */20static void __clk_hfpll_init_once(struct clk_hw *hw)21{22	struct clk_hfpll *h = to_clk_hfpll(hw);23	struct hfpll_data const *hd = h->d;24	struct regmap *regmap = h->clkr.regmap;25 26	if (likely(h->init_done))27		return;28 29	/* Configure PLL parameters for integer mode. */30	if (hd->config_val)31		regmap_write(regmap, hd->config_reg, hd->config_val);32	regmap_write(regmap, hd->m_reg, 0);33	regmap_write(regmap, hd->n_reg, 1);34 35	if (hd->user_reg) {36		u32 regval = hd->user_val;37		unsigned long rate;38 39		rate = clk_hw_get_rate(hw);40 41		/* Pick the right VCO. */42		if (hd->user_vco_mask && rate > hd->low_vco_max_rate)43			regval |= hd->user_vco_mask;44		regmap_write(regmap, hd->user_reg, regval);45	}46 47	/* Write L_VAL from conf if it exist */48	if (hd->l_val)49		regmap_write(regmap, hd->l_reg, hd->l_val);50 51	if (hd->droop_reg)52		regmap_write(regmap, hd->droop_reg, hd->droop_val);53 54	h->init_done = true;55}56 57static void __clk_hfpll_enable(struct clk_hw *hw)58{59	struct clk_hfpll *h = to_clk_hfpll(hw);60	struct hfpll_data const *hd = h->d;61	struct regmap *regmap = h->clkr.regmap;62	u32 val;63 64	__clk_hfpll_init_once(hw);65 66	/* Disable PLL bypass mode. */67	regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL);68 69	/*70	 * H/W requires a 5us delay between disabling the bypass and71	 * de-asserting the reset. Delay 10us just to be safe.72	 */73	udelay(10);74 75	/* De-assert active-low PLL reset. */76	regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N);77 78	/* Wait for PLL to lock. */79	if (hd->status_reg)80		/*81		 * Busy wait. Should never timeout, we add a timeout to82		 * prevent any sort of stall.83		 */84		regmap_read_poll_timeout(regmap, hd->status_reg, val,85					 !(val & BIT(hd->lock_bit)), 0,86					 100 * USEC_PER_MSEC);87	else88		udelay(60);89 90	/* Enable PLL output. */91	regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL);92}93 94/* Enable an already-configured HFPLL. */95static int clk_hfpll_enable(struct clk_hw *hw)96{97	unsigned long flags;98	struct clk_hfpll *h = to_clk_hfpll(hw);99	struct hfpll_data const *hd = h->d;100	struct regmap *regmap = h->clkr.regmap;101	u32 mode;102 103	spin_lock_irqsave(&h->lock, flags);104	regmap_read(regmap, hd->mode_reg, &mode);105	if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)))106		__clk_hfpll_enable(hw);107	spin_unlock_irqrestore(&h->lock, flags);108 109	return 0;110}111 112static void __clk_hfpll_disable(struct clk_hfpll *h)113{114	struct hfpll_data const *hd = h->d;115	struct regmap *regmap = h->clkr.regmap;116 117	/*118	 * Disable the PLL output, disable test mode, enable the bypass mode,119	 * and assert the reset.120	 */121	regmap_update_bits(regmap, hd->mode_reg,122			   PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0);123}124 125static void clk_hfpll_disable(struct clk_hw *hw)126{127	struct clk_hfpll *h = to_clk_hfpll(hw);128	unsigned long flags;129 130	spin_lock_irqsave(&h->lock, flags);131	__clk_hfpll_disable(h);132	spin_unlock_irqrestore(&h->lock, flags);133}134 135static int clk_hfpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)136{137	struct clk_hfpll *h = to_clk_hfpll(hw);138	struct hfpll_data const *hd = h->d;139	unsigned long rrate;140 141	req->rate = clamp(req->rate, hd->min_rate, hd->max_rate);142 143	rrate = DIV_ROUND_UP(req->rate, req->best_parent_rate) * req->best_parent_rate;144	if (rrate > hd->max_rate)145		rrate -= req->best_parent_rate;146 147	req->rate = rrate;148	return 0;149}150 151/*152 * For optimization reasons, assumes no downstream clocks are actively using153 * it.154 */155static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate,156			      unsigned long parent_rate)157{158	struct clk_hfpll *h = to_clk_hfpll(hw);159	struct hfpll_data const *hd = h->d;160	struct regmap *regmap = h->clkr.regmap;161	unsigned long flags;162	u32 l_val, val;163	bool enabled;164 165	l_val = rate / parent_rate;166 167	spin_lock_irqsave(&h->lock, flags);168 169	enabled = __clk_is_enabled(hw->clk);170	if (enabled)171		__clk_hfpll_disable(h);172 173	/* Pick the right VCO. */174	if (hd->user_reg && hd->user_vco_mask) {175		regmap_read(regmap, hd->user_reg, &val);176		if (rate <= hd->low_vco_max_rate)177			val &= ~hd->user_vco_mask;178		else179			val |= hd->user_vco_mask;180		regmap_write(regmap, hd->user_reg, val);181	}182 183	regmap_write(regmap, hd->l_reg, l_val);184 185	if (enabled)186		__clk_hfpll_enable(hw);187 188	spin_unlock_irqrestore(&h->lock, flags);189 190	return 0;191}192 193static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw,194					   unsigned long parent_rate)195{196	struct clk_hfpll *h = to_clk_hfpll(hw);197	struct hfpll_data const *hd = h->d;198	struct regmap *regmap = h->clkr.regmap;199	u32 l_val;200 201	regmap_read(regmap, hd->l_reg, &l_val);202 203	return l_val * parent_rate;204}205 206static int clk_hfpll_init(struct clk_hw *hw)207{208	struct clk_hfpll *h = to_clk_hfpll(hw);209	struct hfpll_data const *hd = h->d;210	struct regmap *regmap = h->clkr.regmap;211	u32 mode, status;212 213	regmap_read(regmap, hd->mode_reg, &mode);214	if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) {215		__clk_hfpll_init_once(hw);216		return 0;217	}218 219	if (hd->status_reg) {220		regmap_read(regmap, hd->status_reg, &status);221		if (!(status & BIT(hd->lock_bit))) {222			WARN(1, "HFPLL %s is ON, but not locked!\n",223			     __clk_get_name(hw->clk));224			clk_hfpll_disable(hw);225			__clk_hfpll_init_once(hw);226		}227	}228 229	return 0;230}231 232static int hfpll_is_enabled(struct clk_hw *hw)233{234	struct clk_hfpll *h = to_clk_hfpll(hw);235	struct hfpll_data const *hd = h->d;236	struct regmap *regmap = h->clkr.regmap;237	u32 mode;238 239	regmap_read(regmap, hd->mode_reg, &mode);240	mode &= 0x7;241	return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL);242}243 244const struct clk_ops clk_ops_hfpll = {245	.enable = clk_hfpll_enable,246	.disable = clk_hfpll_disable,247	.is_enabled = hfpll_is_enabled,248	.determine_rate = clk_hfpll_determine_rate,249	.set_rate = clk_hfpll_set_rate,250	.recalc_rate = clk_hfpll_recalc_rate,251	.init = clk_hfpll_init,252};253EXPORT_SYMBOL_GPL(clk_ops_hfpll);254