diff --git a/boards/arm/mimxrt1024_evk/Kconfig.defconfig b/boards/arm/mimxrt1024_evk/Kconfig.defconfig index 704743ded71b8e..db0a2eb1fedc90 100644 --- a/boards/arm/mimxrt1024_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1024_evk/Kconfig.defconfig @@ -27,9 +27,13 @@ if NETWORKING config NET_L2_ETHERNET default y +if ETH_MCUX + config ETH_MCUX_PHY_RESET default y +endif # ETH_MCUX + endif # NETWORKING endif # BOARD_MIMXRT1024_EVK diff --git a/boards/arm/mimxrt1024_evk/dts/mimxrt1024_evk-enet-experimental.dtsi b/boards/arm/mimxrt1024_evk/dts/mimxrt1024_evk-enet-experimental.dtsi new file mode 100644 index 00000000000000..2229b2a9b70efa --- /dev/null +++ b/boards/arm/mimxrt1024_evk/dts/mimxrt1024_evk-enet-experimental.dtsi @@ -0,0 +1,135 @@ +/* + * Copyright 2023 NXP + * + * Experimental ENET binding overlay + */ + + +/ { + soc { + /delete-node/ ethernet@402d8000; + + enet: enet@402d8000 { + compatible = "nxp,enet"; + reg = <0x402D8000 0x628>; + clocks = <&ccm IMX_CCM_ENET_CLK 0 0>; + enet_mac: ethernet { + compatible = "nxp,enet-mac"; + interrupts = <114 0>; + interrupt-names = "COMMON"; + nxp,mdio = <&enet_mdio>; + nxp,ptp-clock = <&enet_ptp_clock>; + phy-connection-type = "rmii"; + status = "disabled"; + }; + enet_mdio: mdio { + compatible = "nxp,enet-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + enet_ptp_clock: ptp_clock { + compatible = "nxp,enet-ptp-clock"; + interrupts = <115 0>; + status = "disabled"; + clocks = <&ccm IMX_CCM_ENET_PLL 0 0>; + }; + }; + }; +}; + +&enet_mac { + status = "okay"; + pinctrl-0 = <&pinmux_enet>; + pinctrl-names = "default"; + phy-handle = <&phy>; +}; + +&enet_mdio { + status = "okay"; + pinctrl-0 = <&pinmux_enet_mdio>; + pinctrl-names = "default"; + phy: phy@0 { + compatible = "microchip,ksz8081"; + reg = <0>; + status = "okay"; + mc,reset-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; + mc,interrupt-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mc,interface-type = "rmii"; + }; +}; + +&enet_ptp_clock { + status = "okay"; + pinctrl-0 = <&pinmux_ptp>; + pinctrl-names = "default"; +}; + + + +&pinctrl { + /delete-node/ pinmux_ptp; + /delete-node/ pinmux_enet; + + pinmux_enet: pinmux_enet { + group0 { + pinmux = <&iomuxc_gpio_ad_b0_08_enet_ref_clk>; + bias-disable; + drive-strength = "r0-6"; + slew-rate = "fast"; + nxp,speed = "50-mhz"; + input-enable; + }; + group1 { + pinmux = <&iomuxc_gpio_ad_b0_09_enet_rx_data1>, + <&iomuxc_gpio_ad_b0_11_enet_rx_en>, + <&iomuxc_gpio_ad_b0_14_enet_tx_data0>, + <&iomuxc_gpio_ad_b0_15_enet_tx_data1>, + <&iomuxc_gpio_ad_b0_13_enet_tx_en>, + <&iomuxc_gpio_ad_b0_12_enet_rx_er>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + group2 { + pinmux = <&iomuxc_gpio_ad_b0_10_enet_rx_data0>; + drive-strength = "r0-6"; + slew-rate = "slow"; + nxp,speed = "100-mhz"; + }; + }; + + pinmux_enet_mdio: pinmux_enet_mdio { + group0 { + pinmux = <&iomuxc_gpio_emc_40_enet_mdio>, + <&iomuxc_gpio_emc_41_enet_mdc>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + group1 { + pinmux = <&iomuxc_gpio_ad_b1_06_gpio1_io22>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "slow"; + nxp,speed = "100-mhz"; + }; + group2 { + pinmux = <&iomuxc_gpio_ad_b0_04_gpio1_io04>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "100-mhz"; + }; + }; + + pinmux_ptp: pinmux_ptp { + /* Intentionally empty */ + }; +}; diff --git a/boards/arm/mimxrt1050_evk/Kconfig.defconfig b/boards/arm/mimxrt1050_evk/Kconfig.defconfig index 104c6d7710012b..79660c08e781c9 100644 --- a/boards/arm/mimxrt1050_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1050_evk/Kconfig.defconfig @@ -46,9 +46,13 @@ if NETWORKING config NET_L2_ETHERNET default y +if ETH_MCUX + config ETH_MCUX_PHY_RESET default y +endif # ETH_MCUX + endif # NETWORKING if LVGL diff --git a/boards/arm/mimxrt1050_evk/dts/mimxrt1050_evk-enet-experimental.dtsi b/boards/arm/mimxrt1050_evk/dts/mimxrt1050_evk-enet-experimental.dtsi new file mode 100644 index 00000000000000..7fe69f0d52ed01 --- /dev/null +++ b/boards/arm/mimxrt1050_evk/dts/mimxrt1050_evk-enet-experimental.dtsi @@ -0,0 +1,122 @@ +/* + * Copyright 2023 NXP + * + * Experimental ENET binding overlay + */ + + +/ { + soc { + /delete-node/ ethernet@402d8000; + + enet: enet@402d8000 { + compatible = "nxp,enet"; + reg = <0x402D8000 0x628>; + clocks = <&ccm IMX_CCM_ENET_CLK 0 0>; + enet_mac: ethernet { + compatible = "nxp,enet-mac"; + interrupts = <114 0>; + interrupt-names = "COMMON"; + nxp,mdio = <&enet_mdio>; + nxp,ptp-clock = <&enet_ptp_clock>; + phy-connection-type = "rmii"; + status = "disabled"; + }; + enet_mdio: mdio { + compatible = "nxp,enet-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + enet_ptp_clock: ptp_clock { + compatible = "nxp,enet-ptp-clock"; + interrupts = <115 0>; + status = "disabled"; + clocks = <&ccm IMX_CCM_ENET_PLL 0 0>; + }; + }; + }; +}; + +&enet_mac { + status = "okay"; + pinctrl-0 = <&pinmux_enet>; + pinctrl-names = "default"; + phy-handle = <&phy>; +}; + +&enet_mdio { + status = "okay"; + pinctrl-0 = <&pinmux_enet_mdio>; + pinctrl-names = "default"; + phy: phy@0 { + compatible = "microchip,ksz8081"; + reg = <0>; + status = "okay"; + mc,reset-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + mc,interrupt-gpio = <&gpio1 10 GPIO_ACTIVE_HIGH>; + mc,interface-type = "rmii"; + }; +}; + +&enet_ptp_clock { + status = "okay"; + pinctrl-0 = <&pinmux_ptp>; + pinctrl-names = "default"; +}; + + + +&pinctrl { + /delete-node/ pinmux_ptp; + /delete-node/ pinmux_enet; + + pinmux_enet: pinmux_enet { + group0 { + pinmux = <&iomuxc_gpio_b1_10_enet_ref_clk>; + bias-disable; + drive-strength = "r0-6"; + slew-rate = "fast"; + nxp,speed = "50-mhz"; + input-enable; + }; + group1 { + pinmux = <&iomuxc_gpio_b1_04_enet_rx_data0>, + <&iomuxc_gpio_b1_05_enet_rx_data1>, + <&iomuxc_gpio_b1_06_enet_rx_en>, + <&iomuxc_gpio_b1_07_enet_tx_data0>, + <&iomuxc_gpio_b1_08_enet_tx_data1>, + <&iomuxc_gpio_b1_09_enet_tx_en>, + <&iomuxc_gpio_b1_11_enet_rx_er>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_enet_mdio: pinmux_enet_mdio { + group0 { + pinmux = <&iomuxc_gpio_emc_40_enet_mdc>, + <&iomuxc_gpio_emc_41_enet_mdio>, + <&iomuxc_gpio_ad_b0_10_gpio1_io10>, + <&iomuxc_gpio_ad_b0_09_gpio1_io09>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_ptp: pinmux_ptp { + group0 { + pinmux = <&iomuxc_gpio_ad_b1_02_enet_1588_event2_out>, + <&iomuxc_gpio_ad_b1_03_enet_1588_event2_in>; + drive-strength = "r0-6"; + slew-rate = "slow"; + nxp,speed = "100-mhz"; + }; + }; +}; diff --git a/boards/arm/mimxrt1060_evk/Kconfig.defconfig b/boards/arm/mimxrt1060_evk/Kconfig.defconfig index aef6f37c03b01c..b3b325805fa6ea 100644 --- a/boards/arm/mimxrt1060_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1060_evk/Kconfig.defconfig @@ -49,9 +49,13 @@ if NETWORKING config NET_L2_ETHERNET default y +if ETH_MCUX + config ETH_MCUX_PHY_RESET default y +endif # ETH_MCUX + endif # NETWORKING if LVGL diff --git a/boards/arm/mimxrt1060_evk/dts/mimxrt1060_evk-enet-experimental.dtsi b/boards/arm/mimxrt1060_evk/dts/mimxrt1060_evk-enet-experimental.dtsi new file mode 100644 index 00000000000000..7fe69f0d52ed01 --- /dev/null +++ b/boards/arm/mimxrt1060_evk/dts/mimxrt1060_evk-enet-experimental.dtsi @@ -0,0 +1,122 @@ +/* + * Copyright 2023 NXP + * + * Experimental ENET binding overlay + */ + + +/ { + soc { + /delete-node/ ethernet@402d8000; + + enet: enet@402d8000 { + compatible = "nxp,enet"; + reg = <0x402D8000 0x628>; + clocks = <&ccm IMX_CCM_ENET_CLK 0 0>; + enet_mac: ethernet { + compatible = "nxp,enet-mac"; + interrupts = <114 0>; + interrupt-names = "COMMON"; + nxp,mdio = <&enet_mdio>; + nxp,ptp-clock = <&enet_ptp_clock>; + phy-connection-type = "rmii"; + status = "disabled"; + }; + enet_mdio: mdio { + compatible = "nxp,enet-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + enet_ptp_clock: ptp_clock { + compatible = "nxp,enet-ptp-clock"; + interrupts = <115 0>; + status = "disabled"; + clocks = <&ccm IMX_CCM_ENET_PLL 0 0>; + }; + }; + }; +}; + +&enet_mac { + status = "okay"; + pinctrl-0 = <&pinmux_enet>; + pinctrl-names = "default"; + phy-handle = <&phy>; +}; + +&enet_mdio { + status = "okay"; + pinctrl-0 = <&pinmux_enet_mdio>; + pinctrl-names = "default"; + phy: phy@0 { + compatible = "microchip,ksz8081"; + reg = <0>; + status = "okay"; + mc,reset-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + mc,interrupt-gpio = <&gpio1 10 GPIO_ACTIVE_HIGH>; + mc,interface-type = "rmii"; + }; +}; + +&enet_ptp_clock { + status = "okay"; + pinctrl-0 = <&pinmux_ptp>; + pinctrl-names = "default"; +}; + + + +&pinctrl { + /delete-node/ pinmux_ptp; + /delete-node/ pinmux_enet; + + pinmux_enet: pinmux_enet { + group0 { + pinmux = <&iomuxc_gpio_b1_10_enet_ref_clk>; + bias-disable; + drive-strength = "r0-6"; + slew-rate = "fast"; + nxp,speed = "50-mhz"; + input-enable; + }; + group1 { + pinmux = <&iomuxc_gpio_b1_04_enet_rx_data0>, + <&iomuxc_gpio_b1_05_enet_rx_data1>, + <&iomuxc_gpio_b1_06_enet_rx_en>, + <&iomuxc_gpio_b1_07_enet_tx_data0>, + <&iomuxc_gpio_b1_08_enet_tx_data1>, + <&iomuxc_gpio_b1_09_enet_tx_en>, + <&iomuxc_gpio_b1_11_enet_rx_er>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_enet_mdio: pinmux_enet_mdio { + group0 { + pinmux = <&iomuxc_gpio_emc_40_enet_mdc>, + <&iomuxc_gpio_emc_41_enet_mdio>, + <&iomuxc_gpio_ad_b0_10_gpio1_io10>, + <&iomuxc_gpio_ad_b0_09_gpio1_io09>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_ptp: pinmux_ptp { + group0 { + pinmux = <&iomuxc_gpio_ad_b1_02_enet_1588_event2_out>, + <&iomuxc_gpio_ad_b1_03_enet_1588_event2_in>; + drive-strength = "r0-6"; + slew-rate = "slow"; + nxp,speed = "100-mhz"; + }; + }; +}; diff --git a/boards/arm/mimxrt1064_evk/Kconfig.defconfig b/boards/arm/mimxrt1064_evk/Kconfig.defconfig index 9ebf3eaa71b899..e0604d39621012 100644 --- a/boards/arm/mimxrt1064_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1064_evk/Kconfig.defconfig @@ -33,9 +33,13 @@ if NETWORKING config NET_L2_ETHERNET default y +if ETH_MCUX + config ETH_MCUX_PHY_RESET default y +endif # ETH_MCUX + endif # NETWORKING if LVGL diff --git a/boards/arm/mimxrt1064_evk/dts/mimxrt1064_evk-enet-experimental.dtsi b/boards/arm/mimxrt1064_evk/dts/mimxrt1064_evk-enet-experimental.dtsi new file mode 100644 index 00000000000000..7fe69f0d52ed01 --- /dev/null +++ b/boards/arm/mimxrt1064_evk/dts/mimxrt1064_evk-enet-experimental.dtsi @@ -0,0 +1,122 @@ +/* + * Copyright 2023 NXP + * + * Experimental ENET binding overlay + */ + + +/ { + soc { + /delete-node/ ethernet@402d8000; + + enet: enet@402d8000 { + compatible = "nxp,enet"; + reg = <0x402D8000 0x628>; + clocks = <&ccm IMX_CCM_ENET_CLK 0 0>; + enet_mac: ethernet { + compatible = "nxp,enet-mac"; + interrupts = <114 0>; + interrupt-names = "COMMON"; + nxp,mdio = <&enet_mdio>; + nxp,ptp-clock = <&enet_ptp_clock>; + phy-connection-type = "rmii"; + status = "disabled"; + }; + enet_mdio: mdio { + compatible = "nxp,enet-mdio"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + enet_ptp_clock: ptp_clock { + compatible = "nxp,enet-ptp-clock"; + interrupts = <115 0>; + status = "disabled"; + clocks = <&ccm IMX_CCM_ENET_PLL 0 0>; + }; + }; + }; +}; + +&enet_mac { + status = "okay"; + pinctrl-0 = <&pinmux_enet>; + pinctrl-names = "default"; + phy-handle = <&phy>; +}; + +&enet_mdio { + status = "okay"; + pinctrl-0 = <&pinmux_enet_mdio>; + pinctrl-names = "default"; + phy: phy@0 { + compatible = "microchip,ksz8081"; + reg = <0>; + status = "okay"; + mc,reset-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + mc,interrupt-gpio = <&gpio1 10 GPIO_ACTIVE_HIGH>; + mc,interface-type = "rmii"; + }; +}; + +&enet_ptp_clock { + status = "okay"; + pinctrl-0 = <&pinmux_ptp>; + pinctrl-names = "default"; +}; + + + +&pinctrl { + /delete-node/ pinmux_ptp; + /delete-node/ pinmux_enet; + + pinmux_enet: pinmux_enet { + group0 { + pinmux = <&iomuxc_gpio_b1_10_enet_ref_clk>; + bias-disable; + drive-strength = "r0-6"; + slew-rate = "fast"; + nxp,speed = "50-mhz"; + input-enable; + }; + group1 { + pinmux = <&iomuxc_gpio_b1_04_enet_rx_data0>, + <&iomuxc_gpio_b1_05_enet_rx_data1>, + <&iomuxc_gpio_b1_06_enet_rx_en>, + <&iomuxc_gpio_b1_07_enet_tx_data0>, + <&iomuxc_gpio_b1_08_enet_tx_data1>, + <&iomuxc_gpio_b1_09_enet_tx_en>, + <&iomuxc_gpio_b1_11_enet_rx_er>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_enet_mdio: pinmux_enet_mdio { + group0 { + pinmux = <&iomuxc_gpio_emc_40_enet_mdc>, + <&iomuxc_gpio_emc_41_enet_mdio>, + <&iomuxc_gpio_ad_b0_10_gpio1_io10>, + <&iomuxc_gpio_ad_b0_09_gpio1_io09>; + drive-strength = "r0-5"; + bias-pull-up; + bias-pull-up-value = "100k"; + slew-rate = "fast"; + nxp,speed = "200-mhz"; + }; + }; + + pinmux_ptp: pinmux_ptp { + group0 { + pinmux = <&iomuxc_gpio_ad_b1_02_enet_1588_event2_out>, + <&iomuxc_gpio_ad_b1_03_enet_1588_event2_in>; + drive-strength = "r0-6"; + slew-rate = "slow"; + nxp,speed = "100-mhz"; + }; + }; +}; diff --git a/drivers/clock_control/clock_control_mcux_ccm.c b/drivers/clock_control/clock_control_mcux_ccm.c index ab90602d2a4009..1918673142f245 100644 --- a/drivers/clock_control/clock_control_mcux_ccm.c +++ b/drivers/clock_control/clock_control_mcux_ccm.c @@ -231,6 +231,17 @@ static int mcux_ccm_get_subsys_rate(const struct device *dev, break; #endif +#ifdef CONFIG_ETH_NXP_ENET + case IMX_CCM_ENET_CLK: + *rate = CLOCK_GetIpgFreq(); + break; +#endif +#ifdef CONFIG_PTP_CLOCK_NXP_ENET + case IMX_CCM_ENET_PLL: + *rate = CLOCK_GetPllFreq(kCLOCK_PllEnet); + break; +#endif + #ifdef CONFIG_UART_MCUX_IUART case IMX_CCM_UART1_CLK: case IMX_CCM_UART2_CLK: diff --git a/drivers/clock_control/clock_control_mcux_ccm_rev2.c b/drivers/clock_control/clock_control_mcux_ccm_rev2.c index 9a88a36823a9a5..bcf61353b1f33f 100644 --- a/drivers/clock_control/clock_control_mcux_ccm_rev2.c +++ b/drivers/clock_control/clock_control_mcux_ccm_rev2.c @@ -103,6 +103,13 @@ static int mcux_ccm_get_subsys_rate(const struct device *dev, clock_root = kCLOCK_Root_Sai4; break; #endif + +#ifdef CONFIG_ETH_NXP_ENET + case IMX_CCM_ENET_CLK: + clock_root = kCLOCK_Root_Bus; + break; +#endif + default: return -EINVAL; } diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index 8c8795509fded4..7b925b3730aa1e 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_SMSC91X eth_smsc91x.c) zephyr_library_sources_ifdef(CONFIG_ETH_IVSHMEM eth_ivshmem.c eth_ivshmem_queue.c) zephyr_library_sources_ifdef(CONFIG_ETH_ADIN2111 eth_adin2111.c) zephyr_library_sources_ifdef(CONFIG_ETH_LAN865X eth_lan865x.c oa_tc6.c) +zephyr_library_sources_ifdef(CONFIG_ETH_NXP_ENET eth_nxp_enet.c) if(CONFIG_ETH_NXP_S32_NETC) zephyr_library_sources(eth_nxp_s32_netc.c) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 69b60e09bc2add..e26c0bafb6d562 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -63,6 +63,7 @@ source "drivers/ethernet/Kconfig.ivshmem" source "drivers/ethernet/Kconfig.adin2111" source "drivers/ethernet/Kconfig.numaker" source "drivers/ethernet/Kconfig.lan865x" +source "drivers/ethernet/Kconfig.nxp_enet" source "drivers/ethernet/phy/Kconfig" diff --git a/drivers/ethernet/Kconfig.nxp_enet b/drivers/ethernet/Kconfig.nxp_enet new file mode 100644 index 00000000000000..a6182ade32df00 --- /dev/null +++ b/drivers/ethernet/Kconfig.nxp_enet @@ -0,0 +1,54 @@ +# NXP ENET Ethernet driver configuration options + +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig ETH_NXP_ENET + bool "NXP ENET Ethernet driver" + default y + depends on DT_HAS_NXP_ENET_MAC_ENABLED + select NOCACHE_MEMORY if HAS_MCUX_CACHE + select ARM_MPU if CPU_CORTEX_M7 + select MDIO if DT_HAS_NXP_ENET_MDIO_ENABLED + select EXPERIMENTAL + help + Enable NXP ENET Ethernet driver. + +if ETH_NXP_ENET + +config ETH_NXP_ENET_HW_ACCELERATION + bool "Hardware acceleration" + default y + depends on !NET_IPV6 + help + Enable hardware acceleration for the following: + - IPv4, UDP and TCP checksum (both Rx and Tx) + +config ETH_NXP_ENET_USE_DTCM_FOR_DMA_BUFFER + bool "Use DTCM for hardware DMA buffers" + default y + help + Place the hardware DMA buffers into DTCM for better + networking performance. + +config ETH_NXP_ENET_RX_THREAD_STACK_SIZE + int "NXP ENET RX thread stack size" + default 1600 + help + ENET RX thread stack size in bytes. + +config ETH_NXP_ENET_RX_BUFFERS + int "Number of RX buffers for ethernet driver" + default 6 + range 6 16 + help + Set the number of RX buffers provided to the NXP ENET driver. + +config ETH_NXP_ENET_TX_BUFFERS + int "Number of TX buffers for ethernet driver" + default 1 + range 1 16 + help + Set the number of TX buffers provided to the NXP ENET driver. + +endif # ETH_NXP_ENET diff --git a/drivers/ethernet/eth_esp32.c b/drivers/ethernet/eth_esp32.c index 0b1744efe6c80e..d7e9382c9c97e9 100644 --- a/drivers/ethernet/eth_esp32.c +++ b/drivers/ethernet/eth_esp32.c @@ -217,7 +217,9 @@ int eth_esp32_initialize(const struct device *dev) /* Configure phy for Media-Independent Interface (MII) or * Reduced Media-Independent Interface (RMII) mode */ - const char *phy_connection_type = DT_INST_PROP(0, phy_connection_type); + const char *phy_connection_type = DT_INST_PROP_OR(0, + phy_connection_type, + "rmii"); if (strcmp(phy_connection_type, "rmii") == 0) { emac_hal_iomux_init_rmii(); diff --git a/drivers/ethernet/eth_nxp_enet.c b/drivers/ethernet/eth_nxp_enet.c new file mode 100644 index 00000000000000..6620049fcba013 --- /dev/null +++ b/drivers/ethernet/eth_nxp_enet.c @@ -0,0 +1,1043 @@ +/* NXP ENET MAC Driver + * + * Copyright 2023 NXP + * + * Inspiration from eth_mcux.c, which is: + * Copyright (c) 2016-2017 ARM Ltd + * Copyright (c) 2016 Linaro Ltd + * Copyright (c) 2018 Intel Corporation + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_enet_mac + +/* Set up logging module for this driver */ +#define LOG_MODULE_NAME eth_nxp_enet_mac +#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +/* + ************ + * Includes * + ************ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_NET_DSA) +#include +#endif + +#include "fsl_enet.h" + +/* + *********** + * Defines * + *********** + */ + +#define RING_ID 0 + +/* + ********************* + * Driver Structures * + ********************* + */ + +struct nxp_enet_mac_config { + ENET_Type *base; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + void (*generate_mac)(uint8_t *mac_addr); + const struct pinctrl_dev_config *pincfg; + enet_buffer_config_t buffer_config; + uint8_t phy_mode; + void (*irq_config_func)(void); + const struct device *phy_dev; + const struct device *mdio; +#ifdef CONFIG_PTP_CLOCK_NXP_ENET + const struct device *ptp_clock; +#endif +}; + +struct nxp_enet_mac_data { + struct net_if *iface; + uint8_t mac_addr[6]; + enet_handle_t enet_handle; + struct k_sem tx_buf_sem; + + K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ETH_NXP_ENET_RX_THREAD_STACK_SIZE); + + struct k_thread rx_thread; + struct k_sem rx_thread_sem; + struct k_mutex tx_frame_buf_mutex; + struct k_mutex rx_frame_buf_mutex; +#ifdef CONFIG_PTP_CLOCK_NXP_ENET + struct k_sem ptp_ts_sem; + struct k_mutex *ptp_mutex; +#endif + /* TODO: FIXME. This Ethernet frame sized buffer is used for + * interfacing with MCUX. How it works is that hardware uses + * DMA scatter buffers to receive a frame, and then public + * MCUX call gathers them into this buffer (there's no other + * public interface). All this happens only for this driver + * to scatter this buffer again into Zephyr fragment buffers. + * This is not efficient, but proper resolution of this issue + * depends on introduction of zero-copy networking support + * in Zephyr, and adding needed interface to MCUX (or + * bypassing it and writing a more complex driver working + * directly with hardware). + * + * Note that we do not copy FCS into this buffer thus the + * size is 1514 bytes. + */ + uint8_t *tx_frame_buf; /* Max MTU + ethernet header */ + uint8_t *rx_frame_buf; /* Max MTU + ethernet header */ +}; + +/* + ******************** + * Helper Functions * + ******************** + */ + +extern void nxp_enet_mdio_callback(const struct device *mdio_dev, + enum nxp_enet_callback_reason event); + +extern void nxp_enet_ptp_clock_callback(const struct device *dev, + enum nxp_enet_callback_reason event, + union nxp_enet_ptp_data *ptp_data); + +static inline struct net_if *get_iface(struct nxp_enet_mac_data *data, uint16_t vlan_tag) +{ +#if defined(CONFIG_NET_VLAN) + struct net_if *iface; + + iface = net_eth_get_vlan_iface(data->iface, vlan_tag); + if (!iface) { + return data->iface; + } + + return iface; +#else + ARG_UNUSED(vlan_tag); + + return data->iface; +#endif +} + +#if defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6) +static void net_if_mcast_cb(struct net_if *iface, + const struct net_addr *addr, + bool is_joined) +{ + const struct device *dev = net_if_get_device(iface); + const struct nxp_enet_mac_config *config = dev->config; + struct net_eth_addr mac_addr; + + if (IS_ENABLED(CONFIG_NET_IPV4) && addr->family == AF_INET) { + net_eth_ipv4_mcast_to_mac_addr(&addr->in_addr, &mac_addr); + } else if (IS_ENABLED(CONFIG_NET_IPV6) && addr->family == AF_INET6) { + net_eth_ipv6_mcast_to_mac_addr(&addr->in6_addr, &mac_addr); + } else { + return; + } + + if (is_joined) { + ENET_AddMulticastGroup(config->base, mac_addr.addr); + } else { + ENET_LeaveMulticastGroup(config->base, mac_addr.addr); + } +} +#endif /* CONFIG_NET_NATIVE_IPV4 || CONFIG_NET_NATIVE_IPV6 */ + +/* + ***************** + * PTP Functions * + ***************** + */ + +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) +static bool eth_get_ptp_data(struct net_if *iface, struct net_pkt *pkt) +{ + int eth_hlen; + +#if defined(CONFIG_NET_VLAN) + struct net_eth_vlan_hdr *hdr_vlan; + struct ethernet_context *eth_ctx; + bool vlan_enabled = false; + + eth_ctx = net_if_l2_data(iface); + if (net_eth_is_vlan_enabled(eth_ctx, iface)) { + hdr_vlan = (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); + vlan_enabled = true; + + if (ntohs(hdr_vlan->type) != NET_ETH_PTYPE_PTP) { + return false; + } + + eth_hlen = sizeof(struct net_eth_vlan_hdr); + } else +#endif /* CONFIG_NET_VLAN */ + { + if (ntohs(NET_ETH_HDR(pkt)->type) != NET_ETH_PTYPE_PTP) { + return false; + } + + eth_hlen = sizeof(struct net_eth_hdr); + } + + net_pkt_set_priority(pkt, NET_PRIORITY_CA); + + return true; +} + + +#if defined(CONFIG_NET_L2_PTP) +static inline void ts_register_tx_event(const struct device *dev, + enet_frame_info_t *frameinfo) +{ + struct nxp_enet_mac_data *data = dev->data; + struct net_pkt *pkt; + + pkt = frameinfo->context; + if (pkt && atomic_get(&pkt->atomic_ref) > 0) { + if (eth_get_ptp_data(net_pkt_iface(pkt), pkt)) { + if (frameinfo->isTsAvail) { + k_mutex_lock(data->ptp_mutex, K_FOREVER); + + pkt->timestamp.nanosecond = + frameinfo->timeStamp.nanosecond; + pkt->timestamp.second = + frameinfo->timeStamp.second; + + net_if_add_tx_timestamp(pkt); + k_sem_give(&data->ptp_ts_sem); + k_mutex_unlock(data->ptp_mutex); + } + } + + net_pkt_unref(pkt); + } +} +#endif /* CONFIG_NET_L2_PTP */ + +static const struct device *eth_nxp_enet_get_ptp_clock(const struct device *dev) +{ + const struct nxp_enet_mac_config *config = dev->config; + + return config->ptp_clock; +} + +#endif /* CONFIG_PTP_CLOCK_NXP_ENET */ + +/* + ************************************** + * L2 Networking Driver API Functions * + ************************************** + */ + + +static int eth_nxp_enet_tx(const struct device *dev, struct net_pkt *pkt) +{ + const struct nxp_enet_mac_config *config = dev->config; + struct nxp_enet_mac_data *data = dev->data; + uint16_t total_len = net_pkt_get_len(pkt); + status_t status; + +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + bool timestamped_frame; +#endif + + /* Wait for a TX buffer descriptor to be available */ + k_sem_take(&data->tx_buf_sem, K_FOREVER); + + /* Enter critical section for TX frame buffer access */ + k_mutex_lock(&data->tx_frame_buf_mutex, K_FOREVER); + + /* Read network packet from upper layer into frame buffer */ + if (net_pkt_read(pkt, data->tx_frame_buf, total_len)) { + k_sem_give(&data->tx_buf_sem); + k_mutex_unlock(&data->tx_frame_buf_mutex); + return -EIO; + } + +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + timestamped_frame = eth_get_ptp_data(net_pkt_iface(pkt), pkt); + if (timestamped_frame) { + status = ENET_SendFrame(config->base, &data->enet_handle, + data->tx_frame_buf, total_len, RING_ID, true, pkt); + if (!status) { + net_pkt_ref(pkt); + /* + * Network stack will modify the packet upon return, + * so wait for the packet to be timestamped, + * which will occur within the TX ISR, before + * returning + */ + k_sem_take(&data->ptp_ts_sem, K_FOREVER); + } + + } else +#endif + { + /* Send frame to ring buffer for transmit */ + status = ENET_SendFrame(config->base, &data->enet_handle, + data->tx_frame_buf, total_len, + RING_ID, false, NULL); + } + + if (status) { + LOG_ERR("ENET_SendFrame error: %d", (int)status); + k_mutex_unlock(&data->tx_frame_buf_mutex); + ENET_ReclaimTxDescriptor(config->base, + &data->enet_handle, RING_ID); + return -1; + } + + /* Leave critical section for TX frame buffer access */ + k_mutex_unlock(&data->tx_frame_buf_mutex); + + return 0; +} + +static void eth_nxp_enet_iface_init(struct net_if *iface) +{ + const struct device *dev = net_if_get_device(iface); + struct nxp_enet_mac_data *data = dev->data; + const struct nxp_enet_mac_config *config = dev->config; +#if defined(CONFIG_NET_NATIVE_IPV4) || defined(CONFIG_NET_NATIVE_IPV6) + static struct net_if_mcast_monitor mon; + + net_if_mcast_mon_register(&mon, iface, net_if_mcast_cb); +#endif /* CONFIG_NET_NATIVE_IPV4 || CONFIG_NET_NATIVE_IPV6 */ + + net_if_set_link_addr(iface, data->mac_addr, + sizeof(data->mac_addr), + NET_LINK_ETHERNET); + + /* For VLAN, this value is only used to get the correct L2 driver. + * The iface pointer in context should contain the main interface + * if the VLANs are enabled. + */ + if (data->iface == NULL) { + data->iface = iface; + } + +#if defined(CONFIG_NET_DSA) + dsa_register_master_tx(iface, ð_nxp_enet_tx); +#endif + ethernet_init(iface); + net_eth_carrier_off(data->iface); + + config->irq_config_func(); +} + +static enum ethernet_hw_caps eth_nxp_enet_get_capabilities(const struct device *dev) +{ + ARG_UNUSED(dev); + + return ETHERNET_HW_VLAN | ETHERNET_LINK_10BASE_T | +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + ETHERNET_PTP | +#endif +#if defined(CONFIG_NET_DSA) + ETHERNET_DSA_MASTER_PORT | +#endif +#if defined(CONFIG_ETH_NXP_ENET_HW_ACCELERATION) + ETHERNET_HW_TX_CHKSUM_OFFLOAD | + ETHERNET_HW_RX_CHKSUM_OFFLOAD | +#endif + ETHERNET_LINK_100BASE_T; +} + +static int eth_nxp_enet_set_config(const struct device *dev, + enum ethernet_config_type type, + const struct ethernet_config *cfg) +{ + struct nxp_enet_mac_data *data = dev->data; + const struct nxp_enet_mac_config *config = dev->config; + + switch (type) { + case ETHERNET_CONFIG_TYPE_MAC_ADDRESS: + memcpy(data->mac_addr, + cfg->mac_address.addr, + sizeof(data->mac_addr)); + ENET_SetMacAddr(config->base, data->mac_addr); + net_if_set_link_addr(data->iface, data->mac_addr, + sizeof(data->mac_addr), + NET_LINK_ETHERNET); + LOG_DBG("%s MAC set to %02x:%02x:%02x:%02x:%02x:%02x", + dev->name, + data->mac_addr[0], data->mac_addr[1], + data->mac_addr[2], data->mac_addr[3], + data->mac_addr[4], data->mac_addr[5]); + return 0; + default: + break; + } + + return -ENOTSUP; +} + +/* + ***************************** + * Ethernet RX Functionality * + ***************************** + */ + +static int eth_nxp_enet_rx(const struct device *dev) +{ + const struct nxp_enet_mac_config *config = dev->config; + struct nxp_enet_mac_data *data = dev->data; + uint16_t vlan_tag = NET_VLAN_TAG_UNSPEC; + uint32_t frame_length = 0U; + struct net_if *iface; + struct net_pkt *pkt; + status_t status; + uint32_t ts; + +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + enet_ptp_time_t ptp_time_data; +#endif + + status = ENET_GetRxFrameSize(&data->enet_handle, + (uint32_t *)&frame_length, RING_ID); + if (status == kStatus_ENET_RxFrameEmpty) { + return 0; + } else if (status == kStatus_ENET_RxFrameError) { + enet_data_error_stats_t error_stats; + + LOG_ERR("ENET_GetRxFrameSize return: %d", (int)status); + + ENET_GetRxErrBeforeReadFrame(&data->enet_handle, + &error_stats, RING_ID); + goto flush; + } + + if (frame_length > NET_ETH_MAX_FRAME_SIZE) { + LOG_ERR("frame too large (%d)", frame_length); + goto flush; + } + + /* Using root iface. It will be updated in net_recv_data() */ + pkt = net_pkt_rx_alloc_with_buffer(data->iface, frame_length, + AF_UNSPEC, 0, K_NO_WAIT); + if (!pkt) { + goto flush; + } + + /* in case multiply thread access + * we need to protect it with mutex. + */ + k_mutex_lock(&data->rx_frame_buf_mutex, K_FOREVER); + + status = ENET_ReadFrame(config->base, &data->enet_handle, + data->rx_frame_buf, frame_length, RING_ID, &ts); + if (status) { + LOG_ERR("ENET_ReadFrame failed: %d", (int)status); + net_pkt_unref(pkt); + + k_mutex_unlock(&data->rx_frame_buf_mutex); + goto error; + } + + if (net_pkt_write(pkt, data->rx_frame_buf, frame_length)) { + LOG_ERR("Unable to write frame into the pkt"); + net_pkt_unref(pkt); + k_mutex_unlock(&data->rx_frame_buf_mutex); + goto error; + } + + k_mutex_unlock(&data->rx_frame_buf_mutex); + +#if defined(CONFIG_NET_VLAN) + { + struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); + + if (ntohs(hdr->type) == NET_ETH_PTYPE_VLAN) { + struct net_eth_vlan_hdr *hdr_vlan = + (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); + + net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci)); + vlan_tag = net_pkt_vlan_tag(pkt); + +#if CONFIG_NET_TC_RX_COUNT > 1 + { + enum net_priority prio; + + prio = net_vlan2priority( + net_pkt_vlan_priority(pkt)); + net_pkt_set_priority(pkt, prio); + } +#endif /* CONFIG_NET_TC_RX_COUNT > 1 */ + } + } +#endif /* CONFIG_NET_VLAN */ + + /* + * Use MAC timestamp + */ +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + k_mutex_lock(data->ptp_mutex, K_FOREVER); + if (eth_get_ptp_data(get_iface(data, vlan_tag), pkt)) { + ENET_Ptp1588GetTimer(config->base, &data->enet_handle, + &ptp_time_data); + /* If latest timestamp reloads after getting from Rx BD, + * then second - 1 to make sure the actual Rx timestamp is + * accurate + */ + if (ptp_time_data.nanosecond < ts) { + ptp_time_data.second--; + } + + pkt->timestamp.nanosecond = ts; + pkt->timestamp.second = ptp_time_data.second; + } else { + /* Invalid value. */ + pkt->timestamp.nanosecond = UINT32_MAX; + pkt->timestamp.second = UINT64_MAX; + } + k_mutex_unlock(data->ptp_mutex); +#endif /* CONFIG_PTP_CLOCK_NXP_ENET */ + + iface = get_iface(data, vlan_tag); +#if defined(CONFIG_NET_DSA) + iface = dsa_net_recv(iface, &pkt); +#endif + if (net_recv_data(iface, pkt) < 0) { + net_pkt_unref(pkt); + goto error; + } + + return 1; +flush: + /* Flush the current read buffer. This operation can + * only report failure if there is no frame to flush, + * which cannot happen in this context. + */ + status = ENET_ReadFrame(config->base, &data->enet_handle, NULL, + 0, RING_ID, NULL); + __ASSERT_NO_MSG(status == kStatus_Success); +error: + eth_stats_update_errors_rx(get_iface(data, vlan_tag)); + return -EIO; +} + +static void eth_nxp_enet_rx_thread(void *arg1, void *unused1, void *unused2) +{ + const struct device *dev = arg1; + const struct nxp_enet_mac_config *config = dev->config; + struct nxp_enet_mac_data *data = dev->data; + + while (1) { + if (k_sem_take(&data->rx_thread_sem, K_FOREVER) == 0) { + while (eth_nxp_enet_rx(dev) == 1) { + ; + } + /* enable the IRQ for RX */ + ENET_EnableInterrupts(config->base, + kENET_RxFrameInterrupt | kENET_RxBufferInterrupt); + } + } +} + +/* + **************************** + * PHY management functions * + **************************** + */ + +static int nxp_enet_phy_reset_and_configure(const struct device *phy) +{ + int ret = 0; + + /* Reset the PHY */ + ret = phy_write(phy, MII_BMCR, MII_BMCR_RESET); + if (ret) { + return ret; + } + + /* 802.3u standard says reset takes up to 0.5s */ + k_busy_wait(500000); + + /* Configure the PHY */ + ret = phy_configure_link(phy, LINK_HALF_10BASE_T | + LINK_FULL_10BASE_T | + LINK_HALF_100BASE_T | + LINK_FULL_100BASE_T); + if (ret) { + return ret; + } + + return ret; +} + +static void nxp_enet_phy_cb(const struct device *phy, + struct phy_link_state *state, + void *eth_dev) +{ + const struct device *dev = eth_dev; + struct nxp_enet_mac_data *data = dev->data; + + if (!data->iface) { + return; + } + + if (!state->is_up) { + net_eth_carrier_off(data->iface); + nxp_enet_phy_reset_and_configure(phy); + } else { + net_eth_carrier_on(data->iface); + } +} + + +static int nxp_enet_phy_init(const struct device *dev) +{ + const struct nxp_enet_mac_config *config = dev->config; + int ret = 0; + + ret = nxp_enet_phy_reset_and_configure(config->phy_dev); + if (ret) { + return ret; + } + + ret = phy_link_callback_set(config->phy_dev, nxp_enet_phy_cb, (void *)dev); + if (ret) { + return ret; + } + + return ret; +} + +/* + **************************** + * Callbacks and interrupts * + **************************** + */ + +static void eth_callback(ENET_Type *base, enet_handle_t *handle, +#if FSL_FEATURE_ENET_QUEUE > 1 + uint32_t ringId, +#endif /* FSL_FEATURE_ENET_QUEUE > 1 */ + enet_event_t event, enet_frame_info_t *frameinfo, void *param) +{ + const struct device *dev = param; + const struct nxp_enet_mac_config *config = dev->config; + struct nxp_enet_mac_data *data = dev->data; + + switch (event) { + case kENET_RxEvent: + k_sem_give(&data->rx_thread_sem); + break; + case kENET_TxEvent: +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) && defined(CONFIG_NET_L2_PTP) + ts_register_tx_event(dev, frameinfo); +#endif + /* Free the TX buffer. */ + k_sem_give(&data->tx_buf_sem); + break; + case kENET_ErrEvent: + /* Error event: BABR/BABT/EBERR/LC/RL/UN/PLR. */ + break; + case kENET_WakeUpEvent: + /* Wake up from sleep mode event. */ + break; + case kENET_TimeStampEvent: + /* Time stamp event. */ + /* Reset periodic timer to default value. */ + config->base->ATPER = NSEC_PER_SEC; + break; + case kENET_TimeStampAvailEvent: + /* Time stamp available event. */ + break; + } +} + +static void eth_nxp_enet_isr(const struct device *dev) +{ + const struct nxp_enet_mac_config *config = dev->config; + struct nxp_enet_mac_data *data = dev->data; + unsigned int irq_lock_key = irq_lock(); + + uint32_t eir = ENET_GetInterruptStatus(config->base); + + if (eir & (kENET_RxBufferInterrupt | kENET_RxFrameInterrupt)) { +#if FSL_FEATURE_ENET_QUEUE > 1 + /* Only use ring 0 in this driver */ + ENET_ReceiveIRQHandler(config->base, &data->enet_handle, 0); +#else + ENET_ReceiveIRQHandler(config->base, &data->enet_handle); +#endif + ENET_DisableInterrupts(config->base, kENET_RxFrameInterrupt | + kENET_RxBufferInterrupt); + } + + if (eir & kENET_TxFrameInterrupt) { +#if FSL_FEATURE_ENET_QUEUE > 1 + ENET_TransmitIRQHandler(config->base, &data->enet_handle, 0); +#else + ENET_TransmitIRQHandler(config->base, &data->enet_handle); +#endif + } + + if (eir & kENET_TxBufferInterrupt) { + ENET_ClearInterruptStatus(config->base, kENET_TxBufferInterrupt); + ENET_DisableInterrupts(config->base, kENET_TxBufferInterrupt); + } + + if (eir & ENET_EIR_MII_MASK) { + /* Callback to MDIO driver for relevant interrupt */ + nxp_enet_mdio_callback(config->mdio, nxp_enet_interrupt); + } + + irq_unlock(irq_lock_key); +} + + +/* + ****************** + * Initialization * + ****************** + */ + +static int eth_nxp_enet_init(const struct device *dev) +{ + struct nxp_enet_mac_data *data = dev->data; + const struct nxp_enet_mac_config *config = dev->config; + enet_config_t enet_config; + uint32_t enet_module_clock_rate; + int err; + + err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (err) { + return err; + } + + /* Initialize kernel objects */ + k_mutex_init(&data->rx_frame_buf_mutex); + k_mutex_init(&data->tx_frame_buf_mutex); + k_sem_init(&data->rx_thread_sem, 0, CONFIG_ETH_NXP_ENET_RX_BUFFERS); + k_sem_init(&data->tx_buf_sem, + CONFIG_ETH_NXP_ENET_TX_BUFFERS, CONFIG_ETH_NXP_ENET_TX_BUFFERS); +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + k_sem_init(&data->ptp_ts_sem, 0, 1); +#endif + + if (config->generate_mac) { + config->generate_mac(data->mac_addr); + } + + /* Start interruption-poll thread */ + k_thread_create(&data->rx_thread, data->rx_thread_stack, + K_KERNEL_STACK_SIZEOF(data->rx_thread_stack), + eth_nxp_enet_rx_thread, (void *) dev, NULL, NULL, + K_PRIO_COOP(2), + 0, K_NO_WAIT); + k_thread_name_set(&data->rx_thread, "eth_nxp_enet_rx"); + + /* Get ENET IP module clock rate */ + err = clock_control_get_rate(config->clock_dev, config->clock_subsys, + &enet_module_clock_rate); + if (err) { + return err; + } + + /* Use HAL to set up MAC configuration */ + ENET_GetDefaultConfig(&enet_config); + + if (IS_ENABLED(CONFIG_NET_PROMISCUOUS_MODE)) { + enet_config.macSpecialConfig |= kENET_ControlPromiscuousEnable; + } + + if (IS_ENABLED(CONFIG_NET_VLAN)) { + enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable; + } + +#if defined(CONFIG_ETH_NXP_ENET_HW_ACCELERATION) + enet_config.txAccelerConfig |= + kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled; + enet_config.rxAccelerConfig |= + kENET_RxAccelIpCheckEnabled | kENET_RxAccelProtoCheckEnabled; +#endif + + enet_config.interrupt |= kENET_RxFrameInterrupt; + enet_config.interrupt |= kENET_TxFrameInterrupt; + + if (config->phy_mode == NXP_ENET_MII_MODE) { + enet_config.miiMode = kENET_MiiMode; + } else if (config->phy_mode == NXP_ENET_RMII_MODE) { + enet_config.miiMode = kENET_RmiiMode; + } else { + return -EINVAL; + } + + enet_config.callback = eth_callback; + enet_config.userData = (void *)dev; + + ENET_Init(config->base, + &data->enet_handle, + &enet_config, + &config->buffer_config, + data->mac_addr, + enet_module_clock_rate); + + nxp_enet_mdio_callback(config->mdio, nxp_enet_module_reset); + +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + union nxp_enet_ptp_data ptp_data; + + nxp_enet_ptp_clock_callback(config->ptp_clock, nxp_enet_module_reset, &ptp_data); + data->ptp_mutex = ptp_data.for_mac.ptp_mutex; + ENET_SetTxReclaim(&data->enet_handle, true, 0); +#endif + + ENET_ActiveRead(config->base); + + err = nxp_enet_phy_init(dev); + if (err) { + return err; + } + + LOG_DBG("%s MAC %02x:%02x:%02x:%02x:%02x:%02x", + dev->name, + data->mac_addr[0], data->mac_addr[1], + data->mac_addr[2], data->mac_addr[3], + data->mac_addr[4], data->mac_addr[5]); + + return 0; +} + +static const struct ethernet_api api_funcs = { + .iface_api.init = eth_nxp_enet_iface_init, +#if defined(CONFIG_PTP_CLOCK_NXP_ENET) + .get_ptp_clock = eth_nxp_enet_get_ptp_clock, +#endif + .get_capabilities = eth_nxp_enet_get_capabilities, + .set_config = eth_nxp_enet_set_config, + .send = eth_nxp_enet_tx, +#if defined(CONFIG_NET_DSA) + .send = dsa_tx, +#endif +}; + +#define NXP_ENET_CONNECT_IRQ(node_id, irq_names, idx) \ + do { \ + IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \ + DT_IRQ_BY_IDX(node_id, idx, priority), \ + eth_nxp_enet_isr, \ + DEVICE_DT_GET(node_id), \ + 0); \ + irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \ + } while (false); + +#define FREESCALE_OUI_B0 0x00 +#define FREESCALE_OUI_B1 0x04 +#define FREESCALE_OUI_B2 0x9f + +#if defined(CONFIG_SOC_SERIES_IMX_RT10XX) +#define ETH_NXP_ENET_UNIQUE_ID (OCOTP->CFG1 ^ OCOTP->CFG2) +#elif defined(CONFIG_SOC_SERIES_IMX_RT11XX) +#define ETH_NXP_ENET_UNIQUE_ID (OCOTP->FUSEN[40].FUSE) +#elif defined(CONFIG_SOC_SERIES_KINETIS_K6X) +#define ETH_NXP_ENET_UNIQUE_ID (SIM->UIDH ^ SIM->UIDMH ^ SIM->UIDML ^ SIM->UIDL) +#else +#error "Unsupported SOC" +#endif + +#define NXP_ENET_GENERATE_MAC_RANDOM(n) \ + static void generate_eth_##n##_mac(uint8_t *mac_addr) \ + { \ + gen_random_mac(mac_addr, \ + FREESCALE_OUI_B0, \ + FREESCALE_OUI_B1, \ + FREESCALE_OUI_B2); \ + } + +#define NXP_ENET_GENERATE_MAC_UNIQUE(n) \ + static void generate_eth_##n##_mac(uint8_t *mac_addr) \ + { \ + uint32_t id = ETH_NXP_ENET_UNIQUE_ID; \ + \ + mac_addr[0] = FREESCALE_OUI_B0; \ + mac_addr[0] |= 0x02; /* force LAA bit */ \ + mac_addr[1] = FREESCALE_OUI_B1; \ + mac_addr[2] = FREESCALE_OUI_B2; \ + mac_addr[3] = id >> 8; \ + mac_addr[4] = id >> 16; \ + mac_addr[5] = id >> 0; \ + mac_addr[5] += n; \ + } + +#define NXP_ENET_GENERATE_MAC(n) \ + COND_CODE_1(DT_INST_PROP(n, zephyr_random_mac_address), \ + (NXP_ENET_GENERATE_MAC_RANDOM(n)), \ + (NXP_ENET_GENERATE_MAC_UNIQUE(n))) + +#define NXP_ENET_DECIDE_MAC_ADDR(n) \ + COND_CODE_1(NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(n)), \ + (NXP_ENET_MAC_ADDR_LOCAL(n)), \ + (NXP_ENET_MAC_ADDR_GENERATED(n))) + +#define NXP_ENET_DECIDE_MAC_GEN_FUNC(n) \ + COND_CODE_1(NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(n)), \ + (NXP_ENET_GEN_MAC_FUNCTION_NO(n)), \ + (NXP_ENET_GEN_MAC_FUNCTION_YES(n))) + +#define NXP_ENET_MAC_ADDR_LOCAL(n) \ + .mac_addr = DT_INST_PROP(n, local_mac_address), + +#define NXP_ENET_MAC_ADDR_GENERATED(n) \ + .mac_addr = {0}, + +#define NXP_ENET_GEN_MAC_FUNCTION_NO(n) \ + .generate_mac = NULL, + +#define NXP_ENET_GEN_MAC_FUNCTION_YES(n) \ + .generate_mac = generate_eth_##n##_mac, + +#define NXP_ENET_DT_PHY_DEV(node_id, phy_phandle, idx) \ + DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, phy_phandle, idx)) + +#if DT_NODE_HAS_STATUS(DT_CHOSEN(zephyr_dtcm), okay) && \ + CONFIG_ETH_NXP_ENET_USE_DTCM_FOR_DMA_BUFFER +/* Use DTCM for hardware DMA buffers */ +#define _nxp_enet_dma_desc_section __dtcm_bss_section +#define _nxp_enet_dma_buffer_section __dtcm_noinit_section +#define _nxp_enet_driver_buffer_section __dtcm_noinit_section +#elif defined(CONFIG_NOCACHE_MEMORY) +#define _nxp_enet_dma_desc_section __nocache +#define _nxp_enet_dma_buffer_section __nocache +#define _nxp_enet_driver_buffer_section +#else +#define _nxp_enet_dma_desc_section +#define _nxp_enet_dma_buffer_section +#define _nxp_enet_driver_buffer_section +#endif + + /* Use ENET_FRAME_MAX_VLANFRAMELEN for VLAN frame size + * Use ENET_FRAME_MAX_FRAMELEN for Ethernet frame size + */ +#if defined(CONFIG_NET_VLAN) +#if !defined(ENET_FRAME_MAX_VLANFRAMELEN) +#define ENET_FRAME_MAX_VLANFRAMELEN (ENET_FRAME_MAX_FRAMELEN + 4) +#endif +#define ETH_NXP_ENET_BUFFER_SIZE \ + ROUND_UP(ENET_FRAME_MAX_VLANFRAMELEN, ENET_BUFF_ALIGNMENT) +#else +#define ETH_NXP_ENET_BUFFER_SIZE \ + ROUND_UP(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT) +#endif /* CONFIG_NET_VLAN */ + +#define NXP_ENET_PHY_MODE(node_id) \ + DT_ENUM_HAS_VALUE(node_id, phy_connection_type, mii) ? NXP_ENET_MII_MODE : \ + (DT_ENUM_HAS_VALUE(node_id, phy_connection_type, rmii) ? NXP_ENET_RMII_MODE : \ + NXP_ENET_INVALID_MII_MODE) + +#ifdef CONFIG_PTP_CLOCK_NXP_ENET +#define NXP_ENET_PTP_DEV(n) .ptp_clock = DEVICE_DT_GET(DT_INST_PHANDLE(n, nxp_ptp_clock)), +#define NXP_ENET_FRAMEINFO_ARRAY(n) \ + static enet_frame_info_t \ + nxp_enet_##n##_tx_frameinfo_array[CONFIG_ETH_NXP_ENET_TX_BUFFERS]; +#define NXP_ENET_FRAMEINFO(n) \ + .txFrameInfo = nxp_enet_##n##_tx_frameinfo_array, +#else +#define NXP_ENET_PTP_DEV(n) +#define NXP_ENET_FRAMEINFO_ARRAY(n) +#define NXP_ENET_FRAMEINFO(n) \ + .txFrameInfo = NULL +#endif + +#define NXP_ENET_INIT(n) \ + NXP_ENET_GENERATE_MAC(n) \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + NXP_ENET_FRAMEINFO_ARRAY(n) \ + \ + static void nxp_enet_##n##_irq_config_func(void) \ + { \ + DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, \ + NXP_ENET_CONNECT_IRQ); \ + } \ + \ + volatile static __aligned(ENET_BUFF_ALIGNMENT) \ + _nxp_enet_dma_desc_section \ + enet_rx_bd_struct_t \ + nxp_enet_##n##_rx_buffer_desc[CONFIG_ETH_NXP_ENET_RX_BUFFERS]; \ + \ + volatile static __aligned(ENET_BUFF_ALIGNMENT) \ + _nxp_enet_dma_desc_section \ + enet_tx_bd_struct_t \ + nxp_enet_##n##_tx_buffer_desc[CONFIG_ETH_NXP_ENET_TX_BUFFERS]; \ + \ + static uint8_t __aligned(ENET_BUFF_ALIGNMENT) \ + _nxp_enet_dma_buffer_section \ + nxp_enet_##n##_rx_buffer[CONFIG_ETH_NXP_ENET_RX_BUFFERS] \ + [ETH_NXP_ENET_BUFFER_SIZE]; \ + \ + static uint8_t __aligned(ENET_BUFF_ALIGNMENT) \ + _nxp_enet_dma_buffer_section \ + nxp_enet_##n##_tx_buffer[CONFIG_ETH_NXP_ENET_TX_BUFFERS] \ + [ETH_NXP_ENET_BUFFER_SIZE]; \ + \ + const struct nxp_enet_mac_config nxp_enet_##n##_config = { \ + .base = (ENET_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \ + .irq_config_func = nxp_enet_##n##_irq_config_func, \ + .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))), \ + .clock_subsys = (void *)DT_CLOCKS_CELL_BY_IDX( \ + DT_INST_PARENT(n), 0, name), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .buffer_config = { \ + .rxBdNumber = CONFIG_ETH_NXP_ENET_RX_BUFFERS, \ + .txBdNumber = CONFIG_ETH_NXP_ENET_TX_BUFFERS, \ + .rxBuffSizeAlign = ETH_NXP_ENET_BUFFER_SIZE, \ + .txBuffSizeAlign = ETH_NXP_ENET_BUFFER_SIZE, \ + .rxBdStartAddrAlign = nxp_enet_##n##_rx_buffer_desc, \ + .txBdStartAddrAlign = nxp_enet_##n##_tx_buffer_desc, \ + .rxBufferAlign = nxp_enet_##n##_rx_buffer[0], \ + .txBufferAlign = nxp_enet_##n##_tx_buffer[0], \ + .rxMaintainEnable = true, \ + .txMaintainEnable = true, \ + NXP_ENET_FRAMEINFO(n) \ + }, \ + .phy_mode = NXP_ENET_PHY_MODE(DT_DRV_INST(n)), \ + .phy_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle)), \ + .mdio = DEVICE_DT_GET(DT_INST_PHANDLE(n, nxp_mdio)), \ + NXP_ENET_PTP_DEV(n) \ + NXP_ENET_DECIDE_MAC_GEN_FUNC(n) \ + }; \ + \ + static _nxp_enet_driver_buffer_section uint8_t \ + nxp_enet_##n##_tx_frame_buf[NET_ETH_MAX_FRAME_SIZE]; \ + static _nxp_enet_driver_buffer_section uint8_t \ + nxp_enet_##n##_rx_frame_buf[NET_ETH_MAX_FRAME_SIZE]; \ + \ + struct nxp_enet_mac_data nxp_enet_##n##_data = { \ + NXP_ENET_DECIDE_MAC_ADDR(n) \ + .tx_frame_buf = nxp_enet_##n##_tx_frame_buf, \ + .rx_frame_buf = nxp_enet_##n##_rx_frame_buf, \ + }; \ + \ + ETH_NET_DEVICE_DT_INST_DEFINE(n, eth_nxp_enet_init, NULL, \ + &nxp_enet_##n##_data, &nxp_enet_##n##_config, \ + CONFIG_ETH_INIT_PRIORITY, \ + &api_funcs, NET_ETH_MTU); + +DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_INIT) diff --git a/drivers/ethernet/eth_sam_gmac.c b/drivers/ethernet/eth_sam_gmac.c index dec6ea6df20de0..b4746e4e743e26 100644 --- a/drivers/ethernet/eth_sam_gmac.c +++ b/drivers/ethernet/eth_sam_gmac.c @@ -133,6 +133,8 @@ static inline void dcache_clean(uint32_t addr, uint32_t size) #endif #endif /* !CONFIG_NET_TEST */ +BUILD_ASSERT(DT_INST_ENUM_IDX(0, phy_connection_type) <= 1, "Invalid PHY connection"); + /* RX descriptors list */ static struct gmac_desc rx_desc_que0[MAIN_QUEUE_RX_DESC_COUNT] __nocache __aligned(GMAC_DESC_ALIGNMENT); @@ -1113,7 +1115,15 @@ static int gmac_init(Gmac *gmac, uint32_t gmac_ncfgr_val) /* Setup Network Configuration Register */ gmac->GMAC_NCFGR = gmac_ncfgr_val | mck_divisor; - gmac->GMAC_UR = DT_INST_ENUM_IDX(0, phy_connection_type); + switch (DT_INST_ENUM_IDX_OR(0, phy_connection_type, 1)) { + case 0: /* mii */ + gmac->GMAC_UR = 0x1; + case 1: /* rmii */ + gmac->GMAC_UR = 0x0; + default: + /* Build assert at top of file should catch this case */ + return -EINVAL; + } #if defined(CONFIG_PTP_CLOCK_SAM_GMAC) /* Initialize PTP Clock Registers */ diff --git a/drivers/ethernet/phy/CMakeLists.txt b/drivers/ethernet/phy/CMakeLists.txt index d3a7777ae2478c..ff25a9a4fa39c2 100644 --- a/drivers/ethernet/phy/CMakeLists.txt +++ b/drivers/ethernet/phy/CMakeLists.txt @@ -3,3 +3,4 @@ zephyr_library_sources_ifdef(CONFIG_PHY_GENERIC_MII phy_mii.c) zephyr_library_sources_ifdef(CONFIG_PHY_ADIN2111 phy_adin2111.c) zephyr_library_sources_ifdef(CONFIG_PHY_TJA1103 phy_tja1103.c) +zephyr_library_sources_ifdef(CONFIG_PHY_MICROCHIP_KSZ8081 phy_microchip_ksz8081.c) diff --git a/drivers/ethernet/phy/Kconfig b/drivers/ethernet/phy/Kconfig index b148d1c37d1f45..ad7cef7bc17705 100644 --- a/drivers/ethernet/phy/Kconfig +++ b/drivers/ethernet/phy/Kconfig @@ -23,8 +23,7 @@ config PHY_INIT_PRIORITY config PHY_GENERIC_MII bool "Generic MII PHY Driver" - default y - depends on DT_HAS_ETHERNET_PHY_ENABLED + default y if DT_HAS_ETHERNET_PHY_ENABLED depends on MDIO help This is a generic MII PHY interface that communicates with the @@ -47,6 +46,15 @@ config PHY_TJA1103 help Enable TJA1103 PHY driver. +config PHY_MICROCHIP_KSZ8081 + bool "Microchip KSZ8081 Phy Driver" + default y + depends on DT_HAS_MICROCHIP_KSZ8081_ENABLED + depends on MDIO + depends on GPIO + help + Enable Microchip KSZ8081 Ethernet Phy Driver + config PHY_AUTONEG_TIMEOUT_MS int "Auto-negotiation timeout value in milliseconds" default 4000 diff --git a/drivers/ethernet/phy/phy_microchip_ksz8081.c b/drivers/ethernet/phy/phy_microchip_ksz8081.c new file mode 100644 index 00000000000000..46382a37df6645 --- /dev/null +++ b/drivers/ethernet/phy/phy_microchip_ksz8081.c @@ -0,0 +1,443 @@ +/* + * Copyright 2023 NXP + * + * Inspiration from phy_mii.c, which is: + * Copyright (c) 2021 IP-Logix Inc. + * Copyright 2022 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_ksz8081 + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE_NAME phy_mc_ksz8081 +#define LOG_LEVEL CONFIG_PHY_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define PHY_MC_KSZ8081_OMSO_REG 0x16 +#define PHY_MC_KSZ8081_OMSO_FACTORY_MODE_MASK BIT(15) +#define PHY_MC_KSZ8081_OMSO_NAND_TREE_MASK BIT(5) + +#define PHY_MC_KSZ8081_CTRL2_REG 0x1F +#define PHY_MC_KSZ8081_CTRL2_REF_CLK_SEL BIT(7) + +#define PHY_MC_KSZ8081_RESET_HOLD_TIME + +enum ksz8081_interface { + KSZ8081_MII, + KSZ8081_RMII, +}; + +struct mc_ksz8081_config { + uint8_t addr; + const struct device *mdio_dev; + enum ksz8081_interface phy_iface; + const struct gpio_dt_spec reset_gpio; + const struct gpio_dt_spec interrupt_gpio; +}; + +struct mc_ksz8081_data { + const struct device *dev; + struct phy_link_state state; + phy_callback_t cb; + void *cb_data; + struct k_mutex mutex; + struct k_work_delayable phy_monitor_work; +}; + +static int phy_mc_ksz8081_read(const struct device *dev, + uint16_t reg_addr, uint32_t *data) +{ + const struct mc_ksz8081_config *config = dev->config; + int ret; + + ret = mdio_read(config->mdio_dev, config->addr, reg_addr, (uint16_t *)data); + if (ret) { + return ret; + } + + return 0; +} + +static int phy_mc_ksz8081_write(const struct device *dev, + uint16_t reg_addr, uint32_t data) +{ + const struct mc_ksz8081_config *config = dev->config; + int ret; + + ret = mdio_write(config->mdio_dev, config->addr, reg_addr, (uint16_t)data); + if (ret) { + return ret; + } + + return 0; +} + +static int phy_mc_ksz8081_autonegotiate(const struct device *dev) +{ + const struct mc_ksz8081_config *config = dev->config; + int ret; + uint32_t bmcr = 0; + uint32_t bmsr = 0; + uint16_t timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / 100; + + /* Read control register to write back with autonegotiation bit */ + ret = phy_mc_ksz8081_read(dev, MII_BMCR, &bmcr); + if (ret) { + LOG_ERR("Error reading phy (%d) basic control register", config->addr); + return ret; + } + + /* (re)start autonegotiation */ + LOG_DBG("PHY (%d) is entering autonegotiation sequence", config->addr); + bmcr |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART; + bmcr &= ~MII_BMCR_ISOLATE; + + ret = phy_mc_ksz8081_write(dev, MII_BMCR, bmcr); + if (ret) { + LOG_ERR("Error writing phy (%d) basic control register", config->addr); + return ret; + } + + /* TODO change this to GPIO interrupt driven */ + do { + if (timeout-- == 0) { + LOG_DBG("PHY (%d) autonegotiation timed out", config->addr); + return -ETIMEDOUT; + } + k_msleep(100); + + ret = phy_mc_ksz8081_read(dev, MII_BMSR, &bmsr); + if (ret) { + LOG_ERR("Error reading phy (%d) basic status register", config->addr); + return ret; + } + } while (!(bmsr & MII_BMSR_AUTONEG_COMPLETE)); + + LOG_DBG("PHY (%d) autonegotiation completed", config->addr); + + return 0; +} + +static int phy_mc_ksz8081_get_link(const struct device *dev, + struct phy_link_state *state) +{ + const struct mc_ksz8081_config *config = dev->config; + struct mc_ksz8081_data *data = dev->data; + int ret; + uint32_t bmsr = 0; + uint32_t anar = 0; + uint32_t anlpar = 0; + + /* Lock mutex */ + ret = k_mutex_lock(&data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("PHY mutex lock error"); + return ret; + } + + /* Read link state */ + ret = phy_mc_ksz8081_read(dev, MII_BMSR, &bmsr); + if (ret) { + LOG_ERR("Error reading phy (%d) basic status register", config->addr); + k_mutex_unlock(&data->mutex); + return ret; + } + state->is_up = bmsr & MII_BMSR_LINK_STATUS; + + LOG_DBG("PHY %d is %s", config->addr, data->state.is_up ? "up" : "down"); + + /* Read currently configured advertising options */ + ret = phy_mc_ksz8081_read(dev, MII_ANAR, &anar); + if (ret) { + LOG_ERR("Error reading phy (%d) advertising register", config->addr); + k_mutex_unlock(&data->mutex); + return ret; + } + + /* Read link partner capability */ + ret = phy_mc_ksz8081_read(dev, MII_ANLPAR, &anlpar); + if (ret) { + LOG_ERR("Error reading phy (%d) link partner register", config->addr); + k_mutex_unlock(&data->mutex); + return ret; + } + + /* Unlock mutex */ + k_mutex_unlock(&data->mutex); + + uint32_t mutual_capabilities = anar & anlpar; + + if (mutual_capabilities & MII_ADVERTISE_100_FULL) { + state->speed = LINK_FULL_100BASE_T; + } else if (mutual_capabilities & MII_ADVERTISE_100_HALF) { + state->speed = LINK_HALF_100BASE_T; + } else if (mutual_capabilities & MII_ADVERTISE_10_FULL) { + state->speed = LINK_FULL_10BASE_T; + } else if (mutual_capabilities & MII_ADVERTISE_10_HALF) { + state->speed = LINK_HALF_10BASE_T; + } else { + LOG_ERR("No valid PHY %d capabilities", config->addr); + return -EIO; + } + + LOG_DBG("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr, + (PHY_LINK_IS_SPEED_100M(state->speed) ? "100" : "10"), + PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "full" : "half"); + + return 0; +} + +/* + * Configuration set statically (DT) that should never change + * This function is needed in case the PHY is reset then the next call + * to configure the phy will ensure this configuration will be redone + */ +static int phy_mc_ksz8081_static_cfg(const struct device *dev) +{ + const struct mc_ksz8081_config *config = dev->config; + uint32_t omso = 0; + uint32_t ctrl2 = 0; + int ret = 0; + + /* Force normal operation in the case of factory mode */ + ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_OMSO_REG, (uint32_t *)&omso); + if (ret) { + return ret; + } + + omso &= ~PHY_MC_KSZ8081_OMSO_FACTORY_MODE_MASK & + ~PHY_MC_KSZ8081_OMSO_NAND_TREE_MASK; + + ret = phy_mc_ksz8081_write(dev, PHY_MC_KSZ8081_OMSO_REG, (uint32_t)omso); + if (ret) { + return ret; + } + + /* Select correct reference clock mode depending on interface setup */ + ret = phy_mc_ksz8081_read(dev, PHY_MC_KSZ8081_CTRL2_REG, (uint32_t *)&ctrl2); + if (ret) { + return ret; + } + + if (config->phy_iface == KSZ8081_RMII) { + ctrl2 |= PHY_MC_KSZ8081_CTRL2_REF_CLK_SEL; + } else { + ctrl2 &= ~PHY_MC_KSZ8081_CTRL2_REF_CLK_SEL; + } + + ret = phy_mc_ksz8081_write(dev, PHY_MC_KSZ8081_CTRL2_REG, (uint32_t)ctrl2); + if (ret) { + return ret; + } + + return 0; +} + +static int phy_mc_ksz8081_cfg_link(const struct device *dev, + enum phy_link_speed speeds) +{ + const struct mc_ksz8081_config *config = dev->config; + struct mc_ksz8081_data *data = dev->data; + int ret; + uint32_t anar; + + /* Lock mutex */ + ret = k_mutex_lock(&data->mutex, K_FOREVER); + if (ret) { + LOG_ERR("PHY mutex lock error"); + return ret; + } + + /* We are going to reconfigure the phy, don't need to monitor until done */ + k_work_cancel_delayable(&data->phy_monitor_work); + + /* DT configurations */ + ret = phy_mc_ksz8081_static_cfg(dev); + if (ret) { + k_mutex_unlock(&data->mutex); + return ret; + } + + /* Read ANAR register to write back */ + ret = phy_mc_ksz8081_read(dev, MII_ANAR, &anar); + if (ret) { + LOG_ERR("Error reading phy (%d) advertising register", config->addr); + k_mutex_unlock(&data->mutex); + return -EIO; + } + + /* Setup advertising register */ + if (speeds & LINK_FULL_100BASE_T) { + anar |= MII_ADVERTISE_100_FULL; + } else { + anar &= ~MII_ADVERTISE_100_FULL; + } + if (speeds & LINK_HALF_100BASE_T) { + anar |= MII_ADVERTISE_100_HALF; + } else { + anar &= ~MII_ADVERTISE_100_HALF; + } + if (speeds & LINK_FULL_10BASE_T) { + anar |= MII_ADVERTISE_10_FULL; + } else { + anar &= ~MII_ADVERTISE_10_FULL; + } + if (speeds & LINK_HALF_10BASE_T) { + anar |= MII_ADVERTISE_10_HALF; + } else { + anar &= ~MII_ADVERTISE_10_HALF; + } + + /* Write capabilities to advertising register */ + ret = phy_mc_ksz8081_write(dev, MII_ANAR, anar); + if (ret) { + LOG_ERR("Error writing phy (%d) advertising register", config->addr); + k_mutex_unlock(&data->mutex); + return ret; + } + + /* (re)do autonegotiation */ + ret = phy_mc_ksz8081_autonegotiate(dev); + if (ret) { + LOG_ERR("Error in autonegotiation"); + k_mutex_unlock(&data->mutex); + return ret; + } + + /* Unlock mutex */ + k_mutex_unlock(&data->mutex); + + + /* Get link status */ + ret = phy_mc_ksz8081_get_link(dev, &data->state); + if (ret) { + return ret; + } + + /* Start monitoring */ + k_work_schedule(&data->phy_monitor_work, + K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); + + /* Log the results of the configuration */ + LOG_INF("PHY %d is %s", config->addr, data->state.is_up ? "up" : "down"); + LOG_INF("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr, + (PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"), + PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half"); + + return 0; +} + +static int phy_mc_ksz8081_link_cb_set(const struct device *dev, + phy_callback_t cb, void *user_data) +{ + struct mc_ksz8081_data *data = dev->data; + + data->cb = cb; + data->cb_data = user_data; + + phy_mc_ksz8081_get_link(dev, &data->state); + + data->cb(dev, &data->state, data->cb_data); + + return 0; +} + +static void phy_mc_ksz8081_monitor_work_handler(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct mc_ksz8081_data *data = + CONTAINER_OF(dwork, struct mc_ksz8081_data, phy_monitor_work); + const struct device *dev = data->dev; + struct phy_link_state state; + int rc; + + rc = phy_mc_ksz8081_get_link(dev, &state); + + if (rc == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) { + memcpy(&data->state, &state, sizeof(struct phy_link_state)); + if (data->cb) { + data->cb(dev, &data->state, data->cb_data); + } + } + + /* TODO change this to GPIO interrupt driven */ + k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD)); +} + +static int phy_mc_ksz8081_init(const struct device *dev) +{ + const struct mc_ksz8081_config *config = dev->config; + struct mc_ksz8081_data *data = dev->data; + int ret; + + data->dev = dev; + + ret = k_mutex_init(&data->mutex); + if (ret) { + return ret; + } + + mdio_bus_enable(config->mdio_dev); + + /* Prevent NAND TREE mode */ + ret = gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_OUTPUT_ACTIVE); + if (ret) { + return ret; + } + + /* Start reset */ + ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE); + if (ret) { + return ret; + } + + /* Wait for 500 ms as specified by datasheet */ + k_busy_wait(USEC_PER_MSEC * 500); + + /* Reset over */ + ret = gpio_pin_set_dt(&config->reset_gpio, 1); + if (ret) { + return ret; + } + + k_work_init_delayable(&data->phy_monitor_work, + phy_mc_ksz8081_monitor_work_handler); + + return 0; +} + +static const struct ethphy_driver_api mc_ksz8081_phy_api = { + .get_link = phy_mc_ksz8081_get_link, + .cfg_link = phy_mc_ksz8081_cfg_link, + .link_cb_set = phy_mc_ksz8081_link_cb_set, + .read = phy_mc_ksz8081_read, + .write = phy_mc_ksz8081_write, +}; + +#define MICROCHIP_KSZ8081_INIT(n) \ + static const struct mc_ksz8081_config mc_ksz8081_##n##_config = { \ + .addr = DT_INST_REG_ADDR(n), \ + .mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \ + .phy_iface = DT_INST_ENUM_IDX(n, mc_interface_type), \ + .reset_gpio = GPIO_DT_SPEC_INST_GET(n, mc_reset_gpio), \ + .interrupt_gpio = GPIO_DT_SPEC_INST_GET(n, mc_interrupt_gpio), \ + }; \ + \ + static struct mc_ksz8081_data mc_ksz8081_##n##_data; \ + \ + DEVICE_DT_INST_DEFINE(n, &phy_mc_ksz8081_init, NULL, \ + &mc_ksz8081_##n##_data, &mc_ksz8081_##n##_config, \ + POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \ + &mc_ksz8081_phy_api); + +DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_KSZ8081_INIT) diff --git a/drivers/mdio/CMakeLists.txt b/drivers/mdio/CMakeLists.txt index 360111f0c0881b..03391788187370 100644 --- a/drivers/mdio/CMakeLists.txt +++ b/drivers/mdio/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_MDIO_NXP_S32_NETC mdio_nxp_s32_netc.c) 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) diff --git a/drivers/mdio/Kconfig b/drivers/mdio/Kconfig index 37721e885a69c0..7e72b50ceb159d 100644 --- a/drivers/mdio/Kconfig +++ b/drivers/mdio/Kconfig @@ -30,6 +30,7 @@ source "drivers/mdio/Kconfig.nxp_s32_netc" source "drivers/mdio/Kconfig.nxp_s32_gmac" source "drivers/mdio/Kconfig.adin2111" source "drivers/mdio/Kconfig.gpio" +source "drivers/mdio/Kconfig.nxp_enet" config MDIO_INIT_PRIORITY int "Init priority" diff --git a/drivers/mdio/Kconfig.nxp_enet b/drivers/mdio/Kconfig.nxp_enet new file mode 100644 index 00000000000000..dd3c734e1b37cc --- /dev/null +++ b/drivers/mdio/Kconfig.nxp_enet @@ -0,0 +1,21 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config MDIO_NXP_ENET + bool "NXP ENET MDIO Driver" + default y + depends on DT_HAS_NXP_ENET_MDIO_ENABLED + help + Enable NXP ENET MDIO Driver. This Kconfig can be disabled manually + if all ethernet PHYs being used with ENET are not managed by MDIO bus. + +if MDIO_NXP_ENET + +config MDIO_NXP_ENET_TIMEOUT + int "NXP ENET MDIO Timeout time" + default 1 + help + Time in milliseconds before an MDIO transaction that has not + finished is considered to have timed out. + +endif # MDIO_NXP_ENET diff --git a/drivers/mdio/mdio_nxp_enet.c b/drivers/mdio/mdio_nxp_enet.c new file mode 100644 index 00000000000000..c1a3df31692982 --- /dev/null +++ b/drivers/mdio/mdio_nxp_enet.c @@ -0,0 +1,297 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_enet_mdio + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Target MDC frequency 2.5 MHz */ +#define NXP_ENET_MDIO_MDC_FREQ 25000000U + +struct nxp_enet_mdio_config { + ENET_Type *base; + const struct pinctrl_dev_config *pincfg; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + uint16_t timeout; + bool disable_preamble; +}; + +struct nxp_enet_mdio_data { + struct k_mutex mdio_mutex; + struct k_sem mdio_sem; + bool interrupt_up; +}; + +/* + * This function is used for both read and write operations + * in order to wait for the completion of an MDIO transaction. + * It returns -ETIMEDOUT if timeout occurs as specified in DT, + * otherwise returns 0 if EIR MII bit is set indicting completed + * operation, otherwise -EIO. + */ +static int nxp_enet_mdio_wait_xfer(const struct device *dev) +{ + const struct nxp_enet_mdio_config *config = dev->config; + struct nxp_enet_mdio_data *data = dev->data; + ENET_Type *base = config->base; + int ret = 0; + + /* This function will not make sense from IRQ context */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + /* Enable the interrupt */ + base->EIMR |= ENET_EIMR_MII_MASK; + + /* Wait for operation to complete or time out */ + if (!data->interrupt_up) { + /* In the case where the interrupt has not been enabled yet because + * ethernet driver has not initiaized, just do a busy wait + */ + k_busy_wait(USEC_PER_MSEC * config->timeout); + if (base->EIR && ENET_EIR_MII_MASK == ENET_EIR_MII_MASK) { + ret = 0; + } else { + ret = -ETIMEDOUT; + } + } else if (k_sem_take(&data->mdio_sem, K_MSEC(config->timeout))) { + /* Interrupt was enabled but did not occur in time */ + ret = -ETIMEDOUT; + } else if (base->EIR && ENET_EIR_MII_MASK == ENET_EIR_MII_MASK) { + /* Interrupt happened meaning mdio transaction completed */ + ret = 0; + } else { + /* No idea what happened */ + ret = -EIO; + } + + return ret; +} + +/* MDIO Read API implementation */ +static int nxp_enet_mdio_read(const struct device *dev, + uint8_t prtad, uint8_t regad, uint16_t *read_data) +{ + const struct nxp_enet_mdio_config *config = dev->config; + struct nxp_enet_mdio_data *data = dev->data; + int ret; + + /* Only one MDIO bus operation attempt at a time */ + (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER); + + /* + * Clear the bit (W1C) that indicates MDIO transfer is ready to + * prepare to wait for it to be set once this read is done + */ + config->base->EIR |= ENET_EIR_MII_MASK; + + /* + * Write MDIO frame to MII management register which will + * send the read command and data out to the MDIO bus as this frame: + * ST = start, 1 means start + * OP = operation, 2 means read + * PA = PHY/Port address + * RA = Register/Device Address + * TA = Turnaround, must be 2 to be valid + * data = data to be written to the PHY register + */ + config->base->MMFR = ENET_MMFR_ST(0x1U) | + ENET_MMFR_OP(MDIO_OP_C22_READ) | + ENET_MMFR_PA(prtad) | + ENET_MMFR_RA(regad) | + ENET_MMFR_TA(0x2U); + + ret = nxp_enet_mdio_wait_xfer(dev); + if (ret) { + (void)k_mutex_unlock(&data->mdio_mutex); + return ret; + } + + /* The data is received in the same register that we wrote the command to */ + *read_data = (config->base->MMFR & ENET_MMFR_DATA_MASK) >> ENET_MMFR_DATA_SHIFT; + + /* Clear the same bit as before because the event has been handled */ + config->base->EIR |= ENET_EIR_MII_MASK; + + /* This MDIO interaction is finished */ + (void)k_mutex_unlock(&data->mdio_mutex); + + return ret; +} + +/* MDIO Write API implementation */ +static int nxp_enet_mdio_write(const struct device *dev, + uint8_t prtad, uint8_t regad, uint16_t write_data) +{ + const struct nxp_enet_mdio_config *config = dev->config; + struct nxp_enet_mdio_data *data = dev->data; + int ret; + + /* Only one MDIO bus operation attempt at a time */ + (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER); + + /* + * Clear the bit (W1C) that indicates MDIO transfer is ready to + * prepare to wait for it to be set once this write is done + */ + config->base->EIR |= ENET_EIR_MII_MASK; + + /* + * Write MDIO frame to MII management register which will + * send the write command and data out to the MDIO bus as this frame: + * ST = start, 1 means start + * OP = operation, 1 means write + * PA = PHY/Port address + * RA = Register/Device Address + * TA = Turnaround, must be 2 to be valid + * data = data to be written to the PHY register + */ + config->base->MMFR = ENET_MMFR_ST(0x1U) | + ENET_MMFR_OP(MDIO_OP_C22_WRITE) | + ENET_MMFR_PA(prtad) | + ENET_MMFR_RA(regad) | + ENET_MMFR_TA(0x2U) | + write_data; + + ret = nxp_enet_mdio_wait_xfer(dev); + if (ret) { + (void)k_mutex_unlock(&data->mdio_mutex); + return ret; + } + + /* Clear the same bit as before because the event has been handled */ + config->base->EIR |= ENET_EIR_MII_MASK; + + /* This MDIO interaction is finished */ + (void)k_mutex_unlock(&data->mdio_mutex); + + return ret; +} + +/* MDIO bus enable/disable "implementation" */ +static void nxp_enet_mdio_bus_fn(const struct device *dev) +{ + /* + * MDIO bus device is actually part of ethernet device, and + * does not support ability to disable/enable MDIO bus hardware + * independently of the ethernet/MAC hardware, so do nothing. + */ +} + +static const struct mdio_driver_api nxp_enet_mdio_api = { + .read = nxp_enet_mdio_read, + .write = nxp_enet_mdio_write, + .bus_enable = nxp_enet_mdio_bus_fn, + .bus_disable = nxp_enet_mdio_bus_fn, +}; + +static void nxp_enet_mdio_isr_cb(const struct device *dev) +{ + const struct nxp_enet_mdio_config *config = dev->config; + struct nxp_enet_mdio_data *data = dev->data; + + /* Signal that operation finished */ + k_sem_give(&data->mdio_sem); + + /* Disable the interrupt */ + config->base->EIMR &= ~ENET_EIMR_MII_MASK; +} + +static void nxp_enet_mdio_post_module_reset_init(const struct device *dev) +{ + const struct nxp_enet_mdio_config *config = dev->config; + uint32_t enet_module_clock_rate; + + /* Set up MSCR register */ + (void) clock_control_get_rate(config->clock_dev, config->clock_subsys, + &enet_module_clock_rate); + uint32_t mii_speed = (enet_module_clock_rate + 2 * NXP_ENET_MDIO_MDC_FREQ - 1) / + (2 * NXP_ENET_MDIO_MDC_FREQ) - 1; + uint32_t holdtime = (10 + NSEC_PER_SEC / enet_module_clock_rate - 1) / + (NSEC_PER_SEC / enet_module_clock_rate) - 1; + uint32_t mscr = ENET_MSCR_MII_SPEED(mii_speed) | ENET_MSCR_HOLDTIME(holdtime) | + (config->disable_preamble ? ENET_MSCR_DIS_PRE_MASK : 0); + config->base->MSCR = mscr; +} + +void nxp_enet_mdio_callback(const struct device *dev, + enum nxp_enet_callback_reason event) +{ + struct nxp_enet_mdio_data *data = dev->data; + + switch (event) { + case nxp_enet_module_reset: + nxp_enet_mdio_post_module_reset_init(dev); + break; + case nxp_enet_interrupt: + nxp_enet_mdio_isr_cb(dev); + break; + case nxp_enet_interrupt_enabled: + data->interrupt_up = true; + break; + default: + } +} + +static int nxp_enet_mdio_init(const struct device *dev) +{ + const struct nxp_enet_mdio_config *config = dev->config; + struct nxp_enet_mdio_data *data = dev->data; + int ret = 0; + + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + ret = k_mutex_init(&data->mdio_mutex); + if (ret) { + return ret; + } + + ret = k_sem_init(&data->mdio_sem, 0, 1); + if (ret) { + return ret; + } + + /* All operations done after module reset should be done during device init too */ + nxp_enet_mdio_post_module_reset_init(dev); + + return ret; +} + +#define NXP_ENET_MDIO_INIT(inst) \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static const struct nxp_enet_mdio_config nxp_enet_mdio_cfg_##inst = { \ + .base = (ENET_Type *) DT_REG_ADDR(DT_INST_PARENT(inst)), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .timeout = CONFIG_MDIO_NXP_ENET_TIMEOUT, \ + .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(inst))), \ + .clock_subsys = (void *) DT_CLOCKS_CELL_BY_IDX( \ + DT_INST_PARENT(inst), 0, name), \ + .disable_preamble = DT_INST_PROP(inst, suppress_preamble), \ + }; \ + \ + static struct nxp_enet_mdio_data nxp_enet_mdio_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, &nxp_enet_mdio_init, NULL, \ + &nxp_enet_mdio_data_##inst, &nxp_enet_mdio_cfg_##inst, \ + POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \ + &nxp_enet_mdio_api); + + +DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_MDIO_INIT) diff --git a/drivers/mdio/mdio_shell.c b/drivers/mdio/mdio_shell.c index d0fa925ef36787..4cde88a009ffe4 100644 --- a/drivers/mdio/mdio_shell.c +++ b/drivers/mdio/mdio_shell.c @@ -29,6 +29,8 @@ LOG_MODULE_REGISTER(mdio_shell, CONFIG_LOG_DEFAULT_LEVEL); #define DT_DRV_COMPAT smsc_lan91c111_mdio #elif DT_HAS_COMPAT_STATUS_OKAY(zephyr_mdio_gpio) #define DT_DRV_COMPAT zephyr_mdio_gpio +#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_enet_mdio) +#define DT_DRV_COMPAT nxp_enet_mdio #else #error "No known devicetree compatible match for MDIO shell" #endif diff --git a/drivers/ptp_clock/CMakeLists.txt b/drivers/ptp_clock/CMakeLists.txt index 7987b588ceaa96..3dfde253e0901b 100644 --- a/drivers/ptp_clock/CMakeLists.txt +++ b/drivers/ptp_clock/CMakeLists.txt @@ -5,3 +5,4 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/ptp_clock.h) zephyr_library() zephyr_library_sources_ifdef(CONFIG_PTP_CLOCK ptp_clock.c) +zephyr_library_sources_ifdef(CONFIG_PTP_CLOCK_NXP_ENET ptp_clock_nxp_enet.c) diff --git a/drivers/ptp_clock/Kconfig b/drivers/ptp_clock/Kconfig index 9417d7f063326d..74ab3e14eb8523 100644 --- a/drivers/ptp_clock/Kconfig +++ b/drivers/ptp_clock/Kconfig @@ -5,3 +5,15 @@ config PTP_CLOCK bool "Precision Time Protocol (PTP) Clock drivers" help Enable options for Precision Time Protocol Clock drivers. + +if PTP_CLOCK + +source "drivers/ptp_clock/Kconfig.nxp_enet" + +config PTP_CLOCK_INIT_PRIORITY + int "Init priority" + default 75 + help + PTP Clock device driver initialization priority + +endif # PTP_CLOCK diff --git a/drivers/ptp_clock/Kconfig.nxp_enet b/drivers/ptp_clock/Kconfig.nxp_enet new file mode 100644 index 00000000000000..aa9c8d5e41b48a --- /dev/null +++ b/drivers/ptp_clock/Kconfig.nxp_enet @@ -0,0 +1,9 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config PTP_CLOCK_NXP_ENET + bool "NXP ENET PTP Clock driver" + default y if DT_HAS_NXP_ENET_PTP_CLOCK_ENABLED && \ + (PTP_CLOCK || NET_L2_PTP) + help + Enable NXP ENET PTP clock support. diff --git a/drivers/ptp_clock/ptp_clock_nxp_enet.c b/drivers/ptp_clock/ptp_clock_nxp_enet.c new file mode 100644 index 00000000000000..67f561e9bc029b --- /dev/null +++ b/drivers/ptp_clock/ptp_clock_nxp_enet.c @@ -0,0 +1,268 @@ +/* + * Copyright 2023 NXP + * + * Based on a commit to drivers/ethernet/eth_mcux.c which was: + * Copyright (c) 2018 Intel Coporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_enet_ptp_clock + +#include +#include +#include +#include +#include +#include + +#include "fsl_enet.h" + +struct ptp_clock_nxp_enet_config { + ENET_Type *base; + const struct pinctrl_dev_config *pincfg; + const struct device *port; + const struct device *clock_dev; + struct device *clock_subsys; + void (*irq_config_func)(void); +}; + +struct ptp_clock_nxp_enet_data { + double clock_ratio; + enet_handle_t enet_handle; + struct k_mutex ptp_mutex; +}; + +static int ptp_clock_nxp_enet_set(const struct device *dev, + struct net_ptp_time *tm) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + struct ptp_clock_nxp_enet_data *data = dev->data; + enet_ptp_time_t enet_time; + + enet_time.second = tm->second; + enet_time.nanosecond = tm->nanosecond; + + ENET_Ptp1588SetTimer(config->base, &data->enet_handle, &enet_time); + + return 0; +} + +static int ptp_clock_nxp_enet_get(const struct device *dev, + struct net_ptp_time *tm) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + struct ptp_clock_nxp_enet_data *data = dev->data; + enet_ptp_time_t enet_time; + + ENET_Ptp1588GetTimer(config->base, &data->enet_handle, &enet_time); + + tm->second = enet_time.second; + tm->nanosecond = enet_time.nanosecond; + + return 0; +} + +static int ptp_clock_nxp_enet_adjust(const struct device *dev, + int increment) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + int ret = 0; + int key; + + if ((increment <= (int32_t)(-NSEC_PER_SEC)) || + (increment >= (int32_t)NSEC_PER_SEC)) { + ret = -EINVAL; + } else { + key = irq_lock(); + if (config->base->ATPER != NSEC_PER_SEC) { + ret = -EBUSY; + } else { + /* Seconds counter is handled by software. Change the + * period of one software second to adjust the clock. + */ + config->base->ATPER = NSEC_PER_SEC - increment; + ret = 0; + } + irq_unlock(key); + } + + return ret; + +} + +static int ptp_clock_nxp_enet_rate_adjust(const struct device *dev, + double ratio) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + struct ptp_clock_nxp_enet_data *data = dev->data; + int corr; + int32_t mul; + double val; + uint32_t enet_ref_pll_rate; + + (void) clock_control_get_rate(config->clock_dev, config->clock_subsys, + &enet_ref_pll_rate); + int hw_inc = NSEC_PER_SEC / enet_ref_pll_rate; + + /* No change needed. */ + if ((ratio > 1.0 && ratio - 1.0 < 0.00000001) || + (ratio < 1.0 && 1.0 - ratio < 0.00000001)) { + return 0; + } + + ratio *= data->clock_ratio; + + /* Limit possible ratio. */ + if ((ratio > 1.0 + 1.0/(2 * hw_inc)) || + (ratio < 1.0 - 1.0/(2 * hw_inc))) { + return -EINVAL; + } + + /* Save new ratio. */ + data->clock_ratio = ratio; + + if (ratio < 1.0) { + corr = hw_inc - 1; + val = 1.0 / (hw_inc * (1.0 - ratio)); + } else if (ratio > 1.0) { + corr = hw_inc + 1; + val = 1.0 / (hw_inc * (ratio - 1.0)); + } else { + val = 0; + corr = hw_inc; + } + + if (val >= INT32_MAX) { + /* Value is too high. + * It is not possible to adjust the rate of the clock. + */ + mul = 0; + } else { + mul = val; + } + + k_mutex_lock(&data->ptp_mutex, K_FOREVER); + + ENET_Ptp1588AdjustTimer(config->base, corr, mul); + + k_mutex_unlock(&data->ptp_mutex); + + return 0; +} + +void nxp_enet_ptp_clock_callback(const struct device *dev, + enum nxp_enet_callback_reason event, + union nxp_enet_ptp_data *ptp_data) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + struct ptp_clock_nxp_enet_data *data = dev->data; + + if (event == nxp_enet_module_reset) { + enet_ptp_config_t ptp_config; + uint32_t enet_ref_pll_rate; + uint8_t ptp_multicast[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 }; + uint8_t ptp_peer_multicast[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E }; + + (void) clock_control_get_rate(config->clock_dev, config->clock_subsys, + &enet_ref_pll_rate); + + ENET_AddMulticastGroup(config->base, ptp_multicast); + ENET_AddMulticastGroup(config->base, ptp_peer_multicast); + + /* only for ERRATA_2579 */ + ptp_config.channel = kENET_PtpTimerChannel3; + ptp_config.ptp1588ClockSrc_Hz = enet_ref_pll_rate; + data->clock_ratio = 1.0; + + ENET_Ptp1588SetChannelMode(config->base, kENET_PtpTimerChannel3, + kENET_PtpChannelPulseHighonCompare, true); + ENET_Ptp1588Configure(config->base, &data->enet_handle, + &ptp_config); + } + + if (ptp_data != NULL) { + /* Share the mutex with mac driver */ + ptp_data->for_mac.ptp_mutex = &data->ptp_mutex; + } +} + +static int ptp_clock_nxp_enet_init(const struct device *port) +{ + const struct ptp_clock_nxp_enet_config *config = port->config; + struct ptp_clock_nxp_enet_data *data = port->data; + int ret; + + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + k_mutex_init(&data->ptp_mutex); + + config->irq_config_func(); + + return 0; +} + +static void ptp_clock_nxp_enet_isr(const struct device *dev) +{ + const struct ptp_clock_nxp_enet_config *config = dev->config; + struct ptp_clock_nxp_enet_data *data = dev->data; + enet_ptp_timer_channel_t channel; + + unsigned int irq_lock_key = irq_lock(); + + /* clear channel */ + for (channel = kENET_PtpTimerChannel1; channel <= kENET_PtpTimerChannel4; channel++) { + if (ENET_Ptp1588GetChannelStatus(config->base, channel)) { + ENET_Ptp1588ClearChannelStatus(config->base, channel); + } + } + + ENET_TimeStampIRQHandler(config->base, &data->enet_handle); + + irq_unlock(irq_lock_key); +} + +static const struct ptp_clock_driver_api ptp_clock_nxp_enet_api = { + .set = ptp_clock_nxp_enet_set, + .get = ptp_clock_nxp_enet_get, + .adjust = ptp_clock_nxp_enet_adjust, + .rate_adjust = ptp_clock_nxp_enet_rate_adjust, +}; + +#define PTP_CLOCK_NXP_ENET_INIT(n) \ + static void nxp_enet_ptp_clock_##n##_irq_config_func(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq), \ + DT_INST_IRQ_BY_IDX(n, 0, priority), \ + ptp_clock_nxp_enet_isr, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(n, 0, irq)); \ + } \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static const struct ptp_clock_nxp_enet_config \ + ptp_clock_nxp_enet_##n##_config = { \ + .base = (ENET_Type *) DT_REG_ADDR(DT_INST_PARENT(n)), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .port = DEVICE_DT_INST_GET(n), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = (void *) \ + DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name), \ + .irq_config_func = \ + nxp_enet_ptp_clock_##n##_irq_config_func, \ + }; \ + \ + static struct ptp_clock_nxp_enet_data ptp_clock_nxp_enet_##n##_data; \ + \ + DEVICE_DT_INST_DEFINE(n, &ptp_clock_nxp_enet_init, NULL, \ + &ptp_clock_nxp_enet_##n##_data, \ + &ptp_clock_nxp_enet_##n##_config, \ + POST_KERNEL, CONFIG_PTP_CLOCK_INIT_PRIORITY, \ + &ptp_clock_nxp_enet_api); + +DT_INST_FOREACH_STATUS_OKAY(PTP_CLOCK_NXP_ENET_INIT) diff --git a/dts/bindings/ethernet/atmel,gmac-common.yaml b/dts/bindings/ethernet/atmel,gmac-common.yaml index f17d78df696f18..0975647e1875c6 100644 --- a/dts/bindings/ethernet/atmel,gmac-common.yaml +++ b/dts/bindings/ethernet/atmel,gmac-common.yaml @@ -43,19 +43,9 @@ properties: gmac driver supports 10Mbit/s and 100Mbit/s. Using 100, as default value, enables driver to configure 10 and 100Mbit/s speeds. - phy-connection-type: - type: string - enum: - - "rmii" - - "mii" - default: "rmii" - description: | - Phy connection type define the physical interface connection between - PHY and MAC. The default value uses gmac register reset value, which - represents Reduced Media-Independent Interface (RMII) mode. - - This property must be used with pinctrl-0. - mac-eeprom: type: phandle description: phandle to I2C eeprom device node. + + phy-connection-type: + default: "rmii" diff --git a/dts/bindings/ethernet/espressif,esp32-eth.yaml b/dts/bindings/ethernet/espressif,esp32-eth.yaml index 3ffd12cdc5009d..f7687eb0f78bc0 100644 --- a/dts/bindings/ethernet/espressif,esp32-eth.yaml +++ b/dts/bindings/ethernet/espressif,esp32-eth.yaml @@ -9,15 +9,8 @@ include: - name: ethernet-controller.yaml properties: - phy-connection-type: - type: string - enum: - - "rmii" - - "mii" - default: "rmii" - description: | - Phy connection type define the physical interface connection between - PHY and MAC. The default value uses Reduced Media-Independent - Interface (RMII) mode. phy-handle: required: true + + phy-connection-type: + default: "rmii" diff --git a/dts/bindings/ethernet/ethernet-controller.yaml b/dts/bindings/ethernet/ethernet-controller.yaml index a5a9cec2700c6d..8823459d3a9ebc 100644 --- a/dts/bindings/ethernet/ethernet-controller.yaml +++ b/dts/bindings/ethernet/ethernet-controller.yaml @@ -27,3 +27,12 @@ properties: type: phandle description: | Specifies a reference to a node representing a PHY device. + + phy-connection-type: + type: string + description: | + Specifies the interface connection type between ethernet MAC and PHY. + enum: + - "mii" + - "rmii" + - "gmii" diff --git a/dts/bindings/ethernet/microchip,ksz8081.yaml b/dts/bindings/ethernet/microchip,ksz8081.yaml new file mode 100644 index 00000000000000..14ccfe7cc78a82 --- /dev/null +++ b/dts/bindings/ethernet/microchip,ksz8081.yaml @@ -0,0 +1,27 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: Microchip KSZ8081 Ethernet PHY device + +compatible: "microchip,ksz8081" + +include: ethernet-phy.yaml + +properties: + mc,reset-gpio: + type: phandle-array + required: true + specifier-space: gpio + description: GPIO connected to PHY reset signal pin. Reset is active low. + mc,interrupt-gpio: + type: phandle-array + required: true + specifier-space: gpio + description: GPIO for interrupt signal indicating PHY state change. + mc,interface-type: + type: string + required: true + description: Which type of phy connection the phy is set up for + enum: + - "mii" + - "rmii" diff --git a/dts/bindings/ethernet/nxp,enet-mac.yaml b/dts/bindings/ethernet/nxp,enet-mac.yaml new file mode 100644 index 00000000000000..8ca42e769f3cac --- /dev/null +++ b/dts/bindings/ethernet/nxp,enet-mac.yaml @@ -0,0 +1,24 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP ENET MAC/L2 Device + +compatible: "nxp,enet-mac" + +include: ["ethernet-controller.yaml", "ethernet,fixed-link.yaml", "pinctrl-device.yaml"] + +properties: + interrupts: + required: true + + nxp,mdio: + type: phandle + required: true + description: | + Corresponding mdio device + + nxp,ptp-clock: + type: phandle + required: true + description: | + Corresponding ptp clock device diff --git a/dts/bindings/ethernet/nxp,enet-ptp-clock.yaml b/dts/bindings/ethernet/nxp,enet-ptp-clock.yaml new file mode 100644 index 00000000000000..f77e30b30357d0 --- /dev/null +++ b/dts/bindings/ethernet/nxp,enet-ptp-clock.yaml @@ -0,0 +1,12 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP ENET PTP (Precision Time Protocol) Clock + +compatible: "nxp,enet-ptp-clock" + +include: ["base.yaml", "pinctrl-device.yaml"] + +properties: + interrupts: + required: true diff --git a/dts/bindings/ethernet/nxp,enet.yaml b/dts/bindings/ethernet/nxp,enet.yaml new file mode 100644 index 00000000000000..f98af9f002b50c --- /dev/null +++ b/dts/bindings/ethernet/nxp,enet.yaml @@ -0,0 +1,15 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP ENET IP Module + +compatible: "nxp,enet" + +include: ["base.yaml"] + +properties: + reg: + required: true + + clocks: + required: true diff --git a/dts/bindings/ethernet/nxp,s32-gmac.yaml b/dts/bindings/ethernet/nxp,s32-gmac.yaml index 357a5eb15212d5..7767a755f39d84 100644 --- a/dts/bindings/ethernet/nxp,s32-gmac.yaml +++ b/dts/bindings/ethernet/nxp,s32-gmac.yaml @@ -16,13 +16,3 @@ properties: interrupt-names: required: true - - phy-connection-type: - type: string - enum: - - "mii" - - "rmii" - - "rgmii" - description: | - Specifies interface type between the Ethernet device and a physical - layer (PHY) device. diff --git a/dts/bindings/mdio/nxp,enet-mdio.yaml b/dts/bindings/mdio/nxp,enet-mdio.yaml new file mode 100644 index 00000000000000..68bc917444a81b --- /dev/null +++ b/dts/bindings/mdio/nxp,enet-mdio.yaml @@ -0,0 +1,15 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP ENET MDIO Features + +compatible: "nxp,enet-mdio" + +include: [mdio-controller.yaml, pinctrl-device.yaml] + +properties: + pinctrl-0: + required: true + + pinctrl-names: + required: true diff --git a/include/zephyr/drivers/ethernet/eth_nxp_enet.h b/include/zephyr/drivers/ethernet/eth_nxp_enet.h new file mode 100644 index 00000000000000..8dcb5f31c01201 --- /dev/null +++ b/include/zephyr/drivers/ethernet/eth_nxp_enet.h @@ -0,0 +1,59 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_ETH_NXP_ENET_H__ +#define ZEPHYR_INCLUDE_DRIVERS_ETH_NXP_ENET_H__ + +/* + * This header is for NXP ENET driver development + * and has definitions for internal implementations + * not to be used by application + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Reasons for callback to a driver: + * + * Module reset: The ENET module was reset, perhaps because of power management + * actions, and subdriver should reinitialize part of the module. + * Interrupt: An interrupt of a type relevant to the subdriver occurred. + * Interrupt enable: The driver's relevant interrupt was enabled in NVIC + */ +enum nxp_enet_callback_reason { + nxp_enet_module_reset, + nxp_enet_interrupt, + nxp_enet_interrupt_enabled, +}; + +struct nxp_enet_ptp_data_for_mac { + struct k_mutex *ptp_mutex; +}; + +union nxp_enet_ptp_data { + struct nxp_enet_ptp_data_for_mac for_mac; +}; + +/* Calback for mdio device called from mac driver */ +void nxp_enet_mdio_callback(const struct device *mdio_dev, + enum nxp_enet_callback_reason event); + +void nxp_enet_ptp_clock_callback(const struct device *dev, + enum nxp_enet_callback_reason event, + union nxp_enet_ptp_data *ptp_data); + + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DRIVERS_ETH_NXP_ENET_H__ */ diff --git a/include/zephyr/dt-bindings/clock/imx_ccm.h b/include/zephyr/dt-bindings/clock/imx_ccm.h index 93c3b0b3b157d3..3535c06935eea0 100644 --- a/include/zephyr/dt-bindings/clock/imx_ccm.h +++ b/include/zephyr/dt-bindings/clock/imx_ccm.h @@ -55,4 +55,7 @@ #define IMX_CCM_QTMR_CLK 0x0D00UL +#define IMX_CCM_ENET_CLK 0x0E00UL +#define IMX_CCM_ENET_PLL 0x0E01UL + #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_H_ */ diff --git a/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h b/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h index fb105454377575..75de126bd2937d 100644 --- a/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h +++ b/include/zephyr/dt-bindings/clock/imx_ccm_rev2.h @@ -84,5 +84,8 @@ #define IMX_CCM_SAI3_CLK 0x2002UL #define IMX_CCM_SAI4_CLK 0x2003UL +/* ENET */ +#define IMX_CCM_ENET_CLK 0x3000UL + #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_REV2_H_ */ diff --git a/include/zephyr/dt-bindings/ethernet/nxp_enet.h b/include/zephyr/dt-bindings/ethernet/nxp_enet.h new file mode 100644 index 00000000000000..e084825da4c754 --- /dev/null +++ b/include/zephyr/dt-bindings/ethernet/nxp_enet.h @@ -0,0 +1,14 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_NXP_ENET_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_NXP_ENET_H_ + +#define NXP_ENET_MII_MODE 0 +#define NXP_ENET_RMII_MODE 1 +#define NXP_ENET_INVALID_MII_MODE 100 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_NXP_ENET_H_ */ diff --git a/soc/arm/nxp_imx/rt/Kconfig.defconfig.series b/soc/arm/nxp_imx/rt/Kconfig.defconfig.series index b4e8aeb7422026..f8467559d3747d 100644 --- a/soc/arm/nxp_imx/rt/Kconfig.defconfig.series +++ b/soc/arm/nxp_imx/rt/Kconfig.defconfig.series @@ -75,6 +75,13 @@ config PM_MCUX_PMU endif # SOC_SERIES_IMX_RT10XX && PM +if ETH_NXP_ENET + +config SYSTEM_WORKQUEUE_STACK_SIZE + default 1560 + +endif # ETH_NXP_ENET + DT_CHOSEN_Z_FLASH := zephyr,flash DT_COMPAT_FLEXSPI := nxp,imx-flexspi diff --git a/soc/arm/nxp_imx/rt/soc_rt10xx.c b/soc/arm/nxp_imx/rt/soc_rt10xx.c index 91ec748a61ae70..01075ce358234b 100644 --- a/soc/arm/nxp_imx/rt/soc_rt10xx.c +++ b/soc/arm/nxp_imx/rt/soc_rt10xx.c @@ -55,7 +55,7 @@ const clock_enet_pll_config_t ethPllConfig = { defined(CONFIG_SOC_MIMXRT1024) .enableClkOutput500M = true, #endif -#ifdef CONFIG_ETH_MCUX +#if defined(CONFIG_ETH_NXP_ENET) || defined(CONFIG_ETH_MCUX) #if DT_NODE_HAS_STATUS(DT_NODELABEL(enet), okay) .enableClkOutput = true, #endif