blob: 50a7f4986ce0a9794f0b45b2008ee9de03f6045a [file] [log] [blame]
developer3c9c74d2023-09-11 11:36:12 +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;
developer3c9c74d2023-09-11 11:36:12 +0800118 struct en8811h_priv *priv = phydev->priv;
developer722ab5f2024-02-22 11:01:46 +0800119
120 ret = air_buckpbus_reg_write(phydev,
121 0x0f0018, 0x0);
developer3c9c74d2023-09-11 11:36:12 +0800122 if (ret < 0)
123 return ret;
124 pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
125 pbus_value |= BIT(11);
developer722ab5f2024-02-22 11:01:46 +0800126 ret = air_buckpbus_reg_write(phydev,
127 0x800000, pbus_value);
developer3c9c74d2023-09-11 11:36:12 +0800128 if (ret < 0)
129 return ret;
130 firmware = EN8811H_MD32_DM;
131 ret = request_firmware_direct(&fw, firmware, dev);
132 if (ret < 0) {
133 dev_err(dev,
134 "failed to load firmware %s, ret: %d\n", firmware, ret);
135 return ret;
136 }
developer3c9c74d2023-09-11 11:36:12 +0800137 priv->dm_crc32 = ~crc32(~0, fw->data, fw->size);
developer3c9c74d2023-09-11 11:36:12 +0800138 dev_info(dev, "%s: crc32=0x%x\n",
139 firmware, ~crc32(~0, fw->data, fw->size));
140 /* Download DM */
141 ret = MDIOWriteBuf(phydev, 0x00000000, fw);
142 release_firmware(fw);
143 if (ret < 0) {
144 dev_err(dev,
145 "MDIOWriteBuf 0x00000000 fail, ret: %d\n", ret);
146 return ret;
147 }
148
149 firmware = EN8811H_MD32_DSP;
150 ret = request_firmware_direct(&fw, firmware, dev);
151 if (ret < 0) {
152 dev_info(dev,
153 "failed to load firmware %s, ret: %d\n", firmware, ret);
154 return ret;
155 }
developer3c9c74d2023-09-11 11:36:12 +0800156 priv->dsp_crc32 = ~crc32(~0, fw->data, fw->size);
developer3c9c74d2023-09-11 11:36:12 +0800157 dev_info(dev, "%s: crc32=0x%x\n",
158 firmware, ~crc32(~0, fw->data, fw->size));
159 /* Download PM */
160 ret = MDIOWriteBuf(phydev, 0x00100000, fw);
161 release_firmware(fw);
162 if (ret < 0) {
163 dev_err(dev,
164 "MDIOWriteBuf 0x00100000 fail , ret: %d\n", ret);
165 return ret;
166 }
167 pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
168 pbus_value &= ~BIT(11);
169 ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
170 if (ret < 0)
171 return ret;
172 ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
173 if (ret < 0)
174 return ret;
175 return 0;
176}
177
178#ifdef AIR_LED_SUPPORT
179static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity,
180 int polar, u16 on_evt, u16 blk_evt)
181{
182 int ret = 0;
183
184 if (polar == AIR_ACTIVE_HIGH)
185 on_evt |= LED_ON_POL;
186 else
187 on_evt &= ~LED_ON_POL;
188
189 ret = air_mii_cl45_write(phydev, 0x1f,
190 LED_ON_CTRL(entity), on_evt | LED_ON_EN);
191 if (ret < 0)
192 return ret;
193 ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
194 if (ret < 0)
195 return ret;
196 return 0;
197}
198
199static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
200{
201 u16 cl45_data;
202 int err = 0;
203 struct device *dev = phydev_dev(phydev);
204
205 cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
206 switch (mode) {
207 case AIR_LED_MODE_DISABLE:
208 cl45_data &= ~LED_BCR_EXT_CTRL;
209 cl45_data &= ~LED_BCR_MODE_MASK;
210 cl45_data |= LED_BCR_MODE_DISABLE;
211 break;
212 case AIR_LED_MODE_USER_DEFINE:
213 cl45_data |= LED_BCR_EXT_CTRL;
214 cl45_data |= LED_BCR_CLK_EN;
215 break;
216 default:
217 dev_err(dev, "LED mode%d is not supported!\n", mode);
218 return -EINVAL;
219 }
220 err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
221 if (err < 0)
222 return err;
223 return 0;
224}
225
226static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
227{
228 u16 cl45_data = 0;
229 int err;
230
231 cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
232 if (state == 1)
233 cl45_data |= LED_ON_EN;
234 else
235 cl45_data &= ~LED_ON_EN;
236
237 err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
238 if (err < 0)
239 return err;
240 return 0;
241}
242
243static int en8811h_led_init(struct phy_device *phydev)
244{
245
246 unsigned long led_gpio = 0, reg_value = 0;
247 u16 cl45_data = led_dur;
248 int ret = 0, id;
249 struct device *dev = phydev_dev(phydev);
250
251 ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
252 if (ret < 0)
253 return ret;
254 cl45_data >>= 1;
255 ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
256 if (ret < 0)
257 return ret;
258 ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
259 if (ret != 0) {
260 dev_err(dev, "led_set_mode fail(ret:%d)!\n", ret);
261 return ret;
262 }
263 for (id = 0; id < EN8811H_LED_COUNT; id++) {
264 /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
265 if (led_cfg[id].gpio != (id + (AIR_LED0_GPIO5 - (2 * id)))) {
266 dev_err(dev, "LED%d uses incorrect GPIO%d !\n",
267 id, led_cfg[id].gpio);
268 return -EINVAL;
269 }
270 ret = airoha_led_set_state(phydev, id, led_cfg[id].en);
271 if (ret != 0) {
272 dev_err(dev, "led_set_state fail(ret:%d)!\n", ret);
273 return ret;
274 }
275 if (led_cfg[id].en == 1) {
276 led_gpio |= BIT(led_cfg[id].gpio);
277 ret = airoha_led_set_usr_def(phydev, id,
278 led_cfg[id].pol, led_cfg[id].on_cfg,
279 led_cfg[id].blk_cfg);
280 if (ret != 0) {
281 dev_err(dev, "led_set_usr_def fail!\n");
282 return ret;
283 }
284 }
285 }
286 reg_value = air_buckpbus_reg_read(phydev, 0xcf8b8) | led_gpio;
287 ret = air_buckpbus_reg_write(phydev, 0xcf8b8, reg_value);
288 if (ret < 0)
289 return ret;
290 dev_info(dev, "LED initialize OK !\n");
291 return 0;
292}
293#endif /* AIR_LED_SUPPORT */
294#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
295static int en8811h_get_features(struct phy_device *phydev)
296{
297 int ret;
298 struct device *dev = phydev_dev(phydev);
299
300 dev_dbg(dev, "%s()\n", __func__);
301 ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
302 if (ret < 0)
303 return ret;
304 ret = genphy_read_abilities(phydev);
305 if (ret)
306 return ret;
307 /* EN8811H supports 100M/1G/2.5G speed. */
308 linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
309 phydev->supported);
310 linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
311 phydev->supported);
312 linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
313 phydev->supported);
314 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
315 phydev->supported);
316 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
317 phydev->supported);
318 linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
319 phydev->supported);
320 return 0;
321}
322#endif
323
324static int en8811h_probe(struct phy_device *phydev)
325{
326 int ret = 0;
327 int reg_value, pid1 = 0, pid2 = 0;
328 u32 retry, pbus_value = 0;
329 struct device *dev = phydev_dev(phydev);
330 struct mii_bus *mbus = phydev_mdio_bus(phydev);
331 int addr = phydev_addr(phydev);
332
developer3c9c74d2023-09-11 11:36:12 +0800333 struct en8811h_priv *priv;
334
335 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
336 if (!priv)
337 return -ENOMEM;
338 phydev->priv = priv;
developer3c9c74d2023-09-11 11:36:12 +0800339 ret = air_pbus_reg_write(phydev, 0xcf928, 0x0);
340 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800341 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800342 pid1 = air_mii_cl22_read(mbus, addr, MII_PHYSID1);
developer3c9c74d2023-09-11 11:36:12 +0800343 pid2 = air_mii_cl22_read(mbus, addr, MII_PHYSID2);
developer3c9c74d2023-09-11 11:36:12 +0800344 dev_info(dev, "PHY = %x - %x\n", pid1, pid2);
345 if ((pid1 != EN8811H_PHY_ID1) || (pid2 != EN8811H_PHY_ID2)) {
developer722ab5f2024-02-22 11:01:46 +0800346 dev_err(dev, "EN8811H dose not exist!!\n");
347 kfree(priv);
developer3c9c74d2023-09-11 11:36:12 +0800348 return -ENODEV;
349 }
350 pbus_value = air_buckpbus_reg_read(phydev, 0xcf914);
351 dev_info(dev, "Bootmode: %s\n",
352 (GET_BIT(pbus_value, 24) ? "Flash" : "Download Code"));
353
354 ret = en8811h_load_firmware(phydev);
355 if (ret < 0) {
356 dev_err(dev, "EN8811H load firmware fail.\n");
developer722ab5f2024-02-22 11:01:46 +0800357 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800358 }
359#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
360 ret = airphy_debugfs_init(phydev);
361 if (ret < 0) {
362 dev_err(dev, "air_debug_procfs_init fail. (ret=%d)\n", ret);
developer722ab5f2024-02-22 11:01:46 +0800363 airphy_debugfs_remove(phydev);
364 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800365 }
366#endif /* CONFIG_AIROHA_EN8811H_PHY_DEBUGFS */
367 retry = MAX_RETRY;
368 do {
369 mdelay(300);
370 reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
371 if (reg_value == EN8811H_PHY_READY) {
372 dev_info(dev, "EN8811H PHY ready!\n");
373 break;
374 }
375 retry--;
376 } while (retry);
377 if (retry == 0) {
378 dev_err(dev, "MD32 FW is not ready.(Status 0x%x)\n", reg_value);
379 pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
380 dev_err(dev,
381 "Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
382 dev_err(dev,
383 "EN8811H initialize fail!\n");
developer722ab5f2024-02-22 11:01:46 +0800384 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800385 }
386 /* Mode selection*/
387 dev_info(dev, "EN8811H Mode 1 !\n");
388 ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
389 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800390 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800391 ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
392 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800393 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800394 ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
395 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800396 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800397 ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
398 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800399 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800400 /* Serdes polarity */
developer722ab5f2024-02-22 11:01:46 +0800401 ret = en8811h_of_init(phydev);
402 if (ret < 0)
403 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800404 pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
405 pbus_value &= ~0x3;
developer722ab5f2024-02-22 11:01:46 +0800406#if defined(CONFIG_OF)
407 pbus_value |= priv->pol;
408#else
developer3c9c74d2023-09-11 11:36:12 +0800409 pbus_value |= (EN8811H_RX_POL_NORMAL | EN8811H_TX_POL_NORMAL);
developer722ab5f2024-02-22 11:01:46 +0800410#endif
developer3c9c74d2023-09-11 11:36:12 +0800411 ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
412 if (ret < 0)
developer722ab5f2024-02-22 11:01:46 +0800413 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800414 pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
415 dev_info(dev, "Tx, Rx Polarity : %08x\n", pbus_value);
416 pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
417 dev_info(dev, "MD32 FW Version : %08x\n", pbus_value);
418#if defined(AIR_LED_SUPPORT)
419 ret = en8811h_led_init(phydev);
420 if (ret < 0) {
421 dev_err(dev, "en8811h_led_init fail. (ret=%d)\n", ret);
developer722ab5f2024-02-22 11:01:46 +0800422 goto priv_free;
developer3c9c74d2023-09-11 11:36:12 +0800423 }
424#endif
425 dev_info(dev, "EN8811H initialize OK! (%s)\n", EN8811H_DRIVER_VERSION);
426 return 0;
developer722ab5f2024-02-22 11:01:46 +0800427priv_free:
428 kfree(priv);
429 return ret;
developer3c9c74d2023-09-11 11:36:12 +0800430}
431void en8811h_remove(struct phy_device *phydev)
432{
developer722ab5f2024-02-22 11:01:46 +0800433
developer3c9c74d2023-09-11 11:36:12 +0800434 struct en8811h_priv *priv = phydev->priv;
435 struct device *dev = phydev_dev(phydev);
436
437 dev_dbg(dev, "%s: start\n", __func__);
438 if (priv) {
developer722ab5f2024-02-22 11:01:46 +0800439 dev_info(dev, "%s: airphy_debugfs_remove\n", __func__);
440#ifdef CONFIG_AIROHA_EN8811H_PHY_DEBUGFS
441 airphy_debugfs_remove(phydev);
442#endif /*CONFIG_AIROHA_EN8811H_PHY_DEBUGFS*/
developer3c9c74d2023-09-11 11:36:12 +0800443 kfree(priv);
444 }
developer3c9c74d2023-09-11 11:36:12 +0800445}
446
447static struct phy_driver en8811h_driver[] = {
448{
449 .phy_id = EN8811H_PHY_ID,
450 .name = "Airoha EN8811H",
451 .phy_id_mask = 0x0ffffff0,
452 .probe = en8811h_probe,
453 .remove = en8811h_remove,
454#if (KERNEL_VERSION(4, 5, 0) < LINUX_VERSION_CODE)
455 .get_features = en8811h_get_features,
developer722ab5f2024-02-22 11:01:46 +0800456 .read_mmd = __air_mii_cl45_read,
457 .write_mmd = __air_mii_cl45_write,
developer3c9c74d2023-09-11 11:36:12 +0800458#endif
459} };
460
461int __init en8811h_phy_driver_register(void)
462{
463 int ret;
464#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
465 ret = phy_driver_register(en8811h_driver);
466#else
467 ret = phy_driver_register(en8811h_driver, THIS_MODULE);
468#endif
469 if (!ret)
470 return 0;
471
472 phy_driver_unregister(en8811h_driver);
473 return ret;
474}
475
476void __exit en8811h_phy_driver_unregister(void)
477{
478 phy_driver_unregister(en8811h_driver);
479}
480
481module_init(en8811h_phy_driver_register);
482module_exit(en8811h_phy_driver_unregister);