Skip to content

Commit b9f8357

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 b9f8357

File tree

1 file changed

+94
-32
lines changed

1 file changed

+94
-32
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 94 additions & 32 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

@@ -145,13 +150,8 @@ static int update_link_state(const struct device *dev)
145150
struct phy_mii_dev_data *const data = dev->data;
146151
bool link_up;
147152

148-
uint16_t anar_reg = 0;
149153
uint16_t bmcr_reg = 0;
150154
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;
155155

156156
if (phy_mii_reg_read(dev, MII_BMSR, &bmsr_reg) < 0) {
157157
return -EIO;
@@ -178,11 +178,6 @@ static int update_link_state(const struct device *dev)
178178
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence",
179179
cfg->phy_addr);
180180

181-
/* Read PHY default advertising parameters */
182-
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
183-
return -EIO;
184-
}
185-
186181
/* Configure and start auto-negotiation process */
187182
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
188183
return -EIO;
@@ -195,32 +190,51 @@ static int update_link_state(const struct device *dev)
195190
return -EIO;
196191
}
197192

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-
}
193+
/* We have to wait for the auto-negotiation process to complete */
194+
data->autoneg_timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / MII_AUTONEG_POLL_INTERVAL_MS;
195+
return -EINPROGRESS;
196+
}
205197

206-
k_sleep(K_MSEC(100));
198+
static int check_autonegotiation_completion(const struct device *dev)
199+
{
200+
const struct phy_mii_dev_config *const cfg = dev->config;
201+
struct phy_mii_dev_data *const data = dev->data;
207202

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-
}
203+
uint16_t anar_reg = 0;
204+
uint16_t bmsr_reg = 0;
205+
uint16_t anlpar_reg = 0;
206+
uint16_t c1kt_reg = 0;
207+
uint16_t s1kt_reg = 0;
214208

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

221230
LOG_DBG("PHY (%d) auto-negotiate sequence completed",
222231
cfg->phy_addr);
223232

233+
/* Read PHY default advertising parameters */
234+
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
235+
return -EIO;
236+
}
237+
224238
/** Read peer device capability */
225239
if (phy_mii_reg_read(dev, MII_ANLPAR, &anlpar_reg) < 0) {
226240
return -EIO;
@@ -283,7 +297,12 @@ static void monitor_work_handler(struct k_work *work)
283297
const struct device *dev = data->dev;
284298
int rc;
285299

286-
k_sem_take(&data->sem, K_FOREVER);
300+
if (k_sem_take(&data->sem, K_NO_WAIT) != 0) {
301+
/* Try again soon */
302+
k_work_reschedule(&data->monitor_work,
303+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
304+
return;
305+
}
287306

288307
rc = update_link_state(dev);
289308

@@ -294,9 +313,50 @@ static void monitor_work_handler(struct k_work *work)
294313
invoke_link_cb(dev);
295314
}
296315

297-
/* Submit delayed work */
298-
k_work_reschedule(&data->monitor_work,
299-
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
316+
if (rc == -EINPROGRESS) {
317+
/* Check for autonegotiation completion */
318+
k_work_reschedule(&data->autoneg_work,
319+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
320+
} else {
321+
/* Submit delayed work */
322+
k_work_reschedule(&data->monitor_work,
323+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
324+
}
325+
}
326+
327+
static void autoneg_work_handler(struct k_work *work)
328+
{
329+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
330+
struct phy_mii_dev_data *const data =
331+
CONTAINER_OF(dwork, struct phy_mii_dev_data, autoneg_work);
332+
const struct device *dev = data->dev;
333+
int rc;
334+
335+
if (k_sem_take(&data->sem, K_NO_WAIT) != 0) {
336+
/* Try again soon */
337+
k_work_reschedule(&data->autoneg_work,
338+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
339+
return;
340+
}
341+
342+
rc = check_autonegotiation_completion(dev);
343+
344+
k_sem_give(&data->sem);
345+
346+
/* If link state has changed and a callback is set, invoke callback */
347+
if (rc == 0) {
348+
invoke_link_cb(dev);
349+
}
350+
351+
if (rc == -EINPROGRESS) {
352+
/* Check again soon */
353+
k_work_reschedule(&data->autoneg_work,
354+
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
355+
} else {
356+
/* Schedule the next monitoring call */
357+
k_work_reschedule(&data->monitor_work,
358+
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
359+
}
300360
}
301361

302362
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
@@ -479,6 +539,8 @@ static int phy_mii_initialize(const struct device *dev)
479539

480540
k_work_init_delayable(&data->monitor_work,
481541
monitor_work_handler);
542+
k_work_init_delayable(&data->autoneg_work,
543+
autoneg_work_handler);
482544

483545
monitor_work_handler(&data->monitor_work.work);
484546
}

0 commit comments

Comments
 (0)