Skip to content

Commit b5c95eb

Browse files
committed
drivers: ethernet: phy: ksz9131: 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 ad9ce93 commit b5c95eb

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

drivers/ethernet/phy/phy_microchip_ksz9131.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ LOG_MODULE_REGISTER(phy_mchp_ksz9131, CONFIG_PHY_LOG_LEVEL);
2121
struct mchp_ksz9131_config {
2222
uint8_t phy_addr;
2323
const struct device * const mdio;
24+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
25+
const struct gpio_dt_spec interrupt_gpio;
26+
#endif
2427
};
2528

2629
struct mchp_ksz9131_data {
2730
const struct device *dev;
2831
phy_callback_t cb;
32+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
33+
struct gpio_callback gpio_callback;
34+
#endif
2935
void *cb_data;
3036
struct k_work_delayable monitor_work;
3137
struct phy_link_state state;
@@ -127,6 +133,64 @@ static int phy_check_ksz9131_id(const struct device *dev)
127133
return ret;
128134
}
129135

136+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
137+
static int phy_mchp_ksz9131_clear_interrupt(struct mchp_ksz9131_data *data)
138+
{
139+
const struct device *dev = data->dev;
140+
const struct mchp_ksz9131_config *cfg = dev->config;
141+
uint16_t reg_val;
142+
int ret;
143+
144+
k_sem_take(&data->sem, K_FOREVER);
145+
146+
/* Read/clear PHY interrupt status register */
147+
ret = ksz9131_read(dev, PHY_KSZ9131_ICS_REG, &reg_val);
148+
if (ret < 0) {
149+
LOG_ERR("Error reading phy (%d) interrupt status register", cfg->phy_addr);
150+
}
151+
152+
k_sem_give(&data->sem);
153+
154+
return ret;
155+
}
156+
157+
static int phy_mchp_ksz9131_config_interrupt(const struct device *dev)
158+
{
159+
struct mchp_ksz9131_data *data = dev->data;
160+
uint16_t reg_val;
161+
int ret;
162+
163+
/* Read Interrupt Control/Status register to write back */
164+
ret = ksz9131_read(dev, PHY_KSZ9131_ICS_REG, &reg_val);
165+
if (ret < 0) {
166+
return ret;
167+
}
168+
reg_val |= PHY_KSZ9131_ICS_LINK_UP_IE_MASK | PHY_KSZ9131_ICS_LINK_DOWN_IE_MASK;
169+
170+
/* Write settings to Interrupt Control/Status register */
171+
ret = ksz9131_write(dev, 27, reg_val);
172+
if (ret < 0) {
173+
return ret;
174+
}
175+
176+
/* Clear interrupt */
177+
ret = phy_mchp_ksz9131_clear_interrupt(data);
178+
179+
return ret;
180+
}
181+
182+
static void phy_mchp_ksz9131_interrupt_handler(const struct device *port, struct gpio_callback *cb,
183+
gpio_port_pins_t pins)
184+
{
185+
struct mchp_ksz9131_data *data = CONTAINER_OF(cb, struct mchp_ksz9131_data, gpio_callback);
186+
int ret = k_work_reschedule(&data->monitor_work, K_NO_WAIT);
187+
188+
if (ret < 0) {
189+
LOG_ERR("Failed to schedule monitor_work from ISR");
190+
}
191+
}
192+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
193+
130194
static int phy_mchp_ksz9131_autonegotiate(const struct device *dev)
131195
{
132196
const struct mchp_ksz9131_config *const cfg = dev->config;
@@ -186,6 +250,9 @@ static int phy_mchp_ksz9131_autonegotiate(const struct device *dev)
186250

187251
static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds)
188252
{
253+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
254+
const struct mchp_ksz9131_config *const cfg = dev->config;
255+
#endif
189256
struct mchp_ksz9131_data *const data = dev->data;
190257
uint16_t anar;
191258
uint16_t c1kt;
@@ -194,7 +261,13 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
194261
k_sem_take(&data->sem, K_FOREVER);
195262

196263
/* We are going to reconfigure the phy, don't need to monitor until done */
264+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
265+
if (!cfg->interrupt_gpio.port) {
266+
k_work_cancel_delayable(&data->monitor_work);
267+
}
268+
#else
197269
k_work_cancel_delayable(&data->monitor_work);
270+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
198271

199272
ret = ksz9131_read(dev, MII_ANAR, &anar);
200273
if (ret < 0) {
@@ -256,6 +329,12 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
256329
done:
257330
k_sem_give(&data->sem);
258331

332+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
333+
if (cfg->interrupt_gpio.port) {
334+
return ret;
335+
}
336+
#endif
337+
259338
/* Start monitoring */
260339
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
261340

@@ -356,9 +435,21 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
356435
struct mchp_ksz9131_data *const data =
357436
CONTAINER_OF(dwork, struct mchp_ksz9131_data, monitor_work);
358437
const struct device *dev = data->dev;
438+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
439+
const struct mchp_ksz9131_config *cfg = dev->config;
440+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
359441
struct phy_link_state state = {};
360442
int ret;
361443

444+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
445+
if (cfg->interrupt_gpio.port) {
446+
ret = phy_mchp_ksz9131_clear_interrupt(data);
447+
if (ret < 0) {
448+
return;
449+
}
450+
}
451+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
452+
362453
ret = phy_mchp_ksz9131_get_link(dev, &state);
363454
if (ret == 0 && (state.speed != data->state.speed || state.is_up != data->state.is_up)) {
364455
memcpy(&data->state, &state, sizeof(struct phy_link_state));
@@ -367,6 +458,12 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
367458
}
368459
}
369460

461+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
462+
if (cfg->interrupt_gpio.port) {
463+
return;
464+
}
465+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
466+
370467
/* Submit delayed work */
371468
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
372469
}
@@ -396,7 +493,37 @@ static int phy_mchp_ksz9131_init(const struct device *dev)
396493

397494
k_work_init_delayable(&data->monitor_work, phy_mchp_ksz9131_monitor_work_handler);
398495

496+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
497+
if (!cfg->interrupt_gpio.port) {
498+
phy_mchp_ksz9131_monitor_work_handler(&data->monitor_work.work);
499+
goto done;
500+
}
501+
502+
/* Configure interrupt pin */
503+
ret = gpio_pin_configure_dt(&cfg->interrupt_gpio, GPIO_INPUT);
504+
if (ret < 0) {
505+
goto done;
506+
}
507+
508+
gpio_init_callback(&data->gpio_callback, phy_mchp_ksz9131_interrupt_handler,
509+
BIT(cfg->interrupt_gpio.pin));
510+
511+
ret = gpio_add_callback_dt(&cfg->interrupt_gpio, &data->gpio_callback);
512+
if (ret < 0) {
513+
goto done;
514+
}
515+
516+
ret = phy_mchp_ksz9131_config_interrupt(dev);
517+
if (ret < 0) {
518+
goto done;
519+
}
520+
521+
ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt_gpio,
522+
GPIO_INT_EDGE_TO_ACTIVE);
523+
done:
524+
#else
399525
phy_mchp_ksz9131_monitor_work_handler(&data->monitor_work.work);
526+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
400527

401528
if (ret < 0) {
402529
LOG_ERR("PHY (%d) init failed", cfg->phy_addr);
@@ -413,10 +540,17 @@ static DEVICE_API(ethphy, mchp_ksz9131_phy_api) = {
413540
.write = phy_mchp_ksz9131_write,
414541
};
415542

543+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
544+
#define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
545+
#else
546+
#define INTERRUPT_GPIO(n)
547+
#endif /* interrupt gpio */
548+
416549
#define MICROCHIP_KSZ9131_INIT(n) \
417550
static const struct mchp_ksz9131_config mchp_ksz9131_##n##_config = { \
418551
.phy_addr = DT_INST_REG_ADDR(n), \
419552
.mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \
553+
INTERRUPT_GPIO(n) \
420554
}; \
421555
\
422556
static struct mchp_ksz9131_data mchp_ksz9131_##n##_data; \

0 commit comments

Comments
 (0)