blob: f1f76b0a4da8061d1955804a030c2cf6ae7dd6eb [file] [log] [blame]
Tero Kristo814c6112019-09-27 19:14:26 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments CDCE913/925/937/949 clock synthesizer driver
4 *
5 * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
6 * Tero Kristo <t-kristo@ti.com>
7 *
8 * Based on Linux kernel clk-cdce925.c.
9 */
10
11#include <common.h>
12#include <dm.h>
13#include <errno.h>
14#include <clk-uclass.h>
15#include <i2c.h>
Simon Glass9bc15642020-02-03 07:36:16 -070016#include <dm/device_compat.h>
Tero Kristo814c6112019-09-27 19:14:26 +030017
18#define MAX_NUMBER_OF_PLLS 4
19#define MAX_NUMER_OF_OUTPUTS 9
20
21#define CDCE9XX_REG_GLOBAL1 0x01
22#define CDCE9XX_REG_Y1SPIPDIVH 0x02
23#define CDCE9XX_REG_PDIV1L 0x03
24#define CDCE9XX_REG_XCSEL 0x05
25
26#define CDCE9XX_PDIV1_H_MASK 0x3
27
28#define CDCE9XX_REG_PDIV(clk) (0x16 + (((clk) - 1) & 1) + \
29 ((clk) - 1) / 2 * 0x10)
30
31#define CDCE9XX_PDIV_MASK 0x7f
32
33#define CDCE9XX_BYTE_TRANSFER BIT(7)
34
35struct cdce9xx_chip_info {
36 int num_plls;
37 int num_outputs;
38};
39
40struct cdce9xx_clk_data {
41 struct udevice *i2c;
42 struct cdce9xx_chip_info *chip;
43 u32 xtal_rate;
44};
45
46static const struct cdce9xx_chip_info cdce913_chip_info = {
47 .num_plls = 1, .num_outputs = 3,
48};
49
50static const struct cdce9xx_chip_info cdce925_chip_info = {
51 .num_plls = 2, .num_outputs = 5,
52};
53
54static const struct cdce9xx_chip_info cdce937_chip_info = {
55 .num_plls = 3, .num_outputs = 7,
56};
57
58static const struct cdce9xx_chip_info cdce949_chip_info = {
59 .num_plls = 4, .num_outputs = 9,
60};
61
62static int cdce9xx_reg_read(struct udevice *dev, u8 addr, u8 *buf)
63{
64 struct cdce9xx_clk_data *data = dev_get_priv(dev);
65 int ret;
66
67 ret = dm_i2c_read(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, buf, 1);
68 if (ret)
69 dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
70 addr, ret);
71
72 return ret;
73}
74
75static int cdce9xx_reg_write(struct udevice *dev, u8 addr, u8 val)
76{
77 struct cdce9xx_clk_data *data = dev_get_priv(dev);
78 int ret;
79
80 ret = dm_i2c_write(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, &val, 1);
81 if (ret)
82 dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
83 addr, ret);
84
85 return ret;
86}
87
88static int cdce9xx_clk_of_xlate(struct clk *clk,
89 struct ofnode_phandle_args *args)
90{
91 struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
92
93 if (args->args_count != 1)
94 return -EINVAL;
95
96 if (args->args[0] > data->chip->num_outputs)
97 return -EINVAL;
98
99 clk->id = args->args[0];
100
101 return 0;
102}
103
104static int cdce9xx_clk_probe(struct udevice *dev)
105{
106 struct cdce9xx_clk_data *data = dev_get_priv(dev);
107 struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev);
108 int ret;
109 u32 val;
110 struct clk clk;
111
112 val = (u32)dev_read_addr_ptr(dev);
113
114 ret = i2c_get_chip(dev->parent, val, 1, &data->i2c);
115 if (ret) {
116 dev_err(dev, "I2C probe failed.\n");
117 return ret;
118 }
119
120 data->chip = chip;
121
122 ret = clk_get_by_index(dev, 0, &clk);
123 data->xtal_rate = clk_get_rate(&clk);
124
125 val = dev_read_u32_default(dev, "xtal-load-pf", -1);
126 if (val >= 0)
127 cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3);
128
129 return 0;
130}
131
132static u16 cdce9xx_clk_get_pdiv(struct clk *clk)
133{
134 u8 val;
135 u16 pdiv;
136 int ret;
137
138 if (clk->id == 0) {
139 ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
140 if (ret)
141 return 0;
142
143 pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8;
144
145 ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val);
146 if (ret)
147 return 0;
148
149 pdiv |= val;
150 } else {
151 ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
152 &val);
153 if (ret)
154 return 0;
155
156 pdiv = val & CDCE9XX_PDIV_MASK;
157 }
158
159 return pdiv;
160}
161
162static u32 cdce9xx_clk_get_parent_rate(struct clk *clk)
163{
164 struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
165
166 return data->xtal_rate;
167}
168
169static ulong cdce9xx_clk_get_rate(struct clk *clk)
170{
171 u32 parent_rate;
172 u16 pdiv;
173
174 parent_rate = cdce9xx_clk_get_parent_rate(clk);
175
176 pdiv = cdce9xx_clk_get_pdiv(clk);
177
178 return parent_rate / pdiv;
179}
180
181static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate)
182{
183 u32 parent_rate;
184 int pdiv;
185 u32 diff;
186 u8 val;
187 int ret;
188
189 parent_rate = cdce9xx_clk_get_parent_rate(clk);
190
191 pdiv = parent_rate / rate;
192
193 diff = rate - parent_rate / pdiv;
194
195 if (rate - parent_rate / (pdiv + 1) < diff)
196 pdiv++;
197
198 if (clk->id == 0) {
199 ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
200 if (ret)
201 return ret;
202
203 val &= ~CDCE9XX_PDIV1_H_MASK;
204
205 val |= (pdiv >> 8);
206
207 ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val);
208 if (ret)
209 return ret;
210
211 ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L,
212 (pdiv & 0xff));
213 if (ret)
214 return ret;
215 } else {
216 ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
217 &val);
218 if (ret)
219 return ret;
220
221 val &= ~CDCE9XX_PDIV_MASK;
222
223 val |= pdiv;
224
225 ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id),
226 val);
227 if (ret)
228 return ret;
229 }
230
231 return 0;
232}
233
234static const struct udevice_id cdce9xx_clk_of_match[] = {
235 { .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info },
236 { .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info },
237 { .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info },
238 { .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info },
239 { /* sentinel */ },
240};
241
242static const struct clk_ops cdce9xx_clk_ops = {
243 .of_xlate = cdce9xx_clk_of_xlate,
244 .get_rate = cdce9xx_clk_get_rate,
245 .set_rate = cdce9xx_clk_set_rate,
246};
247
248U_BOOT_DRIVER(cdce9xx_clk) = {
249 .name = "cdce9xx-clk",
250 .id = UCLASS_CLK,
251 .of_match = cdce9xx_clk_of_match,
252 .probe = cdce9xx_clk_probe,
253 .priv_auto_alloc_size = sizeof(struct cdce9xx_clk_data),
254 .ops = &cdce9xx_clk_ops,
255};