diff --git a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi index e057aeb1ef6..49c4d164c81 100644 --- a/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi +++ b/boards/arm/mr_canhubk3/mr_canhubk3-pinctrl.dtsi @@ -9,7 +9,7 @@ &pinctrl { eirq0_default: eirq0_default { group1 { - pinmux = , , ; + pinmux = , , , ; input-enable; }; }; diff --git a/boards/arm/mr_canhubk3/mr_canhubk3.dts b/boards/arm/mr_canhubk3/mr_canhubk3.dts index 97bca1402b4..b0866f7073a 100644 --- a/boards/arm/mr_canhubk3/mr_canhubk3.dts +++ b/boards/arm/mr_canhubk3/mr_canhubk3.dts @@ -443,6 +443,7 @@ pinctrl-names = "default"; phy-connection-type = "rmii"; local-mac-address = [02 04 9f aa bb cc]; + phy-handle = <&phy>; status = "okay"; }; @@ -455,6 +456,7 @@ compatible = "nxp,tja1103"; status = "okay"; reg = <0x12>; + int-gpios = <&gpiod_l 5 GPIO_ACTIVE_LOW>; master-slave = "slave"; }; }; diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index ad7cef7bc17..e14a27a398d 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -11,6 +11,7 @@ module-dep = LOG module-str = Log level for Ethernet PHY driver module-help = Sets log level for Ethernet PHY Device Drivers. source "subsys/net/Kconfig.template.log_config.net" +source "drivers/ethernet/phy/Kconfig.tja1103" config PHY_INIT_PRIORITY int "Ethernet PHY driver init priority" @@ -38,14 +39,6 @@ config PHY_ADIN2111 help Enable ADIN2111 PHY driver. -config PHY_TJA1103 - bool "TJA1103 PHY driver" - default y - depends on DT_HAS_NXP_TJA1103_ENABLED - depends on MDIO - help - Enable TJA1103 PHY driver. - config PHY_MICROCHIP_KSZ8081 bool "Microchip KSZ8081 Phy Driver" default y diff --git a/drivers/ethernet/phy/Kconfig.tja1103 b/drivers/ethernet/phy/Kconfig.tja1103 new file mode 100644 index 00000000000..e0a00955ce7 --- /dev/null +++ b/drivers/ethernet/phy/Kconfig.tja1103 @@ -0,0 +1,30 @@ +# NXP PHY TJA1103 driver configuration options + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PHY_TJA1103 + bool "TJA1103 PHY driver" + default y + depends on DT_HAS_NXP_TJA1103_ENABLED + depends on MDIO + help + Enable TJA1103 PHY driver. + +if PHY_TJA1103 + +config PHY_TJA1103_IRQ_THREAD_STACK_SIZE + int "Stack size for a thread that processes TJA1103 IRQ" + default 2048 + help + Size of the stack used for internal thread which is ran to + process raised INT IRQ. + +config PHY_TJA1103_IRQ_THREAD_PRIO + int "Priority for internal incoming packet handler" + default 2 + help + Priority level for internal thread which is ran for TJA1103 + INT IRQ processing. + +endif # PHY_TJA1103 diff --git a/drivers/ethernet/phy/phy_tja1103.c b/drivers/ethernet/phy/phy_tja1103.c index 946a4d16200..659fdbf80aa 100644 --- a/drivers/ethernet/phy/phy_tja1103.c +++ b/drivers/ethernet/phy/phy_tja1103.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -40,15 +41,39 @@ LOG_MODULE_REGISTER(phy_tja1103, CONFIG_PHY_LOG_LEVEL); #define TJA1103_PHY_STATUS (0x8102U) #define TJA1103_PHY_STATUS_LINK_STAT BIT(2) +/* Shared - PHY functional IRQ masked status register */ +#define TJA1103_PHY_FUNC_IRQ_MSTATUS (0x80A2) +#define TJA1103_PHY_FUNC_IRQ_LINK_EVENT BIT(1) +#define TJA1103_PHY_FUNC_IRQ_LINK_AVAIL BIT(2) +/* Shared -PHY functional IRQ source & enable registers */ +#define TJA1103_PHY_FUNC_IRQ_ACK (0x80A0) +#define TJA1103_PHY_FUNC_IRQ_EN (0x80A1) +#define TJA1103_PHY_FUNC_IRQ_LINK_EVENT_EN BIT(1) +#define TJA1103_PHY_FUNC_IRQ_LINK_AVAIL_EN BIT(2) +/* Always accessible reg for NMIs */ +#define TJA1103_ALWAYS_ACCESSIBLE (0x801F) +#define TJA1103_ALWAYS_ACCESSIBLE_FUSA_PASS_IRQ BIT(4) + struct phy_tja1103_config { const struct device *mdio; + struct gpio_dt_spec gpio_interrupt; uint8_t phy_addr; uint8_t master_slave; }; struct phy_tja1103_data { + const struct device *dev; struct phy_link_state state; struct k_sem sem; + struct k_sem offload_sem; + phy_callback_t cb; + struct gpio_callback phy_tja1103_int_callback; + void *cb_data; + + K_KERNEL_STACK_MEMBER(irq_thread_stack, CONFIG_PHY_TJA1103_IRQ_THREAD_STACK_SIZE); + struct k_thread irq_thread; + + struct k_work_delayable monitor_work; }; static inline int phy_tja1103_c22_read(const struct device *dev, uint16_t reg, uint16_t *val) @@ -128,23 +153,189 @@ static int phy_tja1103_id(const struct device *dev, uint32_t *phy_id) return 0; } +static int update_link_state(const struct device *dev) +{ + struct phy_tja1103_data *const data = dev->data; + bool link_up; + uint16_t val; + + if (phy_tja1103_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1, TJA1103_PHY_STATUS, &val) < 0) { + return -EIO; + } + + link_up = (val & TJA1103_PHY_STATUS_LINK_STAT) != 0; + + /* Let workqueue re-schedule and re-check if the + * link status is unchanged this time + */ + if (data->state.is_up == link_up) { + return -EAGAIN; + } + + data->state.is_up = link_up; + + return 0; +} + static int phy_tja1103_get_link_state(const struct device *dev, struct phy_link_state *state) { struct phy_tja1103_data *const data = dev->data; - uint16_t val; + const struct phy_tja1103_config *const cfg = dev->config; + int rc = 0; k_sem_take(&data->sem, K_FOREVER); - if (phy_tja1103_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1, TJA1103_PHY_STATUS, &val) >= 0) { - - data->state.is_up = (val & TJA1103_PHY_STATUS_LINK_STAT) != 0; - - memcpy(state, &data->state, sizeof(struct phy_link_state)); + /* If Interrupt is configured then the workqueue will not + * update the link state periodically so do it explicitly + */ + if (cfg->gpio_interrupt.port != NULL) { + rc = update_link_state(dev); } + memcpy(state, &data->state, sizeof(struct phy_link_state)); + k_sem_give(&data->sem); - return 0; + return rc; +} + +static void invoke_link_cb(const struct device *dev) +{ + struct phy_tja1103_data *const data = dev->data; + struct phy_link_state state; + + if (data->cb == NULL) { + return; + } + + /* Send callback only on link state change */ + if (phy_tja1103_get_link_state(dev, &state) != 0) { + return; + } + + data->cb(dev, &state, data->cb_data); +} + +static void monitor_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct phy_tja1103_data *const data = + CONTAINER_OF(dwork, struct phy_tja1103_data, monitor_work); + const struct device *dev = data->dev; + int rc; + + k_sem_take(&data->sem, K_FOREVER); + + rc = update_link_state(dev); + + k_sem_give(&data->sem); + + /* If link state has changed and a callback is set, invoke callback */ + if (rc == 0) { + invoke_link_cb(dev); + } + + /* Submit delayed work */ + k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); +} + +static void phy_tja1103_irq_offload_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + const struct device *dev = p1; + struct phy_tja1103_data *const data = dev->data; + uint16_t irq; + + for (;;) { + /* await trigger from ISR */ + k_sem_take(&data->offload_sem, K_FOREVER); + + if (phy_tja1103_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1, + TJA1103_PHY_FUNC_IRQ_MSTATUS, &irq) < 0) { + return; + } + + /* Handling Link related Functional IRQs */ + if (irq & (TJA1103_PHY_FUNC_IRQ_LINK_EVENT | TJA1103_PHY_FUNC_IRQ_LINK_AVAIL)) { + /* Send callback to MAC on link status changed */ + invoke_link_cb(dev); + + /* Ack the assered link related interrupts */ + phy_tja1103_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC1, + TJA1103_PHY_FUNC_IRQ_ACK, irq); + } + } +} + +static void phy_tja1103_handle_irq(const struct device *port, struct gpio_callback *cb, + uint32_t pins) +{ + ARG_UNUSED(pins); + ARG_UNUSED(port); + + struct phy_tja1103_data *const data = + CONTAINER_OF(cb, struct phy_tja1103_data, phy_tja1103_int_callback); + + /* Trigger BH before leaving the ISR */ + k_sem_give(&data->offload_sem); +} + +static void phy_tja1103_cfg_irq_poll(const struct device *dev) +{ + struct phy_tja1103_data *const data = dev->data; + const struct phy_tja1103_config *const cfg = dev->config; + int ret; + + if (cfg->gpio_interrupt.port != NULL) { + if (!gpio_is_ready_dt(&cfg->gpio_interrupt)) { + LOG_ERR("Interrupt GPIO device %s is not ready", + cfg->gpio_interrupt.port->name); + return; + } + + ret = gpio_pin_configure_dt(&cfg->gpio_interrupt, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Failed to configure interrupt GPIO, %d", ret); + return; + } + + gpio_init_callback(&(data->phy_tja1103_int_callback), phy_tja1103_handle_irq, + BIT(cfg->gpio_interrupt.pin)); + + /* Add callback structure to global syslist */ + ret = gpio_add_callback(cfg->gpio_interrupt.port, &data->phy_tja1103_int_callback); + if (ret < 0) { + LOG_ERR("Failed to add INT callback, %d", ret); + return; + } + + ret = phy_tja1103_c45_write( + dev, MDIO_MMD_VENDOR_SPECIFIC1, TJA1103_PHY_FUNC_IRQ_EN, + (TJA1103_PHY_FUNC_IRQ_LINK_EVENT_EN | TJA1103_PHY_FUNC_IRQ_LINK_AVAIL_EN)); + if (ret < 0) { + return; + } + + ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_interrupt, GPIO_INT_EDGE_FALLING); + if (ret < 0) { + LOG_ERR("Failed to enable INT, %d", ret); + return; + } + + /* PHY initialized, IRQ configured, now initialize the BH handler */ + k_thread_create(&data->irq_thread, data->irq_thread_stack, + CONFIG_PHY_TJA1103_IRQ_THREAD_STACK_SIZE, + phy_tja1103_irq_offload_thread, (void *)dev, NULL, NULL, + CONFIG_PHY_TJA1103_IRQ_THREAD_PRIO, K_ESSENTIAL, K_NO_WAIT); + k_thread_name_set(&data->irq_thread, "phy_tja1103_irq_offload"); + + } else { + k_work_init_delayable(&data->monitor_work, monitor_work_handler); + + monitor_work_handler(&data->monitor_work.work); + } } static int phy_tja1103_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds) @@ -166,12 +357,14 @@ static int phy_tja1103_init(const struct device *dev) uint16_t val; int ret; + data->dev = dev; + data->cb = NULL; data->state.is_up = false; data->state.speed = LINK_FULL_100BASE_T; ret = WAIT_FOR(!phy_tja1103_id(dev, &phy_id) && phy_id == TJA1103_ID, - TJA1103_AWAIT_RETRY_COUNT * TJA1103_AWAIT_DELAY_POLL_US, - k_sleep(K_USEC(TJA1103_AWAIT_DELAY_POLL_US))); + TJA1103_AWAIT_RETRY_COUNT * TJA1103_AWAIT_DELAY_POLL_US, + k_sleep(K_USEC(TJA1103_AWAIT_DELAY_POLL_US))); if (ret < 0) { LOG_ERR("Unable to obtain PHY ID for device 0x%x", cfg->phy_addr); return -ENODEV; @@ -208,19 +401,38 @@ static int phy_tja1103_init(const struct device *dev) return ret; } - /* Wait for settings to go in affect before checking link */ - k_sleep(K_MSEC(400)); + /* Check always accesible register for handling NMIs */ + ret = phy_tja1103_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1, TJA1103_ALWAYS_ACCESSIBLE, &val); + if (ret < 0) { + return ret; + } + + /* Ack Fusa Pass Interrupt if Startup Self Test Passed successfully */ + if (val & TJA1103_ALWAYS_ACCESSIBLE_FUSA_PASS_IRQ) { + ret = phy_tja1103_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC1, + TJA1103_ALWAYS_ACCESSIBLE, + TJA1103_ALWAYS_ACCESSIBLE_FUSA_PASS_IRQ); + } + + /* Configure interrupt or poll mode for reporting link changes */ + phy_tja1103_cfg_irq_poll(dev); - phy_tja1103_get_link_state(dev, &data->state); return ret; } static int phy_tja1103_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data) { - ARG_UNUSED(dev); - ARG_UNUSED(cb); - ARG_UNUSED(user_data); - return -ENOTSUP; + struct phy_tja1103_data *const data = dev->data; + + data->cb = cb; + data->cb_data = user_data; + + /* Invoke the callback to notify the caller of the current + * link status. + */ + invoke_link_cb(dev); + + return 0; } static const struct ethphy_driver_api phy_tja1103_api = { @@ -235,10 +447,12 @@ static const struct ethphy_driver_api phy_tja1103_api = { static const struct phy_tja1103_config phy_tja1103_config_##n = { \ .phy_addr = DT_INST_REG_ADDR(n), \ .mdio = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .gpio_interrupt = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}), \ .master_slave = DT_INST_ENUM_IDX(n, master_slave), \ }; \ static struct phy_tja1103_data phy_tja1103_data_##n = { \ .sem = Z_SEM_INITIALIZER(phy_tja1103_data_##n.sem, 1, 1), \ + .offload_sem = Z_SEM_INITIALIZER(phy_tja1103_data_##n.offload_sem, 0, 1), \ }; \ DEVICE_DT_INST_DEFINE(n, &phy_tja1103_init, NULL, &phy_tja1103_data_##n, \ &phy_tja1103_config_##n, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \ diff --git a/dts/bindings/ethernet/nxp,tja1103.yaml b/dts/bindings/ethernet/nxp,tja1103.yaml index a15c9be3aba..514dec3d376 100644 --- a/dts/bindings/ethernet/nxp,tja1103.yaml +++ b/dts/bindings/ethernet/nxp,tja1103.yaml @@ -12,6 +12,11 @@ properties: required: true description: PHY address + int-gpios: + type: phandle-array + description: + interrupt GPIO for PHY. Will be pulled high in its default state. + master-slave: type: string required: true