Skip to content

Commit 6df23de

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 6df23de

File tree

1 file changed

+83
-31
lines changed

1 file changed

+83
-31
lines changed

drivers/ethernet/phy/phy_mii.c

Lines changed: 83 additions & 31 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;
@@ -294,9 +308,45 @@ static void monitor_work_handler(struct k_work *work)
294308
invoke_link_cb(dev);
295309
}
296310

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

302352
static int phy_mii_read(const struct device *dev, uint16_t reg_addr,
@@ -479,6 +529,8 @@ static int phy_mii_initialize(const struct device *dev)
479529

480530
k_work_init_delayable(&data->monitor_work,
481531
monitor_work_handler);
532+
k_work_init_delayable(&data->autoneg_work,
533+
autoneg_work_handler);
482534

483535
monitor_work_handler(&data->monitor_work.work);
484536
}

0 commit comments

Comments
 (0)