blob: 694274d9910357b47b2d71355115ec2262723dc9 [file] [log] [blame]
Siva Durga Prasad Paladugu128ec1f2016-11-15 16:15:41 +05301/*
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 <dm/device.h>
13#include <clk.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
36static unsigned long pss_ref_clk;
37
38static 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
65static 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
90static 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
118static 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
171static 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
204static 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
225static struct clk_ops zynqmp_clk_ops = {
226 .set_rate = zynqmp_clk_set_rate,
227 .get_rate = zynqmp_clk_get_rate,
228};
229
230static const struct udevice_id zynqmp_clk_ids[] = {
231 { .compatible = "xlnx,zynqmp-clkc" },
232 { }
233};
234
235U_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};