lin
2025-07-30 fcd736bf35fd93b563e9bbf594f2aa7b62028cc9
feat(eth0): add MAE0621A phy support

Signed-off-by: lin <lin@kickpi.com>
4 files modified
1 files added
491 ■■■■■ changed files
longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig 1 ●●●● patch | view | raw | blame | history
longan/kernel/linux-4.9/drivers/net/phy/Kconfig 5 ●●●●● patch | view | raw | blame | history
longan/kernel/linux-4.9/drivers/net/phy/Makefile 1 ●●●● patch | view | raw | blame | history
longan/kernel/linux-4.9/drivers/net/phy/maxio.c 475 ●●●●● patch | view | raw | blame | history
longan/kernel/linux-4.9/drivers/net/phy/phy_device.c 9 ●●●●● patch | view | raw | blame | history
longan/kernel/linux-4.9/arch/arm64/configs/sun50iw10p1smp_a133_android_defconfig
....@@ -269,6 +269,7 @@
269269 CONFIG_NETDEVICES=y
270270 CONFIG_TUN=y
271271 CONFIG_SUNXI_GMAC=y
272
+CONFIG_MAXIO_PHY=y
272273 CONFIG_PPP=y
273274 CONFIG_PPP_BSDCOMP=y
274275 CONFIG_PPP_DEFLATE=y
longan/kernel/linux-4.9/drivers/net/phy/Kconfig
....@@ -298,6 +298,11 @@
298298 ---help---
299299 Supports the Realtek 821x PHY.
300300
301
+config MAXIO_PHY
302
+ tristate "MAXIO PHYs"
303
+ help
304
+ Supports the Maxio MAExxxx PHY.
305
+
301306 config SMSC_PHY
302307 tristate "SMSC PHYs"
303308 ---help---
longan/kernel/linux-4.9/drivers/net/phy/Makefile
....@@ -48,6 +48,7 @@
4848 obj-$(CONFIG_MICROSEMI_PHY) += mscc.o
4949 obj-$(CONFIG_NATIONAL_PHY) += national.o
5050 obj-$(CONFIG_QSEMI_PHY) += qsemi.o
51
+obj-$(CONFIG_MAXIO_PHY) += maxio.o
5152 obj-$(CONFIG_REALTEK_PHY) += realtek.o
5253 obj-$(CONFIG_SMSC_PHY) += smsc.o
5354 obj-$(CONFIG_STE10XP) += ste10Xp.o
longan/kernel/linux-4.9/drivers/net/phy/maxio.c
....@@ -0,0 +1,475 @@
1
+#include <linux/bitops.h>
2
+#include <linux/phy.h>
3
+#include <linux/module.h>
4
+#include <linux/delay.h>
5
+#include <linux/device.h>
6
+#include <linux/mdio.h>
7
+#include <linux/timer.h>
8
+#include <linux/netdevice.h>
9
+
10
+
11
+
12
+#define MAXIO_PHY_VER "v1.8.1.4"
13
+#define MAXIO_PAGE_SELECT 0x1f
14
+#define MAXIO_MAE0621A_WORK_STATUS_REG 0x1d
15
+#define MAXIO_MAE0621A_CLK_MODE_REG 0x02
16
+
17
+#define MAXIO_PHYSR_P_A43 (0X1A)
18
+#define MAXIO_PHY_LINK (1<<2)
19
+#define MAXIO_PHY_DUPLEX (1<<3)
20
+#define MAXIO_PHY_SPEED (3<<4)
21
+#define MAXIO_PHY_1000M (0X20)
22
+#define MAXIO_PHY_100M (0X10)
23
+#define MAXIO_PHY_10M (0X00)
24
+
25
+#define AUTONEG_COMPLETED_INT_EN (0x8)
26
+#define LINKOK (0x4)
27
+#define AUTONEG_COMPLETED (0x8)
28
+
29
+#define PHY_READ(a,b) phy_read((a),(b))
30
+#define PHY_WRITE(a,b,c) phy_write((a),(b),(c))
31
+
32
+
33
+int maxio_read_paged(struct phy_device *phydev, int page, u32 regnum)
34
+{
35
+ int ret = 0, oldpage;
36
+
37
+ oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
38
+ if (oldpage >= 0) {
39
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, page);
40
+ ret = PHY_READ(phydev, regnum);
41
+ }
42
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, oldpage);
43
+
44
+ return ret;
45
+}
46
+
47
+
48
+int maxio_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
49
+{
50
+ int ret = 0, oldpage;
51
+
52
+ oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
53
+ if (oldpage >= 0) {
54
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, page);
55
+ ret = PHY_WRITE(phydev, regnum, val);
56
+ }
57
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, oldpage);
58
+
59
+ return ret;
60
+}
61
+
62
+
63
+int maxio_adcc_check(struct phy_device *phydev)
64
+{
65
+ int ret = 0;
66
+ int adcvalue;
67
+ u32 regval;
68
+ int i;
69
+
70
+ maxio_write_paged(phydev, 0xd96, 0x2, 0x1fff );
71
+ maxio_write_paged(phydev, 0xd96, 0x2, 0x1000 );
72
+
73
+ for(i = 0; i < 4;i++)
74
+ {
75
+ regval = 0xf908 + i * 0x100;
76
+ maxio_write_paged(phydev, 0xd8f, 0xb, regval );
77
+ adcvalue = maxio_read_paged(phydev, 0xd92, 0xb);
78
+ if(adcvalue & 0x1ff)
79
+ {
80
+ continue;
81
+ }
82
+ else
83
+ {
84
+ ret = -1;
85
+ break;
86
+ }
87
+ }
88
+
89
+ return ret;
90
+}
91
+
92
+
93
+int maxio_self_check(struct phy_device *phydev,int checknum)
94
+{
95
+ int ret = 0;
96
+ int i;
97
+
98
+ for(i = 0;i < checknum; i++)
99
+ {
100
+ ret = maxio_adcc_check(phydev);
101
+ if(0 == ret)
102
+ {
103
+ printk("MAE0621A READY\n");
104
+ break;
105
+ }
106
+ else
107
+ {
108
+ maxio_write_paged(phydev, 0x0, 0x0, 0x1940 );
109
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
110
+ msleep(10);
111
+ maxio_write_paged(phydev, 0x0, 0x0, 0x1140 );
112
+ maxio_write_paged(phydev, 0x0, 0x0, 0x9140 );
113
+ }
114
+ }
115
+
116
+ maxio_write_paged(phydev, 0xd96, 0x2, 0xfff );
117
+ maxio_write_paged(phydev, 0x0, 0x0, 0x9140 );
118
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
119
+
120
+ return ret;
121
+}
122
+
123
+
124
+
125
+
126
+static int maxio_mae0621a_resume(struct phy_device *phydev)
127
+{
128
+ int ret;
129
+
130
+ ret = genphy_resume(phydev);
131
+
132
+ ret |= PHY_WRITE(phydev, MII_BMCR, BMCR_RESET | PHY_READ(phydev, MII_BMCR));
133
+ msleep(20);
134
+
135
+ return ret;
136
+}
137
+
138
+static int maxio_mae0621a_suspend(struct phy_device *phydev)
139
+{
140
+ int ret = 0;
141
+
142
+ ret = genphy_suspend(phydev);
143
+ ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT ,0);
144
+
145
+ return ret;
146
+}
147
+
148
+
149
+
150
+int maxio_restart_aneg(struct phy_device *phydev)
151
+{
152
+ int ctl = PHY_READ(phydev, MII_BMCR);
153
+
154
+ if (ctl < 0)
155
+ return ctl;
156
+
157
+ ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
158
+
159
+
160
+ ctl &= ~BMCR_ISOLATE;
161
+
162
+ return PHY_WRITE(phydev, MII_BMCR, ctl);
163
+}
164
+
165
+static void phy_resolve_aneg_linkmode_maxio(struct phy_device *phydev){
166
+
167
+ int physr_p_a43 = maxio_read_paged(phydev,0xa43,MAXIO_PHYSR_P_A43);
168
+
169
+ if((physr_p_a43&MAXIO_PHY_SPEED)==MAXIO_PHY_1000M){
170
+ phydev->speed=SPEED_1000;
171
+ }else if((physr_p_a43&MAXIO_PHY_SPEED)==MAXIO_PHY_100M){
172
+ phydev->speed=SPEED_100;
173
+ }else if((physr_p_a43&MAXIO_PHY_SPEED)==MAXIO_PHY_10M){
174
+ phydev->speed=SPEED_10;
175
+ }
176
+
177
+ if(physr_p_a43 & MAXIO_PHY_DUPLEX){
178
+ phydev->duplex=DUPLEX_FULL;
179
+ }else{
180
+ phydev->duplex=DUPLEX_HALF;
181
+ }
182
+
183
+ if(phydev->duplex==DUPLEX_FULL){
184
+ int lpa=PHY_READ(phydev,MII_LPA);
185
+ phydev->pause=(lpa&LPA_PAUSE_CAP)?1:0;
186
+ phydev->asym_pause=(lpa&LPA_PAUSE_ASYM)?1:0;
187
+ }
188
+
189
+ }
190
+
191
+void phy_resolve_link_compatibility_maxio(struct phy_device *phydev)
192
+{
193
+ int *paras = (int *)phydev->priv;
194
+ int maxio_an_times = paras[0];
195
+ int link_stable = paras[1];
196
+ int iner, physr, insr;
197
+ iner = maxio_read_paged(phydev, 0xa42, 0x12);
198
+ if(iner&AUTONEG_COMPLETED_INT_EN){
199
+
200
+ physr = maxio_read_paged(phydev, 0xa43, 0x1a);
201
+ if(physr & LINKOK)
202
+ {
203
+ insr = maxio_read_paged(phydev, 0xa43, 0x1d);
204
+ if((insr & AUTONEG_COMPLETED) == 0 && (link_stable == 0)){
205
+ if(maxio_an_times < 4 ){
206
+ maxio_restart_aneg(phydev);
207
+ phydev->link = 0;
208
+ maxio_an_times++;
209
+ }
210
+ else if(maxio_an_times == 4){
211
+ link_stable = 1;
212
+ }
213
+ }
214
+ else if(insr & AUTONEG_COMPLETED){
215
+ maxio_an_times = 0;
216
+ link_stable = 1;
217
+ }
218
+ if (link_stable == 1)
219
+ maxio_an_times = 0;
220
+ }
221
+ else
222
+ link_stable = 0;
223
+ }
224
+ paras[0] = maxio_an_times;
225
+ paras[1] = link_stable;
226
+}
227
+
228
+static int maxio_mae0621a_status(struct phy_device *phydev)
229
+{
230
+
231
+ int err, old_link = phydev->link;
232
+
233
+ err = genphy_update_link(phydev);
234
+ if (err)
235
+ return err;
236
+
237
+
238
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
239
+ return 0;
240
+
241
+ phydev->speed = SPEED_UNKNOWN;
242
+ phydev->duplex = DUPLEX_UNKNOWN;
243
+ phydev->pause = 0;
244
+ phydev->asym_pause = 0;
245
+
246
+ phy_resolve_aneg_linkmode_maxio(phydev);
247
+
248
+ if(phydev->autoneg == AUTONEG_ENABLE)
249
+ phy_resolve_link_compatibility_maxio(phydev);
250
+ return 0;
251
+
252
+}
253
+
254
+static void maxio_mae0621a_remove(struct phy_device *phydev)
255
+{
256
+ printk("maxio driver remove\r\n");
257
+ if(phydev->priv!=NULL){
258
+ kfree(phydev->priv);
259
+ }
260
+ phydev->priv=NULL;
261
+}
262
+
263
+
264
+
265
+
266
+static int maxio_mae0621a_clk_init(struct phy_device *phydev)
267
+{
268
+ u32 workmode,clkmode,oldpage;
269
+
270
+ oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
271
+ if (oldpage == 0xFFFF) {
272
+ oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
273
+ }
274
+
275
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
276
+ PHY_WRITE(phydev, MII_BMCR, 0x9140);
277
+
278
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0xa43);
279
+ workmode = PHY_READ(phydev, MAXIO_MAE0621A_WORK_STATUS_REG);
280
+
281
+ PHY_WRITE( phydev, MAXIO_PAGE_SELECT, 0xd92 );
282
+ clkmode = PHY_READ( phydev, MAXIO_MAE0621A_CLK_MODE_REG );
283
+
284
+ if (0 == (workmode&BIT(5))) {
285
+ if (0 == (clkmode&BIT(8))) {
286
+ PHY_WRITE(phydev, 0x02, clkmode | BIT(8));
287
+ printk("****maxio_mae0621a_clk_init**clkmode**0x210a: 0x%x\n", phydev->phy_id);
288
+ } else {
289
+ printk("****maxio_mae0621a_clk_init**clkmode**0x200a: 0x%x\n", phydev->phy_id);
290
+ PHY_WRITE(phydev, 0x02, clkmode &(~ BIT(8)));
291
+ }
292
+ }
293
+
294
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
295
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, oldpage);
296
+ msleep(1000);
297
+
298
+ return 0;
299
+}
300
+
301
+static int maxio_mae0621a_config_init(struct phy_device *phydev)
302
+{
303
+ int ret = 0;
304
+
305
+ printk("MAXIO_PHY_VER: %s \n",MAXIO_PHY_VER);
306
+
307
+
308
+ maxio_mae0621a_clk_init(phydev);
309
+
310
+ ret |= maxio_write_paged(phydev, 0xda0, 0x10, 0xc13);
311
+ ret |= maxio_write_paged(phydev, 0x0, 0xd, 0x7);
312
+ ret |= maxio_write_paged(phydev, 0x0, 0xe, 0x3c);
313
+ ret |= maxio_write_paged(phydev, 0x0, 0xd, 0x4007);
314
+ ret |= maxio_write_paged(phydev, 0x0, 0xe, 0x0);
315
+ ret |= maxio_write_paged(phydev, 0xd96, 0x13, 0x7bc);
316
+ ret |= maxio_write_paged(phydev, 0xd8f, 0x8, 0x2500);
317
+ ret |= maxio_write_paged(phydev, 0xd90, 0x2, 0x1555);
318
+ ret |= maxio_write_paged(phydev, 0xd90, 0x5, 0x2b15);
319
+ ret |= maxio_write_paged(phydev, 0xd92, 0x14, 0xa);
320
+ ret |= maxio_write_paged(phydev, 0xd91, 0x7, 0x5b00);
321
+ ret |= maxio_write_paged(phydev, 0xd8f, 0x0, 0x300);
322
+ ret |= maxio_write_paged(phydev, 0xd92, 0xa, 0x8506);
323
+ ret |= maxio_write_paged(phydev, 0xd91, 0x6, 0x6870);
324
+ ret |= maxio_write_paged(phydev, 0xd91, 0x1, 0x940);
325
+ ret |= maxio_write_paged(phydev, 0xda0, 0x13, 0x1303);
326
+ ret |= maxio_write_paged(phydev, 0xd97, 0xc, 0x177);
327
+ ret |= maxio_write_paged(phydev, 0xd97, 0xb, 0x9a9);
328
+ ret |= maxio_write_paged(phydev, 0xa42, 0x12, 0x28);
329
+ ret |= maxio_write_paged(phydev, 0x0, 0x4, 0xde1);
330
+ ret |= maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
331
+
332
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
333
+
334
+
335
+ ret |= maxio_self_check(phydev,50);
336
+ msleep(100);
337
+ return 0;
338
+}
339
+
340
+static int maxio_mae0621a_probe(struct phy_device *phydev)
341
+{
342
+ int *paras;
343
+ paras = kzalloc( 2 * sizeof(int), GFP_KERNEL);
344
+ if(!paras)
345
+ return -ENOMEM;
346
+ phydev->priv = paras;
347
+
348
+
349
+ maxio_mae0621a_clk_init(phydev);
350
+ PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
351
+ mdelay(100);
352
+ return 0;
353
+}
354
+
355
+static int maxio_mae0621aq3ci_probe(struct phy_device *phydev)
356
+{
357
+ int *paras;
358
+ paras = kzalloc( 2 * sizeof(int), GFP_KERNEL);
359
+ if(!paras)
360
+ return -ENOMEM;
361
+ phydev->priv = paras;
362
+ printk("maxio_mae0621aQ3C probe PHY_ID: 0x%x\n", phydev->phy_id);
363
+ return 0;
364
+}
365
+
366
+
367
+static int maxio_mae0621aq3ci_config_init(struct phy_device *phydev)
368
+{
369
+ int ret = 0;
370
+ printk("MAXIO_PHY_VER: %s \n",MAXIO_PHY_VER);
371
+
372
+ ret |= maxio_write_paged(phydev, 0xa43, 0x19, 0x823);
373
+
374
+ ret |= maxio_write_paged(phydev, 0xdab, 0x17, 0xC13);
375
+ ret |= maxio_write_paged(phydev, 0xd96, 0x15, 0xc08a);
376
+ ret |= maxio_write_paged(phydev, 0xda4, 0x12, 0x7bc);
377
+ ret |= maxio_write_paged(phydev, 0xd8f, 0x16, 0x2500);
378
+ ret |= maxio_write_paged(phydev, 0xd90, 0x16, 0x1555);
379
+ ret |= maxio_write_paged(phydev, 0xd92, 0x11, 0x2b15);
380
+ ret |= maxio_write_paged(phydev, 0xd96, 0x16, 0x4010);
381
+ ret |= maxio_write_paged(phydev, 0xda5, 0x11, 0x4a12);
382
+ ret |= maxio_write_paged(phydev, 0xda5, 0x12, 0x4a12);
383
+ ret |= maxio_write_paged(phydev, 0xd99, 0x16, 0xa);
384
+ ret |= maxio_write_paged(phydev, 0xd95, 0x13, 0x5b00);
385
+ ret |= maxio_write_paged(phydev, 0xd8f, 0x10, 0x300);
386
+ ret |= maxio_write_paged(phydev, 0xd98, 0x17, 0x8506);
387
+ ret |= maxio_write_paged(phydev, 0xd95, 0x12, 0x6870);
388
+ ret |= maxio_write_paged(phydev, 0xd93, 0x15, 0x940);
389
+ ret |= maxio_write_paged(phydev, 0xdad, 0x12, 0x1303);
390
+ ret |= maxio_write_paged(phydev, 0xda8, 0x11, 0x177);
391
+ ret |= maxio_write_paged(phydev, 0xda8, 0x10, 0x9a9);
392
+ ret |= maxio_write_paged(phydev, 0xa42, 0x12, 0x28);
393
+ ret |= maxio_write_paged(phydev, 0x0, 0x4, 0xde1);
394
+ ret |= maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
395
+
396
+ ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
397
+
398
+ return 0;
399
+}
400
+
401
+
402
+static int maxio_mae0621aq3ci_resume(struct phy_device *phydev)
403
+{
404
+ int ret = 0;
405
+ ret = genphy_resume(phydev);
406
+ ret |= maxio_write_paged(phydev, 0xdaa, 0x17, 0x1001 );
407
+ ret |= maxio_write_paged(phydev, 0xdab, 0x15, 0x0 );
408
+ ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
409
+
410
+ return ret;
411
+}
412
+
413
+static int maxio_mae0621aq3ci_suspend(struct phy_device *phydev)
414
+{
415
+ int ret = 0;
416
+
417
+ ret = maxio_write_paged(phydev, 0xdaa, 0x17, 0x1011 );
418
+ ret |= maxio_write_paged(phydev, 0xdab, 0x15, 0x5550 );
419
+ ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
420
+
421
+ ret |= genphy_suspend(phydev);
422
+
423
+ ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT ,0);
424
+
425
+ return ret;
426
+}
427
+
428
+
429
+
430
+static struct phy_driver maxio_nc_drvs[] = {
431
+ {
432
+ .phy_id = 0x7b744411,
433
+ .phy_id_mask = 0x7fffffff,
434
+ .name = "MAE0621A-Q2C Gigabit Ethernet",
435
+ .features = PHY_GBIT_FEATURES ,
436
+ .probe = maxio_mae0621a_probe,
437
+ .config_init = maxio_mae0621a_config_init,
438
+ .config_aneg = &genphy_config_aneg,
439
+ .read_status = maxio_mae0621a_status,
440
+ .suspend = maxio_mae0621a_suspend,
441
+ .resume = maxio_mae0621a_resume,
442
+ .remove = maxio_mae0621a_remove,
443
+ },
444
+ {
445
+ .phy_id = 0x7b744412,
446
+ .phy_id_mask = 0x7fffffff,
447
+ .name = "MAE0621A/B-Q3C(I) Gigabit Ethernet",
448
+ .features = PHY_GBIT_FEATURES ,
449
+ .probe = maxio_mae0621aq3ci_probe,
450
+ .config_aneg = &genphy_config_aneg,
451
+ .config_init = maxio_mae0621aq3ci_config_init,
452
+ .read_status = &maxio_mae0621a_status,
453
+ .suspend = maxio_mae0621aq3ci_suspend,
454
+ .resume = maxio_mae0621aq3ci_resume,
455
+ .remove = maxio_mae0621a_remove,
456
+ },
457
+};
458
+
459
+module_phy_driver(maxio_nc_drvs);
460
+static struct mdio_device_id __maybe_unused maxio_nc_tbl[] = {
461
+ { 0x7b744411, 0x7fffffff },
462
+ { 0x7b744412, 0x7fffffff },
463
+ { }
464
+};
465
+
466
+MODULE_DEVICE_TABLE(mdio, maxio_nc_tbl);
467
+
468
+
469
+MODULE_DESCRIPTION("Maxio PHY driver");
470
+MODULE_AUTHOR("Zhao Yang");
471
+MODULE_LICENSE("GPL");
472
+
473
+
474
+
475
+
longan/kernel/linux-4.9/drivers/net/phy/phy_device.c
....@@ -487,6 +487,15 @@
487487 if (is_c45)
488488 return get_phy_c45_ids(bus, addr, phy_id, c45_ids);
489489
490
+#ifdef CONFIG_MAXIO_PHY
491
+ /*
492
+ *An MDIO connects to multiple PHYs requiring write before read.
493
+ *This operation does not affect one MDIO connected to a single PHY
494
+ *MII_PHYSID2 is a read-only register and writine to it has no effect
495
+ */
496
+ mdiobus_write(bus,addr,MII_PHYSID2,0);
497
+#endif
498
+
490499 /* Grab the bits from PHYIR1, and put them in the upper half */
491500 phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
492501 if (phy_reg < 0)