Skip to content

Commit fd3af65

Browse files
committed
drivers: eth: phy_mii: Don't block system workqueue
Looping while waiting for auto-negotiation to complete can block the system workqueue for several seconds. Signed-off-by: Kevin ORourke <[email protected]>
1 parent c3f67ab commit fd3af65

File tree

1 file changed

+126
-100
lines changed

1 file changed

+126
-100
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 126 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,32 @@ struct phy_mii_dev_config {
2323
bool no_reset;
2424
bool fixed;
2525
int fixed_speed;
26-
const struct device * const mdio;
26+
const struct device *const mdio;
2727
};
2828

2929
struct phy_mii_dev_data {
3030
const struct device *dev;
3131
phy_callback_t cb;
3232
void *cb_data;
3333
struct k_work_delayable monitor_work;
34+
struct k_work_delayable autoneg_work;
3435
struct phy_link_state state;
3536
struct k_sem sem;
3637
bool gigabit_supported;
38+
uint32_t autoneg_timeout;
3739
};
3840

3941
/* Offset to align capabilities bits of 1000BASE-T Control and Status regs */
4042
#define MII_1KSTSR_OFFSET 2
4143

4244
#define MII_INVALID_PHY_ID UINT32_MAX
4345

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
4648

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)
4952
{
5053
const struct phy_mii_dev_config *const cfg = dev->config;
5154

@@ -61,8 +64,7 @@ static inline int phy_mii_reg_read(const struct device *dev, uint16_t reg_addr,
6164
return mdio_read(cfg->mdio, cfg->phy_addr, reg_addr, value);
6265
}
6366

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)
6668
{
6769
const struct phy_mii_dev_config *const cfg = dev->config;
6870

@@ -92,8 +94,7 @@ static bool is_gigabit_supported(const struct device *dev)
9294
return -EIO;
9395
}
9496

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)) {
9798
return true;
9899
}
99100
}
@@ -155,13 +156,8 @@ static int update_link_state(const struct device *dev)
155156
struct phy_mii_dev_data *const data = dev->data;
156157
bool link_up;
157158

158-
uint16_t anar_reg = 0;
159159
uint16_t bmcr_reg = 0;
160160
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;
165161

166162
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
167163
return -EIO;
@@ -185,51 +181,62 @@ static int update_link_state(const struct device *dev)
185181
/**
186182
* Perform auto-negotiation sequence.
187183
*/
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);
195185

196186
/* Configure and start auto-negotiation process */
197187
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
198188
return -EIO;
199189
}
200190

201191
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 */
203193

204194
if (phy_mii_reg_write(dev, MII_BMCR, bmcr_reg) < 0) {
205195
return -EIO;
206196
}
207197

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+
}
215202

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;
217207

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;
224213

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;
228230
}
229-
} while (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE));
231+
return -EINPROGRESS;
232+
}
230233

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+
}
233240

234241
/** Read peer device capability */
235242
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
@@ -246,11 +253,9 @@ static int update_link_state(const struct device *dev)
246253
s1kt_reg = (uint16_t)(s1kt_reg >> MII_1KSTSR_OFFSET);
247254
}
248255

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)) {
251257
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)) {
254259
data->state.speed = LINK_HALF_1000BASE_T;
255260
} else if ((anar_reg & anlpar_reg) & MII_ADVERTISE_100_FULL) {
256261
data->state.speed = LINK_FULL_100BASE_T;
@@ -262,10 +267,10 @@ static int update_link_state(const struct device *dev)
262267
data->state.speed = LINK_HALF_10BASE_T;
263268
}
264269

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"),
269274
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
270275

271276
return 0;
@@ -293,7 +298,11 @@ static void monitor_work_handler(struct k_work *work)
293298
const struct device *dev = data->dev;
294299
int rc;
295300

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+
}
297306

298307
rc = update_link_state(dev);
299308

@@ -304,25 +313,58 @@ static void monitor_work_handler(struct k_work *work)
304313
invoke_link_cb(dev);
305314
}
306315

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+
}
310323
}
311324

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)
314358
{
315359
return phy_mii_reg_read(dev, reg_addr, (uint16_t *)data);
316360
}
317361

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)
320363
{
321364
return phy_mii_reg_write(dev, reg_addr, (uint16_t)data);
322365
}
323366

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)
326368
{
327369
struct phy_mii_dev_data *const data = dev->data;
328370
const struct phy_mii_dev_config *const cfg = dev->config;
@@ -408,8 +450,7 @@ static int phy_mii_cfg_link(const struct device *dev,
408450
return 0;
409451
}
410452

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)
413454
{
414455
struct phy_mii_dev_data *const data = dev->data;
415456

@@ -422,8 +463,7 @@ static int phy_mii_get_link_state(const struct device *dev,
422463
return 0;
423464
}
424465

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)
427467
{
428468
struct phy_mii_dev_data *const data = dev->data;
429469

@@ -457,12 +497,8 @@ static int phy_mii_initialize(const struct device *dev)
457497
*/
458498
if (cfg->fixed) {
459499
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,
466502
};
467503

468504
data->state.speed = speed_to_phy_link_speed[cfg->fixed_speed];
@@ -478,8 +514,7 @@ static int phy_mii_initialize(const struct device *dev)
478514

479515
if (get_id(dev, &phy_id) == 0) {
480516
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);
483518

484519
return -EINVAL;
485520
}
@@ -490,23 +525,20 @@ static int phy_mii_initialize(const struct device *dev)
490525
data->gigabit_supported = is_gigabit_supported(dev);
491526

492527
/* 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);
499531

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);
502534

503535
monitor_work_handler(&data->monitor_work.work);
504536
}
505537

506538
return 0;
507539
}
508540

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)
510542

511543
static DEVICE_API(ethphy, phy_mii_driver_api) = {
512544
.get_link = phy_mii_get_link_state,
@@ -516,25 +548,19 @@ static DEVICE_API(ethphy, phy_mii_driver_api) = {
516548
.write = phy_mii_write,
517549
};
518550

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, \
538564
&phy_mii_driver_api);
539565

540566
DT_INST_FOREACH_STATUS_OKAY(PHY_MII_DEVICE)

0 commit comments

Comments
 (0)