Skip to content

Commit 828eaba

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 2621638 commit 828eaba

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;
@@ -125,6 +131,64 @@ static int phy_check_ksz9131_id(const struct device *dev)
125131
return ret;
126132
}
127133

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

185249
static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds)
186250
{
251+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
252+
const struct mchp_ksz9131_config *const cfg = dev->config;
253+
#endif
187254
struct mchp_ksz9131_data *const data = dev->data;
188255
uint16_t anar;
189256
uint16_t c1kt;
@@ -192,7 +259,13 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
192259
k_sem_take(&data->sem, K_FOREVER);
193260

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

197270
ret = ksz9131_read(dev, MII_ANAR, &anar);
198271
if (ret < 0) {
@@ -254,6 +327,12 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
254327
done:
255328
k_sem_give(&data->sem);
256329

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

@@ -352,9 +431,21 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
352431
struct mchp_ksz9131_data *const data =
353432
CONTAINER_OF(dwork, struct mchp_ksz9131_data, monitor_work);
354433
const struct device *dev = data->dev;
434+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
435+
const struct mchp_ksz9131_config *cfg = dev->config;
436+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
355437
struct phy_link_state state = {};
356438
int ret;
357439

440+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
441+
if (cfg->interrupt_gpio.port) {
442+
ret = phy_mchp_ksz9131_clear_interrupt(data);
443+
if (ret < 0) {
444+
return;
445+
}
446+
}
447+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
448+
358449
ret = phy_mchp_ksz9131_get_link(dev, &state);
359450
if (ret == 0 && (state.speed != data->state.speed || state.is_up != data->state.is_up)) {
360451
memcpy(&data->state, &state, sizeof(struct phy_link_state));
@@ -363,6 +454,12 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
363454
}
364455
}
365456

457+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
458+
if (cfg->interrupt_gpio.port) {
459+
return;
460+
}
461+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
462+
366463
/* Submit delayed work */
367464
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
368465
}
@@ -392,7 +489,37 @@ static int phy_mchp_ksz9131_init(const struct device *dev)
392489

393490
k_work_init_delayable(&data->monitor_work, phy_mchp_ksz9131_monitor_work_handler);
394491

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

397524
if (ret < 0) {
398525
LOG_ERR("PHY (%d) init failed", cfg->phy_addr);
@@ -409,10 +536,17 @@ static DEVICE_API(ethphy, mchp_ksz9131_phy_api) = {
409536
.write = phy_mchp_ksz9131_write,
410537
};
411538

539+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
540+
#define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
541+
#else
542+
#define INTERRUPT_GPIO(n)
543+
#endif /* interrupt gpio */
544+
412545
#define MICROCHIP_KSZ9131_INIT(n) \
413546
static const struct mchp_ksz9131_config mchp_ksz9131_##n##_config = { \
414547
.phy_addr = DT_INST_REG_ADDR(n), \
415548
.mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \
549+
INTERRUPT_GPIO(n) \
416550
}; \
417551
\
418552
static struct mchp_ksz9131_data mchp_ksz9131_##n##_data; \

0 commit comments

Comments
 (0)