Skip to content

Commit 5649be2

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 2e23dfd commit 5649be2

File tree

1 file changed

+81
-31
lines changed

1 file changed

+81
-31
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ struct phy_mii_dev_data {
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 */
@@ -145,13 +147,8 @@ static int update_link_state(const struct device *dev)
145147
struct phy_mii_dev_data *const data = dev->data;
146148
bool link_up;
147149

148-
uint16_t anar_reg = 0;
149150
uint16_t bmcr_reg = 0;
150151
uint16_t bmsr_reg = 0;
151-
uint16_t anlpar_reg = 0;
152-
uint16_t c1kt_reg = 0;
153-
uint16_t s1kt_reg = 0;
154-
uint32_t timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / 100;
155152

156153
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
157154
return -EIO;
@@ -178,11 +175,6 @@ static int update_link_state(const struct device *dev)
178175
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence",
179176
cfg->phy_addr);
180177

181-
/* Read PHY default advertising parameters */
182-
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
183-
return -EIO;
184-
}
185-
186178
/* Configure and start auto-negotiation process */
187179
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
188180
return -EIO;
@@ -195,32 +187,52 @@ static int update_link_state(const struct device *dev)
195187
return -EIO;
196188
}
197189

198-
/* Wait for the auto-negotiation process to complete */
199-
do {
200-
if (timeout-- == 0U) {
201-
LOG_DBG("PHY (%d) auto-negotiate timedout",
202-
cfg->phy_addr);
203-
return -ETIMEDOUT;
204-
}
190+
/* We have to wait for the auto-negotiation process to complete */
191+
data->autoneg_timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / 100;
192+
return -EINPROGRESS;
193+
}
205194

206-
k_sleep(K_MSEC(100));
195+
static int check_autonegotiation_completion(const struct device *dev)
196+
{
197+
const struct phy_mii_dev_config *const cfg = dev->config;
198+
struct phy_mii_dev_data *const data = dev->data;
207199

208-
/* On some PHY chips, the BMSR bits are latched, so the first read may
209-
* show incorrect status. A second read ensures correct values.
210-
*/
211-
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
212-
return -EIO;
213-
}
200+
uint16_t anar_reg = 0;
201+
uint16_t bmsr_reg = 0;
202+
uint16_t anlpar_reg = 0;
203+
uint16_t c1kt_reg = 0;
204+
uint16_t s1kt_reg = 0;
214205

215-
/* Second read, clears the latched bits and gives the correct status */
216-
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
217-
return -EIO;
206+
/* On some PHY chips, the BMSR bits are latched, so the first read may
207+
* show incorrect status. A second read ensures correct values.
208+
*/
209+
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
210+
return -EIO;
211+
}
212+
213+
/* Second read, clears the latched bits and gives the correct status */
214+
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
215+
return -EIO;
216+
}
217+
218+
if (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE)) {
219+
if (data->autoneg_timeout-- == 0U) {
220+
LOG_DBG("PHY (%d) auto-negotiate timedout",
221+
cfg->phy_addr);
222+
return -ETIMEDOUT;
218223
}
219-
} while (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE));
224+
return -EINPROGRESS;
225+
}
220226

221227
LOG_DBG("PHY (%d) auto-negotiate sequence completed",
222228
cfg->phy_addr);
223229

230+
/* Read PHY default advertising parameters */
231+
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
232+
return -EIO;
233+
}
234+
LOG_ERR("ANAR after %04x", anar_reg);
235+
224236
/** Read peer device capability */
225237
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
226238
return -EIO;
@@ -294,9 +306,45 @@ static void monitor_work_handler(struct k_work *work)
294306
invoke_link_cb(dev);
295307
}
296308

297-
/* Submit delayed work */
298-
k_work_reschedule(&data->monitor_work,
299-
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
309+
if (rc == -EINPROGRESS) {
310+
/* Check for autonegotiation completion */
311+
k_work_reschedule(&data->autoneg_work,
312+
K_MSEC(100));
313+
} else {
314+
/* Submit delayed work */
315+
k_work_reschedule(&data->monitor_work,
316+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
317+
}
318+
}
319+
320+
static void autoneg_work_handler(struct k_work *work)
321+
{
322+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
323+
struct phy_mii_dev_data *const data =
324+
CONTAINER_OF(dwork, struct phy_mii_dev_data, autoneg_work);
325+
const struct device *dev = data->dev;
326+
int rc;
327+
328+
k_sem_take(&data->sem, K_FOREVER);
329+
330+
rc = check_autonegotiation_completion(dev);
331+
332+
k_sem_give(&data->sem);
333+
334+
/* If link state has changed and a callback is set, invoke callback */
335+
if (rc == 0) {
336+
invoke_link_cb(dev);
337+
}
338+
339+
if (rc == -EINPROGRESS) {
340+
/* Check again soon */
341+
k_work_reschedule(&data->autoneg_work,
342+
K_MSEC(100));
343+
} else {
344+
/* Schedule the next monitoring call */
345+
k_work_reschedule(&data->monitor_work,
346+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
347+
}
300348
}
301349

302350
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
@@ -479,6 +527,8 @@ static int phy_mii_initialize(const struct device *dev)
479527

480528
k_work_init_delayable(&data->monitor_work,
481529
monitor_work_handler);
530+
k_work_init_delayable(&data->autoneg_work,
531+
autoneg_work_handler);
482532

483533
monitor_work_handler(&data->monitor_work.work);
484534
}

0 commit comments

Comments
 (0)