Merge tag 'drm-intel-gt-next-2020-11-12-1' of git://anongit.freedesktop.org/drm/drm...
[linux-2.6-microblaze.git] / drivers / clk / imx / clk-pllv4.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Freescale Semiconductor, Inc.
4  * Copyright 2017~2018 NXP
5  *
6  * Author: Dong Aisheng <aisheng.dong@nxp.com>
7  *
8  */
9
10 #include <linux/bits.h>
11 #include <linux/clk-provider.h>
12 #include <linux/err.h>
13 #include <linux/io.h>
14 #include <linux/iopoll.h>
15 #include <linux/slab.h>
16
17 #include "clk.h"
18
19 /* PLL Control Status Register (xPLLCSR) */
20 #define PLL_CSR_OFFSET          0x0
21 #define PLL_VLD                 BIT(24)
22 #define PLL_EN                  BIT(0)
23
24 /* PLL Configuration Register (xPLLCFG) */
25 #define PLL_CFG_OFFSET          0x08
26 #define BP_PLL_MULT             16
27 #define BM_PLL_MULT             (0x7f << 16)
28
29 /* PLL Numerator Register (xPLLNUM) */
30 #define PLL_NUM_OFFSET          0x10
31
32 /* PLL Denominator Register (xPLLDENOM) */
33 #define PLL_DENOM_OFFSET        0x14
34
35 #define MAX_MFD                 0x3fffffff
36 #define DEFAULT_MFD             1000000
37
38 struct clk_pllv4 {
39         struct clk_hw   hw;
40         void __iomem    *base;
41 };
42
43 /* Valid PLL MULT Table */
44 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
45
46 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
47
48 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
49
50 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
51 {
52         u32 csr;
53
54         return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
55                                   csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
56 }
57
58 static int clk_pllv4_is_prepared(struct clk_hw *hw)
59 {
60         struct clk_pllv4 *pll = to_clk_pllv4(hw);
61
62         if (readl_relaxed(pll->base) & PLL_EN)
63                 return 1;
64
65         return 0;
66 }
67
68 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
69                                            unsigned long parent_rate)
70 {
71         struct clk_pllv4 *pll = to_clk_pllv4(hw);
72         u32 mult, mfn, mfd;
73         u64 temp64;
74
75         mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
76         mult &= BM_PLL_MULT;
77         mult >>= BP_PLL_MULT;
78
79         mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
80         mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
81         temp64 = parent_rate;
82         temp64 *= mfn;
83         do_div(temp64, mfd);
84
85         return (parent_rate * mult) + (u32)temp64;
86 }
87
88 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
89                                  unsigned long *prate)
90 {
91         unsigned long parent_rate = *prate;
92         unsigned long round_rate, i;
93         u32 mfn, mfd = DEFAULT_MFD;
94         bool found = false;
95         u64 temp64;
96
97         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
98                 round_rate = parent_rate * pllv4_mult_table[i];
99                 if (rate >= round_rate) {
100                         found = true;
101                         break;
102                 }
103         }
104
105         if (!found) {
106                 pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
107                         clk_hw_get_name(hw), rate, parent_rate);
108                 return 0;
109         }
110
111         if (parent_rate <= MAX_MFD)
112                 mfd = parent_rate;
113
114         temp64 = (u64)(rate - round_rate);
115         temp64 *= mfd;
116         do_div(temp64, parent_rate);
117         mfn = temp64;
118
119         /*
120          * NOTE: The value of numerator must always be configured to be
121          * less than the value of the denominator. If we can't get a proper
122          * pair of mfn/mfd, we simply return the round_rate without using
123          * the frac part.
124          */
125         if (mfn >= mfd)
126                 return round_rate;
127
128         temp64 = (u64)parent_rate;
129         temp64 *= mfn;
130         do_div(temp64, mfd);
131
132         return round_rate + (u32)temp64;
133 }
134
135 static bool clk_pllv4_is_valid_mult(unsigned int mult)
136 {
137         int i;
138
139         /* check if mult is in valid MULT table */
140         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
141                 if (pllv4_mult_table[i] == mult)
142                         return true;
143         }
144
145         return false;
146 }
147
148 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
149                               unsigned long parent_rate)
150 {
151         struct clk_pllv4 *pll = to_clk_pllv4(hw);
152         u32 val, mult, mfn, mfd = DEFAULT_MFD;
153         u64 temp64;
154
155         mult = rate / parent_rate;
156
157         if (!clk_pllv4_is_valid_mult(mult))
158                 return -EINVAL;
159
160         if (parent_rate <= MAX_MFD)
161                 mfd = parent_rate;
162
163         temp64 = (u64)(rate - mult * parent_rate);
164         temp64 *= mfd;
165         do_div(temp64, parent_rate);
166         mfn = temp64;
167
168         val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
169         val &= ~BM_PLL_MULT;
170         val |= mult << BP_PLL_MULT;
171         writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
172
173         writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
174         writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
175
176         return 0;
177 }
178
179 static int clk_pllv4_prepare(struct clk_hw *hw)
180 {
181         u32 val;
182         struct clk_pllv4 *pll = to_clk_pllv4(hw);
183
184         val = readl_relaxed(pll->base);
185         val |= PLL_EN;
186         writel_relaxed(val, pll->base);
187
188         return clk_pllv4_wait_lock(pll);
189 }
190
191 static void clk_pllv4_unprepare(struct clk_hw *hw)
192 {
193         u32 val;
194         struct clk_pllv4 *pll = to_clk_pllv4(hw);
195
196         val = readl_relaxed(pll->base);
197         val &= ~PLL_EN;
198         writel_relaxed(val, pll->base);
199 }
200
201 static const struct clk_ops clk_pllv4_ops = {
202         .recalc_rate    = clk_pllv4_recalc_rate,
203         .round_rate     = clk_pllv4_round_rate,
204         .set_rate       = clk_pllv4_set_rate,
205         .prepare        = clk_pllv4_prepare,
206         .unprepare      = clk_pllv4_unprepare,
207         .is_prepared    = clk_pllv4_is_prepared,
208 };
209
210 struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name,
211                           void __iomem *base)
212 {
213         struct clk_pllv4 *pll;
214         struct clk_hw *hw;
215         struct clk_init_data init;
216         int ret;
217
218         pll = kzalloc(sizeof(*pll), GFP_KERNEL);
219         if (!pll)
220                 return ERR_PTR(-ENOMEM);
221
222         pll->base = base;
223
224         init.name = name;
225         init.ops = &clk_pllv4_ops;
226         init.parent_names = &parent_name;
227         init.num_parents = 1;
228         init.flags = CLK_SET_RATE_GATE;
229
230         pll->hw.init = &init;
231
232         hw = &pll->hw;
233         ret = clk_hw_register(NULL, hw);
234         if (ret) {
235                 kfree(pll);
236                 hw = ERR_PTR(ret);
237         }
238
239         return hw;
240 }