Skip to content

Commit 403ba1c

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 c53fb67 commit 403ba1c

File tree

1 file changed

+82
-20
lines changed

1 file changed

+82
-20
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ 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 */
4042
#define MII_1KSTSR_OFFSET 2
4143

4244
#define MII_INVALID_PHY_ID UINT32_MAX
4345

46+
/* How often to poll auto-negotiation status while waiting for it to complete */
47+
#define MII_AUTONEG_POLL_INTERVAL_MS 100
48+
4449
static int phy_mii_get_link_state(const struct device *dev,
4550
struct phy_link_state *state);
4651

@@ -155,13 +160,8 @@ static int update_link_state(const struct device *dev)
155160
struct phy_mii_dev_data *const data = dev->data;
156161
bool link_up;
157162

158-
uint16_t anar_reg = 0;
159163
uint16_t bmcr_reg = 0;
160164
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;
165165

166166
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
167167
return -EIO;
@@ -188,11 +188,6 @@ static int update_link_state(const struct device *dev)
188188
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence",
189189
cfg->phy_addr);
190190

191-
/* Read PHY default advertising parameters */
192-
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
193-
return -EIO;
194-
}
195-
196191
/* Configure and start auto-negotiation process */
197192
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
198193
return -EIO;
@@ -205,15 +200,21 @@ static int update_link_state(const struct device *dev)
205200
return -EIO;
206201
}
207202

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-
}
203+
/* We have to wait for the auto-negotiation process to complete */
204+
data->autoneg_timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / MII_AUTONEG_POLL_INTERVAL_MS;
205+
return -EINPROGRESS;
206+
}
215207

216-
k_sleep(K_MSEC(100));
208+
static int check_autonegotiation_completion(const struct device *dev)
209+
{
210+
const struct phy_mii_dev_config *const cfg = dev->config;
211+
struct phy_mii_dev_data *const data = dev->data;
212+
213+
uint16_t anar_reg = 0;
214+
uint16_t bmsr_reg = 0;
215+
uint16_t anlpar_reg = 0;
216+
uint16_t c1kt_reg = 0;
217+
uint16_t s1kt_reg = 0;
217218

218219
/* On some PHY chips, the BMSR bits are latched, so the first read may
219220
* show incorrect status. A second read ensures correct values.
@@ -226,11 +227,24 @@ static int update_link_state(const struct device *dev)
226227
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
227228
return -EIO;
228229
}
229-
} while (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE));
230+
231+
if (!(bmsr_reg & MII_BMSR_AUTONEG_COMPLETE)) {
232+
if (data->autoneg_timeout-- == 0U) {
233+
LOG_DBG("PHY (%d) auto-negotiate timedout",
234+
cfg->phy_addr);
235+
return -ETIMEDOUT;
236+
}
237+
return -EINPROGRESS;
238+
}
230239

231240
LOG_DBG("PHY (%d) auto-negotiate sequence completed",
232241
cfg->phy_addr);
233242

243+
/* Read PHY default advertising parameters */
244+
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
245+
return -EIO;
246+
}
247+
234248
/** Read peer device capability */
235249
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
236250
return -EIO;
@@ -293,7 +307,12 @@ static void monitor_work_handler(struct k_work *work)
293307
const struct device *dev = data->dev;
294308
int rc;
295309

296-
k_sem_take(&data->sem, K_FOREVER);
310+
if (k_sem_take(&data->sem, K_NO_WAIT) != 0) {
311+
/* Try again soon */
312+
k_work_reschedule(&data->monitor_work,
313+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
314+
return;
315+
}
297316

298317
rc = update_link_state(dev);
299318

@@ -304,9 +323,50 @@ static void monitor_work_handler(struct k_work *work)
304323
invoke_link_cb(dev);
305324
}
306325

326+
if (rc == -EINPROGRESS) {
327+
/* Check for autonegotiation completion */
328+
k_work_reschedule(&data->autoneg_work,
329+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
330+
} else {
307331
/* Submit delayed work */
308332
k_work_reschedule(&data->monitor_work,
309333
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
334+
}
335+
}
336+
337+
static void autoneg_work_handler(struct k_work *work)
338+
{
339+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
340+
struct phy_mii_dev_data *const data =
341+
CONTAINER_OF(dwork, struct phy_mii_dev_data, autoneg_work);
342+
const struct device *dev = data->dev;
343+
int rc;
344+
345+
if (k_sem_take(&data->sem, K_NO_WAIT) != 0) {
346+
/* Try again soon */
347+
k_work_reschedule(&data->autoneg_work,
348+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
349+
return;
350+
}
351+
352+
rc = check_autonegotiation_completion(dev);
353+
354+
k_sem_give(&data->sem);
355+
356+
/* If link state has changed and a callback is set, invoke callback */
357+
if (rc == 0) {
358+
invoke_link_cb(dev);
359+
}
360+
361+
if (rc == -EINPROGRESS) {
362+
/* Check again soon */
363+
k_work_reschedule(&data->autoneg_work,
364+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
365+
} else {
366+
/* Schedule the next monitoring call */
367+
k_work_reschedule(&data->monitor_work,
368+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
369+
}
310370
}
311371

312372
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
@@ -499,6 +559,8 @@ static int phy_mii_initialize(const struct device *dev)
499559

500560
k_work_init_delayable(&data->monitor_work,
501561
monitor_work_handler);
562+
k_work_init_delayable(&data->autoneg_work,
563+
autoneg_work_handler);
502564

503565
monitor_work_handler(&data->monitor_work.work);
504566
}

0 commit comments

Comments
 (0)