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