Skip to content

Commit 6ead65b

Browse files
stanislav-poborilaescolar
authored andcommitted
drivers: ethernet: phy: KSZ8081 PHY Driver improvements
Added changes required for nxp_enet ethernet driver to work with multiple PHYs and fixed few problems: - The cfg_link API resets PHY before configuring link. It was moved here so the ethernet driver does not have to reset it - not all PHYs need reset before configuring link and moving the reset code here makes possible to have the reset done in a PHY specific way (for example to reset by toggling GPIO pin). It also avoids ethernet driver touching PHY registers without locking. - When reset GPIO is not defined, reset is performed by setting reset bit in control register. - The cfg_link API does not return error when autonegotiation fails. This fixes situation when the link is down at system start - ethernet driver then skipped setting link-change callback and link was never to be detected again. - Added reset of excessive bits 16-31 when reading register values. As only 16 bits are read from PHY, but the API is supposed to read into uint32_t, the remaining bits contained previous data after a successful read. - Fixed missing mutex unlock when querying link state and link was down. - Added missing initializer to link state variables. This could result in link state change detection while link was still down, because the speed/duplex settings could be random and old and new state could be wrongly detected as different. - Not logging link speed/duplex status when link is not up. Signed-off-by: Stanislav Poboril <[email protected]>
1 parent 6692dfd commit 6ead65b

File tree

1 file changed

+91
-28
lines changed

1 file changed

+91
-28
lines changed

drivers/ethernet/phy/phy_microchip_ksz8081.c

Lines changed: 91 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 NXP
2+
* Copyright 2023-2024 NXP
33
*
44
* Inspiration from phy_mii.c, which is:
55
* Copyright (c) 2021 IP-Logix Inc.
@@ -65,6 +65,9 @@ static int phy_mc_ksz8081_read(const struct device *dev,
6565
const struct mc_ksz8081_config *config = dev->config;
6666
int ret;
6767

68+
/* Make sure excessive bits 16-31 are reset */
69+
*data = 0U;
70+
6871
ret = mdio_read(config->mdio_dev, config->addr, reg_addr, (uint16_t *)data);
6972
if (ret) {
7073
return ret;
@@ -117,7 +120,10 @@ static int phy_mc_ksz8081_autonegotiate(const struct device *dev)
117120
do {
118121
if (timeout-- == 0) {
119122
LOG_DBG("PHY (%d) autonegotiation timed out", config->addr);
120-
return -ETIMEDOUT;
123+
/* The value -ETIMEDOUT can be returned by PHY read/write functions, so
124+
* return -ENETDOWN instead to distinguish link timeout from PHY timeout.
125+
*/
126+
return -ENETDOWN;
121127
}
122128
k_msleep(100);
123129

@@ -161,6 +167,7 @@ static int phy_mc_ksz8081_get_link(const struct device *dev,
161167
state->is_up = bmsr & MII_BMSR_LINK_STATUS;
162168

163169
if (!state->is_up) {
170+
k_mutex_unlock(&data->mutex);
164171
goto result;
165172
}
166173

@@ -200,9 +207,11 @@ static int phy_mc_ksz8081_get_link(const struct device *dev,
200207
result:
201208
if (memcmp(&old_state, state, sizeof(struct phy_link_state)) != 0) {
202209
LOG_DBG("PHY %d is %s", config->addr, state->is_up ? "up" : "down");
203-
LOG_DBG("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr,
204-
(PHY_LINK_IS_SPEED_100M(state->speed) ? "100" : "10"),
205-
PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "full" : "half");
210+
if (state->is_up) {
211+
LOG_DBG("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr,
212+
(PHY_LINK_IS_SPEED_100M(state->speed) ? "100" : "10"),
213+
PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "full" : "half");
214+
}
206215
}
207216

208217
return ret;
@@ -254,11 +263,59 @@ static int phy_mc_ksz8081_static_cfg(const struct device *dev)
254263
return 0;
255264
}
256265

266+
static int phy_mc_ksz8081_reset(const struct device *dev)
267+
{
268+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio)
269+
const struct mc_ksz8081_config *config = dev->config;
270+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio) */
271+
struct mc_ksz8081_data *data = dev->data;
272+
int ret;
273+
274+
/* Lock mutex */
275+
ret = k_mutex_lock(&data->mutex, K_FOREVER);
276+
if (ret) {
277+
LOG_ERR("PHY mutex lock error");
278+
return ret;
279+
}
280+
281+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio)
282+
if (!config->reset_gpio.port) {
283+
goto skip_reset_gpio;
284+
}
285+
286+
/* Start reset */
287+
ret = gpio_pin_set_dt(&config->reset_gpio, 0);
288+
if (ret) {
289+
goto done;
290+
}
291+
292+
/* Wait for 500 ms as specified by datasheet */
293+
k_busy_wait(USEC_PER_MSEC * 500);
294+
295+
/* Reset over */
296+
ret = gpio_pin_set_dt(&config->reset_gpio, 1);
297+
goto done;
298+
skip_reset_gpio:
299+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio) */
300+
ret = phy_mc_ksz8081_write(dev, MII_BMCR, MII_BMCR_RESET);
301+
if (ret) {
302+
goto done;
303+
}
304+
/* Wait for 500 ms as specified by datasheet */
305+
k_busy_wait(USEC_PER_MSEC * 500);
306+
307+
done:
308+
/* Unlock mutex */
309+
k_mutex_unlock(&data->mutex);
310+
return ret;
311+
}
312+
257313
static int phy_mc_ksz8081_cfg_link(const struct device *dev,
258314
enum phy_link_speed speeds)
259315
{
260316
const struct mc_ksz8081_config *config = dev->config;
261317
struct mc_ksz8081_data *data = dev->data;
318+
struct phy_link_state state = {};
262319
int ret;
263320
uint32_t anar;
264321

@@ -272,6 +329,12 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
272329
/* We are going to reconfigure the phy, don't need to monitor until done */
273330
k_work_cancel_delayable(&data->phy_monitor_work);
274331

332+
/* Reset PHY */
333+
ret = phy_mc_ksz8081_reset(dev);
334+
if (ret) {
335+
goto done;
336+
}
337+
275338
/* DT configurations */
276339
ret = phy_mc_ksz8081_static_cfg(dev);
277340
if (ret) {
@@ -316,19 +379,28 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
316379

317380
/* (re)do autonegotiation */
318381
ret = phy_mc_ksz8081_autonegotiate(dev);
319-
if (ret) {
382+
if (ret && (ret != -ENETDOWN)) {
320383
LOG_ERR("Error in autonegotiation");
321384
goto done;
322385
}
323386

324387
/* Get link status */
325-
ret = phy_mc_ksz8081_get_link(dev, &data->state);
388+
ret = phy_mc_ksz8081_get_link(dev, &state);
389+
390+
if (ret == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) {
391+
memcpy(&data->state, &state, sizeof(struct phy_link_state));
392+
if (data->cb) {
393+
data->cb(dev, &data->state, data->cb_data);
394+
}
395+
}
326396

327397
/* Log the results of the configuration */
328398
LOG_INF("PHY %d is %s", config->addr, data->state.is_up ? "up" : "down");
329-
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr,
330-
(PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"),
331-
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
399+
if (data->state.is_up) {
400+
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr,
401+
(PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"),
402+
PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
403+
}
332404

333405
done:
334406
/* Unlock mutex */
@@ -362,7 +434,7 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
362434
struct mc_ksz8081_data *data =
363435
CONTAINER_OF(dwork, struct mc_ksz8081_data, phy_monitor_work);
364436
const struct device *dev = data->dev;
365-
struct phy_link_state state;
437+
struct phy_link_state state = {};
366438
int rc;
367439

368440
rc = phy_mc_ksz8081_get_link(dev, &state);
@@ -407,30 +479,21 @@ static int phy_mc_ksz8081_init(const struct device *dev)
407479
skip_int_gpio:
408480
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_interrupt_gpio) */
409481

410-
411482
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio)
412-
if (!config->reset_gpio.port) {
413-
goto skip_reset_gpio;
414-
}
415-
416-
/* Start reset */
417-
ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
418-
if (ret) {
419-
return ret;
483+
if (config->reset_gpio.port) {
484+
ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_ACTIVE);
485+
if (ret) {
486+
return ret;
487+
}
420488
}
489+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio) */
421490

422-
/* Wait for 500 ms as specified by datasheet */
423-
k_busy_wait(USEC_PER_MSEC * 500);
424-
425-
/* Reset over */
426-
ret = gpio_pin_set_dt(&config->reset_gpio, 1);
491+
/* Reset PHY */
492+
ret = phy_mc_ksz8081_reset(dev);
427493
if (ret) {
428494
return ret;
429495
}
430496

431-
skip_reset_gpio:
432-
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(mc_reset_gpio) */
433-
434497
k_work_init_delayable(&data->phy_monitor_work,
435498
phy_mc_ksz8081_monitor_work_handler);
436499

0 commit comments

Comments
 (0)