Skip to content

Commit a3610af

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 e8c379a commit a3610af

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

drivers/ethernet/phy/phy_microchip_ksz9131.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@ LOG_MODULE_REGISTER(phy_mchp_ksz9131, CONFIG_PHY_LOG_LEVEL);
2828
struct mchp_ksz9131_config {
2929
uint8_t phy_addr;
3030
const struct device * const mdio;
31+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
32+
const struct gpio_dt_spec interrupt_gpio;
33+
#endif
3134
};
3235

3336
struct mchp_ksz9131_data {
3437
const struct device *dev;
3538
phy_callback_t cb;
39+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
40+
struct gpio_callback gpio_callback;
41+
#endif
3642
void *cb_data;
3743
struct k_work_delayable monitor_work;
3844
struct phy_link_state state;
@@ -128,6 +134,67 @@ static int phy_check_ksz9131_id(const struct device *dev)
128134
return ret;
129135
}
130136

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

184251
static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds)
185252
{
253+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
254+
const struct mchp_ksz9131_config *const cfg = dev->config;
255+
#endif
186256
struct mchp_ksz9131_data *const data = dev->data;
187257
uint16_t anar;
188258
uint16_t c1kt;
@@ -191,7 +261,13 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
191261
k_sem_take(&data->sem, K_FOREVER);
192262

193263
/* 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
194269
k_work_cancel_delayable(&data->monitor_work);
270+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
195271

196272
do {
197273
BREAK_IF_ERR(ret, ksz9131_read(dev, MII_ANAR, &anar));
@@ -243,6 +319,12 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
243319

244320
k_sem_give(&data->sem);
245321

322+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
323+
if (cfg->interrupt_gpio.port) {
324+
return ret;
325+
}
326+
#endif
327+
246328
/* Start monitoring */
247329
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
248330

@@ -331,9 +413,21 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
331413
struct mchp_ksz9131_data *const data =
332414
CONTAINER_OF(dwork, struct mchp_ksz9131_data, monitor_work);
333415
const struct device *dev = data->dev;
416+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
417+
const struct mchp_ksz9131_config *cfg = dev->config;
418+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
334419
struct phy_link_state state = {};
335420
int ret;
336421

422+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
423+
if (cfg->interrupt_gpio.port) {
424+
ret = phy_mchp_ksz9131_clear_interrupt(data);
425+
if (ret) {
426+
return;
427+
}
428+
}
429+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
430+
337431
ret = phy_mchp_ksz9131_get_link(dev, &state);
338432
if (ret == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) {
339433
memcpy(&data->state, &state, sizeof(struct phy_link_state));
@@ -342,6 +436,12 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
342436
}
343437
}
344438

439+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
440+
if (cfg->interrupt_gpio.port) {
441+
return;
442+
}
443+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
444+
345445
/* Submit delayed work */
346446
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
347447
}
@@ -371,7 +471,30 @@ static int phy_mchp_ksz9131_init(const struct device *dev)
371471

372472
k_work_init_delayable(&data->monitor_work, phy_mchp_ksz9131_monitor_work_handler);
373473

474+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
475+
do {
476+
if (!cfg->interrupt_gpio.port) {
477+
phy_mchp_ksz9131_monitor_work_handler(&data->monitor_work.work);
478+
break;
479+
}
480+
481+
/* Configure interrupt pin */
482+
BREAK_IF_ERR(ret, gpio_pin_configure_dt(&cfg->interrupt_gpio, GPIO_INPUT));
483+
484+
gpio_init_callback(&data->gpio_callback, phy_mchp_ksz9131_interrupt_handler,
485+
BIT(cfg->interrupt_gpio.pin));
486+
487+
BREAK_IF_ERR(ret, gpio_add_callback_dt(&cfg->interrupt_gpio,
488+
&data->gpio_callback));
489+
490+
BREAK_IF_ERR(ret, phy_mchp_ksz9131_config_interrupt(dev));
491+
492+
ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt_gpio,
493+
GPIO_INT_EDGE_TO_ACTIVE);
494+
} while (0);
495+
#else
374496
phy_mchp_ksz9131_monitor_work_handler(&data->monitor_work.work);
497+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
375498

376499
if (ret) {
377500
LOG_ERR("PHY (%d) init failed", cfg->phy_addr);
@@ -388,10 +511,17 @@ static DEVICE_API(ethphy, mchp_ksz9131_phy_api) = {
388511
.write = phy_mchp_ksz9131_write,
389512
};
390513

514+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
515+
#define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
516+
#else
517+
#define INTERRUPT_GPIO(n)
518+
#endif /* interrupt gpio */
519+
391520
#define MICROCHIP_KSZ9131_INIT(n) \
392521
static const struct mchp_ksz9131_config mchp_ksz9131_##n##_config = { \
393522
.phy_addr = DT_INST_REG_ADDR(n), \
394523
.mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \
524+
INTERRUPT_GPIO(n) \
395525
}; \
396526
\
397527
static struct mchp_ksz9131_data mchp_ksz9131_##n##_data; \

0 commit comments

Comments
 (0)