diff --git a/drivers/ethernet/phy/CMakeLists.txt b/drivers/ethernet/phy/CMakeLists.txt index 34d769eca6c..38efd692c2b 100644 --- a/drivers/ethernet/phy/CMakeLists.txt +++ b/drivers/ethernet/phy/CMakeLists.txt @@ -13,5 +13,6 @@ zephyr_library_sources_ifdef(CONFIG_PHY_QUALCOMM_AR8031 phy_qualcomm_ar8031.c) zephyr_library_sources_ifdef(CONFIG_PHY_REALTEK_RTL8211F phy_realtek_rtl8211f.c) zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83825 phy_ti_dp83825.c) zephyr_library_sources_ifdef(CONFIG_PHY_TI_DP83867 phy_ti_dp83867.c) -zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c) +zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c) +zephyr_library_sources_ifdef(CONFIG_PHY_TJA11XX phy_tja11xx.c) # zephyr-keep-sorted-stop diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index c9d0c61baa2..4caf58d1f53 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -14,6 +14,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.tja11xx" source "drivers/ethernet/phy/Kconfig.tja1103" source "drivers/ethernet/phy/Kconfig.dm8806" source "drivers/ethernet/phy/Kconfig.microchip_t1s" diff --git a/drivers/ethernet/phy/Kconfig.tja11xx b/drivers/ethernet/phy/Kconfig.tja11xx new file mode 100644 index 00000000000..b4cff9b7485 --- /dev/null +++ b/drivers/ethernet/phy/Kconfig.tja11xx @@ -0,0 +1,12 @@ +# NXP PHY TJA11xx driver configuration options + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config PHY_TJA11XX + bool "TJA11XX PHY driver" + default y + depends on DT_HAS_NXP_TJA11XX_ENABLED + depends on MDIO + help + Enable TJA11xx PHY driver. diff --git a/drivers/ethernet/phy/phy_tja11xx.c b/drivers/ethernet/phy/phy_tja11xx.c new file mode 100644 index 00000000000..f4bd2c72a39 --- /dev/null +++ b/drivers/ethernet/phy/phy_tja11xx.c @@ -0,0 +1,254 @@ +/* + * Copyright 2023 NXP + * Copyright 2023 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_tja11xx + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(phy_tja11xx, CONFIG_PHY_LOG_LEVEL); + +/* Extended control register */ +#define TJA11XX_EXTENDED_CONTROL 0x0017U +/* Configuration register 1 */ +#define TJA11XX_CONFIGURATION_1 0x0018U + +struct phy_tja11xx_config { + const struct device *mdio; + uint8_t phy_addr; +}; + +struct phy_tja11xx_data { + const struct device *dev; + struct phy_link_state state; + struct k_sem sem; + phy_callback_t cb; + void *cb_data; + + struct k_work_delayable monitor_work; +}; + +static inline int phy_tja11xx_c22_read(const struct device *dev, uint16_t reg, uint16_t *val) +{ + const struct phy_tja11xx_config *const cfg = dev->config; + + return mdio_read(cfg->mdio, cfg->phy_addr, reg, val); +} + +static inline int phy_tja11xx_c22_write(const struct device *dev, uint16_t reg, uint16_t val) +{ + const struct phy_tja11xx_config *const cfg = dev->config; + + return mdio_write(cfg->mdio, cfg->phy_addr, reg, val); +} + +static int phy_tja11xx_reg_read(const struct device *dev, uint16_t reg_addr, uint32_t *data) +{ + const struct phy_tja11xx_config *cfg = dev->config; + int ret; + + mdio_bus_enable(cfg->mdio); + + ret = phy_tja11xx_c22_read(dev, reg_addr, (uint16_t *)data); + + mdio_bus_disable(cfg->mdio); + + return ret; +} + +static int phy_tja11xx_reg_write(const struct device *dev, uint16_t reg_addr, uint32_t data) +{ + const struct phy_tja11xx_config *cfg = dev->config; + int ret; + + mdio_bus_enable(cfg->mdio); + + ret = phy_tja11xx_c22_write(dev, reg_addr, (uint16_t)data); + + mdio_bus_disable(cfg->mdio); + + return ret; +} + +static int update_link_state(const struct device *dev) +{ + struct phy_tja11xx_data *const data = dev->data; + bool link_up; + uint16_t val; + + if (phy_tja11xx_c22_read(dev, MII_BMSR, &val) < 0) { + return -EIO; + } + + link_up = (val & MII_BMSR_LINK_STATUS) != 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_tja11xx_get_link_state(const struct device *dev, struct phy_link_state *state) +{ + struct phy_tja11xx_data *const data = dev->data; + int rc = 0; + + k_sem_take(&data->sem, K_FOREVER); + + memcpy(state, &data->state, sizeof(struct phy_link_state)); + + k_sem_give(&data->sem); + + return rc; +} + +static void invoke_link_cb(const struct device *dev) +{ + struct phy_tja11xx_data *const data = dev->data; + struct phy_link_state state; + + if (data->cb == NULL) { + return; + } + + /* Send callback only on link state change */ + if (phy_tja11xx_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_tja11xx_data *const data = + CONTAINER_OF(dwork, struct phy_tja11xx_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_tja11xx_cfg_irq_poll(const struct device *dev) +{ + struct phy_tja11xx_data *const data = dev->data; + + k_work_init_delayable(&data->monitor_work, monitor_work_handler); + + monitor_work_handler(&data->monitor_work.work); +} + +static int phy_tja11xx_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds) +{ + ARG_UNUSED(dev); + + if (adv_speeds & LINK_FULL_100BASE) { + return 0; + } + + return -ENOTSUP; +} + +static int phy_tja11xx_init(const struct device *dev) +{ + struct phy_tja11xx_data *const data = dev->data; + int ret; + + data->dev = dev; + data->cb = NULL; + data->state.is_up = false; + data->state.speed = LINK_FULL_100BASE; + + ret = phy_tja11xx_reg_write(dev, TJA11XX_EXTENDED_CONTROL, 0x1804); + if (ret < 0) { + return ret; + } + + ret = phy_tja11xx_reg_write(dev, MII_BMCR, 0x2100); + if (ret < 0) { + return ret; + } + + ret = phy_tja11xx_reg_write(dev, TJA11XX_CONFIGURATION_1, 0x8A00); + if (ret < 0) { + return ret; + } + + ret = phy_tja11xx_reg_write(dev, TJA11XX_EXTENDED_CONTROL, 0x9804); + if (ret < 0) { + return ret; + } + phy_tja11xx_cfg_irq_poll(dev); + + return ret; +} + +static int phy_tja11xx_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data) +{ + struct phy_tja11xx_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_tja11xx_api = { + .get_link = phy_tja11xx_get_link_state, + .cfg_link = phy_tja11xx_cfg_link, + .link_cb_set = phy_tja11xx_link_cb_set, + .read = phy_tja11xx_reg_read, + .write = phy_tja11xx_reg_write, +}; + +#define TJA11xx_INITIALIZE(n) \ + static const struct phy_tja11xx_config phy_tja11xx_config_##n = { \ + .phy_addr = DT_INST_REG_ADDR(n), \ + .mdio = DEVICE_DT_GET(DT_INST_BUS(n)) \ + }; \ + static struct phy_tja11xx_data phy_tja11xx_data_##n = { \ + .sem = Z_SEM_INITIALIZER(phy_tja11xx_data_##n.sem, 1, 1), \ + }; \ + DEVICE_DT_INST_DEFINE(n, &phy_tja11xx_init, NULL, &phy_tja11xx_data_##n, \ + &phy_tja11xx_config_##n, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \ + &phy_tja11xx_api); + +DT_INST_FOREACH_STATUS_OKAY(TJA11xx_INITIALIZE) diff --git a/dts/arm/nxp/nxp_s32k148.dtsi b/dts/arm/nxp/nxp_s32k148.dtsi index 7f09ed1ce06..7f24ffae40b 100644 --- a/dts/arm/nxp/nxp_s32k148.dtsi +++ b/dts/arm/nxp/nxp_s32k148.dtsi @@ -30,6 +30,33 @@ compatible = "mmio-sram"; reg = <0x20000000 DT_SIZE_K(124)>; }; + + enet: ethernet@40079000 { + compatible = "nxp,enet"; + reg = <0x40079000 0x628>; + clocks = <&clock NXP_S32_ENET_CLK>; + + enet_mac: ethernet { + compatible = "nxp,enet-mac"; + interrupts = <73 0>, <74 0>, <75 0>; + interrupt-names = "TX", "RX", "ERR"; + nxp,mdio = <&enet_mdio>; + nxp,ptp-clock = <&enet_ptp_clock>; + phy-connection-type = "rmii"; + }; + + enet_mdio: mdio { + compatible = "nxp,enet-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + + enet_ptp_clock: ptp_clock { + compatible = "nxp,enet-ptp-clock"; + interrupts = <72 0>; + clocks = <&clock NXP_S32_ENET_CLK>; + }; + }; }; }; diff --git a/dts/bindings/ethernet/nxp,tja11xx.yaml b/dts/bindings/ethernet/nxp,tja11xx.yaml new file mode 100644 index 00000000000..f8f79858a55 --- /dev/null +++ b/dts/bindings/ethernet/nxp,tja11xx.yaml @@ -0,0 +1,13 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: TJA11xx PHY + +compatible: "nxp,tja11xx" + +include: phy.yaml + +properties: + reg: + required: true + description: PHY address