Skip to content

Commit 1fd4886

Browse files
TonyHan11kartben
authored andcommitted
drivers: ethernet: phy: ksz8081: add support for 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 <[email protected]>
1 parent 44845e1 commit 1fd4886

File tree

1 file changed

+124
-5
lines changed

1 file changed

+124
-5
lines changed

drivers/ethernet/phy/phy_microchip_ksz8081.c

Lines changed: 124 additions & 5 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

@@ -58,10 +62,18 @@ struct mc_ksz8081_config {
5862
#define KSZ8081_SILENCE_DEBUG_LOGS BIT(1)
5963
#define KSZ8081_LINK_STATE_VALID BIT(2)
6064

65+
#define USING_INTERRUPT_GPIO \
66+
UTIL_OR(DT_ALL_INST_HAS_PROP_STATUS_OKAY(int_gpios), \
67+
UTIL_AND(DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios), \
68+
(config->interrupt_gpio.port != NULL)))
69+
6170
struct mc_ksz8081_data {
6271
const struct device *dev;
6372
struct phy_link_state state;
6473
phy_callback_t cb;
74+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
75+
struct gpio_callback gpio_callback;
76+
#endif
6577
void *cb_data;
6678
struct k_mutex mutex;
6779
struct k_work_delayable phy_monitor_work;
@@ -111,6 +123,74 @@ static int phy_mc_ksz8081_write(const struct device *dev,
111123
return 0;
112124
}
113125

126+
#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
127+
#define phy_mc_ksz8081_clear_interrupt(data) 0
128+
#else
129+
static int phy_mc_ksz8081_clear_interrupt(struct mc_ksz8081_data *data)
130+
{
131+
const struct device *dev = data->dev;
132+
const struct mc_ksz8081_config *config = dev->config;
133+
uint32_t ics;
134+
int ret;
135+
136+
/* Lock mutex */
137+
ret = k_mutex_lock(&data->mutex, K_FOREVER);
138+
if (ret < 0) {
139+
LOG_ERR("PHY mutex lock error");
140+
return ret;
141+
}
142+
143+
/* Read/clear PHY interrupt status register */
144+
ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_ICS_REG, &ics);
145+
if (ret < 0) {
146+
LOG_ERR("Error reading phy (%d) interrupt status register", config->addr);
147+
}
148+
149+
/* Unlock mutex */
150+
k_mutex_unlock(&data->mutex);
151+
return ret;
152+
}
153+
154+
static int phy_mc_ksz8081_config_interrupt(const struct device *dev)
155+
{
156+
struct mc_ksz8081_data *data = dev->data;
157+
uint32_t ics;
158+
int ret;
159+
160+
/* Read Interrupt Control/Status register to write back */
161+
ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_ICS_REG, &ics);
162+
if (ret < 0) {
163+
return ret;
164+
}
165+
ics |= PHY_MC_KSZ8081_ICS_LINK_UP_IE_MASK | PHY_MC_KSZ8081_ICS_LINK_DOWN_IE_MASK;
166+
167+
/* Write settings to Interrupt Control/Status register */
168+
ret = phy_mc_ksz8081_write(dev, PHY_MC_KSZ8081_ICS_REG, ics);
169+
if (ret < 0) {
170+
return ret;
171+
}
172+
173+
/* Clear interrupt */
174+
ret = phy_mc_ksz8081_clear_interrupt(data);
175+
if (ret < 0) {
176+
return ret;
177+
}
178+
179+
return ret;
180+
}
181+
182+
static void phy_mc_ksz8081_interrupt_handler(const struct device *port, struct gpio_callback *cb,
183+
gpio_port_pins_t pins)
184+
{
185+
struct mc_ksz8081_data *data = CONTAINER_OF(cb, struct mc_ksz8081_data, gpio_callback);
186+
int ret = k_work_reschedule(&data->phy_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+
114194
static int phy_mc_ksz8081_autonegotiate(const struct device *dev)
115195
{
116196
const struct mc_ksz8081_config *config = dev->config;
@@ -142,7 +222,6 @@ static int phy_mc_ksz8081_autonegotiate(const struct device *dev)
142222
goto done;
143223
}
144224

145-
/* TODO change this to GPIO interrupt driven */
146225
data->flags |= KSZ8081_SILENCE_DEBUG_LOGS;
147226
do {
148227
if (timeout-- == 0) {
@@ -432,6 +511,10 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev, enum phy_link_speed
432511
/* Unlock mutex */
433512
k_mutex_unlock(&data->mutex);
434513

514+
if (USING_INTERRUPT_GPIO) {
515+
return ret;
516+
}
517+
435518
/* Start monitoring */
436519
k_work_reschedule(&data->phy_monitor_work,
437520
K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
@@ -465,6 +548,13 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
465548
bool turn_on_logs = false;
466549
int rc = 0;
467550

551+
if (USING_INTERRUPT_GPIO) {
552+
rc = phy_mc_ksz8081_clear_interrupt(data);
553+
if (rc < 0) {
554+
return;
555+
}
556+
}
557+
468558
if (!data->state.is_up) {
469559
/* some overrides might need set on cold reset way late for some reason */
470560
phy_mc_ksz8081_static_cfg(dev);
@@ -508,21 +598,50 @@ static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work)
508598
data->flags |= KSZ8081_SILENCE_DEBUG_LOGS;
509599
}
510600

511-
/* TODO change this to GPIO interrupt driven */
601+
if (USING_INTERRUPT_GPIO) {
602+
return;
603+
}
604+
512605
k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
513606
}
514607

515608
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
516609
static int ksz8081_init_int_gpios(const struct device *dev)
517610
{
518611
const struct mc_ksz8081_config *config = dev->config;
612+
struct mc_ksz8081_data *data = dev->data;
613+
int ret;
519614

520615
if (config->interrupt_gpio.port == NULL) {
521616
return 0;
522617
}
523618

524-
/* Prevent NAND TREE mode */
525-
return gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_OUTPUT_ACTIVE);
619+
/* Configure interrupt pin */
620+
ret = gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_INPUT);
621+
if (ret < 0) {
622+
goto done;
623+
}
624+
625+
gpio_init_callback(&data->gpio_callback, phy_mc_ksz8081_interrupt_handler,
626+
BIT(config->interrupt_gpio.pin));
627+
628+
ret = gpio_add_callback_dt(&config->interrupt_gpio, &data->gpio_callback);
629+
if (ret < 0) {
630+
goto done;
631+
}
632+
633+
ret = phy_mc_ksz8081_config_interrupt(dev);
634+
if (ret < 0) {
635+
goto done;
636+
}
637+
638+
ret = gpio_pin_interrupt_configure_dt(&config->interrupt_gpio, GPIO_INT_EDGE_TO_ACTIVE);
639+
done:
640+
if (ret < 0) {
641+
LOG_ERR("PHY (%d) config interrupt failed", config->addr);
642+
}
643+
644+
return ret;
526645
}
527646
#else
528647
#define ksz8081_init_int_gpios(dev) 0
@@ -574,7 +693,7 @@ static int phy_mc_ksz8081_init(const struct device *dev)
574693
}
575694

576695
ret = ksz8081_init_int_gpios(dev);
577-
if (ret) {
696+
if (ret < 0) {
578697
return ret;
579698
}
580699

0 commit comments

Comments
 (0)