18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2014 Broadcom Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any
98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty
108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
118c2ecf20Sopenharmony_ci * GNU General Public License for more details.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
208c2ecf20Sopenharmony_ci#include <linux/of_address.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "clk-iproc.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define PLL_VCO_HIGH_SHIFT 19
268c2ecf20Sopenharmony_ci#define PLL_VCO_LOW_SHIFT  30
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * PLL MACRO_SELECT modes 0 to 5 choose pre-calculated PLL output frequencies
308c2ecf20Sopenharmony_ci * from a look-up table. Mode 7 allows user to manipulate PLL clock dividers
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#define PLL_USER_MODE 7
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* number of delay loops waiting for PLL to lock */
358c2ecf20Sopenharmony_ci#define LOCK_DELAY 100
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* number of VCO frequency bands */
388c2ecf20Sopenharmony_ci#define NUM_FREQ_BANDS 8
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define NUM_KP_BANDS 3
418c2ecf20Sopenharmony_cienum kp_band {
428c2ecf20Sopenharmony_ci	KP_BAND_MID = 0,
438c2ecf20Sopenharmony_ci	KP_BAND_HIGH,
448c2ecf20Sopenharmony_ci	KP_BAND_HIGH_HIGH
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = {
488c2ecf20Sopenharmony_ci	{ 5, 6, 6, 7, 7, 8, 9, 10 },
498c2ecf20Sopenharmony_ci	{ 4, 4, 5, 5, 6, 7, 8, 9  },
508c2ecf20Sopenharmony_ci	{ 4, 5, 5, 6, 7, 8, 9, 10 },
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = {
548c2ecf20Sopenharmony_ci	{ 10000000,  12500000  },
558c2ecf20Sopenharmony_ci	{ 12500000,  15000000  },
568c2ecf20Sopenharmony_ci	{ 15000000,  20000000  },
578c2ecf20Sopenharmony_ci	{ 20000000,  25000000  },
588c2ecf20Sopenharmony_ci	{ 25000000,  50000000  },
598c2ecf20Sopenharmony_ci	{ 50000000,  75000000  },
608c2ecf20Sopenharmony_ci	{ 75000000,  100000000 },
618c2ecf20Sopenharmony_ci	{ 100000000, 125000000 },
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cienum vco_freq_range {
658c2ecf20Sopenharmony_ci	VCO_LOW       = 700000000U,
668c2ecf20Sopenharmony_ci	VCO_MID       = 1200000000U,
678c2ecf20Sopenharmony_ci	VCO_HIGH      = 2200000000U,
688c2ecf20Sopenharmony_ci	VCO_HIGH_HIGH = 3100000000U,
698c2ecf20Sopenharmony_ci	VCO_MAX       = 4000000000U,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct iproc_pll {
738c2ecf20Sopenharmony_ci	void __iomem *status_base;
748c2ecf20Sopenharmony_ci	void __iomem *control_base;
758c2ecf20Sopenharmony_ci	void __iomem *pwr_base;
768c2ecf20Sopenharmony_ci	void __iomem *asiu_base;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl;
798c2ecf20Sopenharmony_ci	const struct iproc_pll_vco_param *vco_param;
808c2ecf20Sopenharmony_ci	unsigned int num_vco_entries;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct iproc_clk {
848c2ecf20Sopenharmony_ci	struct clk_hw hw;
858c2ecf20Sopenharmony_ci	struct iproc_pll *pll;
868c2ecf20Sopenharmony_ci	const struct iproc_clk_ctrl *ctrl;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw)
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int pll_calc_param(unsigned long target_rate,
928c2ecf20Sopenharmony_ci			unsigned long parent_rate,
938c2ecf20Sopenharmony_ci			struct iproc_pll_vco_param *vco_out)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	u64 ndiv_int, ndiv_frac, residual;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ndiv_int = target_rate / parent_rate;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (!ndiv_int || (ndiv_int > 255))
1008c2ecf20Sopenharmony_ci		return -EINVAL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	residual = target_rate - (ndiv_int * parent_rate);
1038c2ecf20Sopenharmony_ci	residual <<= 20;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/*
1068c2ecf20Sopenharmony_ci	 * Add half of the divisor so the result will be rounded to closest
1078c2ecf20Sopenharmony_ci	 * instead of rounded down.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	residual += (parent_rate / 2);
1108c2ecf20Sopenharmony_ci	ndiv_frac = div64_u64((u64)residual, (u64)parent_rate);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	vco_out->ndiv_int = ndiv_int;
1138c2ecf20Sopenharmony_ci	vco_out->ndiv_frac = ndiv_frac;
1148c2ecf20Sopenharmony_ci	vco_out->pdiv = 1;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	vco_out->rate = vco_out->ndiv_int * parent_rate;
1178c2ecf20Sopenharmony_ci	residual = (u64)vco_out->ndiv_frac * (u64)parent_rate;
1188c2ecf20Sopenharmony_ci	residual >>= 20;
1198c2ecf20Sopenharmony_ci	vco_out->rate += residual;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * Based on the target frequency, find a match from the VCO frequency parameter
1268c2ecf20Sopenharmony_ci * table and return its index
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int i;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < pll->num_vco_entries; i++)
1338c2ecf20Sopenharmony_ci		if (target_rate == pll->vco_param[i].rate)
1348c2ecf20Sopenharmony_ci			break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (i >= pll->num_vco_entries)
1378c2ecf20Sopenharmony_ci		return -EINVAL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return i;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int get_kp(unsigned long ref_freq, enum kp_band kp_index)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int i;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (ref_freq < ref_freq_table[0][0])
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_FREQ_BANDS; i++) {
1508c2ecf20Sopenharmony_ci		if (ref_freq >= ref_freq_table[i][0] &&
1518c2ecf20Sopenharmony_ci		    ref_freq < ref_freq_table[i][1])
1528c2ecf20Sopenharmony_ci			return kp_table[kp_index][i];
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	return -EINVAL;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int pll_wait_for_lock(struct iproc_pll *pll)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int i;
1608c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	for (i = 0; i < LOCK_DELAY; i++) {
1638c2ecf20Sopenharmony_ci		u32 val = readl(pll->status_base + ctrl->status.offset);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (val & (1 << ctrl->status.shift))
1668c2ecf20Sopenharmony_ci			return 0;
1678c2ecf20Sopenharmony_ci		udelay(10);
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return -EIO;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void iproc_pll_write(const struct iproc_pll *pll, void __iomem *base,
1748c2ecf20Sopenharmony_ci			    const u32 offset, u32 val)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	writel(val, base + offset);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK &&
1818c2ecf20Sopenharmony_ci		     (base == pll->status_base || base == pll->control_base)))
1828c2ecf20Sopenharmony_ci		val = readl(base + offset);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void __pll_disable(struct iproc_pll *pll)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1888c2ecf20Sopenharmony_ci	u32 val;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
1918c2ecf20Sopenharmony_ci		val = readl(pll->asiu_base + ctrl->asiu.offset);
1928c2ecf20Sopenharmony_ci		val &= ~(1 << ctrl->asiu.en_shift);
1938c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
1978c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->aon.offset);
1988c2ecf20Sopenharmony_ci		val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
1998c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (pll->pwr_base) {
2038c2ecf20Sopenharmony_ci		/* latch input value so core power can be shut down */
2048c2ecf20Sopenharmony_ci		val = readl(pll->pwr_base + ctrl->aon.offset);
2058c2ecf20Sopenharmony_ci		val |= 1 << ctrl->aon.iso_shift;
2068c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		/* power down the core */
2098c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
2108c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int __pll_enable(struct iproc_pll *pll)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2178c2ecf20Sopenharmony_ci	u32 val;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
2208c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->aon.offset);
2218c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
2228c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (pll->pwr_base) {
2268c2ecf20Sopenharmony_ci		/* power up the PLL and make sure it's not latched */
2278c2ecf20Sopenharmony_ci		val = readl(pll->pwr_base + ctrl->aon.offset);
2288c2ecf20Sopenharmony_ci		val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
2298c2ecf20Sopenharmony_ci		val &= ~(1 << ctrl->aon.iso_shift);
2308c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* certain PLLs also need to be ungated from the ASIU top level */
2348c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
2358c2ecf20Sopenharmony_ci		val = readl(pll->asiu_base + ctrl->asiu.offset);
2368c2ecf20Sopenharmony_ci		val |= (1 << ctrl->asiu.en_shift);
2378c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic void __pll_put_in_reset(struct iproc_pll *pll)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	u32 val;
2468c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2478c2ecf20Sopenharmony_ci	const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	val = readl(pll->control_base + reset->offset);
2508c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW)
2518c2ecf20Sopenharmony_ci		val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift);
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift));
2548c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, reset->offset, val);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp,
2588c2ecf20Sopenharmony_ci				  unsigned int ka, unsigned int ki)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	u32 val;
2618c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2628c2ecf20Sopenharmony_ci	const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
2638c2ecf20Sopenharmony_ci	const struct iproc_pll_dig_filter_ctrl *dig_filter = &ctrl->dig_filter;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	val = readl(pll->control_base + dig_filter->offset);
2668c2ecf20Sopenharmony_ci	val &= ~(bit_mask(dig_filter->ki_width) << dig_filter->ki_shift |
2678c2ecf20Sopenharmony_ci		bit_mask(dig_filter->kp_width) << dig_filter->kp_shift |
2688c2ecf20Sopenharmony_ci		bit_mask(dig_filter->ka_width) << dig_filter->ka_shift);
2698c2ecf20Sopenharmony_ci	val |= ki << dig_filter->ki_shift | kp << dig_filter->kp_shift |
2708c2ecf20Sopenharmony_ci	       ka << dig_filter->ka_shift;
2718c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, dig_filter->offset, val);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	val = readl(pll->control_base + reset->offset);
2748c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW)
2758c2ecf20Sopenharmony_ci		val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift));
2768c2ecf20Sopenharmony_ci	else
2778c2ecf20Sopenharmony_ci		val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift);
2788c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, reset->offset, val);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*
2828c2ecf20Sopenharmony_ci * Determines if the change to be applied to the PLL is minor (just an update
2838c2ecf20Sopenharmony_ci * or the fractional divider). If so, then we can avoid going through a
2848c2ecf20Sopenharmony_ci * disruptive reset and lock sequence.
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_cistatic bool pll_fractional_change_only(struct iproc_pll *pll,
2878c2ecf20Sopenharmony_ci				       struct iproc_pll_vco_param *vco)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2908c2ecf20Sopenharmony_ci	u32 val;
2918c2ecf20Sopenharmony_ci	u32 ndiv_int;
2928c2ecf20Sopenharmony_ci	unsigned int pdiv;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* PLL needs to be locked */
2958c2ecf20Sopenharmony_ci	val = readl(pll->status_base + ctrl->status.offset);
2968c2ecf20Sopenharmony_ci	if ((val & (1 << ctrl->status.shift)) == 0)
2978c2ecf20Sopenharmony_ci		return false;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->ndiv_int.offset);
3008c2ecf20Sopenharmony_ci	ndiv_int = (val >> ctrl->ndiv_int.shift) &
3018c2ecf20Sopenharmony_ci		bit_mask(ctrl->ndiv_int.width);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (ndiv_int != vco->ndiv_int)
3048c2ecf20Sopenharmony_ci		return false;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->pdiv.offset);
3078c2ecf20Sopenharmony_ci	pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (pdiv != vco->pdiv)
3108c2ecf20Sopenharmony_ci		return false;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return true;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco,
3168c2ecf20Sopenharmony_ci			unsigned long parent_rate)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
3198c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
3208c2ecf20Sopenharmony_ci	int ka = 0, ki, kp, ret;
3218c2ecf20Sopenharmony_ci	unsigned long rate = vco->rate;
3228c2ecf20Sopenharmony_ci	u32 val;
3238c2ecf20Sopenharmony_ci	enum kp_band kp_index;
3248c2ecf20Sopenharmony_ci	unsigned long ref_freq;
3258c2ecf20Sopenharmony_ci	const char *clk_name = clk_hw_get_name(&clk->hw);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/*
3288c2ecf20Sopenharmony_ci	 * reference frequency = parent frequency / PDIV
3298c2ecf20Sopenharmony_ci	 * If PDIV = 0, then it becomes a multiplier (x2)
3308c2ecf20Sopenharmony_ci	 */
3318c2ecf20Sopenharmony_ci	if (vco->pdiv == 0)
3328c2ecf20Sopenharmony_ci		ref_freq = parent_rate * 2;
3338c2ecf20Sopenharmony_ci	else
3348c2ecf20Sopenharmony_ci		ref_freq = parent_rate / vco->pdiv;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* determine Ki and Kp index based on target VCO frequency */
3378c2ecf20Sopenharmony_ci	if (rate >= VCO_LOW && rate < VCO_HIGH) {
3388c2ecf20Sopenharmony_ci		ki = 4;
3398c2ecf20Sopenharmony_ci		kp_index = KP_BAND_MID;
3408c2ecf20Sopenharmony_ci	} else if (rate >= VCO_HIGH && rate < VCO_HIGH_HIGH) {
3418c2ecf20Sopenharmony_ci		ki = 3;
3428c2ecf20Sopenharmony_ci		kp_index = KP_BAND_HIGH;
3438c2ecf20Sopenharmony_ci	} else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
3448c2ecf20Sopenharmony_ci		ki = 3;
3458c2ecf20Sopenharmony_ci		kp_index = KP_BAND_HIGH_HIGH;
3468c2ecf20Sopenharmony_ci	} else {
3478c2ecf20Sopenharmony_ci		pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
3488c2ecf20Sopenharmony_ci				clk_name, rate);
3498c2ecf20Sopenharmony_ci		return -EINVAL;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	kp = get_kp(ref_freq, kp_index);
3538c2ecf20Sopenharmony_ci	if (kp < 0) {
3548c2ecf20Sopenharmony_ci		pr_err("%s: pll: %s has invalid kp\n", __func__, clk_name);
3558c2ecf20Sopenharmony_ci		return kp;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ret = __pll_enable(pll);
3598c2ecf20Sopenharmony_ci	if (ret) {
3608c2ecf20Sopenharmony_ci		pr_err("%s: pll: %s fails to enable\n", __func__, clk_name);
3618c2ecf20Sopenharmony_ci		return ret;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (pll_fractional_change_only(clk->pll, vco)) {
3658c2ecf20Sopenharmony_ci		/* program fractional part of NDIV */
3668c2ecf20Sopenharmony_ci		if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
3678c2ecf20Sopenharmony_ci			val = readl(pll->control_base + ctrl->ndiv_frac.offset);
3688c2ecf20Sopenharmony_ci			val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
3698c2ecf20Sopenharmony_ci				 ctrl->ndiv_frac.shift);
3708c2ecf20Sopenharmony_ci			val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
3718c2ecf20Sopenharmony_ci			iproc_pll_write(pll, pll->control_base,
3728c2ecf20Sopenharmony_ci					ctrl->ndiv_frac.offset, val);
3738c2ecf20Sopenharmony_ci			return 0;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* put PLL in reset */
3788c2ecf20Sopenharmony_ci	__pll_put_in_reset(pll);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* set PLL in user mode before modifying PLL controls */
3818c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_USER_MODE_ON) {
3828c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->macro_mode.offset);
3838c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->macro_mode.width) <<
3848c2ecf20Sopenharmony_ci			ctrl->macro_mode.shift);
3858c2ecf20Sopenharmony_ci		val |= PLL_USER_MODE << ctrl->macro_mode.shift;
3868c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->control_base,
3878c2ecf20Sopenharmony_ci			ctrl->macro_mode.offset, val);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.u_offset, 0);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->vco_ctrl.l_offset);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (rate >= VCO_LOW && rate < VCO_MID)
3958c2ecf20Sopenharmony_ci		val |= (1 << PLL_VCO_LOW_SHIFT);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (rate < VCO_HIGH)
3988c2ecf20Sopenharmony_ci		val &= ~(1 << PLL_VCO_HIGH_SHIFT);
3998c2ecf20Sopenharmony_ci	else
4008c2ecf20Sopenharmony_ci		val |= (1 << PLL_VCO_HIGH_SHIFT);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.l_offset, val);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* program integer part of NDIV */
4058c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->ndiv_int.offset);
4068c2ecf20Sopenharmony_ci	val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
4078c2ecf20Sopenharmony_ci	val |= vco->ndiv_int << ctrl->ndiv_int.shift;
4088c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->ndiv_int.offset, val);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* program fractional part of NDIV */
4118c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
4128c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->ndiv_frac.offset);
4138c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
4148c2ecf20Sopenharmony_ci			 ctrl->ndiv_frac.shift);
4158c2ecf20Sopenharmony_ci		val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
4168c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->control_base, ctrl->ndiv_frac.offset,
4178c2ecf20Sopenharmony_ci				val);
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* program PDIV */
4218c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->pdiv.offset);
4228c2ecf20Sopenharmony_ci	val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
4238c2ecf20Sopenharmony_ci	val |= vco->pdiv << ctrl->pdiv.shift;
4248c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->pdiv.offset, val);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	__pll_bring_out_reset(pll, kp, ka, ki);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ret = pll_wait_for_lock(pll);
4298c2ecf20Sopenharmony_ci	if (ret < 0) {
4308c2ecf20Sopenharmony_ci		pr_err("%s: pll: %s failed to lock\n", __func__, clk_name);
4318c2ecf20Sopenharmony_ci		return ret;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int iproc_pll_enable(struct clk_hw *hw)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
4408c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return __pll_enable(pll);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void iproc_pll_disable(struct clk_hw *hw)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
4488c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
4498c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_AON)
4528c2ecf20Sopenharmony_ci		return;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	__pll_disable(pll);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic unsigned long iproc_pll_recalc_rate(struct clk_hw *hw,
4588c2ecf20Sopenharmony_ci					   unsigned long parent_rate)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
4618c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
4628c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
4638c2ecf20Sopenharmony_ci	u32 val;
4648c2ecf20Sopenharmony_ci	u64 ndiv, ndiv_int, ndiv_frac;
4658c2ecf20Sopenharmony_ci	unsigned int pdiv;
4668c2ecf20Sopenharmony_ci	unsigned long rate;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	if (parent_rate == 0)
4698c2ecf20Sopenharmony_ci		return 0;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* PLL needs to be locked */
4728c2ecf20Sopenharmony_ci	val = readl(pll->status_base + ctrl->status.offset);
4738c2ecf20Sopenharmony_ci	if ((val & (1 << ctrl->status.shift)) == 0)
4748c2ecf20Sopenharmony_ci		return 0;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/*
4778c2ecf20Sopenharmony_ci	 * PLL output frequency =
4788c2ecf20Sopenharmony_ci	 *
4798c2ecf20Sopenharmony_ci	 * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv)
4808c2ecf20Sopenharmony_ci	 */
4818c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->ndiv_int.offset);
4828c2ecf20Sopenharmony_ci	ndiv_int = (val >> ctrl->ndiv_int.shift) &
4838c2ecf20Sopenharmony_ci		bit_mask(ctrl->ndiv_int.width);
4848c2ecf20Sopenharmony_ci	ndiv = ndiv_int << 20;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
4878c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->ndiv_frac.offset);
4888c2ecf20Sopenharmony_ci		ndiv_frac = (val >> ctrl->ndiv_frac.shift) &
4898c2ecf20Sopenharmony_ci			bit_mask(ctrl->ndiv_frac.width);
4908c2ecf20Sopenharmony_ci		ndiv += ndiv_frac;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->pdiv.offset);
4948c2ecf20Sopenharmony_ci	pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	rate = (ndiv * parent_rate) >> 20;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (pdiv == 0)
4998c2ecf20Sopenharmony_ci		rate *= 2;
5008c2ecf20Sopenharmony_ci	else
5018c2ecf20Sopenharmony_ci		rate /= pdiv;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return rate;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int iproc_pll_determine_rate(struct clk_hw *hw,
5078c2ecf20Sopenharmony_ci		struct clk_rate_request *req)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	unsigned int  i;
5108c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
5118c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
5128c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
5138c2ecf20Sopenharmony_ci	unsigned long  diff, best_diff;
5148c2ecf20Sopenharmony_ci	unsigned int  best_idx = 0;
5158c2ecf20Sopenharmony_ci	int ret;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (req->rate == 0 || req->best_parent_rate == 0)
5188c2ecf20Sopenharmony_ci		return -EINVAL;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) {
5218c2ecf20Sopenharmony_ci		struct iproc_pll_vco_param vco_param;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		ret = pll_calc_param(req->rate, req->best_parent_rate,
5248c2ecf20Sopenharmony_ci					&vco_param);
5258c2ecf20Sopenharmony_ci		if (ret)
5268c2ecf20Sopenharmony_ci			return ret;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		req->rate = vco_param.rate;
5298c2ecf20Sopenharmony_ci		return 0;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (!pll->vco_param)
5338c2ecf20Sopenharmony_ci		return -EINVAL;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	best_diff = ULONG_MAX;
5368c2ecf20Sopenharmony_ci	for (i = 0; i < pll->num_vco_entries; i++) {
5378c2ecf20Sopenharmony_ci		diff = abs(req->rate - pll->vco_param[i].rate);
5388c2ecf20Sopenharmony_ci		if (diff <= best_diff) {
5398c2ecf20Sopenharmony_ci			best_diff = diff;
5408c2ecf20Sopenharmony_ci			best_idx = i;
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci		/* break now if perfect match */
5438c2ecf20Sopenharmony_ci		if (diff == 0)
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	req->rate = pll->vco_param[best_idx].rate;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return 0;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate,
5538c2ecf20Sopenharmony_ci		unsigned long parent_rate)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
5568c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
5578c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
5588c2ecf20Sopenharmony_ci	struct iproc_pll_vco_param vco_param;
5598c2ecf20Sopenharmony_ci	int rate_index, ret;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) {
5628c2ecf20Sopenharmony_ci		ret = pll_calc_param(rate, parent_rate, &vco_param);
5638c2ecf20Sopenharmony_ci		if (ret)
5648c2ecf20Sopenharmony_ci			return ret;
5658c2ecf20Sopenharmony_ci	} else {
5668c2ecf20Sopenharmony_ci		rate_index = pll_get_rate_index(pll, rate);
5678c2ecf20Sopenharmony_ci		if (rate_index < 0)
5688c2ecf20Sopenharmony_ci			return rate_index;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		vco_param = pll->vco_param[rate_index];
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	ret = pll_set_rate(clk, &vco_param, parent_rate);
5748c2ecf20Sopenharmony_ci	return ret;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic const struct clk_ops iproc_pll_ops = {
5788c2ecf20Sopenharmony_ci	.enable = iproc_pll_enable,
5798c2ecf20Sopenharmony_ci	.disable = iproc_pll_disable,
5808c2ecf20Sopenharmony_ci	.recalc_rate = iproc_pll_recalc_rate,
5818c2ecf20Sopenharmony_ci	.determine_rate = iproc_pll_determine_rate,
5828c2ecf20Sopenharmony_ci	.set_rate = iproc_pll_set_rate,
5838c2ecf20Sopenharmony_ci};
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int iproc_clk_enable(struct clk_hw *hw)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
5888c2ecf20Sopenharmony_ci	const struct iproc_clk_ctrl *ctrl = clk->ctrl;
5898c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
5908c2ecf20Sopenharmony_ci	u32 val;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* channel enable is active low */
5938c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->enable.offset);
5948c2ecf20Sopenharmony_ci	val &= ~(1 << ctrl->enable.enable_shift);
5958c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* also make sure channel is not held */
5988c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->enable.offset);
5998c2ecf20Sopenharmony_ci	val &= ~(1 << ctrl->enable.hold_shift);
6008c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return 0;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic void iproc_clk_disable(struct clk_hw *hw)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
6088c2ecf20Sopenharmony_ci	const struct iproc_clk_ctrl *ctrl = clk->ctrl;
6098c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
6108c2ecf20Sopenharmony_ci	u32 val;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_AON)
6138c2ecf20Sopenharmony_ci		return;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->enable.offset);
6168c2ecf20Sopenharmony_ci	val |= 1 << ctrl->enable.enable_shift;
6178c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic unsigned long iproc_clk_recalc_rate(struct clk_hw *hw,
6218c2ecf20Sopenharmony_ci		unsigned long parent_rate)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
6248c2ecf20Sopenharmony_ci	const struct iproc_clk_ctrl *ctrl = clk->ctrl;
6258c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
6268c2ecf20Sopenharmony_ci	u32 val;
6278c2ecf20Sopenharmony_ci	unsigned int mdiv;
6288c2ecf20Sopenharmony_ci	unsigned long rate;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (parent_rate == 0)
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->mdiv.offset);
6348c2ecf20Sopenharmony_ci	mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width);
6358c2ecf20Sopenharmony_ci	if (mdiv == 0)
6368c2ecf20Sopenharmony_ci		mdiv = 256;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2)
6398c2ecf20Sopenharmony_ci		rate = parent_rate / (mdiv * 2);
6408c2ecf20Sopenharmony_ci	else
6418c2ecf20Sopenharmony_ci		rate = parent_rate / mdiv;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return rate;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int iproc_clk_determine_rate(struct clk_hw *hw,
6478c2ecf20Sopenharmony_ci		struct clk_rate_request *req)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	unsigned int bestdiv;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (req->rate == 0)
6528c2ecf20Sopenharmony_ci		return -EINVAL;
6538c2ecf20Sopenharmony_ci	if (req->rate == req->best_parent_rate)
6548c2ecf20Sopenharmony_ci		return 0;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	bestdiv = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
6578c2ecf20Sopenharmony_ci	if (bestdiv < 2)
6588c2ecf20Sopenharmony_ci		req->rate = req->best_parent_rate;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (bestdiv > 256)
6618c2ecf20Sopenharmony_ci		bestdiv = 256;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	req->rate = req->best_parent_rate / bestdiv;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return 0;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
6698c2ecf20Sopenharmony_ci		unsigned long parent_rate)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct iproc_clk *clk = to_iproc_clk(hw);
6728c2ecf20Sopenharmony_ci	const struct iproc_clk_ctrl *ctrl = clk->ctrl;
6738c2ecf20Sopenharmony_ci	struct iproc_pll *pll = clk->pll;
6748c2ecf20Sopenharmony_ci	u32 val;
6758c2ecf20Sopenharmony_ci	unsigned int div;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (rate == 0 || parent_rate == 0)
6788c2ecf20Sopenharmony_ci		return -EINVAL;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	div = DIV_ROUND_CLOSEST(parent_rate, rate);
6818c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2)
6828c2ecf20Sopenharmony_ci		div /=  2;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (div > 256)
6858c2ecf20Sopenharmony_ci		return -EINVAL;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	val = readl(pll->control_base + ctrl->mdiv.offset);
6888c2ecf20Sopenharmony_ci	if (div == 256) {
6898c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
6908c2ecf20Sopenharmony_ci	} else {
6918c2ecf20Sopenharmony_ci		val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
6928c2ecf20Sopenharmony_ci		val |= div << ctrl->mdiv.shift;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci	iproc_pll_write(pll, pll->control_base, ctrl->mdiv.offset, val);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return 0;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic const struct clk_ops iproc_clk_ops = {
7008c2ecf20Sopenharmony_ci	.enable = iproc_clk_enable,
7018c2ecf20Sopenharmony_ci	.disable = iproc_clk_disable,
7028c2ecf20Sopenharmony_ci	.recalc_rate = iproc_clk_recalc_rate,
7038c2ecf20Sopenharmony_ci	.determine_rate = iproc_clk_determine_rate,
7048c2ecf20Sopenharmony_ci	.set_rate = iproc_clk_set_rate,
7058c2ecf20Sopenharmony_ci};
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci/**
7088c2ecf20Sopenharmony_ci * Some PLLs require the PLL SW override bit to be set before changes can be
7098c2ecf20Sopenharmony_ci * applied to the PLL
7108c2ecf20Sopenharmony_ci */
7118c2ecf20Sopenharmony_cistatic void iproc_pll_sw_cfg(struct iproc_pll *pll)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	const struct iproc_pll_ctrl *ctrl = pll->ctrl;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) {
7168c2ecf20Sopenharmony_ci		u32 val;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		val = readl(pll->control_base + ctrl->sw_ctrl.offset);
7198c2ecf20Sopenharmony_ci		val |= BIT(ctrl->sw_ctrl.shift);
7208c2ecf20Sopenharmony_ci		iproc_pll_write(pll, pll->control_base, ctrl->sw_ctrl.offset,
7218c2ecf20Sopenharmony_ci				val);
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_civoid iproc_pll_clk_setup(struct device_node *node,
7268c2ecf20Sopenharmony_ci			 const struct iproc_pll_ctrl *pll_ctrl,
7278c2ecf20Sopenharmony_ci			 const struct iproc_pll_vco_param *vco,
7288c2ecf20Sopenharmony_ci			 unsigned int num_vco_entries,
7298c2ecf20Sopenharmony_ci			 const struct iproc_clk_ctrl *clk_ctrl,
7308c2ecf20Sopenharmony_ci			 unsigned int num_clks)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	int i, ret;
7338c2ecf20Sopenharmony_ci	struct iproc_pll *pll;
7348c2ecf20Sopenharmony_ci	struct iproc_clk *iclk;
7358c2ecf20Sopenharmony_ci	struct clk_init_data init;
7368c2ecf20Sopenharmony_ci	const char *parent_name;
7378c2ecf20Sopenharmony_ci	struct iproc_clk *iclk_array;
7388c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data *clk_data;
7398c2ecf20Sopenharmony_ci	const char *clk_name;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl))
7428c2ecf20Sopenharmony_ci		return;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
7458c2ecf20Sopenharmony_ci	if (WARN_ON(!pll))
7468c2ecf20Sopenharmony_ci		return;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	clk_data = kzalloc(struct_size(clk_data, hws, num_clks), GFP_KERNEL);
7498c2ecf20Sopenharmony_ci	if (WARN_ON(!clk_data))
7508c2ecf20Sopenharmony_ci		goto err_clk_data;
7518c2ecf20Sopenharmony_ci	clk_data->num = num_clks;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	iclk_array = kcalloc(num_clks, sizeof(struct iproc_clk), GFP_KERNEL);
7548c2ecf20Sopenharmony_ci	if (WARN_ON(!iclk_array))
7558c2ecf20Sopenharmony_ci		goto err_clks;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	pll->control_base = of_iomap(node, 0);
7588c2ecf20Sopenharmony_ci	if (WARN_ON(!pll->control_base))
7598c2ecf20Sopenharmony_ci		goto err_pll_iomap;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* Some SoCs do not require the pwr_base, thus failing is not fatal */
7628c2ecf20Sopenharmony_ci	pll->pwr_base = of_iomap(node, 1);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* some PLLs require gating control at the top ASIU level */
7658c2ecf20Sopenharmony_ci	if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) {
7668c2ecf20Sopenharmony_ci		pll->asiu_base = of_iomap(node, 2);
7678c2ecf20Sopenharmony_ci		if (WARN_ON(!pll->asiu_base))
7688c2ecf20Sopenharmony_ci			goto err_asiu_iomap;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (pll_ctrl->flags & IPROC_CLK_PLL_SPLIT_STAT_CTRL) {
7728c2ecf20Sopenharmony_ci		/* Some SoCs have a split status/control.  If this does not
7738c2ecf20Sopenharmony_ci		 * exist, assume they are unified.
7748c2ecf20Sopenharmony_ci		 */
7758c2ecf20Sopenharmony_ci		pll->status_base = of_iomap(node, 2);
7768c2ecf20Sopenharmony_ci		if (!pll->status_base)
7778c2ecf20Sopenharmony_ci			goto err_status_iomap;
7788c2ecf20Sopenharmony_ci	} else
7798c2ecf20Sopenharmony_ci		pll->status_base = pll->control_base;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* initialize and register the PLL itself */
7828c2ecf20Sopenharmony_ci	pll->ctrl = pll_ctrl;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	iclk = &iclk_array[0];
7858c2ecf20Sopenharmony_ci	iclk->pll = pll;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	ret = of_property_read_string_index(node, "clock-output-names",
7888c2ecf20Sopenharmony_ci					    0, &clk_name);
7898c2ecf20Sopenharmony_ci	if (WARN_ON(ret))
7908c2ecf20Sopenharmony_ci		goto err_pll_register;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	init.name = clk_name;
7938c2ecf20Sopenharmony_ci	init.ops = &iproc_pll_ops;
7948c2ecf20Sopenharmony_ci	init.flags = 0;
7958c2ecf20Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
7968c2ecf20Sopenharmony_ci	init.parent_names = (parent_name ? &parent_name : NULL);
7978c2ecf20Sopenharmony_ci	init.num_parents = (parent_name ? 1 : 0);
7988c2ecf20Sopenharmony_ci	iclk->hw.init = &init;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (vco) {
8018c2ecf20Sopenharmony_ci		pll->num_vco_entries = num_vco_entries;
8028c2ecf20Sopenharmony_ci		pll->vco_param = vco;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	iproc_pll_sw_cfg(pll);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	ret = clk_hw_register(NULL, &iclk->hw);
8088c2ecf20Sopenharmony_ci	if (WARN_ON(ret))
8098c2ecf20Sopenharmony_ci		goto err_pll_register;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	clk_data->hws[0] = &iclk->hw;
8128c2ecf20Sopenharmony_ci	parent_name = clk_name;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* now initialize and register all leaf clocks */
8158c2ecf20Sopenharmony_ci	for (i = 1; i < num_clks; i++) {
8168c2ecf20Sopenharmony_ci		memset(&init, 0, sizeof(init));
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		ret = of_property_read_string_index(node, "clock-output-names",
8198c2ecf20Sopenharmony_ci						    i, &clk_name);
8208c2ecf20Sopenharmony_ci		if (WARN_ON(ret))
8218c2ecf20Sopenharmony_ci			goto err_clk_register;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci		iclk = &iclk_array[i];
8248c2ecf20Sopenharmony_ci		iclk->pll = pll;
8258c2ecf20Sopenharmony_ci		iclk->ctrl = &clk_ctrl[i];
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		init.name = clk_name;
8288c2ecf20Sopenharmony_ci		init.ops = &iproc_clk_ops;
8298c2ecf20Sopenharmony_ci		init.flags = 0;
8308c2ecf20Sopenharmony_ci		init.parent_names = (parent_name ? &parent_name : NULL);
8318c2ecf20Sopenharmony_ci		init.num_parents = (parent_name ? 1 : 0);
8328c2ecf20Sopenharmony_ci		iclk->hw.init = &init;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci		ret = clk_hw_register(NULL, &iclk->hw);
8358c2ecf20Sopenharmony_ci		if (WARN_ON(ret))
8368c2ecf20Sopenharmony_ci			goto err_clk_register;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		clk_data->hws[i] = &iclk->hw;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
8428c2ecf20Sopenharmony_ci	if (WARN_ON(ret))
8438c2ecf20Sopenharmony_ci		goto err_clk_register;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	return;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cierr_clk_register:
8488c2ecf20Sopenharmony_ci	while (--i >= 0)
8498c2ecf20Sopenharmony_ci		clk_hw_unregister(clk_data->hws[i]);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cierr_pll_register:
8528c2ecf20Sopenharmony_ci	if (pll->status_base != pll->control_base)
8538c2ecf20Sopenharmony_ci		iounmap(pll->status_base);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cierr_status_iomap:
8568c2ecf20Sopenharmony_ci	if (pll->asiu_base)
8578c2ecf20Sopenharmony_ci		iounmap(pll->asiu_base);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cierr_asiu_iomap:
8608c2ecf20Sopenharmony_ci	if (pll->pwr_base)
8618c2ecf20Sopenharmony_ci		iounmap(pll->pwr_base);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	iounmap(pll->control_base);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cierr_pll_iomap:
8668c2ecf20Sopenharmony_ci	kfree(iclk_array);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cierr_clks:
8698c2ecf20Sopenharmony_ci	kfree(clk_data);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cierr_clk_data:
8728c2ecf20Sopenharmony_ci	kfree(pll);
8738c2ecf20Sopenharmony_ci}
874