]> Git Repo - J-linux.git/blob - drivers/clk/imx/clk-pllv4.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.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 <[email protected]>
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 IMX8ULP_PLL_CFG_OFFSET  0x10
27 #define BP_PLL_MULT             16
28 #define BM_PLL_MULT             (0x7f << 16)
29
30 /* PLL Numerator Register (xPLLNUM) */
31 #define PLL_NUM_OFFSET          0x10
32 #define IMX8ULP_PLL_NUM_OFFSET  0x1c
33
34 /* PLL Denominator Register (xPLLDENOM) */
35 #define PLL_DENOM_OFFSET        0x14
36 #define IMX8ULP_PLL_DENOM_OFFSET        0x18
37
38 #define MAX_MFD                 0x3fffffff
39 #define DEFAULT_MFD             1000000
40
41 struct clk_pllv4 {
42         struct clk_hw   hw;
43         void __iomem    *base;
44         u32             cfg_offset;
45         u32             num_offset;
46         u32             denom_offset;
47 };
48
49 /* Valid PLL MULT Table */
50 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
51
52 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
53
54 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
55
56 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
57 {
58         u32 csr;
59
60         return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
61                                   csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
62 }
63
64 static int clk_pllv4_is_prepared(struct clk_hw *hw)
65 {
66         struct clk_pllv4 *pll = to_clk_pllv4(hw);
67
68         if (readl_relaxed(pll->base) & PLL_EN)
69                 return 1;
70
71         return 0;
72 }
73
74 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
75                                            unsigned long parent_rate)
76 {
77         struct clk_pllv4 *pll = to_clk_pllv4(hw);
78         u32 mult, mfn, mfd;
79         u64 temp64;
80
81         mult = readl_relaxed(pll->base + pll->cfg_offset);
82         mult &= BM_PLL_MULT;
83         mult >>= BP_PLL_MULT;
84
85         mfn = readl_relaxed(pll->base + pll->num_offset);
86         mfd = readl_relaxed(pll->base + pll->denom_offset);
87         temp64 = parent_rate;
88         temp64 *= mfn;
89         do_div(temp64, mfd);
90
91         return (parent_rate * mult) + (u32)temp64;
92 }
93
94 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
95                                  unsigned long *prate)
96 {
97         unsigned long parent_rate = *prate;
98         unsigned long round_rate, i;
99         u32 mfn, mfd = DEFAULT_MFD;
100         bool found = false;
101         u64 temp64;
102
103         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
104                 round_rate = parent_rate * pllv4_mult_table[i];
105                 if (rate >= round_rate) {
106                         found = true;
107                         break;
108                 }
109         }
110
111         if (!found) {
112                 pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
113                         clk_hw_get_name(hw), rate, parent_rate);
114                 return 0;
115         }
116
117         if (parent_rate <= MAX_MFD)
118                 mfd = parent_rate;
119
120         temp64 = (u64)(rate - round_rate);
121         temp64 *= mfd;
122         do_div(temp64, parent_rate);
123         mfn = temp64;
124
125         /*
126          * NOTE: The value of numerator must always be configured to be
127          * less than the value of the denominator. If we can't get a proper
128          * pair of mfn/mfd, we simply return the round_rate without using
129          * the frac part.
130          */
131         if (mfn >= mfd)
132                 return round_rate;
133
134         temp64 = (u64)parent_rate;
135         temp64 *= mfn;
136         do_div(temp64, mfd);
137
138         return round_rate + (u32)temp64;
139 }
140
141 static bool clk_pllv4_is_valid_mult(unsigned int mult)
142 {
143         int i;
144
145         /* check if mult is in valid MULT table */
146         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
147                 if (pllv4_mult_table[i] == mult)
148                         return true;
149         }
150
151         return false;
152 }
153
154 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
155                               unsigned long parent_rate)
156 {
157         struct clk_pllv4 *pll = to_clk_pllv4(hw);
158         u32 val, mult, mfn, mfd = DEFAULT_MFD;
159         u64 temp64;
160
161         mult = rate / parent_rate;
162
163         if (!clk_pllv4_is_valid_mult(mult))
164                 return -EINVAL;
165
166         if (parent_rate <= MAX_MFD)
167                 mfd = parent_rate;
168
169         temp64 = (u64)(rate - mult * parent_rate);
170         temp64 *= mfd;
171         do_div(temp64, parent_rate);
172         mfn = temp64;
173
174         val = readl_relaxed(pll->base + pll->cfg_offset);
175         val &= ~BM_PLL_MULT;
176         val |= mult << BP_PLL_MULT;
177         writel_relaxed(val, pll->base + pll->cfg_offset);
178
179         writel_relaxed(mfn, pll->base + pll->num_offset);
180         writel_relaxed(mfd, pll->base + pll->denom_offset);
181
182         return 0;
183 }
184
185 static int clk_pllv4_prepare(struct clk_hw *hw)
186 {
187         u32 val;
188         struct clk_pllv4 *pll = to_clk_pllv4(hw);
189
190         val = readl_relaxed(pll->base);
191         val |= PLL_EN;
192         writel_relaxed(val, pll->base);
193
194         return clk_pllv4_wait_lock(pll);
195 }
196
197 static void clk_pllv4_unprepare(struct clk_hw *hw)
198 {
199         u32 val;
200         struct clk_pllv4 *pll = to_clk_pllv4(hw);
201
202         val = readl_relaxed(pll->base);
203         val &= ~PLL_EN;
204         writel_relaxed(val, pll->base);
205 }
206
207 static const struct clk_ops clk_pllv4_ops = {
208         .recalc_rate    = clk_pllv4_recalc_rate,
209         .round_rate     = clk_pllv4_round_rate,
210         .set_rate       = clk_pllv4_set_rate,
211         .prepare        = clk_pllv4_prepare,
212         .unprepare      = clk_pllv4_unprepare,
213         .is_prepared    = clk_pllv4_is_prepared,
214 };
215
216 struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name,
217                  const char *parent_name, void __iomem *base)
218 {
219         struct clk_pllv4 *pll;
220         struct clk_hw *hw;
221         struct clk_init_data init;
222         int ret;
223
224         pll = kzalloc(sizeof(*pll), GFP_KERNEL);
225         if (!pll)
226                 return ERR_PTR(-ENOMEM);
227
228         pll->base = base;
229
230         if (type == IMX_PLLV4_IMX8ULP) {
231                 pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET;
232                 pll->num_offset = IMX8ULP_PLL_NUM_OFFSET;
233                 pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET;
234         } else {
235                 pll->cfg_offset = PLL_CFG_OFFSET;
236                 pll->num_offset = PLL_NUM_OFFSET;
237                 pll->denom_offset = PLL_DENOM_OFFSET;
238         }
239
240         init.name = name;
241         init.ops = &clk_pllv4_ops;
242         init.parent_names = &parent_name;
243         init.num_parents = 1;
244         init.flags = CLK_SET_RATE_GATE;
245
246         pll->hw.init = &init;
247
248         hw = &pll->hw;
249         ret = clk_hw_register(NULL, hw);
250         if (ret) {
251                 kfree(pll);
252                 hw = ERR_PTR(ret);
253         }
254
255         return hw;
256 }
257 EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4);
This page took 0.043869 seconds and 4 git commands to generate.