Skip to content

Commit dc8ed73

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 3708ab7 commit dc8ed73

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

drivers/ethernet/phy/phy_microchip_ksz9131.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ struct mchp_ksz9131_config {
2424
uint8_t phy_addr;
2525
const struct device *const mdio;
2626
enum phy_link_speed default_speeds;
27+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
28+
const struct gpio_dt_spec interrupt_gpio;
29+
#endif
2730
};
2831

2932
struct mchp_ksz9131_data {
3033
const struct device *dev;
3134
phy_callback_t cb;
35+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
36+
struct gpio_callback gpio_callback;
37+
#endif
3238
void *cb_data;
3339
struct k_work_delayable monitor_work;
3440
struct phy_link_state state;
@@ -189,6 +195,64 @@ static int phy_mchp_ksz9131_link_status(const struct device *dev, bool *link_up)
189195
return ret;
190196
}
191197

198+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
199+
static int phy_mchp_ksz9131_clear_interrupt(struct mchp_ksz9131_data *data)
200+
{
201+
const struct device *dev = data->dev;
202+
const struct mchp_ksz9131_config *cfg = dev->config;
203+
uint16_t reg_val;
204+
int ret;
205+
206+
k_sem_take(&data->sem, K_FOREVER);
207+
208+
/* Read/clear PHY interrupt status register */
209+
ret = ksz9131_read(dev, PHY_KSZ9131_ICS_REG, &reg_val);
210+
if (ret < 0) {
211+
LOG_ERR("Error reading phy (%d) interrupt status register", cfg->phy_addr);
212+
}
213+
214+
k_sem_give(&data->sem);
215+
216+
return ret;
217+
}
218+
219+
static int phy_mchp_ksz9131_config_interrupt(const struct device *dev)
220+
{
221+
struct mchp_ksz9131_data *data = dev->data;
222+
uint16_t reg_val;
223+
int ret;
224+
225+
/* Read Interrupt Control/Status register to write back */
226+
ret = ksz9131_read(dev, PHY_KSZ9131_ICS_REG, &reg_val);
227+
if (ret < 0) {
228+
return ret;
229+
}
230+
reg_val |= PHY_KSZ9131_ICS_LINK_UP_IE_MASK | PHY_KSZ9131_ICS_LINK_DOWN_IE_MASK;
231+
232+
/* Write settings to Interrupt Control/Status register */
233+
ret = ksz9131_write(dev, PHY_KSZ9131_ICS_REG, reg_val);
234+
if (ret < 0) {
235+
return ret;
236+
}
237+
238+
/* Clear interrupt */
239+
ret = phy_mchp_ksz9131_clear_interrupt(data);
240+
241+
return ret;
242+
}
243+
244+
static void phy_mchp_ksz9131_interrupt_handler(const struct device *port, struct gpio_callback *cb,
245+
gpio_port_pins_t pins)
246+
{
247+
struct mchp_ksz9131_data *data = CONTAINER_OF(cb, struct mchp_ksz9131_data, gpio_callback);
248+
int ret = k_work_reschedule(&data->monitor_work, K_NO_WAIT);
249+
250+
if (ret < 0) {
251+
LOG_ERR("Failed to schedule monitor_work from ISR");
252+
}
253+
}
254+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
255+
192256
static int phy_mchp_ksz9131_autonegotiate(const struct device *dev)
193257
{
194258
const struct mchp_ksz9131_config *const cfg = dev->config;
@@ -247,6 +311,9 @@ static int phy_mchp_ksz9131_autonegotiate(const struct device *dev)
247311
static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds,
248312
enum phy_cfg_link_flag flags)
249313
{
314+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
315+
const struct mchp_ksz9131_config *const cfg = dev->config;
316+
#endif
250317
struct mchp_ksz9131_data *const data = dev->data;
251318
uint16_t c1kt;
252319
int ret;
@@ -259,7 +326,13 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
259326
k_sem_take(&data->sem, K_FOREVER);
260327

261328
/* We are going to reconfigure the phy, don't need to monitor until done */
329+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
330+
if (!cfg->interrupt_gpio.port) {
331+
k_work_cancel_delayable(&data->monitor_work);
332+
}
333+
#else
262334
k_work_cancel_delayable(&data->monitor_work);
335+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
263336

264337
ret = phy_mchp_ksz9131_update_anar(dev, adv_speeds);
265338
if (ret < 0) {
@@ -270,6 +343,12 @@ static int phy_mchp_ksz9131_cfg_link(const struct device *dev, enum phy_link_spe
270343
done:
271344
k_sem_give(&data->sem);
272345

346+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
347+
if (cfg->interrupt_gpio.port) {
348+
return ret;
349+
}
350+
#endif
351+
273352
/* Start monitoring */
274353
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
275354

@@ -359,9 +438,21 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
359438
struct mchp_ksz9131_data *const data =
360439
CONTAINER_OF(dwork, struct mchp_ksz9131_data, monitor_work);
361440
const struct device *dev = data->dev;
441+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
442+
const struct mchp_ksz9131_config *cfg = dev->config;
443+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
362444
struct phy_link_state state = {};
363445
int ret;
364446

447+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
448+
if (cfg->interrupt_gpio.port) {
449+
ret = phy_mchp_ksz9131_clear_interrupt(data);
450+
if (ret < 0) {
451+
return;
452+
}
453+
}
454+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
455+
365456
ret = phy_mchp_ksz9131_get_link(dev, &state);
366457
if (ret == 0 && (state.speed != data->state.speed || state.is_up != data->state.is_up)) {
367458
memcpy(&data->state, &state, sizeof(struct phy_link_state));
@@ -370,6 +461,12 @@ static void phy_mchp_ksz9131_monitor_work_handler(struct k_work *work)
370461
}
371462
}
372463

464+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
465+
if (cfg->interrupt_gpio.port) {
466+
return;
467+
}
468+
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
469+
373470
/* Submit delayed work */
374471
k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
375472
}
@@ -399,7 +496,36 @@ static int phy_mchp_ksz9131_init(const struct device *dev)
399496

400497
k_work_init_delayable(&data->monitor_work, phy_mchp_ksz9131_monitor_work_handler);
401498

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

404530
if (ret < 0) {
405531
LOG_ERR("PHY (%d) init failed", cfg->phy_addr);
@@ -418,11 +544,18 @@ static DEVICE_API(ethphy, mchp_ksz9131_phy_api) = {
418544
.write = phy_mchp_ksz9131_write,
419545
};
420546

547+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
548+
#define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
549+
#else
550+
#define INTERRUPT_GPIO(n)
551+
#endif /* interrupt gpio */
552+
421553
#define MICROCHIP_KSZ9131_INIT(n) \
422554
static const struct mchp_ksz9131_config mchp_ksz9131_##n##_config = { \
423555
.phy_addr = DT_INST_REG_ADDR(n), \
424556
.mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \
425557
.default_speeds = PHY_INST_GENERATE_DEFAULT_SPEEDS(n), \
558+
INTERRUPT_GPIO(n) \
426559
}; \
427560
\
428561
static struct mchp_ksz9131_data mchp_ksz9131_##n##_data; \

0 commit comments

Comments
 (0)