Skip to content

Commit ffe8e46

Browse files
committed
drivers: ethernet: phy: ksz8081: add support for phy interrupt mode
Enable Link-Up and Link-Down interrupts. On the interrupt handling the monitor work is scheduled to update the link status and calling corresponding callback routine. Signed-off-by: Tony Han <tony.han@microchip.com>
1 parent b5c95eb commit ffe8e46

File tree

1 file changed

+138
-2
lines changed

1 file changed

+138
-2
lines changed

drivers/ethernet/phy/phy_microchip_ksz8081.c

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
2929
#define PHY_MC_KSZ8081_OMSO_RMII_OVERRIDE_MASK BIT(1)
3030
#define PHY_MC_KSZ8081_OMSO_MII_OVERRIDE_MASK BIT(0)
3131

32+
#define PHY_MC_KSZ8081_ICS_REG 0x1B
33+
#define PHY_MC_KSZ8081_ICS_LINK_DOWN_IE_MASK BIT(10)
34+
#define PHY_MC_KSZ8081_ICS_LINK_UP_IE_MASK BIT(8)
35+
3236
#define PHY_MC_KSZ8081_CTRL2_REG 0x1F
3337
#define PHY_MC_KSZ8081_CTRL2_REF_CLK_SEL BIT(7)
3438

@@ -54,6 +58,9 @@ struct mc_ksz8081_data {
5458
const struct device *dev;
5559
struct phy_link_state state;
5660
phy_callback_t cb;
61+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
62+
struct gpio_callback gpio_callback;
63+
#endif
5764
void *cb_data;
5865
struct k_mutex mutex;
5966
struct k_work_delayable phy_monitor_work;
@@ -90,6 +97,72 @@ static int phy_mc_ksz8081_write(const struct device *dev,
9097
return 0;
9198
}
9299

100+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
101+
static int phy_mc_ksz8081_clear_interrupt(struct mc_ksz8081_data *data)
102+
{
103+
const struct device *dev = data->dev;
104+
const struct mc_ksz8081_config *config = dev->config;
105+
uint32_t ics;
106+
int ret;
107+
108+
/* Lock mutex */
109+
ret = k_mutex_lock(&data->mutex, K_FOREVER);
110+
if (ret < 0) {
111+
LOG_ERR("PHY mutex lock error");
112+
return ret;
113+
}
114+
115+
/* Read/clear PHY interrupt status register */
116+
ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_ICS_REG, &ics);
117+
if (ret < 0) {
118+
LOG_ERR("Error reading phy (%d) interrupt status register", config->addr);
119+
}
120+
121+
/* Unlock mutex */
122+
k_mutex_unlock(&data->mutex);
123+
return ret;
124+
}
125+
126+
static int phy_mc_ksz8081_config_interrupt(const struct device *dev)
127+
{
128+
struct mc_ksz8081_data *data = dev->data;
129+
uint32_t ics;
130+
int ret;
131+
132+
/* Read Interrupt Control/Status register to write back */
133+
ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_ICS_REG, &ics);
134+
if (ret < 0) {
135+
return ret;
136+
}
137+
ics |= PHY_MC_KSZ8081_ICS_LINK_UP_IE_MASK | PHY_MC_KSZ8081_ICS_LINK_DOWN_IE_MASK;
138+
139+
/* Write settings to Interrupt Control/Status register */
140+
ret = phy_mc_ksz8081_write(dev, PHY_MC_KSZ8081_ICS_REG, ics);
141+
if (ret < 0) {
142+
return ret;
143+
}
144+
145+
/* Clear interrupt */
146+
ret = phy_mc_ksz8081_clear_interrupt(data);
147+
if (ret < 0) {
148+
return ret;
149+
}
150+
151+
return ret;
152+
}
153+
154+
static void phy_mc_ksz8081_interrupt_handler(const struct device *port, struct gpio_callback *cb,
155+
gpio_port_pins_t pins)
156+
{
157+
struct mc_ksz8081_data *data = CONTAINER_OF(cb, struct mc_ksz8081_data, gpio_callback);
158+
int ret = k_work_reschedule(&data->phy_monitor_work, K_NO_WAIT);
159+
160+
if (ret < 0) {
161+
LOG_ERR("Failed to schedule monitor_work from ISR");
162+
}
163+
}
164+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
165+
93166
static int phy_mc_ksz8081_autonegotiate(const struct device *dev)
94167
{
95168
const struct mc_ksz8081_config *config = dev->config;
@@ -116,7 +189,6 @@ static int phy_mc_ksz8081_autonegotiate(const struct device *dev)
116189
return ret;
117190
}
118191

119-
/* TODO change this to GPIO interrupt driven */
120192
do {
121193
if (timeout-- == 0) {
122194
LOG_DBG("PHY (%d) autonegotiation timed out", config->addr);
@@ -349,7 +421,13 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
349421
}
350422

351423
/* We are going to reconfigure the phy, don't need to monitor until done */
424+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
425+
if (!config->interrupt_gpio.port) {
426+
k_work_cancel_delayable(&data->phy_monitor_work);
427+
}
428+
#else
352429
k_work_cancel_delayable(&data->phy_monitor_work);
430+
#endif
353431

354432
/* DT configurations */
355433
ret = phy_mc_ksz8081_static_cfg(dev);
@@ -422,6 +500,12 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
422500
/* Unlock mutex */
423501
k_mutex_unlock(&data->mutex);
424502

503+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
504+
if (config->interrupt_gpio.port) {
505+
return ret;
506+
}
507+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
508+
425509
/* Start monitoring */
426510
k_work_reschedule(&data->phy_monitor_work,
427511
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
@@ -450,9 +534,21 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
450534
struct mc_ksz8081_data *data =
451535
CONTAINER_OF(dwork, struct mc_ksz8081_data, phy_monitor_work);
452536
const struct device *dev = data->dev;
537+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
538+
const struct mc_ksz8081_config *config = dev->config;
539+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
453540
struct phy_link_state state = {};
454541
int rc;
455542

543+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
544+
if (config->interrupt_gpio.port) {
545+
rc = phy_mc_ksz8081_clear_interrupt(data);
546+
if (rc < 0) {
547+
return;
548+
}
549+
}
550+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
551+
456552
rc = phy_mc_ksz8081_get_link(dev, &state);
457553

458554
if (rc == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) {
@@ -462,7 +558,12 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
462558
}
463559
}
464560

465-
/* TODO change this to GPIO interrupt driven */
561+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
562+
if (config->interrupt_gpio.port) {
563+
return;
564+
}
565+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
566+
466567
k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
467568
}
468569

@@ -513,6 +614,41 @@ static int phy_mc_ksz8081_init(const struct device *dev)
513614
k_work_init_delayable(&data->phy_monitor_work,
514615
phy_mc_ksz8081_monitor_work_handler);
515616

617+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
618+
if (!config->interrupt_gpio.port) {
619+
phy_mc_ksz8081_monitor_work_handler(&data->phy_monitor_work.work);
620+
goto done;
621+
}
622+
623+
/* Configure interrupt pin */
624+
ret = gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_INPUT);
625+
if (ret < 0) {
626+
goto done;
627+
}
628+
629+
gpio_init_callback(&data->gpio_callback, phy_mc_ksz8081_interrupt_handler,
630+
BIT(config->interrupt_gpio.pin));
631+
if (ret < 0) {
632+
goto done;
633+
}
634+
635+
ret = gpio_add_callback_dt(&config->interrupt_gpio, &data->gpio_callback);
636+
if (ret < 0) {
637+
goto done;
638+
}
639+
640+
ret = phy_mc_ksz8081_config_interrupt(dev);
641+
if (ret < 0) {
642+
goto done;
643+
}
644+
645+
ret = gpio_pin_interrupt_configure_dt(&config->interrupt_gpio,
646+
GPIO_INT_EDGE_TO_ACTIVE);
647+
done:
648+
if (ret < 0) {
649+
LOG_ERR("PHY (%d) config interrupt failed", config->addr);
650+
}
651+
#endif
516652
return 0;
517653
}
518654

0 commit comments

Comments
 (0)