Skip to content

Commit 209a063

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 209a063

File tree

1 file changed

+80
-31
lines changed

1 file changed

+80
-31
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 80 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,51 @@ 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+
224235
/** Read peer device capability */
225236
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
226237
return -EIO;
@@ -294,9 +305,45 @@ static void monitor_work_handler(struct k_work *work)
294305
invoke_link_cb(dev);
295306
}
296307

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

302349
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
@@ -479,6 +526,8 @@ static int phy_mii_initialize(const struct device *dev)
479526

480527
k_work_init_delayable(&data->monitor_work,
481528
monitor_work_handler);
529+
k_work_init_delayable(&data->autoneg_work,
530+
autoneg_work_handler);
482531

483532
monitor_work_handler(&data->monitor_work.work);
484533
}

0 commit comments

Comments
 (0)