clk: mediatek: add support to configure clock driver parent
[platform/kernel/u-boot.git] / drivers / clk / clk-cdce9xx.c
1 // 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>
16 #include <dm/device_compat.h>
17 #include <linux/bitops.h>
18
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
36 struct cdce9xx_chip_info {
37         int num_plls;
38         int num_outputs;
39 };
40
41 struct cdce9xx_clk_data {
42         struct udevice *i2c;
43         struct cdce9xx_chip_info *chip;
44         u32 xtal_rate;
45 };
46
47 static const struct cdce9xx_chip_info cdce913_chip_info = {
48         .num_plls = 1, .num_outputs = 3,
49 };
50
51 static const struct cdce9xx_chip_info cdce925_chip_info = {
52         .num_plls = 2, .num_outputs = 5,
53 };
54
55 static const struct cdce9xx_chip_info cdce937_chip_info = {
56         .num_plls = 3, .num_outputs = 7,
57 };
58
59 static const struct cdce9xx_chip_info cdce949_chip_info = {
60         .num_plls = 4, .num_outputs = 9,
61 };
62
63 static 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
76 static 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
89 static int cdce9xx_clk_request(struct clk *clk)
90 {
91         struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
92
93         if (clk->id > data->chip->num_outputs)
94                 return -EINVAL;
95
96         return 0;
97 }
98
99 static 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
127 static 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
157 static 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
164 static 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
176 static 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
229 static 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
237 static const struct clk_ops cdce9xx_clk_ops = {
238         .request = cdce9xx_clk_request,
239         .get_rate = cdce9xx_clk_get_rate,
240         .set_rate = cdce9xx_clk_set_rate,
241 };
242
243 U_BOOT_DRIVER(cdce9xx_clk) = {
244         .name = "cdce9xx-clk",
245         .id = UCLASS_CLK,
246         .of_match = cdce9xx_clk_of_match,
247         .probe = cdce9xx_clk_probe,
248         .priv_auto      = sizeof(struct cdce9xx_clk_data),
249         .ops = &cdce9xx_clk_ops,
250 };