xref: /openbmc/u-boot/drivers/clk/clk_zynqmp.c (revision 5d9828563f80a1319e793166974dd6003dc1d941)
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  };
242