From d540407fc8ee575944a6df873390c1fedab15a4e Mon Sep 17 00:00:00 2001 From: Andriy Gelman Date: Tue, 19 Dec 2023 09:50:22 -0500 Subject: [PATCH] drivers: mdio: Add xmc4xxx mdio drivers Add mdio drivers for xmc4xxx SoCs. Signed-off-by: Andriy Gelman --- drivers/mdio/CMakeLists.txt | 1 + drivers/mdio/Kconfig | 1 + drivers/mdio/Kconfig.xmc4xxx | 9 + drivers/mdio/mdio_shell.c | 2 + drivers/mdio/mdio_xmc4xxx.c | 185 ++++++++++++++++++ .../infineon/xmc4500_F100x1024-pinctrl.dtsi | 23 +++ .../infineon/xmc4700_F144x2048-pinctrl.dtsi | 23 +++ dts/arm/infineon/xmc4xxx.dtsi | 11 ++ dts/bindings/mdio/infineon,xmc4xxx-mdio.yaml | 31 +++ modules/Kconfig.infineon | 5 + soc/arm/infineon_xmc/4xxx/Kconfig.series | 1 + soc/arm/infineon_xmc/4xxx/soc.c | 3 + 12 files changed, 295 insertions(+) create mode 100644 drivers/mdio/Kconfig.xmc4xxx create mode 100644 drivers/mdio/mdio_xmc4xxx.c create mode 100644 dts/bindings/mdio/infineon,xmc4xxx-mdio.yaml diff --git a/drivers/mdio/CMakeLists.txt b/drivers/mdio/CMakeLists.txt index 03391788187..30c6b9f8306 100644 --- a/drivers/mdio/CMakeLists.txt +++ b/drivers/mdio/CMakeLists.txt @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_S32_GMAC mdio_nxp_s32_gmac.c) zephyr_library_sources_ifdef(CONFIG_MDIO_ADIN2111 mdio_adin2111.c) zephyr_library_sources_ifdef(CONFIG_MDIO_GPIO mdio_gpio.c) zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_ENET mdio_nxp_enet.c) +zephyr_library_sources_ifdef(CONFIG_MDIO_INFINEON_XMC4XXX mdio_xmc4xxx.c) diff --git a/drivers/mdio/Kconfig b/drivers/mdio/Kconfig index 7e72b50ceb1..2ff20df0b24 100644 --- a/drivers/mdio/Kconfig +++ b/drivers/mdio/Kconfig @@ -31,6 +31,7 @@ source "drivers/mdio/Kconfig.nxp_s32_gmac" source "drivers/mdio/Kconfig.adin2111" source "drivers/mdio/Kconfig.gpio" source "drivers/mdio/Kconfig.nxp_enet" +source "drivers/mdio/Kconfig.xmc4xxx" config MDIO_INIT_PRIORITY int "Init priority" diff --git a/drivers/mdio/Kconfig.xmc4xxx b/drivers/mdio/Kconfig.xmc4xxx new file mode 100644 index 00000000000..75793fa1508 --- /dev/null +++ b/drivers/mdio/Kconfig.xmc4xxx @@ -0,0 +1,9 @@ +# Copyright (c) 2023 SLB +# SPDX-License-Identifier: Apache-2.0 + +config MDIO_INFINEON_XMC4XXX + bool "Infineon XMC4XXX MDIO driver" + default y + depends on DT_HAS_INFINEON_XMC4XXX_MDIO_ENABLED + help + Enable Infineon XMC4XXX MDIO driver. diff --git a/drivers/mdio/mdio_shell.c b/drivers/mdio/mdio_shell.c index 4cde88a009f..376e95244fa 100644 --- a/drivers/mdio/mdio_shell.c +++ b/drivers/mdio/mdio_shell.c @@ -31,6 +31,8 @@ LOG_MODULE_REGISTER(mdio_shell, CONFIG_LOG_DEFAULT_LEVEL); #define DT_DRV_COMPAT zephyr_mdio_gpio #elif DT_HAS_COMPAT_STATUS_OKAY(nxp_enet_mdio) #define DT_DRV_COMPAT nxp_enet_mdio +#elif DT_HAS_COMPAT_STATUS_OKAY(infineon_xmc4xxx_mdio) +#define DT_DRV_COMPAT infineon_xmc4xxx_mdio #else #error "No known devicetree compatible match for MDIO shell" #endif diff --git a/drivers/mdio/mdio_xmc4xxx.c b/drivers/mdio/mdio_xmc4xxx.c new file mode 100644 index 00000000000..02ebfc5096d --- /dev/null +++ b/drivers/mdio/mdio_xmc4xxx.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 SLB + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT infineon_xmc4xxx_mdio + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +LOG_MODULE_REGISTER(mdio_xmc4xxx, CONFIG_MDIO_LOG_LEVEL); + +#define MDIO_TRANSFER_TIMEOUT_US 250000 + +#define MAX_MDC_FREQUENCY 2500000u /* 400ns period */ +#define MIN_MDC_FREQUENCY 1000000u /* 1us period */ + +struct mdio_xmc4xxx_clock_divider { + uint8_t divider; + uint8_t reg_val; +}; + +static const struct mdio_xmc4xxx_clock_divider mdio_clock_divider[] = { + {.divider = 8, .reg_val = 2}, {.divider = 13, .reg_val = 3}, + {.divider = 21, .reg_val = 0}, {.divider = 31, .reg_val = 1}, + {.divider = 51, .reg_val = 4}, {.divider = 62, .reg_val = 5}, +}; + +struct mdio_xmc4xxx_dev_data { + struct k_mutex mutex; + uint32_t reg_value_gmii_address; +}; + +struct mdio_xmc4xxx_dev_config { + ETH_GLOBAL_TypeDef *const regs; + const struct pinctrl_dev_config *pcfg; + uint8_t mdi_port_ctrl; +}; + +static int mdio_xmc4xxx_transfer(const struct device *dev, uint8_t phy_addr, uint8_t reg_addr, + uint8_t is_write, uint16_t data_write, uint16_t *data_read) +{ + const struct mdio_xmc4xxx_dev_config *const dev_cfg = dev->config; + ETH_GLOBAL_TypeDef *const regs = dev_cfg->regs; + struct mdio_xmc4xxx_dev_data *const dev_data = dev->data; + uint32_t reg; + int ret = 0; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + if ((regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) != 0) { + ret = -EBUSY; + goto finish; + } + + reg = dev_data->reg_value_gmii_address; + if (is_write) { + reg |= ETH_GMII_ADDRESS_MW_Msk; + regs->GMII_DATA = data_write; + } + + regs->GMII_ADDRESS = reg | ETH_GMII_ADDRESS_MB_Msk | + FIELD_PREP(ETH_GMII_ADDRESS_PA_Msk, phy_addr) | + FIELD_PREP(ETH_GMII_ADDRESS_MR_Msk, reg_addr); + + if (!WAIT_FOR((regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) == 0, + MDIO_TRANSFER_TIMEOUT_US, k_msleep(5))) { + LOG_WRN("mdio transfer timedout"); + ret = -ETIMEDOUT; + goto finish; + } + + if (!is_write && data_read != NULL) { + *data_read = regs->GMII_DATA; + } + +finish: + k_mutex_unlock(&dev_data->mutex); + + return ret; +} + +static int mdio_xmc4xxx_read(const struct device *dev, uint8_t phy_addr, uint8_t reg_addr, + uint16_t *data) +{ + return mdio_xmc4xxx_transfer(dev, phy_addr, reg_addr, 0, 0, data); +} + +static int mdio_xmc4xxx_write(const struct device *dev, uint8_t phy_addr, + uint8_t reg_addr, uint16_t data) +{ + return mdio_xmc4xxx_transfer(dev, phy_addr, reg_addr, 1, data, NULL); +} + +static void mdio_xmc4xxx_bus_enable(const struct device *dev) +{ + ARG_UNUSED(dev); + /* this will enable the clock for ETH, which generates to MDIO clk */ + XMC_ETH_MAC_Enable(NULL); +} + +static void mdio_xmc4xxx_bus_disable(const struct device *dev) +{ + ARG_UNUSED(dev); + XMC_ETH_MAC_Disable(NULL); +} + +static int mdio_xmc4xxx_set_clock_divider(const struct device *dev) +{ + struct mdio_xmc4xxx_dev_data *dev_data = dev->data; + uint32_t eth_mac_clk = XMC_SCU_CLOCK_GetEthernetClockFrequency(); + + for (int i = 0; i < ARRAY_SIZE(mdio_clock_divider); i++) { + uint8_t divider = mdio_clock_divider[i].divider; + uint8_t reg_val = mdio_clock_divider[i].reg_val; + uint32_t mdc_clk = eth_mac_clk / divider; + + if (mdc_clk > MIN_MDC_FREQUENCY && mdc_clk < MAX_MDC_FREQUENCY) { + LOG_DBG("Using MDC clock divider %d", divider); + LOG_DBG("MDC clock %dHz", mdc_clk); + dev_data->reg_value_gmii_address = + FIELD_PREP(ETH_GMII_ADDRESS_CR_Msk, reg_val); + return 0; + } + } + + return -EINVAL; +} + +static int mdio_xmc4xxx_initialize(const struct device *dev) +{ + const struct mdio_xmc4xxx_dev_config *dev_cfg = dev->config; + struct mdio_xmc4xxx_dev_data *dev_data = dev->data; + XMC_ETH_MAC_PORT_CTRL_t port_ctrl = {0}; + int ret; + + k_mutex_init(&dev_data->mutex); + + ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret != 0) { + return ret; + } + + ret = mdio_xmc4xxx_set_clock_divider(dev); + if (ret != 0) { + LOG_ERR("Error setting MDIO clock divider"); + return -EINVAL; + } + + port_ctrl.mdio = dev_cfg->mdi_port_ctrl; + ETH0_CON->CON = port_ctrl.raw; + + return ret; +} + +static const struct mdio_driver_api mdio_xmc4xxx_driver_api = { + .read = mdio_xmc4xxx_read, + .write = mdio_xmc4xxx_write, + .bus_enable = mdio_xmc4xxx_bus_enable, + .bus_disable = mdio_xmc4xxx_bus_disable, +}; + +PINCTRL_DT_INST_DEFINE(0); +static const struct mdio_xmc4xxx_dev_config mdio_xmc4xxx_dev_config_0 = { + .regs = (ETH_GLOBAL_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(0)), + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), + .mdi_port_ctrl = DT_INST_ENUM_IDX(0, mdi_port_ctrl), +}; + +static struct mdio_xmc4xxx_dev_data mdio_xmc4xxx_dev_data_0; + +DEVICE_DT_INST_DEFINE(0, &mdio_xmc4xxx_initialize, NULL, &mdio_xmc4xxx_dev_data_0, + &mdio_xmc4xxx_dev_config_0, POST_KERNEL, + CONFIG_MDIO_INIT_PRIORITY, &mdio_xmc4xxx_driver_api); diff --git a/dts/arm/infineon/xmc4500_F100x1024-pinctrl.dtsi b/dts/arm/infineon/xmc4500_F100x1024-pinctrl.dtsi index fd05d415d3a..f41623c5e83 100644 --- a/dts/arm/infineon/xmc4500_F100x1024-pinctrl.dtsi +++ b/dts/arm/infineon/xmc4500_F100x1024-pinctrl.dtsi @@ -425,4 +425,27 @@ /omit-if-no-ref/ i2c_scl_dout1_p1_10_u0c0: i2c_scl_dout1_p1_10_u0c0 { pinmux = ; }; + + /omit-if-no-ref/ eth_p0_9_mdo: ebu_p0_9_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + /omit-if-no-ref/ eth_p1_11_mdo: ebu_p1_11_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + /omit-if-no-ref/ eth_p2_0_mdo: ebu_p2_0_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + + /omit-if-no-ref/ eth_p0_9_mdio: eth_p0_9_mdio { + pinmux = ; + }; + /omit-if-no-ref/ eth_p2_0_mdio: eth_p2_0_mdio { + pinmux = ; + }; + /omit-if-no-ref/ eth_p1_11_mdio: eth_p1_11_mdio { + pinmux = ; + }; }; diff --git a/dts/arm/infineon/xmc4700_F144x2048-pinctrl.dtsi b/dts/arm/infineon/xmc4700_F144x2048-pinctrl.dtsi index 75988d493e7..af2fe761a3c 100644 --- a/dts/arm/infineon/xmc4700_F144x2048-pinctrl.dtsi +++ b/dts/arm/infineon/xmc4700_F144x2048-pinctrl.dtsi @@ -985,4 +985,27 @@ /omit-if-no-ref/ i2c_scl_dout1_p3_9_u2c0: i2c_scl_dout1_p3_9_u2c0 { pinmux = ; }; + + /omit-if-no-ref/ eth_p0_9_mdo: eth_p0_9_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + /omit-if-no-ref/ eth_p1_11_mdo: eth_p1_11_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + /omit-if-no-ref/ eth_p2_0_mdo: eth_p2_0_mdo { + pinmux = ; + hwctrl = "periph1"; + }; + + /omit-if-no-ref/ eth_p0_9_mdio: eth_p0_9_mdio { + pinmux = ; + }; + /omit-if-no-ref/ eth_p2_0_mdio: eth_p2_0_mdio { + pinmux = ; + }; + /omit-if-no-ref/ eth_p1_11_mdio: eth_p1_11_mdio { + pinmux = ; + }; }; diff --git a/dts/arm/infineon/xmc4xxx.dtsi b/dts/arm/infineon/xmc4xxx.dtsi index 82845683fc6..7d6831deb63 100644 --- a/dts/arm/infineon/xmc4xxx.dtsi +++ b/dts/arm/infineon/xmc4xxx.dtsi @@ -236,6 +236,17 @@ interrupts = <0 1>; status = "disabled"; }; + + ethernet@5000c000 { + reg = <0x5000C000 0x3FFF>; + + mdio: mdio { + compatible = "infineon,xmc4xxx-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; }; }; diff --git a/dts/bindings/mdio/infineon,xmc4xxx-mdio.yaml b/dts/bindings/mdio/infineon,xmc4xxx-mdio.yaml new file mode 100644 index 00000000000..b9da5926d6b --- /dev/null +++ b/dts/bindings/mdio/infineon,xmc4xxx-mdio.yaml @@ -0,0 +1,31 @@ +# Copyright (c) 2023 SLB +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon xmc4xxx Family MDIO Driver node + +compatible: "infineon,xmc4xxx-mdio" + +include: + - name: mdio-controller.yaml + - name: pinctrl-device.yaml + +properties: + mdi-port-ctrl: + description: | + The MDIO input is connected to several port/pins via a mux. + This is not handled by pinctrl because the mux is located at the + peripheral and not GPIO. The possible connections are defined by + an enum. + type: string + + enum: + - "P0_9" + - "P2_0" + - "P1_11" + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true diff --git a/modules/Kconfig.infineon b/modules/Kconfig.infineon index 4147d138de3..070508ccce4 100644 --- a/modules/Kconfig.infineon +++ b/modules/Kconfig.infineon @@ -55,4 +55,9 @@ config HAS_XMCLIB_WDT help Enable XMCLIB WDT +config HAS_XMCLIB_ETH + bool + help + Enable XMCLIB Ethernet MAC + endif # HAS_XMCLIB diff --git a/soc/arm/infineon_xmc/4xxx/Kconfig.series b/soc/arm/infineon_xmc/4xxx/Kconfig.series index 53312dfaf7b..bfdbcc93091 100644 --- a/soc/arm/infineon_xmc/4xxx/Kconfig.series +++ b/soc/arm/infineon_xmc/4xxx/Kconfig.series @@ -21,5 +21,6 @@ config SOC_SERIES_XMC_4XXX select HAS_XMCLIB_I2C select HAS_XMCLIB_CCU select HAS_XMCLIB_WDT + select HAS_XMCLIB_ETH help Enable support for XMC 4xxx MCU series diff --git a/soc/arm/infineon_xmc/4xxx/soc.c b/soc/arm/infineon_xmc/4xxx/soc.c index 0d4060568a3..44e83a63c6f 100644 --- a/soc/arm/infineon_xmc/4xxx/soc.c +++ b/soc/arm/infineon_xmc/4xxx/soc.c @@ -37,6 +37,9 @@ void z_arm_platform_init(void) #endif #ifdef CONFIG_PWM_XMC4XXX_CCU8 | XMC_SCU_CLOCK_SLEEP_MODE_CONFIG_ENABLE_CCU +#endif +#ifdef CONFIG_ETH_XMC4XXX + | XMC_SCU_CLOCK_SLEEP_MODE_CONFIG_ENABLE_ETH #endif );