blob: 353e226d1ecb622f4b88e1306dab778a61bbec9a [file] [log] [blame]
developer5e9bfbf2023-09-01 13:53:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*************************************************
3 * FILE NAME: air_en8811h_main.c
4 * PURPOSE:
5 * EN8811H PHY Driver for Linux
6 * NOTES:
7 *
8 * Copyright (C) 2023 Airoha Technology Corp.
9 *************************************************/
10
11/* INCLUDE FILE DECLARATIONS
12*/
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/mii.h>
18#include <linux/phy.h>
19#include <linux/delay.h>
20#include <linux/ethtool.h>
21#include <linux/delay.h>
22#include <linux/firmware.h>
23#include <linux/crc32.h>
24#include <linux/debugfs.h>
25#include "air_en8811h_api.h"
26#include "air_en8811h.h"
27
28MODULE_DESCRIPTION("Airoha EN8811H PHY Drivers");
29MODULE_AUTHOR("Airoha");
30MODULE_LICENSE("GPL");
31
32/**************************
33 * GPIO5 <-> BASE_T_LED0,
34 * GPIO4 <-> BASE_T_LED1,
35 * GPIO3 <-> BASE_T_LED2,
36 **************************/
37/* User-defined.B */
38#define AIR_LED_SUPPORT
39#ifdef AIR_LED_SUPPORT
40static const struct air_base_t_led_cfg led_cfg[3] = {
41/********************************************************************
42 *Enable, GPIO, LED Polarity, LED ON, LED Blink
43*********************************************************************/
44 {1, AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, AIR_LED0_ON, AIR_LED0_BLK},
45 {1, AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLK},
46 {1, AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLK},
47};
48static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
49#endif
50/* User-defined.E */
51
52/***********************************************************
53 * F U N C T I O N S
54 ***********************************************************/
55
56
57static int MDIOWriteBuf(struct phy_device *phydev,
58 unsigned long address, const struct firmware *fw)
59{
60 unsigned int write_data, offset;
61 int ret = 0;
62 struct device *dev = phydev_dev(phydev);
63 struct mii_bus *mbus = phydev_mdio_bus(phydev);
64 int addr = phydev_addr(phydev);
65 /* page 4 */
66 ret = air_mii_cl22_write(mbus, addr, 0x1F, 4);
67 if (ret < 0) {
68 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
69 return ret;
70 }
71 /* address increment*/
72 ret = air_mii_cl22_write(mbus, addr, 0x10, 0x8000);
73 if (ret < 0) {
74 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
75 return ret;
76 }
77 ret = air_mii_cl22_write(mbus, addr, 0x11,
78 (u32)((address >> 16) & 0xffff));
79 if (ret < 0) {
80 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
81 return ret;
82 }
83 ret = air_mii_cl22_write(mbus, addr, 0x12, (u32)(address & 0xffff));
84 if (ret < 0) {
85 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
86 return ret;
87 }
88
89 for (offset = 0; offset < fw->size; offset += 4) {
90 write_data = (fw->data[offset + 3] << 8) | fw->data[offset + 2];
91 ret = air_mii_cl22_write(mbus, addr, 0x13, write_data);
92 if (ret < 0) {
93 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
94 return ret;
95 }
96 write_data = (fw->data[offset + 1] << 8) | fw->data[offset];
97 ret = air_mii_cl22_write(mbus, addr, 0x14, write_data);
98 if (ret < 0) {
99 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
100 return ret;
101 }
102 }
103 ret = air_mii_cl22_write(mbus, addr, 0x1F, 0);
104 if (ret < 0) {
105 dev_err(dev, "air_mii_cl22_write, ret: %d\n", ret);
106 return ret;
107 }
108 return 0;
109}
110
111static int en8811h_load_firmware(struct phy_device *phydev)
112{
113 struct device *dev = phydev_dev(phydev);
114 const struct firmware *fw;
115 const char *firmware;
116 int ret = 0;
117 u32 pbus_value = 0;
118#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
119 struct en8811h_priv *priv = phydev->priv;
120#endif
121 ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
122 if (ret < 0)
123 return ret;
124 pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
125 pbus_value |= BIT(11);
126 ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
127 if (ret < 0)
128 return ret;
129 firmware = EN8811H_MD32_DM;
130 ret = request_firmware_direct(&fw, firmware, dev);
131 if (ret < 0) {
132 dev_err(dev,
133 "failed to load firmware %s, ret: %d\n", firmware, ret);
134 return ret;
135 }
136#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
137 priv->dm_crc32 = ~crc32(~0, fw->data, fw->size);
138#endif
139 dev_info(dev, "%s: crc32=0x%x\n",
140 firmware, ~crc32(~0, fw->data, fw->size));
141 /* Download DM */
142 ret = MDIOWriteBuf(phydev, 0x00000000, fw);
143 release_firmware(fw);
144 if (ret < 0) {
145 dev_err(dev,
146 "MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
147 return ret;
148 }
149
150 firmware = EN8811H_MD32_DSP;
151 ret = request_firmware_direct(&fw, firmware, dev);
152 if (ret < 0) {
153 dev_info(dev,
154 "failed to load firmware %s, ret: %d\n", firmware, ret);
155 return ret;
156 }
157#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
158 priv->dsp_crc32 = ~crc32(~0, fw->data, fw->size);
159#endif
160 dev_info(dev, "%s: crc32=0x%x\n",
161 firmware, ~crc32(~0, fw->data, fw->size));
162 /* Download PM */
163 ret = MDIOWriteBuf(phydev, 0x00100000, fw);
164 release_firmware(fw);
165 if (ret < 0) {
166 dev_err(dev,
167 "MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
168 return ret;
169 }
170 pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
171 pbus_value &= ~BIT(11);
172 ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
173 if (ret < 0)
174 return ret;
175 ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
176 if (ret < 0)
177 return ret;
178 return 0;
179}
180
181#ifdef AIR_LED_SUPPORT
182static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity,
183 int polar, u16 on_evt, u16 blk_evt)
184{
185 int ret = 0;
186
187 if (polar == AIR_ACTIVE_HIGH)
188 on_evt |= LED_ON_POL;
189 else
190 on_evt &= ~LED_ON_POL;
191
192 ret = air_mii_cl45_write(phydev, 0x1f,
193 LED_ON_CTRL(entity), on_evt | LED_ON_EN);
194 if (ret < 0)
195 return ret;
196 ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
197 if (ret < 0)
198 return ret;
199 return 0;
200}
201
202static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
203{
204 u16 cl45_data;
205 int err = 0;
206 struct device *dev = phydev_dev(phydev);
207
208 cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
209 switch (mode) {
210 case AIR_LED_MODE_DISABLE:
211 cl45_data &= ~LED_BCR_EXT_CTRL;
212 cl45_data &= ~LED_BCR_MODE_MASK;
213 cl45_data |= LED_BCR_MODE_DISABLE;
214 break;
215 case AIR_LED_MODE_USER_DEFINE:
216 cl45_data |= LED_BCR_EXT_CTRL;
217 cl45_data |= LED_BCR_CLK_EN;
218 break;
219 default:
220 dev_err(dev, "LED mode%d is not supported!\n", mode);
221 return -EINVAL;
222 }
223 err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
224 if (err < 0)
225 return err;
226 return 0;
227}
228
229static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
230{
231 u16 cl45_data = 0;
232 int err;
233
234 cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
235 if (state == 1)
236 cl45_data |= LED_ON_EN;
237 else
238 cl45_data &= ~LED_ON_EN;
239
240 err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
241 if (err < 0)
242 return err;
243 return 0;
244}
245
246static int en8811h_led_init(struct phy_device *phydev)
247{
248
249 unsigned long led_gpio = 0, reg_value = 0;
250 u16 cl45_data = led_dur;
251 int ret = 0, id;
252 struct device *dev = phydev_dev(phydev);
253
254 ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
255 if (ret < 0)
256 return ret;
257 cl45_data >>= 1;
258 ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
259 if (ret < 0)
260 return ret;
261 ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
262 if (ret != 0) {
263 dev_err(dev, "led_set_mode fail(ret:%d)!\n", ret);
264 return ret;
265 }
266 for (id = 0; id < EN8811H_LED_COUNT; id++) {
267 /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
268 if (led_cfg[id].gpio != (id + (AIR_LED0_GPIO5 - (2 * id)))) {
269 dev_err(dev, "LED%d uses incorrect GPIO%d !\n",
270 id, led_cfg[id].gpio);
271 return -EINVAL;
272 }
273 ret = airoha_led_set_state(phydev, id, led_cfg[id].en);
274 if (ret != 0) {
275 dev_err(dev, "led_set_state fail(ret:%d)!\n", ret);
276 return ret;
277 }
278 if (led_cfg[id].en == 1) {
279 led_gpio |= BIT(led_cfg[id].gpio);
280 ret = airoha_led_set_usr_def(phydev, id,
281 led_cfg[id].pol, led_cfg[id].on_cfg,
282 led_cfg[id].blk_cfg);
283 if (ret != 0) {
284 dev_err(dev, "led_set_usr_def fail!\n");
285 return ret;
286 }
287 }
288 }
289 reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
290 ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
291 if (ret < 0)
292 return ret;
293 dev_info(dev, "LED initialize OK !\n");
294 return 0;
295}
296#endif /* AIR_LED_SUPPORT */
297#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
298static int en8811h_get_features(struct phy_device *phydev)
299{
300 int ret;
301 struct device *dev = phydev_dev(phydev);
302
303 dev_dbg(dev, "%s()\n", __func__);
304 ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
305 if (ret < 0)
306 return ret;
307 ret = genphy_read_abilities(phydev);
308 if (ret)
309 return ret;
310 /* EN8811H supports 100M/1G/2.5G speed. */
311 linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
312 phydev->supported);
313 linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
314 phydev->supported);
315 linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
316 phydev->supported);
317 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
318 phydev->supported);
319 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
320 phydev->supported);
321 linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
322 phydev->supported);
323 return 0;
324}
325#endif
326
327static int en8811h_probe(struct phy_device *phydev)
328{
329 int ret = 0;
330 int reg_value, pid1 = 0, pid2 = 0;
331 u32 retry, pbus_value = 0;
332 struct device *dev = phydev_dev(phydev);
333 struct mii_bus *mbus = phydev_mdio_bus(phydev);
334 int addr = phydev_addr(phydev);
335
336#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
337 struct en8811h_priv *priv;
338
339 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
340 if (!priv)
341 return -ENOMEM;
342 phydev->priv = priv;
343#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
344 ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
345 if (ret < 0)
346 return ret;
347 pid1 = air_mii_cl22_read(mbus, addr, MII_PHYSID1);
348 if (pid1 < 0)
349 return pid1;
350 pid2 = air_mii_cl22_read(mbus, addr, MII_PHYSID2);
351 if (pid2 < 0)
352 return pid2;
353 dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
354 if ((pid1 != EN8811H_PHY_ID1) || (pid2 != EN8811H_PHY_ID2)) {
355 dev_err(dev, "EN8811H dose not exist !\n");
356 return -ENODEV;
357 }
358 pbus_value = air_buckpbus_reg_read(phydev, 0xcf914);
359 dev_info(dev, "Bootmode: %s\n",
360 (GET_BIT(pbus_value, 24) ? "Flash" : "Download Code"));
361
362 ret = en8811h_load_firmware(phydev);
363 if (ret < 0) {
364 dev_err(dev, "EN8811H load firmware fail.\n");
365 return ret;
366 }
367#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
368 ret = airphy_debugfs_init(phydev);
369 if (ret < 0) {
370 dev_err(dev, "air_debug_procfs_init fail. (ret=%d)\n", ret);
371 air_debugfs_remove(phydev);
372 kfree(priv);
373 return ret;
374 }
375#endif /* CONFIG_AIROHA_EN8811H_PHY_DEBUGFS */
376 retry = MAX_RETRY;
377 do {
378 mdelay(300);
379 reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
380 if (reg_value == EN8811H_PHY_READY) {
381 dev_info(dev, "EN8811H PHY ready!\n");
382 break;
383 }
384 retry--;
385 } while (retry);
386 if (retry == 0) {
387 dev_err(dev, "MD32 FW is not ready.(Status 0x%x)\n", reg_value);
388 pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
389 dev_err(dev,
390 "Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
391 dev_err(dev,
392 "EN8811H initialize fail!\n");
393 return 0;
394 }
395 /* Mode selection*/
396 dev_info(dev, "EN8811H Mode 1 !\n");
397 ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
398 if (ret < 0)
399 return ret;
400 ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
401 if (ret < 0)
402 return ret;
403 ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
404 if (ret < 0)
405 return ret;
406 ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
407 if (ret < 0)
408 return ret;
409 /* Serdes polarity */
410 pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
411 pbus_value &= ~0x3;
412 pbus_value |= (EN8811H_RX_POL_NORMAL | EN8811H_TX_POL_NORMAL);
413 ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
414 if (ret < 0)
415 return ret;
416 pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
417 dev_info(dev, "Tx, Rx Polarity : %08x\n", pbus_value);
418 pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
419 dev_info(dev, "MD32 FW Version : %08x\n", pbus_value);
420#if defined(AIR_LED_SUPPORT)
421 ret = en8811h_led_init(phydev);
422 if (ret < 0) {
423 dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
424 return ret;
425 }
426#endif
427 dev_info(dev, "EN8811H initialize OK! (%s)\n", EN8811H_DRIVER_VERSION);
428 return 0;
429}
430void en8811h_remove(struct phy_device *phydev)
431{
432#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
433 struct en8811h_priv *priv = phydev->priv;
434 struct device *dev = phydev_dev(phydev);
435
436 dev_dbg(dev, "%s: start\n", __func__);
437 if (priv) {
438 dev_info(dev, "%s: air_debugfs_remove\n", __func__);
439 air_debugfs_remove(phydev);
440 kfree(priv);
441 }
442#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
443}
444
445static struct phy_driver en8811h_driver[] = {
446{
447 .phy_id = EN8811H_PHY_ID,
448 .name = "Airoha EN8811H",
449 .phy_id_mask = 0x0ffffff0,
450 .probe = en8811h_probe,
451 .remove = en8811h_remove,
452#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
453 .get_features = en8811h_get_features,
454 .read_mmd = air_mii_cl45_read,
455 .write_mmd = air_mii_cl45_write,
456#endif
457} };
458
459int __init en8811h_phy_driver_register(void)
460{
461 int ret;
462#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
463 ret = phy_driver_register(en8811h_driver);
464#else
465 ret = phy_driver_register(en8811h_driver, THIS_MODULE);
466#endif
467 if (!ret)
468 return 0;
469
470 phy_driver_unregister(en8811h_driver);
471 return ret;
472}
473
474void __exit en8811h_phy_driver_unregister(void)
475{
476 phy_driver_unregister(en8811h_driver);
477}
478
479module_init(en8811h_phy_driver_register);
480module_exit(en8811h_phy_driver_unregister);