Skip to content

Commit 3708ab7

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 37cd1a1 commit 3708ab7

File tree

1 file changed

+135
-2
lines changed

1 file changed

+135
-2
lines changed

drivers/ethernet/phy/phy_microchip_ksz8081.c

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
3131
#define PHY_MC_KSZ8081_OMSO_RMII_OVERRIDE_MASK BIT(1)
3232
#define PHY_MC_KSZ8081_OMSO_MII_OVERRIDE_MASK BIT(0)
3333

34+
#define PHY_MC_KSZ8081_ICS_REG 0x1B
35+
#define PHY_MC_KSZ8081_ICS_LINK_DOWN_IE_MASK BIT(10)
36+
#define PHY_MC_KSZ8081_ICS_LINK_UP_IE_MASK BIT(8)
37+
3438
#define PHY_MC_KSZ8081_CTRL2_REG 0x1F
3539
#define PHY_MC_KSZ8081_CTRL2_REF_CLK_SEL BIT(7)
3640

@@ -57,6 +61,9 @@ struct mc_ksz8081_data {
5761
const struct device *dev;
5862
struct phy_link_state state;
5963
phy_callback_t cb;
64+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
65+
struct gpio_callback gpio_callback;
66+
#endif
6067
void *cb_data;
6168
struct k_mutex mutex;
6269
struct k_work_delayable phy_monitor_work;
@@ -93,6 +100,72 @@ static int phy_mc_ksz8081_write(const struct device *dev,
93100
return 0;
94101
}
95102

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

122-
/* TODO change this to GPIO interrupt driven */
123195
do {
124196
if (timeout-- == 0) {
125197
LOG_DBG("PHY (%d) autonegotiation timed out", config->addr);
@@ -356,7 +428,13 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev, enum phy_link_speed
356428
}
357429

358430
/* We are going to reconfigure the phy, don't need to monitor until done */
431+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
432+
if (!config->interrupt_gpio.port) {
433+
k_work_cancel_delayable(&data->phy_monitor_work);
434+
}
435+
#else
359436
k_work_cancel_delayable(&data->phy_monitor_work);
437+
#endif
360438

361439
/* DT configurations */
362440
ret = phy_mc_ksz8081_static_cfg(dev);
@@ -399,6 +477,12 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev, enum phy_link_speed
399477
/* Unlock mutex */
400478
k_mutex_unlock(&data->mutex);
401479

480+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
481+
if (config->interrupt_gpio.port) {
482+
return ret;
483+
}
484+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
485+
402486
/* Start monitoring */
403487
k_work_reschedule(&data->phy_monitor_work,
404488
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
@@ -427,9 +511,21 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
427511
struct mc_ksz8081_data *data =
428512
CONTAINER_OF(dwork, struct mc_ksz8081_data, phy_monitor_work);
429513
const struct device *dev = data->dev;
514+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
515+
const struct mc_ksz8081_config *config = dev->config;
516+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
430517
struct phy_link_state state = {};
431518
int rc;
432519

520+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
521+
if (config->interrupt_gpio.port) {
522+
rc = phy_mc_ksz8081_clear_interrupt(data);
523+
if (rc < 0) {
524+
return;
525+
}
526+
}
527+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
528+
433529
rc = phy_mc_ksz8081_get_link(dev, &state);
434530

435531
if (rc == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) {
@@ -439,7 +535,12 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
439535
}
440536
}
441537

442-
/* TODO change this to GPIO interrupt driven */
538+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
539+
if (config->interrupt_gpio.port) {
540+
return;
541+
}
542+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
543+
443544
k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
444545
}
445546

@@ -490,6 +591,38 @@ static int phy_mc_ksz8081_init(const struct device *dev)
490591
k_work_init_delayable(&data->phy_monitor_work,
491592
phy_mc_ksz8081_monitor_work_handler);
492593

594+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
595+
if (!config->interrupt_gpio.port) {
596+
goto done;
597+
}
598+
599+
/* Configure interrupt pin */
600+
ret = gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_INPUT);
601+
if (ret < 0) {
602+
goto done;
603+
}
604+
605+
gpio_init_callback(&data->gpio_callback, phy_mc_ksz8081_interrupt_handler,
606+
BIT(config->interrupt_gpio.pin));
607+
608+
ret = gpio_add_callback_dt(&config->interrupt_gpio, &data->gpio_callback);
609+
if (ret < 0) {
610+
goto done;
611+
}
612+
613+
ret = phy_mc_ksz8081_config_interrupt(dev);
614+
if (ret < 0) {
615+
goto done;
616+
}
617+
618+
ret = gpio_pin_interrupt_configure_dt(&config->interrupt_gpio, GPIO_INT_EDGE_TO_ACTIVE);
619+
done:
620+
if (ret < 0) {
621+
LOG_ERR("PHY (%d) config interrupt failed", config->addr);
622+
return ret;
623+
}
624+
#endif
625+
493626
/* Advertise default speeds */
494627
phy_mc_ksz8081_cfg_link(dev, config->default_speeds, 0);
495628

0 commit comments

Comments
 (0)