]> git.sur5r.net Git - u-boot/blob - drivers/clk/clk_zynqmp.c
imx: mx7: fix build warning when CONFIG_IMX_RDC not enabled
[u-boot] / drivers / clk / clk_zynqmp.c
1 /*
2  * ZynqMP clock driver
3  *
4  * Copyright (C) 2016 Xilinx, Inc.
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <common.h>
10 #include <linux/bitops.h>
11 #include <clk-uclass.h>
12 #include <clk.h>
13 #include <dm.h>
14
15 #define ZYNQMP_GEM0_REF_CTRL            0xFF5E0050
16 #define ZYNQMP_IOPLL_CTRL               0xFF5E0020
17 #define ZYNQMP_RPLL_CTRL                0xFF5E0030
18 #define ZYNQMP_DPLL_CTRL                0xFD1A002C
19 #define ZYNQMP_SIP_SVC_MMIO_WRITE       0xC2000013
20 #define ZYNQMP_SIP_SVC_MMIO_WRITE       0xC2000013
21 #define ZYNQMP_SIP_SVC_MMIO_WRITE       0xC2000013
22 #define ZYNQMP_SIP_SVC_MMIO_READ        0xC2000014
23 #define ZYNQMP_DIV_MAX_VAL              0x3F
24 #define ZYNQMP_DIV1_SHFT                8
25 #define ZYNQMP_DIV1_SHFT                8
26 #define ZYNQMP_DIV2_SHFT                16
27 #define ZYNQMP_DIV_MASK                 0x3F
28 #define ZYNQMP_PLL_CTRL_FBDIV_MASK      0x7F
29 #define ZYNQMP_PLL_CTRL_FBDIV_SHFT      8
30 #define ZYNQMP_GEM_REF_CTRL_SRC_MASK    0x7
31 #define ZYNQMP_GEM0_CLK_ID              45
32 #define ZYNQMP_GEM1_CLK_ID              46
33 #define ZYNQMP_GEM2_CLK_ID              47
34 #define ZYNQMP_GEM3_CLK_ID              48
35
36 static unsigned long pss_ref_clk;
37
38 static int zynqmp_calculate_divisors(unsigned long req_rate,
39                                      unsigned long parent_rate,
40                                      u32 *div1, u32 *div2)
41 {
42         u32 req_div = 1;
43         u32 i;
44
45         /*
46          * calculate two divisors to get
47          * required rate and each divisor
48          * should be less than 63
49          */
50         req_div = DIV_ROUND_UP(parent_rate, req_rate);
51
52         for (i = 1; i <= req_div; i++) {
53                 if ((req_div % i) == 0) {
54                         *div1 = req_div / i;
55                         *div2 = i;
56                         if ((*div1 < ZYNQMP_DIV_MAX_VAL) &&
57                             (*div2 < ZYNQMP_DIV_MAX_VAL))
58                                 return 0;
59                 }
60         }
61
62         return -1;
63 }
64
65 static int zynqmp_get_periph_id(unsigned long id)
66 {
67         int periph_id;
68
69         switch (id) {
70         case ZYNQMP_GEM0_CLK_ID:
71                 periph_id = 0;
72                 break;
73         case ZYNQMP_GEM1_CLK_ID:
74                 periph_id = 1;
75                 break;
76         case ZYNQMP_GEM2_CLK_ID:
77                 periph_id = 2;
78                 break;
79         case ZYNQMP_GEM3_CLK_ID:
80                 periph_id = 3;
81                 break;
82         default:
83                 printf("%s, Invalid clock id:%ld\n", __func__, id);
84                 return -EINVAL;
85         }
86
87         return periph_id;
88 }
89
90 static int zynqmp_set_clk(unsigned long id, u32 div1, u32 div2)
91 {
92         struct pt_regs regs;
93         ulong reg;
94         u32 mask, value;
95
96         id = zynqmp_get_periph_id(id);
97         if (id < 0)
98                 return -EINVAL;
99
100         reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id);
101         mask = (ZYNQMP_DIV_MASK << ZYNQMP_DIV1_SHFT) |
102                (ZYNQMP_DIV_MASK << ZYNQMP_DIV2_SHFT);
103         value = (div1 << ZYNQMP_DIV1_SHFT) | (div2 << ZYNQMP_DIV2_SHFT);
104
105         debug("%s: reg:0x%lx, mask:0x%x, value:0x%x\n", __func__, reg, mask,
106               value);
107
108         regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_WRITE;
109         regs.regs[1] = ((u64)mask << 32) | reg;
110         regs.regs[2] = value;
111         regs.regs[3] = 0;
112
113         smc_call(&regs);
114
115         return regs.regs[0];
116 }
117
118 static unsigned long zynqmp_clk_get_rate(struct clk *clk)
119 {
120         struct pt_regs regs;
121         ulong reg;
122         unsigned long value;
123         int id;
124
125         id = zynqmp_get_periph_id(clk->id);
126         if (id < 0)
127                 return -EINVAL;
128
129         reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id);
130
131         regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ;
132         regs.regs[1] = reg;
133         regs.regs[2] = 0;
134         regs.regs[3] = 0;
135
136         smc_call(&regs);
137
138         value = upper_32_bits(regs.regs[0]);
139
140         value &= ZYNQMP_GEM_REF_CTRL_SRC_MASK;
141
142         switch (value) {
143         case 0:
144                 regs.regs[1] = ZYNQMP_IOPLL_CTRL;
145                 break;
146         case 2:
147                 regs.regs[1] = ZYNQMP_RPLL_CTRL;
148                 break;
149         case 3:
150                 regs.regs[1] = ZYNQMP_DPLL_CTRL;
151                 break;
152         default:
153                 return -EINVAL;
154         }
155
156         regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ;
157         regs.regs[2] = 0;
158         regs.regs[3] = 0;
159
160         smc_call(&regs);
161
162         value = upper_32_bits(regs.regs[0]) &
163                  (ZYNQMP_PLL_CTRL_FBDIV_MASK <<
164                  ZYNQMP_PLL_CTRL_FBDIV_SHFT);
165         value >>= ZYNQMP_PLL_CTRL_FBDIV_SHFT;
166         value *= pss_ref_clk;
167
168         return value;
169 }
170
171 static ulong zynqmp_clk_set_rate(struct clk *clk, unsigned long clk_rate)
172 {
173         int ret;
174         u32 div1 = 0;
175         u32 div2 = 0;
176         unsigned long input_clk;
177
178         input_clk = zynqmp_clk_get_rate(clk);
179         if (IS_ERR_VALUE(input_clk)) {
180                 dev_err(dev, "failed to get input_clk\n");
181                 return -EINVAL;
182         }
183
184         debug("%s: i/p CLK %ld, clk_rate:0x%ld\n", __func__, input_clk,
185               clk_rate);
186
187         ret = zynqmp_calculate_divisors(clk_rate, input_clk, &div1, &div2);
188         if (ret) {
189                 dev_err(dev, "failed to proper divisors\n");
190                 return -EINVAL;
191         }
192
193         debug("%s: Div1:%d, Div2:%d\n", __func__, div1, div2);
194
195         ret = zynqmp_set_clk(clk->id, div1, div2);
196         if (ret) {
197                 dev_err(dev, "failed to set gem clk\n");
198                 return -EINVAL;
199         }
200
201         return 0;
202 }
203
204 static int zynqmp_clk_probe(struct udevice *dev)
205 {
206         struct clk clk;
207         int ret;
208
209         debug("%s\n", __func__);
210         ret = clk_get_by_name(dev, "pss_ref_clk", &clk);
211         if (ret < 0) {
212                 dev_err(dev, "failed to get pss_ref_clk\n");
213                 return ret;
214         }
215
216         pss_ref_clk = clk_get_rate(&clk);
217         if (IS_ERR_VALUE(pss_ref_clk)) {
218                 dev_err(dev, "failed to get rate pss_ref_clk\n");
219                 return -EINVAL;
220         }
221
222         return 0;
223 }
224
225 static struct clk_ops zynqmp_clk_ops = {
226         .set_rate = zynqmp_clk_set_rate,
227         .get_rate = zynqmp_clk_get_rate,
228 };
229
230 static const struct udevice_id zynqmp_clk_ids[] = {
231         { .compatible = "xlnx,zynqmp-clkc" },
232         { }
233 };
234
235 U_BOOT_DRIVER(zynqmp_clk) = {
236         .name = "zynqmp-clk",
237         .id = UCLASS_CLK,
238         .of_match = zynqmp_clk_ids,
239         .probe = zynqmp_clk_probe,
240         .ops = &zynqmp_clk_ops,
241 };