1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Renesas Ethernet SERDES device driver |
3 | * |
4 | * Copyright (C) 2022 Renesas Electronics Corporation |
5 | */ |
6 | |
7 | #include <linux/delay.h> |
8 | #include <linux/err.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/of.h> |
12 | #include <linux/phy.h> |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/reset.h> |
16 | |
17 | #define R8A779F0_ETH_SERDES_NUM 3 |
18 | #define R8A779F0_ETH_SERDES_OFFSET 0x0400 |
19 | #define R8A779F0_ETH_SERDES_BANK_SELECT 0x03fc |
20 | #define R8A779F0_ETH_SERDES_TIMEOUT_US 100000 |
21 | #define R8A779F0_ETH_SERDES_NUM_RETRY_LINKUP 3 |
22 | |
23 | struct r8a779f0_eth_serdes_drv_data; |
24 | struct r8a779f0_eth_serdes_channel { |
25 | struct r8a779f0_eth_serdes_drv_data *dd; |
26 | struct phy *phy; |
27 | void __iomem *addr; |
28 | phy_interface_t phy_interface; |
29 | int speed; |
30 | int index; |
31 | }; |
32 | |
33 | struct r8a779f0_eth_serdes_drv_data { |
34 | void __iomem *addr; |
35 | struct platform_device *pdev; |
36 | struct reset_control *reset; |
37 | struct r8a779f0_eth_serdes_channel channel[R8A779F0_ETH_SERDES_NUM]; |
38 | bool initialized; |
39 | }; |
40 | |
41 | /* |
42 | * The datasheet describes initialization procedure without any information |
43 | * about registers' name/bits. So, this is all black magic to initialize |
44 | * the hardware. |
45 | */ |
46 | static void r8a779f0_eth_serdes_write32(void __iomem *addr, u32 offs, u32 bank, u32 data) |
47 | { |
48 | iowrite32(bank, addr + R8A779F0_ETH_SERDES_BANK_SELECT); |
49 | iowrite32(data, addr + offs); |
50 | } |
51 | |
52 | static int |
53 | r8a779f0_eth_serdes_reg_wait(struct r8a779f0_eth_serdes_channel *channel, |
54 | u32 offs, u32 bank, u32 mask, u32 expected) |
55 | { |
56 | int ret; |
57 | u32 val; |
58 | |
59 | iowrite32(bank, channel->addr + R8A779F0_ETH_SERDES_BANK_SELECT); |
60 | |
61 | ret = readl_poll_timeout_atomic(channel->addr + offs, val, |
62 | (val & mask) == expected, |
63 | 1, R8A779F0_ETH_SERDES_TIMEOUT_US); |
64 | if (ret) |
65 | dev_dbg(&channel->phy->dev, |
66 | "%s: index %d, offs %x, bank %x, mask %x, expected %x\n" , |
67 | __func__, channel->index, offs, bank, mask, expected); |
68 | |
69 | return ret; |
70 | } |
71 | |
72 | static int |
73 | r8a779f0_eth_serdes_common_init_ram(struct r8a779f0_eth_serdes_drv_data *dd) |
74 | { |
75 | struct r8a779f0_eth_serdes_channel *channel; |
76 | int i, ret; |
77 | |
78 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) { |
79 | channel = &dd->channel[i]; |
80 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x026c, bank: 0x180, BIT(0), expected: 0x01); |
81 | if (ret) |
82 | return ret; |
83 | } |
84 | |
85 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x026c, bank: 0x180, data: 0x03); |
86 | |
87 | return ret; |
88 | } |
89 | |
90 | static int |
91 | r8a779f0_eth_serdes_common_setting(struct r8a779f0_eth_serdes_channel *channel) |
92 | { |
93 | struct r8a779f0_eth_serdes_drv_data *dd = channel->dd; |
94 | |
95 | switch (channel->phy_interface) { |
96 | case PHY_INTERFACE_MODE_SGMII: |
97 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x0244, bank: 0x180, data: 0x0097); |
98 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x01d0, bank: 0x180, data: 0x0060); |
99 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x01d8, bank: 0x180, data: 0x2200); |
100 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x01d4, bank: 0x180, data: 0x0000); |
101 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x01e0, bank: 0x180, data: 0x003d); |
102 | return 0; |
103 | default: |
104 | return -EOPNOTSUPP; |
105 | } |
106 | } |
107 | |
108 | static int |
109 | r8a779f0_eth_serdes_chan_setting(struct r8a779f0_eth_serdes_channel *channel) |
110 | { |
111 | int ret; |
112 | |
113 | switch (channel->phy_interface) { |
114 | case PHY_INTERFACE_MODE_SGMII: |
115 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x380, data: 0x2000); |
116 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x01c0, bank: 0x180, data: 0x0011); |
117 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0248, bank: 0x180, data: 0x0540); |
118 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0258, bank: 0x180, data: 0x0015); |
119 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0144, bank: 0x180, data: 0x0100); |
120 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x01a0, bank: 0x180, data: 0x0000); |
121 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00d0, bank: 0x180, data: 0x0002); |
122 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0150, bank: 0x180, data: 0x0003); |
123 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00c8, bank: 0x180, data: 0x0100); |
124 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0148, bank: 0x180, data: 0x0100); |
125 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0174, bank: 0x180, data: 0x0000); |
126 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0160, bank: 0x180, data: 0x0007); |
127 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x01ac, bank: 0x180, data: 0x0000); |
128 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00c4, bank: 0x180, data: 0x0310); |
129 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00c8, bank: 0x180, data: 0x0101); |
130 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x00c8, bank: 0x0180, BIT(0), expected: 0); |
131 | if (ret) |
132 | return ret; |
133 | |
134 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0148, bank: 0x180, data: 0x0101); |
135 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x0148, bank: 0x0180, BIT(0), expected: 0); |
136 | if (ret) |
137 | return ret; |
138 | |
139 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00c4, bank: 0x180, data: 0x1310); |
140 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00d8, bank: 0x180, data: 0x1800); |
141 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x00dc, bank: 0x180, data: 0x0000); |
142 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x001c, bank: 0x300, data: 0x0001); |
143 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x380, data: 0x2100); |
144 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x0000, bank: 0x0380, BIT(8), expected: 0); |
145 | if (ret) |
146 | return ret; |
147 | |
148 | if (channel->speed == 1000) |
149 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x1f00, data: 0x0140); |
150 | else if (channel->speed == 100) |
151 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x1f00, data: 0x2100); |
152 | |
153 | /* For AN_ON */ |
154 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0004, bank: 0x1f80, data: 0x0005); |
155 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0028, bank: 0x1f80, data: 0x07a1); |
156 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x1f80, data: 0x0208); |
157 | break; |
158 | default: |
159 | return -EOPNOTSUPP; |
160 | } |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | static int |
166 | r8a779f0_eth_serdes_chan_speed(struct r8a779f0_eth_serdes_channel *channel) |
167 | { |
168 | int ret; |
169 | |
170 | switch (channel->phy_interface) { |
171 | case PHY_INTERFACE_MODE_SGMII: |
172 | /* For AN_ON */ |
173 | if (channel->speed == 1000) |
174 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x1f00, data: 0x1140); |
175 | else if (channel->speed == 100) |
176 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0000, bank: 0x1f00, data: 0x3100); |
177 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x0008, bank: 0x1f80, BIT(0), expected: 1); |
178 | if (ret) |
179 | return ret; |
180 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0008, bank: 0x1f80, data: 0x0000); |
181 | break; |
182 | default: |
183 | return -EOPNOTSUPP; |
184 | } |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | |
190 | static int r8a779f0_eth_serdes_monitor_linkup(struct r8a779f0_eth_serdes_channel *channel) |
191 | { |
192 | int i, ret; |
193 | |
194 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM_RETRY_LINKUP; i++) { |
195 | ret = r8a779f0_eth_serdes_reg_wait(channel, offs: 0x0004, bank: 0x300, |
196 | BIT(2), BIT(2)); |
197 | if (!ret) |
198 | break; |
199 | |
200 | /* restart */ |
201 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0144, bank: 0x180, data: 0x0100); |
202 | udelay(1); |
203 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x0144, bank: 0x180, data: 0x0000); |
204 | } |
205 | |
206 | return ret; |
207 | } |
208 | |
209 | static int r8a779f0_eth_serdes_hw_init(struct r8a779f0_eth_serdes_channel *channel) |
210 | { |
211 | struct r8a779f0_eth_serdes_drv_data *dd = channel->dd; |
212 | int i, ret; |
213 | |
214 | if (dd->initialized) |
215 | return 0; |
216 | |
217 | reset_control_reset(rstc: dd->reset); |
218 | |
219 | usleep_range(min: 1000, max: 2000); |
220 | |
221 | ret = r8a779f0_eth_serdes_common_init_ram(dd); |
222 | if (ret) |
223 | return ret; |
224 | |
225 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) { |
226 | ret = r8a779f0_eth_serdes_reg_wait(channel: &dd->channel[i], offs: 0x0000, |
227 | bank: 0x300, BIT(15), expected: 0); |
228 | if (ret) |
229 | return ret; |
230 | } |
231 | |
232 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) |
233 | r8a779f0_eth_serdes_write32(addr: dd->channel[i].addr, offs: 0x03d4, bank: 0x380, data: 0x0443); |
234 | |
235 | ret = r8a779f0_eth_serdes_common_setting(channel); |
236 | if (ret) |
237 | return ret; |
238 | |
239 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) |
240 | r8a779f0_eth_serdes_write32(addr: dd->channel[i].addr, offs: 0x03d0, bank: 0x380, data: 0x0001); |
241 | |
242 | |
243 | r8a779f0_eth_serdes_write32(addr: dd->addr, offs: 0x0000, bank: 0x380, data: 0x8000); |
244 | |
245 | ret = r8a779f0_eth_serdes_common_init_ram(dd); |
246 | if (ret) |
247 | return ret; |
248 | |
249 | return r8a779f0_eth_serdes_reg_wait(channel: &dd->channel[0], offs: 0x0000, bank: 0x380, BIT(15), expected: 0); |
250 | } |
251 | |
252 | static int r8a779f0_eth_serdes_init(struct phy *p) |
253 | { |
254 | struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(phy: p); |
255 | int ret; |
256 | |
257 | ret = r8a779f0_eth_serdes_hw_init(channel); |
258 | if (!ret) |
259 | channel->dd->initialized = true; |
260 | |
261 | return ret; |
262 | } |
263 | |
264 | static int r8a779f0_eth_serdes_exit(struct phy *p) |
265 | { |
266 | struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(phy: p); |
267 | |
268 | channel->dd->initialized = false; |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static int r8a779f0_eth_serdes_hw_init_late(struct r8a779f0_eth_serdes_channel |
274 | *channel) |
275 | { |
276 | int ret; |
277 | |
278 | ret = r8a779f0_eth_serdes_chan_setting(channel); |
279 | if (ret) |
280 | return ret; |
281 | |
282 | ret = r8a779f0_eth_serdes_chan_speed(channel); |
283 | if (ret) |
284 | return ret; |
285 | |
286 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x03c0, bank: 0x380, data: 0x0000); |
287 | |
288 | r8a779f0_eth_serdes_write32(addr: channel->addr, offs: 0x03d0, bank: 0x380, data: 0x0000); |
289 | |
290 | return r8a779f0_eth_serdes_monitor_linkup(channel); |
291 | } |
292 | |
293 | static int r8a779f0_eth_serdes_power_on(struct phy *p) |
294 | { |
295 | struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(phy: p); |
296 | |
297 | return r8a779f0_eth_serdes_hw_init_late(channel); |
298 | } |
299 | |
300 | static int r8a779f0_eth_serdes_set_mode(struct phy *p, enum phy_mode mode, |
301 | int submode) |
302 | { |
303 | struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(phy: p); |
304 | |
305 | if (mode != PHY_MODE_ETHERNET) |
306 | return -EOPNOTSUPP; |
307 | |
308 | switch (submode) { |
309 | case PHY_INTERFACE_MODE_GMII: |
310 | case PHY_INTERFACE_MODE_SGMII: |
311 | case PHY_INTERFACE_MODE_USXGMII: |
312 | channel->phy_interface = submode; |
313 | return 0; |
314 | default: |
315 | return -EOPNOTSUPP; |
316 | } |
317 | } |
318 | |
319 | static int r8a779f0_eth_serdes_set_speed(struct phy *p, int speed) |
320 | { |
321 | struct r8a779f0_eth_serdes_channel *channel = phy_get_drvdata(phy: p); |
322 | |
323 | channel->speed = speed; |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | static const struct phy_ops r8a779f0_eth_serdes_ops = { |
329 | .init = r8a779f0_eth_serdes_init, |
330 | .exit = r8a779f0_eth_serdes_exit, |
331 | .power_on = r8a779f0_eth_serdes_power_on, |
332 | .set_mode = r8a779f0_eth_serdes_set_mode, |
333 | .set_speed = r8a779f0_eth_serdes_set_speed, |
334 | }; |
335 | |
336 | static struct phy *r8a779f0_eth_serdes_xlate(struct device *dev, |
337 | const struct of_phandle_args *args) |
338 | { |
339 | struct r8a779f0_eth_serdes_drv_data *dd = dev_get_drvdata(dev); |
340 | |
341 | if (args->args[0] >= R8A779F0_ETH_SERDES_NUM) |
342 | return ERR_PTR(error: -ENODEV); |
343 | |
344 | return dd->channel[args->args[0]].phy; |
345 | } |
346 | |
347 | static const struct of_device_id r8a779f0_eth_serdes_of_table[] = { |
348 | { .compatible = "renesas,r8a779f0-ether-serdes" , }, |
349 | { } |
350 | }; |
351 | MODULE_DEVICE_TABLE(of, r8a779f0_eth_serdes_of_table); |
352 | |
353 | static int r8a779f0_eth_serdes_probe(struct platform_device *pdev) |
354 | { |
355 | struct r8a779f0_eth_serdes_drv_data *dd; |
356 | struct phy_provider *provider; |
357 | int i; |
358 | |
359 | dd = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dd), GFP_KERNEL); |
360 | if (!dd) |
361 | return -ENOMEM; |
362 | |
363 | platform_set_drvdata(pdev, data: dd); |
364 | dd->pdev = pdev; |
365 | dd->addr = devm_platform_ioremap_resource(pdev, index: 0); |
366 | if (IS_ERR(ptr: dd->addr)) |
367 | return PTR_ERR(ptr: dd->addr); |
368 | |
369 | dd->reset = devm_reset_control_get(dev: &pdev->dev, NULL); |
370 | if (IS_ERR(ptr: dd->reset)) |
371 | return PTR_ERR(ptr: dd->reset); |
372 | |
373 | for (i = 0; i < R8A779F0_ETH_SERDES_NUM; i++) { |
374 | struct r8a779f0_eth_serdes_channel *channel = &dd->channel[i]; |
375 | |
376 | channel->phy = devm_phy_create(dev: &pdev->dev, NULL, |
377 | ops: &r8a779f0_eth_serdes_ops); |
378 | if (IS_ERR(ptr: channel->phy)) |
379 | return PTR_ERR(ptr: channel->phy); |
380 | channel->addr = dd->addr + R8A779F0_ETH_SERDES_OFFSET * i; |
381 | channel->dd = dd; |
382 | channel->index = i; |
383 | phy_set_drvdata(phy: channel->phy, data: channel); |
384 | } |
385 | |
386 | provider = devm_of_phy_provider_register(&pdev->dev, |
387 | r8a779f0_eth_serdes_xlate); |
388 | if (IS_ERR(ptr: provider)) |
389 | return PTR_ERR(ptr: provider); |
390 | |
391 | pm_runtime_enable(dev: &pdev->dev); |
392 | pm_runtime_get_sync(dev: &pdev->dev); |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | static void r8a779f0_eth_serdes_remove(struct platform_device *pdev) |
398 | { |
399 | pm_runtime_put(dev: &pdev->dev); |
400 | pm_runtime_disable(dev: &pdev->dev); |
401 | |
402 | platform_set_drvdata(pdev, NULL); |
403 | } |
404 | |
405 | static struct platform_driver r8a779f0_eth_serdes_driver_platform = { |
406 | .probe = r8a779f0_eth_serdes_probe, |
407 | .remove_new = r8a779f0_eth_serdes_remove, |
408 | .driver = { |
409 | .name = "r8a779f0_eth_serdes" , |
410 | .of_match_table = r8a779f0_eth_serdes_of_table, |
411 | } |
412 | }; |
413 | module_platform_driver(r8a779f0_eth_serdes_driver_platform); |
414 | MODULE_AUTHOR("Yoshihiro Shimoda" ); |
415 | MODULE_DESCRIPTION("Renesas Ethernet SERDES device driver" ); |
416 | MODULE_LICENSE("GPL" ); |
417 | |