@@ -23,29 +23,32 @@ struct phy_mii_dev_config {
23
23
bool no_reset ;
24
24
bool fixed ;
25
25
int fixed_speed ;
26
- const struct device * const mdio ;
26
+ const struct device * const mdio ;
27
27
};
28
28
29
29
struct phy_mii_dev_data {
30
30
const struct device * dev ;
31
31
phy_callback_t cb ;
32
32
void * cb_data ;
33
33
struct k_work_delayable monitor_work ;
34
+ struct k_work_delayable autoneg_work ;
34
35
struct phy_link_state state ;
35
36
struct k_sem sem ;
36
37
bool gigabit_supported ;
38
+ uint32_t autoneg_timeout ;
37
39
};
38
40
39
41
/* Offset to align capabilities bits of 1000BASE-T Control and Status regs */
40
42
#define MII_1KSTSR_OFFSET 2
41
43
42
44
#define MII_INVALID_PHY_ID UINT32_MAX
43
45
44
- static int phy_mii_get_link_state ( const struct device * dev ,
45
- struct phy_link_state * state );
46
+ /* How often to poll auto-negotiation status while waiting for it to complete */
47
+ #define MII_AUTONEG_POLL_INTERVAL_MS 100
46
48
47
- static inline int phy_mii_reg_read (const struct device * dev , uint16_t reg_addr ,
48
- uint16_t * value )
49
+ static int phy_mii_get_link_state (const struct device * dev , struct phy_link_state * state );
50
+
51
+ static inline int phy_mii_reg_read (const struct device * dev , uint16_t reg_addr , uint16_t * value )
49
52
{
50
53
const struct phy_mii_dev_config * const cfg = dev -> config ;
51
54
@@ -61,8 +64,7 @@ static inline int phy_mii_reg_read(const struct device *dev, uint16_t reg_addr,
61
64
return mdio_read (cfg -> mdio , cfg -> phy_addr , reg_addr , value );
62
65
}
63
66
64
- static inline int phy_mii_reg_write (const struct device * dev , uint16_t reg_addr ,
65
- uint16_t value )
67
+ static inline int phy_mii_reg_write (const struct device * dev , uint16_t reg_addr , uint16_t value )
66
68
{
67
69
const struct phy_mii_dev_config * const cfg = dev -> config ;
68
70
@@ -92,8 +94,7 @@ static bool is_gigabit_supported(const struct device *dev)
92
94
return - EIO ;
93
95
}
94
96
95
- if (estat_reg & (MII_ESTAT_1000BASE_T_HALF
96
- | MII_ESTAT_1000BASE_T_FULL )) {
97
+ if (estat_reg & (MII_ESTAT_1000BASE_T_HALF | MII_ESTAT_1000BASE_T_FULL )) {
97
98
return true;
98
99
}
99
100
}
@@ -155,13 +156,8 @@ static int update_link_state(const struct device *dev)
155
156
struct phy_mii_dev_data * const data = dev -> data ;
156
157
bool link_up ;
157
158
158
- uint16_t anar_reg = 0 ;
159
159
uint16_t bmcr_reg = 0 ;
160
160
uint16_t bmsr_reg = 0 ;
161
- uint16_t anlpar_reg = 0 ;
162
- uint16_t c1kt_reg = 0 ;
163
- uint16_t s1kt_reg = 0 ;
164
- uint32_t timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / 100 ;
165
161
166
162
if (phy_mii_reg_read (dev , MII_BMSR , & bmsr_reg ) < 0 ) {
167
163
return - EIO ;
@@ -185,51 +181,62 @@ static int update_link_state(const struct device *dev)
185
181
/**
186
182
* Perform auto-negotiation sequence.
187
183
*/
188
- LOG_DBG ("PHY (%d) Starting MII PHY auto-negotiate sequence" ,
189
- cfg -> phy_addr );
190
-
191
- /* Read PHY default advertising parameters */
192
- if (phy_mii_reg_read (dev , MII_ANAR , & anar_reg ) < 0 ) {
193
- return - EIO ;
194
- }
184
+ LOG_DBG ("PHY (%d) Starting MII PHY auto-negotiate sequence" , cfg -> phy_addr );
195
185
196
186
/* Configure and start auto-negotiation process */
197
187
if (phy_mii_reg_read (dev , MII_BMCR , & bmcr_reg ) < 0 ) {
198
188
return - EIO ;
199
189
}
200
190
201
191
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART ;
202
- bmcr_reg &= ~MII_BMCR_ISOLATE ; /* Don't isolate the PHY */
192
+ bmcr_reg &= ~MII_BMCR_ISOLATE ; /* Don't isolate the PHY */
203
193
204
194
if (phy_mii_reg_write (dev , MII_BMCR , bmcr_reg ) < 0 ) {
205
195
return - EIO ;
206
196
}
207
197
208
- /* Wait for the auto-negotiation process to complete */
209
- do {
210
- if (timeout -- == 0U ) {
211
- LOG_DBG ("PHY (%d) auto-negotiate timedout" ,
212
- cfg -> phy_addr );
213
- return - ETIMEDOUT ;
214
- }
198
+ /* We have to wait for the auto-negotiation process to complete */
199
+ data -> autoneg_timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / MII_AUTONEG_POLL_INTERVAL_MS ;
200
+ return - EINPROGRESS ;
201
+ }
215
202
216
- k_sleep (K_MSEC (100 ));
203
+ static int check_autonegotiation_completion (const struct device * dev )
204
+ {
205
+ const struct phy_mii_dev_config * const cfg = dev -> config ;
206
+ struct phy_mii_dev_data * const data = dev -> data ;
217
207
218
- /* On some PHY chips, the BMSR bits are latched, so the first read may
219
- * show incorrect status. A second read ensures correct values.
220
- */
221
- if (phy_mii_reg_read (dev , MII_BMSR , & bmsr_reg ) < 0 ) {
222
- return - EIO ;
223
- }
208
+ uint16_t anar_reg = 0 ;
209
+ uint16_t bmsr_reg = 0 ;
210
+ uint16_t anlpar_reg = 0 ;
211
+ uint16_t c1kt_reg = 0 ;
212
+ uint16_t s1kt_reg = 0 ;
224
213
225
- /* Second read, clears the latched bits and gives the correct status */
226
- if (phy_mii_reg_read (dev , MII_BMSR , & bmsr_reg ) < 0 ) {
227
- return - EIO ;
214
+ /* On some PHY chips, the BMSR bits are latched, so the first read may
215
+ * show incorrect status. A second read ensures correct values.
216
+ */
217
+ if (phy_mii_reg_read (dev , MII_BMSR , & bmsr_reg ) < 0 ) {
218
+ return - EIO ;
219
+ }
220
+
221
+ /* Second read, clears the latched bits and gives the correct status */
222
+ if (phy_mii_reg_read (dev , MII_BMSR , & bmsr_reg ) < 0 ) {
223
+ return - EIO ;
224
+ }
225
+
226
+ if (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE )) {
227
+ if (data -> autoneg_timeout -- == 0U ) {
228
+ LOG_DBG ("PHY (%d) auto-negotiate timedout" , cfg -> phy_addr );
229
+ return - ETIMEDOUT ;
228
230
}
229
- } while (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE ));
231
+ return - EINPROGRESS ;
232
+ }
230
233
231
- LOG_DBG ("PHY (%d) auto-negotiate sequence completed" ,
232
- cfg -> phy_addr );
234
+ LOG_DBG ("PHY (%d) auto-negotiate sequence completed" , cfg -> phy_addr );
235
+
236
+ /* Read PHY default advertising parameters */
237
+ if (phy_mii_reg_read (dev , MII_ANAR , & anar_reg ) < 0 ) {
238
+ return - EIO ;
239
+ }
233
240
234
241
/** Read peer device capability */
235
242
if (phy_mii_reg_read (dev , MII_ANLPAR , & anlpar_reg ) < 0 ) {
@@ -246,11 +253,9 @@ static int update_link_state(const struct device *dev)
246
253
s1kt_reg = (uint16_t )(s1kt_reg >> MII_1KSTSR_OFFSET );
247
254
}
248
255
249
- if (data -> gigabit_supported &&
250
- ((c1kt_reg & s1kt_reg ) & MII_ADVERTISE_1000_FULL )) {
256
+ if (data -> gigabit_supported && ((c1kt_reg & s1kt_reg ) & MII_ADVERTISE_1000_FULL )) {
251
257
data -> state .speed = LINK_FULL_1000BASE_T ;
252
- } else if (data -> gigabit_supported &&
253
- ((c1kt_reg & s1kt_reg ) & MII_ADVERTISE_1000_HALF )) {
258
+ } else if (data -> gigabit_supported && ((c1kt_reg & s1kt_reg ) & MII_ADVERTISE_1000_HALF )) {
254
259
data -> state .speed = LINK_HALF_1000BASE_T ;
255
260
} else if ((anar_reg & anlpar_reg ) & MII_ADVERTISE_100_FULL ) {
256
261
data -> state .speed = LINK_FULL_100BASE_T ;
@@ -262,10 +267,10 @@ static int update_link_state(const struct device *dev)
262
267
data -> state .speed = LINK_HALF_10BASE_T ;
263
268
}
264
269
265
- LOG_INF ("PHY (%d) Link speed %s Mb, %s duplex" ,
266
- cfg -> phy_addr ,
267
- PHY_LINK_IS_SPEED_1000M ( data -> state . speed ) ? "1000" :
268
- (PHY_LINK_IS_SPEED_100M (data -> state .speed ) ? "100" : "10" ),
270
+ LOG_INF ("PHY (%d) Link speed %s Mb, %s duplex" , cfg -> phy_addr ,
271
+ PHY_LINK_IS_SPEED_1000M ( data -> state . speed )
272
+ ? "1000"
273
+ : (PHY_LINK_IS_SPEED_100M (data -> state .speed ) ? "100" : "10" ),
269
274
PHY_LINK_IS_FULL_DUPLEX (data -> state .speed ) ? "full" : "half" );
270
275
271
276
return 0 ;
@@ -293,7 +298,11 @@ static void monitor_work_handler(struct k_work *work)
293
298
const struct device * dev = data -> dev ;
294
299
int rc ;
295
300
296
- k_sem_take (& data -> sem , K_FOREVER );
301
+ if (k_sem_take (& data -> sem , K_NO_WAIT ) != 0 ) {
302
+ /* Try again soon */
303
+ k_work_reschedule (& data -> monitor_work , K_MSEC (MII_AUTONEG_POLL_INTERVAL_MS ));
304
+ return ;
305
+ }
297
306
298
307
rc = update_link_state (dev );
299
308
@@ -304,25 +313,58 @@ static void monitor_work_handler(struct k_work *work)
304
313
invoke_link_cb (dev );
305
314
}
306
315
307
- /* Submit delayed work */
308
- k_work_reschedule (& data -> monitor_work ,
309
- K_MSEC (CONFIG_PHY_MONITOR_PERIOD ));
316
+ if (rc == - EINPROGRESS ) {
317
+ /* Check for autonegotiation completion */
318
+ k_work_reschedule (& data -> autoneg_work , K_MSEC (MII_AUTONEG_POLL_INTERVAL_MS ));
319
+ } else {
320
+ /* Submit delayed work */
321
+ k_work_reschedule (& data -> monitor_work , K_MSEC (CONFIG_PHY_MONITOR_PERIOD ));
322
+ }
310
323
}
311
324
312
- static int phy_mii_read (const struct device * dev , uint16_t reg_addr ,
313
- uint32_t * data )
325
+ static void autoneg_work_handler (struct k_work * work )
326
+ {
327
+ struct k_work_delayable * dwork = k_work_delayable_from_work (work );
328
+ struct phy_mii_dev_data * const data =
329
+ CONTAINER_OF (dwork , struct phy_mii_dev_data , autoneg_work );
330
+ const struct device * dev = data -> dev ;
331
+ int rc ;
332
+
333
+ if (k_sem_take (& data -> sem , K_NO_WAIT ) != 0 ) {
334
+ /* Try again soon */
335
+ k_work_reschedule (& data -> autoneg_work , K_MSEC (MII_AUTONEG_POLL_INTERVAL_MS ));
336
+ return ;
337
+ }
338
+
339
+ rc = check_autonegotiation_completion (dev );
340
+
341
+ k_sem_give (& data -> sem );
342
+
343
+ /* If link state has changed and a callback is set, invoke callback */
344
+ if (rc == 0 ) {
345
+ invoke_link_cb (dev );
346
+ }
347
+
348
+ if (rc == - EINPROGRESS ) {
349
+ /* Check again soon */
350
+ k_work_reschedule (& data -> autoneg_work , K_MSEC (MII_AUTONEG_POLL_INTERVAL_MS ));
351
+ } else {
352
+ /* Schedule the next monitoring call */
353
+ k_work_reschedule (& data -> monitor_work , K_MSEC (CONFIG_PHY_MONITOR_PERIOD ));
354
+ }
355
+ }
356
+
357
+ static int phy_mii_read (const struct device * dev , uint16_t reg_addr , uint32_t * data )
314
358
{
315
359
return phy_mii_reg_read (dev , reg_addr , (uint16_t * )data );
316
360
}
317
361
318
- static int phy_mii_write (const struct device * dev , uint16_t reg_addr ,
319
- uint32_t data )
362
+ static int phy_mii_write (const struct device * dev , uint16_t reg_addr , uint32_t data )
320
363
{
321
364
return phy_mii_reg_write (dev , reg_addr , (uint16_t )data );
322
365
}
323
366
324
- static int phy_mii_cfg_link (const struct device * dev ,
325
- enum phy_link_speed adv_speeds )
367
+ static int phy_mii_cfg_link (const struct device * dev , enum phy_link_speed adv_speeds )
326
368
{
327
369
struct phy_mii_dev_data * const data = dev -> data ;
328
370
const struct phy_mii_dev_config * const cfg = dev -> config ;
@@ -408,8 +450,7 @@ static int phy_mii_cfg_link(const struct device *dev,
408
450
return 0 ;
409
451
}
410
452
411
- static int phy_mii_get_link_state (const struct device * dev ,
412
- struct phy_link_state * state )
453
+ static int phy_mii_get_link_state (const struct device * dev , struct phy_link_state * state )
413
454
{
414
455
struct phy_mii_dev_data * const data = dev -> data ;
415
456
@@ -422,8 +463,7 @@ static int phy_mii_get_link_state(const struct device *dev,
422
463
return 0 ;
423
464
}
424
465
425
- static int phy_mii_link_cb_set (const struct device * dev , phy_callback_t cb ,
426
- void * user_data )
466
+ static int phy_mii_link_cb_set (const struct device * dev , phy_callback_t cb , void * user_data )
427
467
{
428
468
struct phy_mii_dev_data * const data = dev -> data ;
429
469
@@ -457,12 +497,8 @@ static int phy_mii_initialize(const struct device *dev)
457
497
*/
458
498
if (cfg -> fixed ) {
459
499
const static int speed_to_phy_link_speed [] = {
460
- LINK_HALF_10BASE_T ,
461
- LINK_FULL_10BASE_T ,
462
- LINK_HALF_100BASE_T ,
463
- LINK_FULL_100BASE_T ,
464
- LINK_HALF_1000BASE_T ,
465
- LINK_FULL_1000BASE_T ,
500
+ LINK_HALF_10BASE_T , LINK_FULL_10BASE_T , LINK_HALF_100BASE_T ,
501
+ LINK_FULL_100BASE_T , LINK_HALF_1000BASE_T , LINK_FULL_1000BASE_T ,
466
502
};
467
503
468
504
data -> state .speed = speed_to_phy_link_speed [cfg -> fixed_speed ];
@@ -478,8 +514,7 @@ static int phy_mii_initialize(const struct device *dev)
478
514
479
515
if (get_id (dev , & phy_id ) == 0 ) {
480
516
if (phy_id == MII_INVALID_PHY_ID ) {
481
- LOG_ERR ("No PHY found at address %d" ,
482
- cfg -> phy_addr );
517
+ LOG_ERR ("No PHY found at address %d" , cfg -> phy_addr );
483
518
484
519
return - EINVAL ;
485
520
}
@@ -490,23 +525,20 @@ static int phy_mii_initialize(const struct device *dev)
490
525
data -> gigabit_supported = is_gigabit_supported (dev );
491
526
492
527
/* Advertise all speeds */
493
- phy_mii_cfg_link (dev , LINK_HALF_10BASE_T |
494
- LINK_FULL_10BASE_T |
495
- LINK_HALF_100BASE_T |
496
- LINK_FULL_100BASE_T |
497
- LINK_HALF_1000BASE_T |
498
- LINK_FULL_1000BASE_T );
528
+ phy_mii_cfg_link (dev , LINK_HALF_10BASE_T | LINK_FULL_10BASE_T |
529
+ LINK_HALF_100BASE_T | LINK_FULL_100BASE_T |
530
+ LINK_HALF_1000BASE_T | LINK_FULL_1000BASE_T );
499
531
500
- k_work_init_delayable (& data -> monitor_work ,
501
- monitor_work_handler );
532
+ k_work_init_delayable (& data -> monitor_work , monitor_work_handler );
533
+ k_work_init_delayable ( & data -> autoneg_work , autoneg_work_handler );
502
534
503
535
monitor_work_handler (& data -> monitor_work .work );
504
536
}
505
537
506
538
return 0 ;
507
539
}
508
540
509
- #define IS_FIXED_LINK (n ) DT_INST_NODE_HAS_PROP(n, fixed_link)
541
+ #define IS_FIXED_LINK (n ) DT_INST_NODE_HAS_PROP(n, fixed_link)
510
542
511
543
static DEVICE_API (ethphy , phy_mii_driver_api ) = {
512
544
.get_link = phy_mii_get_link_state ,
@@ -516,25 +548,19 @@ static DEVICE_API(ethphy, phy_mii_driver_api) = {
516
548
.write = phy_mii_write ,
517
549
};
518
550
519
- #define PHY_MII_CONFIG (n ) \
520
- static const struct phy_mii_dev_config phy_mii_dev_config_##n = { \
521
- .phy_addr = DT_INST_REG_ADDR(n), \
522
- .no_reset = DT_INST_PROP(n, no_reset), \
523
- .fixed = IS_FIXED_LINK(n), \
524
- .fixed_speed = DT_INST_ENUM_IDX_OR(n, fixed_link, 0), \
525
- .mdio = UTIL_AND(UTIL_NOT(IS_FIXED_LINK(n)), \
526
- DEVICE_DT_GET(DT_INST_BUS(n))) \
527
- };
528
-
529
- #define PHY_MII_DEVICE (n ) \
530
- PHY_MII_CONFIG(n); \
531
- static struct phy_mii_dev_data phy_mii_dev_data_##n; \
532
- DEVICE_DT_INST_DEFINE(n, \
533
- &phy_mii_initialize, \
534
- NULL, \
535
- &phy_mii_dev_data_##n, \
536
- &phy_mii_dev_config_##n, POST_KERNEL, \
537
- CONFIG_PHY_INIT_PRIORITY, \
551
+ #define PHY_MII_CONFIG (n ) \
552
+ static const struct phy_mii_dev_config phy_mii_dev_config_##n = { \
553
+ .phy_addr = DT_INST_REG_ADDR(n), \
554
+ .no_reset = DT_INST_PROP(n, no_reset), \
555
+ .fixed = IS_FIXED_LINK(n), \
556
+ .fixed_speed = DT_INST_ENUM_IDX_OR(n, fixed_link, 0), \
557
+ .mdio = UTIL_AND(UTIL_NOT(IS_FIXED_LINK(n)), DEVICE_DT_GET(DT_INST_BUS(n)))};
558
+
559
+ #define PHY_MII_DEVICE (n ) \
560
+ PHY_MII_CONFIG(n); \
561
+ static struct phy_mii_dev_data phy_mii_dev_data_##n; \
562
+ DEVICE_DT_INST_DEFINE(n, &phy_mii_initialize, NULL, &phy_mii_dev_data_##n, \
563
+ &phy_mii_dev_config_##n, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \
538
564
&phy_mii_driver_api);
539
565
540
566
DT_INST_FOREACH_STATUS_OKAY (PHY_MII_DEVICE )
0 commit comments