152e6676eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
252e6676eSThomas Gleixner // Copyright (C) 2014 Broadcom Corporation
35fe225c1SRay Jui
45fe225c1SRay Jui #include <linux/kernel.h>
55fe225c1SRay Jui #include <linux/err.h>
65fe225c1SRay Jui #include <linux/clk-provider.h>
75fe225c1SRay Jui #include <linux/io.h>
85fe225c1SRay Jui #include <linux/of.h>
95fe225c1SRay Jui #include <linux/clkdev.h>
105fe225c1SRay Jui #include <linux/of_address.h>
115fe225c1SRay Jui #include <linux/delay.h>
125fe225c1SRay Jui
135fe225c1SRay Jui #include "clk-iproc.h"
145fe225c1SRay Jui
155fe225c1SRay Jui #define PLL_VCO_HIGH_SHIFT 19
165fe225c1SRay Jui #define PLL_VCO_LOW_SHIFT 30
175fe225c1SRay Jui
18bcd8be13SSimran Rai /*
19bcd8be13SSimran Rai * PLL MACRO_SELECT modes 0 to 5 choose pre-calculated PLL output frequencies
20bcd8be13SSimran Rai * from a look-up table. Mode 7 allows user to manipulate PLL clock dividers
21bcd8be13SSimran Rai */
22bcd8be13SSimran Rai #define PLL_USER_MODE 7
23bcd8be13SSimran Rai
245fe225c1SRay Jui /* number of delay loops waiting for PLL to lock */
255fe225c1SRay Jui #define LOCK_DELAY 100
265fe225c1SRay Jui
275fe225c1SRay Jui /* number of VCO frequency bands */
285fe225c1SRay Jui #define NUM_FREQ_BANDS 8
295fe225c1SRay Jui
305fe225c1SRay Jui #define NUM_KP_BANDS 3
315fe225c1SRay Jui enum kp_band {
325fe225c1SRay Jui KP_BAND_MID = 0,
335fe225c1SRay Jui KP_BAND_HIGH,
345fe225c1SRay Jui KP_BAND_HIGH_HIGH
355fe225c1SRay Jui };
365fe225c1SRay Jui
375fe225c1SRay Jui static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = {
385fe225c1SRay Jui { 5, 6, 6, 7, 7, 8, 9, 10 },
395fe225c1SRay Jui { 4, 4, 5, 5, 6, 7, 8, 9 },
405fe225c1SRay Jui { 4, 5, 5, 6, 7, 8, 9, 10 },
415fe225c1SRay Jui };
425fe225c1SRay Jui
435fe225c1SRay Jui static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = {
445fe225c1SRay Jui { 10000000, 12500000 },
455fe225c1SRay Jui { 12500000, 15000000 },
465fe225c1SRay Jui { 15000000, 20000000 },
475fe225c1SRay Jui { 20000000, 25000000 },
485fe225c1SRay Jui { 25000000, 50000000 },
495fe225c1SRay Jui { 50000000, 75000000 },
505fe225c1SRay Jui { 75000000, 100000000 },
515fe225c1SRay Jui { 100000000, 125000000 },
525fe225c1SRay Jui };
535fe225c1SRay Jui
545fe225c1SRay Jui enum vco_freq_range {
555fe225c1SRay Jui VCO_LOW = 700000000U,
565fe225c1SRay Jui VCO_MID = 1200000000U,
575fe225c1SRay Jui VCO_HIGH = 2200000000U,
585fe225c1SRay Jui VCO_HIGH_HIGH = 3100000000U,
595fe225c1SRay Jui VCO_MAX = 4000000000U,
605fe225c1SRay Jui };
615fe225c1SRay Jui
625fe225c1SRay Jui struct iproc_pll {
6340c8bec3SJon Mason void __iomem *status_base;
6440c8bec3SJon Mason void __iomem *control_base;
655fe225c1SRay Jui void __iomem *pwr_base;
665fe225c1SRay Jui void __iomem *asiu_base;
675fe225c1SRay Jui
685fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl;
695fe225c1SRay Jui const struct iproc_pll_vco_param *vco_param;
705fe225c1SRay Jui unsigned int num_vco_entries;
71b33db497SLori Hikichi };
725fe225c1SRay Jui
73b33db497SLori Hikichi struct iproc_clk {
74b33db497SLori Hikichi struct clk_hw hw;
75b33db497SLori Hikichi struct iproc_pll *pll;
76b33db497SLori Hikichi const struct iproc_clk_ctrl *ctrl;
775fe225c1SRay Jui };
785fe225c1SRay Jui
795fe225c1SRay Jui #define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw)
805fe225c1SRay Jui
pll_calc_param(unsigned long target_rate,unsigned long parent_rate,struct iproc_pll_vco_param * vco_out)81becf1237SLori Hikichi static int pll_calc_param(unsigned long target_rate,
82becf1237SLori Hikichi unsigned long parent_rate,
83becf1237SLori Hikichi struct iproc_pll_vco_param *vco_out)
84becf1237SLori Hikichi {
85becf1237SLori Hikichi u64 ndiv_int, ndiv_frac, residual;
86becf1237SLori Hikichi
87becf1237SLori Hikichi ndiv_int = target_rate / parent_rate;
88becf1237SLori Hikichi
89becf1237SLori Hikichi if (!ndiv_int || (ndiv_int > 255))
90becf1237SLori Hikichi return -EINVAL;
91becf1237SLori Hikichi
92becf1237SLori Hikichi residual = target_rate - (ndiv_int * parent_rate);
93becf1237SLori Hikichi residual <<= 20;
94becf1237SLori Hikichi
95becf1237SLori Hikichi /*
96becf1237SLori Hikichi * Add half of the divisor so the result will be rounded to closest
97becf1237SLori Hikichi * instead of rounded down.
98becf1237SLori Hikichi */
99becf1237SLori Hikichi residual += (parent_rate / 2);
100becf1237SLori Hikichi ndiv_frac = div64_u64((u64)residual, (u64)parent_rate);
101becf1237SLori Hikichi
102becf1237SLori Hikichi vco_out->ndiv_int = ndiv_int;
103becf1237SLori Hikichi vco_out->ndiv_frac = ndiv_frac;
104becf1237SLori Hikichi vco_out->pdiv = 1;
105becf1237SLori Hikichi
106becf1237SLori Hikichi vco_out->rate = vco_out->ndiv_int * parent_rate;
107becf1237SLori Hikichi residual = (u64)vco_out->ndiv_frac * (u64)parent_rate;
108becf1237SLori Hikichi residual >>= 20;
109becf1237SLori Hikichi vco_out->rate += residual;
110becf1237SLori Hikichi
111becf1237SLori Hikichi return 0;
112becf1237SLori Hikichi }
113becf1237SLori Hikichi
1145fe225c1SRay Jui /*
1155fe225c1SRay Jui * Based on the target frequency, find a match from the VCO frequency parameter
1165fe225c1SRay Jui * table and return its index
1175fe225c1SRay Jui */
pll_get_rate_index(struct iproc_pll * pll,unsigned int target_rate)1185fe225c1SRay Jui static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate)
1195fe225c1SRay Jui {
1205fe225c1SRay Jui int i;
1215fe225c1SRay Jui
1225fe225c1SRay Jui for (i = 0; i < pll->num_vco_entries; i++)
1235fe225c1SRay Jui if (target_rate == pll->vco_param[i].rate)
1245fe225c1SRay Jui break;
1255fe225c1SRay Jui
1265fe225c1SRay Jui if (i >= pll->num_vco_entries)
1275fe225c1SRay Jui return -EINVAL;
1285fe225c1SRay Jui
1295fe225c1SRay Jui return i;
1305fe225c1SRay Jui }
1315fe225c1SRay Jui
get_kp(unsigned long ref_freq,enum kp_band kp_index)1325fe225c1SRay Jui static int get_kp(unsigned long ref_freq, enum kp_band kp_index)
1335fe225c1SRay Jui {
1345fe225c1SRay Jui int i;
1355fe225c1SRay Jui
1365fe225c1SRay Jui if (ref_freq < ref_freq_table[0][0])
1375fe225c1SRay Jui return -EINVAL;
1385fe225c1SRay Jui
1395fe225c1SRay Jui for (i = 0; i < NUM_FREQ_BANDS; i++) {
1405fe225c1SRay Jui if (ref_freq >= ref_freq_table[i][0] &&
1415fe225c1SRay Jui ref_freq < ref_freq_table[i][1])
1425fe225c1SRay Jui return kp_table[kp_index][i];
1435fe225c1SRay Jui }
1445fe225c1SRay Jui return -EINVAL;
1455fe225c1SRay Jui }
1465fe225c1SRay Jui
pll_wait_for_lock(struct iproc_pll * pll)1475fe225c1SRay Jui static int pll_wait_for_lock(struct iproc_pll *pll)
1485fe225c1SRay Jui {
1495fe225c1SRay Jui int i;
1505fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1515fe225c1SRay Jui
1525fe225c1SRay Jui for (i = 0; i < LOCK_DELAY; i++) {
15340c8bec3SJon Mason u32 val = readl(pll->status_base + ctrl->status.offset);
1545fe225c1SRay Jui
1555fe225c1SRay Jui if (val & (1 << ctrl->status.shift))
1565fe225c1SRay Jui return 0;
1575fe225c1SRay Jui udelay(10);
1585fe225c1SRay Jui }
1595fe225c1SRay Jui
1605fe225c1SRay Jui return -EIO;
1615fe225c1SRay Jui }
1625fe225c1SRay Jui
iproc_pll_write(const struct iproc_pll * pll,void __iomem * base,const u32 offset,u32 val)1637968d241SJon Mason static void iproc_pll_write(const struct iproc_pll *pll, void __iomem *base,
1647968d241SJon Mason const u32 offset, u32 val)
1657968d241SJon Mason {
1667968d241SJon Mason const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1677968d241SJon Mason
1687968d241SJon Mason writel(val, base + offset);
1697968d241SJon Mason
1707968d241SJon Mason if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK &&
17140c8bec3SJon Mason (base == pll->status_base || base == pll->control_base)))
1727968d241SJon Mason val = readl(base + offset);
1737968d241SJon Mason }
1747968d241SJon Mason
__pll_disable(struct iproc_pll * pll)1755fe225c1SRay Jui static void __pll_disable(struct iproc_pll *pll)
1765fe225c1SRay Jui {
1775fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
1785fe225c1SRay Jui u32 val;
1795fe225c1SRay Jui
1805fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
1815fe225c1SRay Jui val = readl(pll->asiu_base + ctrl->asiu.offset);
1825fe225c1SRay Jui val &= ~(1 << ctrl->asiu.en_shift);
1837968d241SJon Mason iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
1845fe225c1SRay Jui }
1855fe225c1SRay Jui
18601b6722fSJon Mason if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
18740c8bec3SJon Mason val = readl(pll->control_base + ctrl->aon.offset);
18801b6722fSJon Mason val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
18940c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
19001b6722fSJon Mason }
19101b6722fSJon Mason
19201b6722fSJon Mason if (pll->pwr_base) {
1935fe225c1SRay Jui /* latch input value so core power can be shut down */
1945fe225c1SRay Jui val = readl(pll->pwr_base + ctrl->aon.offset);
19501b6722fSJon Mason val |= 1 << ctrl->aon.iso_shift;
1967968d241SJon Mason iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
1975fe225c1SRay Jui
1985fe225c1SRay Jui /* power down the core */
1995fe225c1SRay Jui val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
2007968d241SJon Mason iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
2015fe225c1SRay Jui }
2025fe225c1SRay Jui }
2035fe225c1SRay Jui
__pll_enable(struct iproc_pll * pll)2045fe225c1SRay Jui static int __pll_enable(struct iproc_pll *pll)
2055fe225c1SRay Jui {
2065fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2075fe225c1SRay Jui u32 val;
2085fe225c1SRay Jui
20901b6722fSJon Mason if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) {
21040c8bec3SJon Mason val = readl(pll->control_base + ctrl->aon.offset);
21101b6722fSJon Mason val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
21240c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val);
21301b6722fSJon Mason }
21401b6722fSJon Mason
21501b6722fSJon Mason if (pll->pwr_base) {
2165fe225c1SRay Jui /* power up the PLL and make sure it's not latched */
2175fe225c1SRay Jui val = readl(pll->pwr_base + ctrl->aon.offset);
2185fe225c1SRay Jui val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
2195fe225c1SRay Jui val &= ~(1 << ctrl->aon.iso_shift);
2207968d241SJon Mason iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val);
22101b6722fSJon Mason }
2225fe225c1SRay Jui
2235fe225c1SRay Jui /* certain PLLs also need to be ungated from the ASIU top level */
2245fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
2255fe225c1SRay Jui val = readl(pll->asiu_base + ctrl->asiu.offset);
2265fe225c1SRay Jui val |= (1 << ctrl->asiu.en_shift);
2277968d241SJon Mason iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val);
2285fe225c1SRay Jui }
2295fe225c1SRay Jui
2305fe225c1SRay Jui return 0;
2315fe225c1SRay Jui }
2325fe225c1SRay Jui
__pll_put_in_reset(struct iproc_pll * pll)2335fe225c1SRay Jui static void __pll_put_in_reset(struct iproc_pll *pll)
2345fe225c1SRay Jui {
2355fe225c1SRay Jui u32 val;
2365fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2375fe225c1SRay Jui const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
2385fe225c1SRay Jui
23940c8bec3SJon Mason val = readl(pll->control_base + reset->offset);
240bcd8be13SSimran Rai if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW)
241bcd8be13SSimran Rai val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift);
242bcd8be13SSimran Rai else
243bcd8be13SSimran Rai val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift));
24440c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, reset->offset, val);
2455fe225c1SRay Jui }
2465fe225c1SRay Jui
__pll_bring_out_reset(struct iproc_pll * pll,unsigned int kp,unsigned int ka,unsigned int ki)2475fe225c1SRay Jui static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp,
2485fe225c1SRay Jui unsigned int ka, unsigned int ki)
2495fe225c1SRay Jui {
2505fe225c1SRay Jui u32 val;
2515fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
2525fe225c1SRay Jui const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
253f713c6bfSJon Mason const struct iproc_pll_dig_filter_ctrl *dig_filter = &ctrl->dig_filter;
2545fe225c1SRay Jui
25540c8bec3SJon Mason val = readl(pll->control_base + dig_filter->offset);
256f713c6bfSJon Mason val &= ~(bit_mask(dig_filter->ki_width) << dig_filter->ki_shift |
257f713c6bfSJon Mason bit_mask(dig_filter->kp_width) << dig_filter->kp_shift |
258f713c6bfSJon Mason bit_mask(dig_filter->ka_width) << dig_filter->ka_shift);
259f713c6bfSJon Mason val |= ki << dig_filter->ki_shift | kp << dig_filter->kp_shift |
260f713c6bfSJon Mason ka << dig_filter->ka_shift;
26140c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, dig_filter->offset, val);
2625fe225c1SRay Jui
26340c8bec3SJon Mason val = readl(pll->control_base + reset->offset);
264bcd8be13SSimran Rai if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW)
265bcd8be13SSimran Rai val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift));
266bcd8be13SSimran Rai else
267bcd8be13SSimran Rai val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift);
26840c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, reset->offset, val);
2695fe225c1SRay Jui }
2705fe225c1SRay Jui
271f3f739c9SLori Hikichi /*
272f3f739c9SLori Hikichi * Determines if the change to be applied to the PLL is minor (just an update
273f3f739c9SLori Hikichi * or the fractional divider). If so, then we can avoid going through a
274f3f739c9SLori Hikichi * disruptive reset and lock sequence.
275f3f739c9SLori Hikichi */
pll_fractional_change_only(struct iproc_pll * pll,struct iproc_pll_vco_param * vco)276f3f739c9SLori Hikichi static bool pll_fractional_change_only(struct iproc_pll *pll,
277f3f739c9SLori Hikichi struct iproc_pll_vco_param *vco)
278f3f739c9SLori Hikichi {
279f3f739c9SLori Hikichi const struct iproc_pll_ctrl *ctrl = pll->ctrl;
280f3f739c9SLori Hikichi u32 val;
281f3f739c9SLori Hikichi u32 ndiv_int;
282f3f739c9SLori Hikichi unsigned int pdiv;
283f3f739c9SLori Hikichi
284f3f739c9SLori Hikichi /* PLL needs to be locked */
285f3f739c9SLori Hikichi val = readl(pll->status_base + ctrl->status.offset);
286f3f739c9SLori Hikichi if ((val & (1 << ctrl->status.shift)) == 0)
287f3f739c9SLori Hikichi return false;
288f3f739c9SLori Hikichi
289f3f739c9SLori Hikichi val = readl(pll->control_base + ctrl->ndiv_int.offset);
290f3f739c9SLori Hikichi ndiv_int = (val >> ctrl->ndiv_int.shift) &
291f3f739c9SLori Hikichi bit_mask(ctrl->ndiv_int.width);
292f3f739c9SLori Hikichi
293f3f739c9SLori Hikichi if (ndiv_int != vco->ndiv_int)
294f3f739c9SLori Hikichi return false;
295f3f739c9SLori Hikichi
296f3f739c9SLori Hikichi val = readl(pll->control_base + ctrl->pdiv.offset);
297f3f739c9SLori Hikichi pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
298f3f739c9SLori Hikichi
299f3f739c9SLori Hikichi if (pdiv != vco->pdiv)
300f3f739c9SLori Hikichi return false;
301f3f739c9SLori Hikichi
302f3f739c9SLori Hikichi return true;
303f3f739c9SLori Hikichi }
304f3f739c9SLori Hikichi
pll_set_rate(struct iproc_clk * clk,struct iproc_pll_vco_param * vco,unsigned long parent_rate)305becf1237SLori Hikichi static int pll_set_rate(struct iproc_clk *clk, struct iproc_pll_vco_param *vco,
3065fe225c1SRay Jui unsigned long parent_rate)
3075fe225c1SRay Jui {
3085fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
3095fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
3105fe225c1SRay Jui int ka = 0, ki, kp, ret;
3115fe225c1SRay Jui unsigned long rate = vco->rate;
3125fe225c1SRay Jui u32 val;
3135fe225c1SRay Jui enum kp_band kp_index;
3145fe225c1SRay Jui unsigned long ref_freq;
315b33db497SLori Hikichi const char *clk_name = clk_hw_get_name(&clk->hw);
3165fe225c1SRay Jui
3175fe225c1SRay Jui /*
3185fe225c1SRay Jui * reference frequency = parent frequency / PDIV
3195fe225c1SRay Jui * If PDIV = 0, then it becomes a multiplier (x2)
3205fe225c1SRay Jui */
3215fe225c1SRay Jui if (vco->pdiv == 0)
3225fe225c1SRay Jui ref_freq = parent_rate * 2;
3235fe225c1SRay Jui else
3245fe225c1SRay Jui ref_freq = parent_rate / vco->pdiv;
3255fe225c1SRay Jui
3265fe225c1SRay Jui /* determine Ki and Kp index based on target VCO frequency */
3275fe225c1SRay Jui if (rate >= VCO_LOW && rate < VCO_HIGH) {
3285fe225c1SRay Jui ki = 4;
3295fe225c1SRay Jui kp_index = KP_BAND_MID;
330d5a0945fSRay Jui } else if (rate >= VCO_HIGH && rate < VCO_HIGH_HIGH) {
3315fe225c1SRay Jui ki = 3;
3325fe225c1SRay Jui kp_index = KP_BAND_HIGH;
3335fe225c1SRay Jui } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
3345fe225c1SRay Jui ki = 3;
3355fe225c1SRay Jui kp_index = KP_BAND_HIGH_HIGH;
3365fe225c1SRay Jui } else {
3375fe225c1SRay Jui pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
338b33db497SLori Hikichi clk_name, rate);
3395fe225c1SRay Jui return -EINVAL;
3405fe225c1SRay Jui }
3415fe225c1SRay Jui
3425fe225c1SRay Jui kp = get_kp(ref_freq, kp_index);
3435fe225c1SRay Jui if (kp < 0) {
344b33db497SLori Hikichi pr_err("%s: pll: %s has invalid kp\n", __func__, clk_name);
3455fe225c1SRay Jui return kp;
3465fe225c1SRay Jui }
3475fe225c1SRay Jui
3485fe225c1SRay Jui ret = __pll_enable(pll);
3495fe225c1SRay Jui if (ret) {
350b33db497SLori Hikichi pr_err("%s: pll: %s fails to enable\n", __func__, clk_name);
3515fe225c1SRay Jui return ret;
3525fe225c1SRay Jui }
3535fe225c1SRay Jui
354f3f739c9SLori Hikichi if (pll_fractional_change_only(clk->pll, vco)) {
355f3f739c9SLori Hikichi /* program fractional part of NDIV */
356f3f739c9SLori Hikichi if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
357f3f739c9SLori Hikichi val = readl(pll->control_base + ctrl->ndiv_frac.offset);
358f3f739c9SLori Hikichi val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
359f3f739c9SLori Hikichi ctrl->ndiv_frac.shift);
360f3f739c9SLori Hikichi val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
361f3f739c9SLori Hikichi iproc_pll_write(pll, pll->control_base,
362f3f739c9SLori Hikichi ctrl->ndiv_frac.offset, val);
363f3f739c9SLori Hikichi return 0;
364f3f739c9SLori Hikichi }
365f3f739c9SLori Hikichi }
366f3f739c9SLori Hikichi
3675fe225c1SRay Jui /* put PLL in reset */
3685fe225c1SRay Jui __pll_put_in_reset(pll);
3695fe225c1SRay Jui
370bcd8be13SSimran Rai /* set PLL in user mode before modifying PLL controls */
371bcd8be13SSimran Rai if (ctrl->flags & IPROC_CLK_PLL_USER_MODE_ON) {
372bcd8be13SSimran Rai val = readl(pll->control_base + ctrl->macro_mode.offset);
373bcd8be13SSimran Rai val &= ~(bit_mask(ctrl->macro_mode.width) <<
374bcd8be13SSimran Rai ctrl->macro_mode.shift);
375bcd8be13SSimran Rai val |= PLL_USER_MODE << ctrl->macro_mode.shift;
376bcd8be13SSimran Rai iproc_pll_write(pll, pll->control_base,
377bcd8be13SSimran Rai ctrl->macro_mode.offset, val);
378bcd8be13SSimran Rai }
379bcd8be13SSimran Rai
38040c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.u_offset, 0);
3817968d241SJon Mason
38240c8bec3SJon Mason val = readl(pll->control_base + ctrl->vco_ctrl.l_offset);
3835fe225c1SRay Jui
3845fe225c1SRay Jui if (rate >= VCO_LOW && rate < VCO_MID)
3855fe225c1SRay Jui val |= (1 << PLL_VCO_LOW_SHIFT);
3865fe225c1SRay Jui
3875fe225c1SRay Jui if (rate < VCO_HIGH)
3885fe225c1SRay Jui val &= ~(1 << PLL_VCO_HIGH_SHIFT);
3895fe225c1SRay Jui else
3905fe225c1SRay Jui val |= (1 << PLL_VCO_HIGH_SHIFT);
3915fe225c1SRay Jui
39240c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.l_offset, val);
3935fe225c1SRay Jui
3945fe225c1SRay Jui /* program integer part of NDIV */
39540c8bec3SJon Mason val = readl(pll->control_base + ctrl->ndiv_int.offset);
3965fe225c1SRay Jui val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
3975fe225c1SRay Jui val |= vco->ndiv_int << ctrl->ndiv_int.shift;
39840c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->ndiv_int.offset, val);
3995fe225c1SRay Jui
4005fe225c1SRay Jui /* program fractional part of NDIV */
4015fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
40240c8bec3SJon Mason val = readl(pll->control_base + ctrl->ndiv_frac.offset);
4035fe225c1SRay Jui val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
4045fe225c1SRay Jui ctrl->ndiv_frac.shift);
4055fe225c1SRay Jui val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
40640c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->ndiv_frac.offset,
4077968d241SJon Mason val);
4085fe225c1SRay Jui }
4095fe225c1SRay Jui
4105fe225c1SRay Jui /* program PDIV */
41140c8bec3SJon Mason val = readl(pll->control_base + ctrl->pdiv.offset);
4125fe225c1SRay Jui val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
4135fe225c1SRay Jui val |= vco->pdiv << ctrl->pdiv.shift;
41440c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->pdiv.offset, val);
4155fe225c1SRay Jui
4165fe225c1SRay Jui __pll_bring_out_reset(pll, kp, ka, ki);
4175fe225c1SRay Jui
4185fe225c1SRay Jui ret = pll_wait_for_lock(pll);
4195fe225c1SRay Jui if (ret < 0) {
420b33db497SLori Hikichi pr_err("%s: pll: %s failed to lock\n", __func__, clk_name);
4215fe225c1SRay Jui return ret;
4225fe225c1SRay Jui }
4235fe225c1SRay Jui
4245fe225c1SRay Jui return 0;
4255fe225c1SRay Jui }
4265fe225c1SRay Jui
iproc_pll_enable(struct clk_hw * hw)4275fe225c1SRay Jui static int iproc_pll_enable(struct clk_hw *hw)
4285fe225c1SRay Jui {
4295fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
4305fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
4315fe225c1SRay Jui
4325fe225c1SRay Jui return __pll_enable(pll);
4335fe225c1SRay Jui }
4345fe225c1SRay Jui
iproc_pll_disable(struct clk_hw * hw)4355fe225c1SRay Jui static void iproc_pll_disable(struct clk_hw *hw)
4365fe225c1SRay Jui {
4375fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
4385fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
4395fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
4405fe225c1SRay Jui
4415fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_AON)
4425fe225c1SRay Jui return;
4435fe225c1SRay Jui
4445fe225c1SRay Jui __pll_disable(pll);
4455fe225c1SRay Jui }
4465fe225c1SRay Jui
iproc_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)4475fe225c1SRay Jui static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw,
4485fe225c1SRay Jui unsigned long parent_rate)
4495fe225c1SRay Jui {
4505fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
4515fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
4525fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
4535fe225c1SRay Jui u32 val;
45463243a4dSSimran Rai u64 ndiv, ndiv_int, ndiv_frac;
45563243a4dSSimran Rai unsigned int pdiv;
456b33db497SLori Hikichi unsigned long rate;
4575fe225c1SRay Jui
4585fe225c1SRay Jui if (parent_rate == 0)
4595fe225c1SRay Jui return 0;
4605fe225c1SRay Jui
4615fe225c1SRay Jui /* PLL needs to be locked */
46240c8bec3SJon Mason val = readl(pll->status_base + ctrl->status.offset);
463b33db497SLori Hikichi if ((val & (1 << ctrl->status.shift)) == 0)
4645fe225c1SRay Jui return 0;
4655fe225c1SRay Jui
4665fe225c1SRay Jui /*
4675fe225c1SRay Jui * PLL output frequency =
4685fe225c1SRay Jui *
4695fe225c1SRay Jui * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv)
4705fe225c1SRay Jui */
47140c8bec3SJon Mason val = readl(pll->control_base + ctrl->ndiv_int.offset);
4725fe225c1SRay Jui ndiv_int = (val >> ctrl->ndiv_int.shift) &
4735fe225c1SRay Jui bit_mask(ctrl->ndiv_int.width);
47463243a4dSSimran Rai ndiv = ndiv_int << 20;
4755fe225c1SRay Jui
4765fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
47740c8bec3SJon Mason val = readl(pll->control_base + ctrl->ndiv_frac.offset);
4785fe225c1SRay Jui ndiv_frac = (val >> ctrl->ndiv_frac.shift) &
4795fe225c1SRay Jui bit_mask(ctrl->ndiv_frac.width);
48063243a4dSSimran Rai ndiv += ndiv_frac;
4815fe225c1SRay Jui }
4825fe225c1SRay Jui
48340c8bec3SJon Mason val = readl(pll->control_base + ctrl->pdiv.offset);
4845fe225c1SRay Jui pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
4855fe225c1SRay Jui
486b33db497SLori Hikichi rate = (ndiv * parent_rate) >> 20;
4875fe225c1SRay Jui
4885fe225c1SRay Jui if (pdiv == 0)
489b33db497SLori Hikichi rate *= 2;
4905fe225c1SRay Jui else
491b33db497SLori Hikichi rate /= pdiv;
4925fe225c1SRay Jui
493b33db497SLori Hikichi return rate;
4945fe225c1SRay Jui }
4955fe225c1SRay Jui
iproc_pll_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)496becf1237SLori Hikichi static int iproc_pll_determine_rate(struct clk_hw *hw,
497becf1237SLori Hikichi struct clk_rate_request *req)
4985fe225c1SRay Jui {
499becf1237SLori Hikichi unsigned int i;
5005fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
5015fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
502becf1237SLori Hikichi const struct iproc_pll_ctrl *ctrl = pll->ctrl;
503becf1237SLori Hikichi unsigned long diff, best_diff;
504becf1237SLori Hikichi unsigned int best_idx = 0;
505becf1237SLori Hikichi int ret;
5065fe225c1SRay Jui
507becf1237SLori Hikichi if (req->rate == 0 || req->best_parent_rate == 0)
5085fe225c1SRay Jui return -EINVAL;
5095fe225c1SRay Jui
510becf1237SLori Hikichi if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) {
511becf1237SLori Hikichi struct iproc_pll_vco_param vco_param;
512becf1237SLori Hikichi
513becf1237SLori Hikichi ret = pll_calc_param(req->rate, req->best_parent_rate,
514becf1237SLori Hikichi &vco_param);
515becf1237SLori Hikichi if (ret)
516becf1237SLori Hikichi return ret;
517becf1237SLori Hikichi
518becf1237SLori Hikichi req->rate = vco_param.rate;
519becf1237SLori Hikichi return 0;
520becf1237SLori Hikichi }
521becf1237SLori Hikichi
522becf1237SLori Hikichi if (!pll->vco_param)
523becf1237SLori Hikichi return -EINVAL;
524becf1237SLori Hikichi
525becf1237SLori Hikichi best_diff = ULONG_MAX;
5265fe225c1SRay Jui for (i = 0; i < pll->num_vco_entries; i++) {
527becf1237SLori Hikichi diff = abs(req->rate - pll->vco_param[i].rate);
528becf1237SLori Hikichi if (diff <= best_diff) {
529becf1237SLori Hikichi best_diff = diff;
530becf1237SLori Hikichi best_idx = i;
531becf1237SLori Hikichi }
532becf1237SLori Hikichi /* break now if perfect match */
533becf1237SLori Hikichi if (diff == 0)
5345fe225c1SRay Jui break;
5355fe225c1SRay Jui }
5365fe225c1SRay Jui
537becf1237SLori Hikichi req->rate = pll->vco_param[best_idx].rate;
5385fe225c1SRay Jui
539becf1237SLori Hikichi return 0;
5405fe225c1SRay Jui }
5415fe225c1SRay Jui
iproc_pll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)5425fe225c1SRay Jui static int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate,
5435fe225c1SRay Jui unsigned long parent_rate)
5445fe225c1SRay Jui {
5455fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
5465fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
547becf1237SLori Hikichi const struct iproc_pll_ctrl *ctrl = pll->ctrl;
548becf1237SLori Hikichi struct iproc_pll_vco_param vco_param;
5495fe225c1SRay Jui int rate_index, ret;
5505fe225c1SRay Jui
551becf1237SLori Hikichi if (ctrl->flags & IPROC_CLK_PLL_CALC_PARAM) {
552becf1237SLori Hikichi ret = pll_calc_param(rate, parent_rate, &vco_param);
553becf1237SLori Hikichi if (ret)
554becf1237SLori Hikichi return ret;
555becf1237SLori Hikichi } else {
5565fe225c1SRay Jui rate_index = pll_get_rate_index(pll, rate);
5575fe225c1SRay Jui if (rate_index < 0)
5585fe225c1SRay Jui return rate_index;
5595fe225c1SRay Jui
560becf1237SLori Hikichi vco_param = pll->vco_param[rate_index];
561becf1237SLori Hikichi }
562becf1237SLori Hikichi
563becf1237SLori Hikichi ret = pll_set_rate(clk, &vco_param, parent_rate);
5645fe225c1SRay Jui return ret;
5655fe225c1SRay Jui }
5665fe225c1SRay Jui
5675fe225c1SRay Jui static const struct clk_ops iproc_pll_ops = {
5685fe225c1SRay Jui .enable = iproc_pll_enable,
5695fe225c1SRay Jui .disable = iproc_pll_disable,
5705fe225c1SRay Jui .recalc_rate = iproc_pll_recalc_rate,
571becf1237SLori Hikichi .determine_rate = iproc_pll_determine_rate,
5725fe225c1SRay Jui .set_rate = iproc_pll_set_rate,
5735fe225c1SRay Jui };
5745fe225c1SRay Jui
iproc_clk_enable(struct clk_hw * hw)5755fe225c1SRay Jui static int iproc_clk_enable(struct clk_hw *hw)
5765fe225c1SRay Jui {
5775fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
5785fe225c1SRay Jui const struct iproc_clk_ctrl *ctrl = clk->ctrl;
5795fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
5805fe225c1SRay Jui u32 val;
5815fe225c1SRay Jui
5825fe225c1SRay Jui /* channel enable is active low */
58340c8bec3SJon Mason val = readl(pll->control_base + ctrl->enable.offset);
5845fe225c1SRay Jui val &= ~(1 << ctrl->enable.enable_shift);
58540c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
5865fe225c1SRay Jui
5875fe225c1SRay Jui /* also make sure channel is not held */
58840c8bec3SJon Mason val = readl(pll->control_base + ctrl->enable.offset);
5895fe225c1SRay Jui val &= ~(1 << ctrl->enable.hold_shift);
59040c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
5915fe225c1SRay Jui
5925fe225c1SRay Jui return 0;
5935fe225c1SRay Jui }
5945fe225c1SRay Jui
iproc_clk_disable(struct clk_hw * hw)5955fe225c1SRay Jui static void iproc_clk_disable(struct clk_hw *hw)
5965fe225c1SRay Jui {
5975fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
5985fe225c1SRay Jui const struct iproc_clk_ctrl *ctrl = clk->ctrl;
5995fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
6005fe225c1SRay Jui u32 val;
6015fe225c1SRay Jui
6025fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_AON)
6035fe225c1SRay Jui return;
6045fe225c1SRay Jui
60540c8bec3SJon Mason val = readl(pll->control_base + ctrl->enable.offset);
6065fe225c1SRay Jui val |= 1 << ctrl->enable.enable_shift;
60740c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val);
6085fe225c1SRay Jui }
6095fe225c1SRay Jui
iproc_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)6105fe225c1SRay Jui static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw,
6115fe225c1SRay Jui unsigned long parent_rate)
6125fe225c1SRay Jui {
6135fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
6145fe225c1SRay Jui const struct iproc_clk_ctrl *ctrl = clk->ctrl;
6155fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
6165fe225c1SRay Jui u32 val;
6175fe225c1SRay Jui unsigned int mdiv;
618b33db497SLori Hikichi unsigned long rate;
6195fe225c1SRay Jui
6205fe225c1SRay Jui if (parent_rate == 0)
6215fe225c1SRay Jui return 0;
6225fe225c1SRay Jui
62340c8bec3SJon Mason val = readl(pll->control_base + ctrl->mdiv.offset);
6245fe225c1SRay Jui mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width);
6255fe225c1SRay Jui if (mdiv == 0)
6265fe225c1SRay Jui mdiv = 256;
6275fe225c1SRay Jui
628bcd8be13SSimran Rai if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2)
629b33db497SLori Hikichi rate = parent_rate / (mdiv * 2);
630bcd8be13SSimran Rai else
631b33db497SLori Hikichi rate = parent_rate / mdiv;
6325fe225c1SRay Jui
633b33db497SLori Hikichi return rate;
6345fe225c1SRay Jui }
6355fe225c1SRay Jui
iproc_clk_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)63685151a6bSLori Hikichi static int iproc_clk_determine_rate(struct clk_hw *hw,
63785151a6bSLori Hikichi struct clk_rate_request *req)
6385fe225c1SRay Jui {
63985151a6bSLori Hikichi unsigned int bestdiv;
6405fe225c1SRay Jui
64185151a6bSLori Hikichi if (req->rate == 0)
6425fe225c1SRay Jui return -EINVAL;
64385151a6bSLori Hikichi if (req->rate == req->best_parent_rate)
64485151a6bSLori Hikichi return 0;
6455fe225c1SRay Jui
64685151a6bSLori Hikichi bestdiv = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
64785151a6bSLori Hikichi if (bestdiv < 2)
64885151a6bSLori Hikichi req->rate = req->best_parent_rate;
6495fe225c1SRay Jui
65085151a6bSLori Hikichi if (bestdiv > 256)
65185151a6bSLori Hikichi bestdiv = 256;
6525fe225c1SRay Jui
65385151a6bSLori Hikichi req->rate = req->best_parent_rate / bestdiv;
6545fe225c1SRay Jui
65585151a6bSLori Hikichi return 0;
6565fe225c1SRay Jui }
6575fe225c1SRay Jui
iproc_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)6585fe225c1SRay Jui static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
6595fe225c1SRay Jui unsigned long parent_rate)
6605fe225c1SRay Jui {
6615fe225c1SRay Jui struct iproc_clk *clk = to_iproc_clk(hw);
6625fe225c1SRay Jui const struct iproc_clk_ctrl *ctrl = clk->ctrl;
6635fe225c1SRay Jui struct iproc_pll *pll = clk->pll;
6645fe225c1SRay Jui u32 val;
6655fe225c1SRay Jui unsigned int div;
6665fe225c1SRay Jui
6675fe225c1SRay Jui if (rate == 0 || parent_rate == 0)
6685fe225c1SRay Jui return -EINVAL;
6695fe225c1SRay Jui
67085151a6bSLori Hikichi div = DIV_ROUND_CLOSEST(parent_rate, rate);
671bcd8be13SSimran Rai if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2)
67285151a6bSLori Hikichi div /= 2;
67385151a6bSLori Hikichi
6745fe225c1SRay Jui if (div > 256)
6755fe225c1SRay Jui return -EINVAL;
6765fe225c1SRay Jui
67740c8bec3SJon Mason val = readl(pll->control_base + ctrl->mdiv.offset);
6785fe225c1SRay Jui if (div == 256) {
6795fe225c1SRay Jui val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
6805fe225c1SRay Jui } else {
6815fe225c1SRay Jui val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
6825fe225c1SRay Jui val |= div << ctrl->mdiv.shift;
6835fe225c1SRay Jui }
68440c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->mdiv.offset, val);
6855fe225c1SRay Jui
6865fe225c1SRay Jui return 0;
6875fe225c1SRay Jui }
6885fe225c1SRay Jui
6895fe225c1SRay Jui static const struct clk_ops iproc_clk_ops = {
6905fe225c1SRay Jui .enable = iproc_clk_enable,
6915fe225c1SRay Jui .disable = iproc_clk_disable,
6925fe225c1SRay Jui .recalc_rate = iproc_clk_recalc_rate,
69385151a6bSLori Hikichi .determine_rate = iproc_clk_determine_rate,
6945fe225c1SRay Jui .set_rate = iproc_clk_set_rate,
6955fe225c1SRay Jui };
6965fe225c1SRay Jui
697ee70d49aSLee Jones /*
6985fe225c1SRay Jui * Some PLLs require the PLL SW override bit to be set before changes can be
6995fe225c1SRay Jui * applied to the PLL
7005fe225c1SRay Jui */
iproc_pll_sw_cfg(struct iproc_pll * pll)7015fe225c1SRay Jui static void iproc_pll_sw_cfg(struct iproc_pll *pll)
7025fe225c1SRay Jui {
7035fe225c1SRay Jui const struct iproc_pll_ctrl *ctrl = pll->ctrl;
7045fe225c1SRay Jui
7055fe225c1SRay Jui if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) {
7065fe225c1SRay Jui u32 val;
7075fe225c1SRay Jui
70840c8bec3SJon Mason val = readl(pll->control_base + ctrl->sw_ctrl.offset);
7095fe225c1SRay Jui val |= BIT(ctrl->sw_ctrl.shift);
71040c8bec3SJon Mason iproc_pll_write(pll, pll->control_base, ctrl->sw_ctrl.offset,
71140c8bec3SJon Mason val);
7125fe225c1SRay Jui }
7135fe225c1SRay Jui }
7145fe225c1SRay Jui
iproc_pll_clk_setup(struct device_node * node,const struct iproc_pll_ctrl * pll_ctrl,const struct iproc_pll_vco_param * vco,unsigned int num_vco_entries,const struct iproc_clk_ctrl * clk_ctrl,unsigned int num_clks)715e293915aSStephen Boyd void iproc_pll_clk_setup(struct device_node *node,
7165fe225c1SRay Jui const struct iproc_pll_ctrl *pll_ctrl,
7175fe225c1SRay Jui const struct iproc_pll_vco_param *vco,
7185fe225c1SRay Jui unsigned int num_vco_entries,
7195fe225c1SRay Jui const struct iproc_clk_ctrl *clk_ctrl,
7205fe225c1SRay Jui unsigned int num_clks)
7215fe225c1SRay Jui {
7225fe225c1SRay Jui int i, ret;
7235fe225c1SRay Jui struct iproc_pll *pll;
7245fe225c1SRay Jui struct iproc_clk *iclk;
7255fe225c1SRay Jui struct clk_init_data init;
7265fe225c1SRay Jui const char *parent_name;
727b33db497SLori Hikichi struct iproc_clk *iclk_array;
728b33db497SLori Hikichi struct clk_hw_onecell_data *clk_data;
729*1b24a132SFlorian Fainelli const char *clk_name;
7305fe225c1SRay Jui
7315fe225c1SRay Jui if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl))
7325fe225c1SRay Jui return;
7335fe225c1SRay Jui
7345fe225c1SRay Jui pll = kzalloc(sizeof(*pll), GFP_KERNEL);
7355fe225c1SRay Jui if (WARN_ON(!pll))
7365fe225c1SRay Jui return;
7375fe225c1SRay Jui
738acafe7e3SKees Cook clk_data = kzalloc(struct_size(clk_data, hws, num_clks), GFP_KERNEL);
739b33db497SLori Hikichi if (WARN_ON(!clk_data))
7405fe225c1SRay Jui goto err_clk_data;
741b33db497SLori Hikichi clk_data->num = num_clks;
7425fe225c1SRay Jui
743b33db497SLori Hikichi iclk_array = kcalloc(num_clks, sizeof(struct iproc_clk), GFP_KERNEL);
744b33db497SLori Hikichi if (WARN_ON(!iclk_array))
7455fe225c1SRay Jui goto err_clks;
7465fe225c1SRay Jui
74740c8bec3SJon Mason pll->control_base = of_iomap(node, 0);
74840c8bec3SJon Mason if (WARN_ON(!pll->control_base))
7495fe225c1SRay Jui goto err_pll_iomap;
7505fe225c1SRay Jui
75101b6722fSJon Mason /* Some SoCs do not require the pwr_base, thus failing is not fatal */
7525fe225c1SRay Jui pll->pwr_base = of_iomap(node, 1);
7535fe225c1SRay Jui
7545fe225c1SRay Jui /* some PLLs require gating control at the top ASIU level */
7555fe225c1SRay Jui if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) {
7565fe225c1SRay Jui pll->asiu_base = of_iomap(node, 2);
7575fe225c1SRay Jui if (WARN_ON(!pll->asiu_base))
7585fe225c1SRay Jui goto err_asiu_iomap;
7595fe225c1SRay Jui }
7605fe225c1SRay Jui
76140c8bec3SJon Mason if (pll_ctrl->flags & IPROC_CLK_PLL_SPLIT_STAT_CTRL) {
76240c8bec3SJon Mason /* Some SoCs have a split status/control. If this does not
76340c8bec3SJon Mason * exist, assume they are unified.
76440c8bec3SJon Mason */
76540c8bec3SJon Mason pll->status_base = of_iomap(node, 2);
76640c8bec3SJon Mason if (!pll->status_base)
76740c8bec3SJon Mason goto err_status_iomap;
76840c8bec3SJon Mason } else
76940c8bec3SJon Mason pll->status_base = pll->control_base;
77040c8bec3SJon Mason
7715fe225c1SRay Jui /* initialize and register the PLL itself */
7725fe225c1SRay Jui pll->ctrl = pll_ctrl;
7735fe225c1SRay Jui
774b33db497SLori Hikichi iclk = &iclk_array[0];
7755fe225c1SRay Jui iclk->pll = pll;
7765fe225c1SRay Jui
777*1b24a132SFlorian Fainelli ret = of_property_read_string_index(node, "clock-output-names",
778*1b24a132SFlorian Fainelli 0, &clk_name);
779*1b24a132SFlorian Fainelli if (WARN_ON(ret))
780*1b24a132SFlorian Fainelli goto err_pll_register;
781*1b24a132SFlorian Fainelli
782*1b24a132SFlorian Fainelli init.name = clk_name;
7835fe225c1SRay Jui init.ops = &iproc_pll_ops;
7845fe225c1SRay Jui init.flags = 0;
7855fe225c1SRay Jui parent_name = of_clk_get_parent_name(node, 0);
7865fe225c1SRay Jui init.parent_names = (parent_name ? &parent_name : NULL);
7875fe225c1SRay Jui init.num_parents = (parent_name ? 1 : 0);
7885fe225c1SRay Jui iclk->hw.init = &init;
7895fe225c1SRay Jui
7905fe225c1SRay Jui if (vco) {
7915fe225c1SRay Jui pll->num_vco_entries = num_vco_entries;
7925fe225c1SRay Jui pll->vco_param = vco;
7935fe225c1SRay Jui }
7945fe225c1SRay Jui
7955fe225c1SRay Jui iproc_pll_sw_cfg(pll);
7965fe225c1SRay Jui
797ff02c6c0SStephen Boyd ret = clk_hw_register(NULL, &iclk->hw);
798ff02c6c0SStephen Boyd if (WARN_ON(ret))
7995fe225c1SRay Jui goto err_pll_register;
8005fe225c1SRay Jui
801b33db497SLori Hikichi clk_data->hws[0] = &iclk->hw;
802*1b24a132SFlorian Fainelli parent_name = clk_name;
8035fe225c1SRay Jui
8045fe225c1SRay Jui /* now initialize and register all leaf clocks */
8055fe225c1SRay Jui for (i = 1; i < num_clks; i++) {
8065fe225c1SRay Jui memset(&init, 0, sizeof(init));
8075fe225c1SRay Jui
8085fe225c1SRay Jui ret = of_property_read_string_index(node, "clock-output-names",
8095fe225c1SRay Jui i, &clk_name);
8105fe225c1SRay Jui if (WARN_ON(ret))
8115fe225c1SRay Jui goto err_clk_register;
8125fe225c1SRay Jui
813b33db497SLori Hikichi iclk = &iclk_array[i];
8145fe225c1SRay Jui iclk->pll = pll;
8155fe225c1SRay Jui iclk->ctrl = &clk_ctrl[i];
8165fe225c1SRay Jui
8175fe225c1SRay Jui init.name = clk_name;
8185fe225c1SRay Jui init.ops = &iproc_clk_ops;
8195fe225c1SRay Jui init.flags = 0;
8205fe225c1SRay Jui init.parent_names = (parent_name ? &parent_name : NULL);
8215fe225c1SRay Jui init.num_parents = (parent_name ? 1 : 0);
8225fe225c1SRay Jui iclk->hw.init = &init;
8235fe225c1SRay Jui
824ff02c6c0SStephen Boyd ret = clk_hw_register(NULL, &iclk->hw);
825ff02c6c0SStephen Boyd if (WARN_ON(ret))
8265fe225c1SRay Jui goto err_clk_register;
8275fe225c1SRay Jui
828b33db497SLori Hikichi clk_data->hws[i] = &iclk->hw;
8295fe225c1SRay Jui }
8305fe225c1SRay Jui
831b33db497SLori Hikichi ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
8325fe225c1SRay Jui if (WARN_ON(ret))
8335fe225c1SRay Jui goto err_clk_register;
8345fe225c1SRay Jui
8355fe225c1SRay Jui return;
8365fe225c1SRay Jui
8375fe225c1SRay Jui err_clk_register:
838ff02c6c0SStephen Boyd while (--i >= 0)
839b33db497SLori Hikichi clk_hw_unregister(clk_data->hws[i]);
8405fe225c1SRay Jui
8415fe225c1SRay Jui err_pll_register:
84240c8bec3SJon Mason if (pll->status_base != pll->control_base)
84340c8bec3SJon Mason iounmap(pll->status_base);
84440c8bec3SJon Mason
84540c8bec3SJon Mason err_status_iomap:
8465fe225c1SRay Jui if (pll->asiu_base)
8475fe225c1SRay Jui iounmap(pll->asiu_base);
8485fe225c1SRay Jui
8495fe225c1SRay Jui err_asiu_iomap:
85001b6722fSJon Mason if (pll->pwr_base)
8515fe225c1SRay Jui iounmap(pll->pwr_base);
8525fe225c1SRay Jui
85340c8bec3SJon Mason iounmap(pll->control_base);
8545fe225c1SRay Jui
8555fe225c1SRay Jui err_pll_iomap:
856b33db497SLori Hikichi kfree(iclk_array);
8575fe225c1SRay Jui
8585fe225c1SRay Jui err_clks:
859b33db497SLori Hikichi kfree(clk_data);
8605fe225c1SRay Jui
8615fe225c1SRay Jui err_clk_data:
8625fe225c1SRay Jui kfree(pll);
8635fe225c1SRay Jui }
864