diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 1b83f36abf..16aac389d1 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -43,7 +43,7 @@ MICROPY_VFS_FAT ?= 1 # Include py core make definitions include $(TOP)/py/py.mk -GIT_SUBMODULES = lib/tinyusb lib/nxp_driver +GIT_SUBMODULES = lib/tinyusb lib/nxp_driver lib/lwip lib/mbedtls MCU_DIR = lib/nxp_driver/sdk/devices/$(MCU_SERIES) LD_FILES = boards/$(MCU_SERIES).ld boards/common.ld @@ -79,7 +79,7 @@ INC += -I$(TOP)/lib/tinyusb/src CFLAGS_MCU = -mtune=cortex-m7 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 CFLAGS += $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 -nostdlib -mthumb $(CFLAGS_MCU) -CFLAGS += -DCPU_$(MCU_SERIES) -DCPU_$(MCU_VARIANT) +CFLAGS += -DCPU_$(MCU_SERIES) -DCPU_$(MCU_VARIANT) -DBOARD_$(BOARD) CFLAGS += -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ -DFSL_SDK_ENABLE_DRIVER_CACHE_CONTROL=1 \ @@ -134,6 +134,34 @@ LDFLAGS += --gc-sections CFLAGS += -fdata-sections -ffunction-sections endif +# All settings for Ethernet support are controller by the value of MICROPY_PY_LWIP +ifeq ($(MICROPY_PY_LWIP), 1) + +INC += -Ilwip_inc +INC += -Ihal/phy + +SRC_MOD := $(filter-out %/mbedtls/library/error.c, $(SRC_MOD)) + +SRC_ETH_C += \ + hal/phy/mdio/enet/fsl_enet_mdio.c \ + hal/phy/device/phydp83825/fsl_phydp83825.c \ + hal/phy/device/phyksz8081/fsl_phyksz8081.c \ + hal/phy/device/phylan8720/fsl_phylan8720.c \ + lib/mbedtls_errors/mp_mbedtls_errors.c \ + $(MCU_DIR)/drivers/fsl_enet.c \ + +SRC_QSTR += \ + extmod/modlwip.c \ + extmod/modnetwork.c \ + extmod/moduwebsocket.c \ + extmod/modusocket.c \ + network_lan.c \ + +CFLAGS += -DFSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE=1 \ + -DMBEDTLS_CONFIG_FILE='"mbedtls/mbedtls_config.h"' + +endif + # TinyUSB Stack source SRC_TINYUSB_C += \ lib/tinyusb/src/class/cdc/cdc_device.c \ @@ -164,6 +192,7 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpspi.c \ $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ + $(MCU_DIR)/drivers/fsl_ocotp.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_trng.c \ @@ -183,7 +212,11 @@ SRC_C += \ board_init.c \ dma_channel.c \ drivers/bus/softspi.c \ + eth.c \ + extmod/modnetwork.c \ extmod/modonewire.c \ + extmod/modusocket.c \ + extmod/uos_dupterm.c \ fatfs_port.c \ led.c \ machine_adc.c \ @@ -197,6 +230,7 @@ SRC_C += \ machine_timer.c \ machine_uart.c \ main.c \ + mbedtls/mbedtls_port.c \ mimxrt_flash.c \ mimxrt_sdram.c \ modmachine.c \ @@ -204,10 +238,16 @@ SRC_C += \ moduos.c \ modutime.c \ mphalport.c \ + mpnetworkport.c \ + network_lan.c \ + pendsv.c \ pin.c \ sdcard.c \ shared/libc/printf.c \ shared/libc/string0.c \ + shared/netutils/netutils.c \ + shared/netutils/trace.c \ + shared/netutils/dhcpserver.c \ shared/readline/readline.c \ shared/runtime/gchelper_native.c \ shared/runtime/mpirq.c \ @@ -215,10 +255,13 @@ SRC_C += \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ + systick.c \ ticks.c \ tusb_port.c \ $(SRC_TINYUSB_C) \ $(SRC_HAL_IMX_C) \ + $(SRC_ETH_C) \ + ifeq ($(MICROPY_HW_FLASH_TYPE), qspi_nor) SRC_C += \ @@ -334,6 +377,7 @@ SRC_S += shared/runtime/gchelper_m3.s \ # List of sources for qstr extraction SRC_QSTR += \ extmod/modonewire.c \ + extmod/uos_dupterm.c \ machine_adc.c \ machine_led.c \ machine_pin.c \ diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index e37ed1f149..d96645feff 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -56,6 +56,9 @@ void board_init(void) { mimxrt_sdram_init(); #endif + // 1ms tick timer + SysTick_Config(SystemCoreClock / 1000); + // ------------- USB0 ------------- // // Clock CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U); diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 4f33bb2087..b546700e7a 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -117,3 +117,24 @@ #define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_09_SEMC_WE #define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_12_SEMC_CS0 + +// Network definitions +// Transceiver Phy Address +#define ENET_PHY_ADDRESS (2) +#define ENET_PHY_OPS phyksz8081_ops + +// Etherner PIN definitions +#define ENET_RESET_PIN pin_GPIO_AD_B0_04 +#define ENET_INT_PIN pin_GPIO_AD_B1_06 + +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_AD_B0_08_ENET_REF_CLK1, 1, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_09_ENET_RDATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_10_ENET_RDATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_11_ENET_RX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_12_ENET_RX_ER, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_13_ENET_TX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_14_ENET_TDATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_AD_B0_15_ENET_TDATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_40_ENET_MDIO, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_41_ENET_MDC, 0, 0xB0E9u }, diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index e08b3357fd..c1e1678e59 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -9,6 +9,10 @@ MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 + JLINK_PATH ?= /media/RT1020-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld index 8d981a7c61..64f4e537e0 100644 --- a/ports/mimxrt/boards/MIMXRT1021.ld +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -42,6 +42,7 @@ _gc_heap_start = ORIGIN(m_sdram); _gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); #else /* Use second OCRAM bank for GC heap. */ +/* Use all OCRAM for the GC heap. */ _gc_heap_start = ORIGIN(m_ocrm); _gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); #endif diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index f6021ac788..3837315f7d 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -1,4 +1,4 @@ -#define MICROPY_HW_BOARD_NAME "i.MX RT1050 EVK" +#define MICROPY_HW_BOARD_NAME "i.MX RT1050 EVKB-A1" #define MICROPY_HW_MCU_NAME "MIMXRT1052DVL6B" // MIMXRT1050_EVKB has 1 user LED @@ -108,3 +108,24 @@ #define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_28_SEMC_WE #define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_29_SEMC_CS0 + +// Network definitions +// Transceiver Phy Address & Type +#define ENET_PHY_ADDRESS (2) +#define ENET_PHY_OPS phyksz8081_ops + +// Etherner PIN definitions +#define ENET_RESET_PIN pin_GPIO_AD_B0_09 +#define ENET_INT_PIN pin_GPIO_AD_B0_10 + +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_B1_04_ENET_RX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_05_ENET_RX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_06_ENET_RX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_07_ENET_TX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_08_ENET_TX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_09_ENET_TX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_10_ENET_REF_CLK, 1, 0x71u }, \ + { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 4165b72c26..65e3d30963 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -9,7 +9,6 @@ MICROPY_HW_FLASH_SIZE ?= 0x4000000 # 64MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB -JLINK_PATH ?= /media/RT1050-EVKB/ - -deploy: $(BUILD)/firmware.bin - cp $< $(JLINK_PATH) +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index af44010486..a66fcf9ae9 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -107,3 +107,24 @@ #define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_28_SEMC_WE #define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_29_SEMC_CS0 + +// Network definitions +// Transceiver Phy Address +#define ENET_PHY_ADDRESS (2) +#define ENET_PHY_OPS phyksz8081_ops + +// Etherner PIN definitions +#define ENET_RESET_PIN pin_GPIO_AD_B0_09 +#define ENET_INT_PIN pin_GPIO_AD_B0_10 + +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_B1_04_ENET_RX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_05_ENET_RX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_06_ENET_RX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_07_ENET_TX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_08_ENET_TX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_09_ENET_TX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_10_ENET_REF_CLK, 1, 0x71u }, \ + { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index 4c4a2ff980..56ccba6e59 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -9,6 +9,10 @@ MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 + JLINK_PATH ?= /media/RT1060-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 17268b0a56..1dcdd378a2 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -107,3 +107,24 @@ #define MIMXRT_IOMUXC_SEMC_WE IOMUXC_GPIO_EMC_28_SEMC_WE #define MIMXRT_IOMUXC_SEMC_CS0 IOMUXC_GPIO_EMC_29_SEMC_CS0 + +// Network definitions +// Transceiver Phy Address +#define ENET_PHY_ADDRESS (2) +#define ENET_PHY_OPS phyksz8081_ops + +// Etherner PIN definitions +#define ENET_RESET_PIN pin_GPIO_AD_B0_09 +#define ENET_INT_PIN pin_GPIO_AD_B0_10 + +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_B1_04_ENET_RX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_05_ENET_RX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_06_ENET_RX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_07_ENET_TX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_08_ENET_TX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_09_ENET_TX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_10_ENET_REF_CLK, 1, 0x71u }, \ + { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_41_ENET_MDIO, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_EMC_40_ENET_MDC, 0, 0xB0E9u }, diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index 8bc35dd0dd..5cd7d49abd 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -9,6 +9,10 @@ MICROPY_HW_FLASH_SIZE ?= 0x4000000 # 64MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 + JLINK_PATH ?= /media/RT1064-EVK/ deploy: $(BUILD)/firmware.bin diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 7890ba762c..4ca82d4b65 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -65,3 +65,24 @@ .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 }, \ .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 }, \ } + +// Network definitions +// Transceiver Phy Address & Type +#define ENET_PHY_ADDRESS (0) +#define ENET_PHY_OPS phydp83825_ops + +// Ethernet PIN definitions +#define ENET_RESET_PIN pin_GPIO_B0_14 +#define ENET_INT_PIN pin_GPIO_B0_15 + +#define IOMUX_TABLE_ENET \ + { IOMUXC_GPIO_B1_04_ENET_RX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_05_ENET_RX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_06_ENET_RX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_07_ENET_TX_DATA00, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_08_ENET_TX_DATA01, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_09_ENET_TX_EN, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_10_ENET_REF_CLK, 1, 0x71u }, \ + { IOMUXC_GPIO_B1_11_ENET_RX_ER, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_15_ENET_MDIO, 0, 0xB0E9u }, \ + { IOMUXC_GPIO_B1_14_ENET_MDC, 0, 0xB0E9u }, diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index 27039648b8..a012444caa 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -7,5 +7,9 @@ MICROPY_HW_FLASH_TYPE ?= qspi_nor MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 + deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c new file mode 100644 index 0000000000..414e8d51f2 --- /dev/null +++ b/ports/mimxrt/eth.c @@ -0,0 +1,448 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * Copyright (c) 2021 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "ticks.h" + +#if defined(MICROPY_HW_ETH_MDC) + +#include "eth.h" +#include "pin.h" +#include "shared/netutils/netutils.h" +#include "extmod/modnetwork.h" + +#include "fsl_iomuxc.h" +#include "fsl_enet.h" +#include "fsl_phy.h" +#include "hal/phy/mdio/enet/fsl_enet_mdio.h" +#include "hal/phy/device/phyksz8081/fsl_phyksz8081.h" +#include "hal/phy/device/phydp83825/fsl_phydp83825.h" +#include "hal/phy/device/phylan8720/fsl_phylan8720.h" + +#include "lwip/etharp.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "netif/ethernet.h" + +#include "ticks.h" + +// Configuration values +enet_config_t enet_config; +phy_config_t phyConfig = {0}; + +// Prepare the buffer configuration. + +#define ENET_RXBD_NUM (5) +#define ENET_TXBD_NUM (5) + +AT_NONCACHEABLE_SECTION_ALIGN(enet_rx_bd_struct_t g_rxBuffDescrip[ENET_RXBD_NUM], ENET_BUFF_ALIGNMENT); +AT_NONCACHEABLE_SECTION_ALIGN(enet_tx_bd_struct_t g_txBuffDescrip[ENET_TXBD_NUM], ENET_BUFF_ALIGNMENT); +SDK_ALIGN(uint8_t g_rxDataBuff[ENET_RXBD_NUM][SDK_SIZEALIGN(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT)], + ENET_BUFF_ALIGNMENT); +SDK_ALIGN(uint8_t g_txDataBuff[ENET_TXBD_NUM][SDK_SIZEALIGN(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT)], + ENET_BUFF_ALIGNMENT); + +// ENET Handles & Buffers +enet_handle_t g_handle; + +static mdio_handle_t mdioHandle = {.ops = &enet_ops}; +static phy_handle_t phyHandle = {.phyAddr = ENET_PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &ENET_PHY_OPS}; + +enet_buffer_config_t buffConfig[] = {{ + ENET_RXBD_NUM, + ENET_TXBD_NUM, + SDK_SIZEALIGN(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT), + SDK_SIZEALIGN(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT), + &g_rxBuffDescrip[0], + &g_txBuffDescrip[0], + &g_rxDataBuff[0][0], + &g_txDataBuff[0][0], + }}; + +static uint8_t hw_addr[6]; // The MAC address field +eth_t eth_instance; + +#define PHY_INIT_TIMEOUT_MS (10000) +#define PHY_AUTONEGO_TIMEOUT_US (5000000) + +typedef struct _eth_t { + uint32_t trace_flags; + struct netif netif; + struct dhcp dhcp_struct; +} eth_t; + +typedef struct _iomux_table_t { + uint32_t muxRegister; + uint32_t muxMode; + uint32_t inputRegister; + uint32_t inputDaisy; + uint32_t configRegister; + uint32_t inputOnfield; + uint32_t configValue; +} iomux_table_t; + +static const iomux_table_t iomux_table_enet[] = { + IOMUX_TABLE_ENET +}; + +#define IOTE (iomux_table_enet[i]) +#ifndef ENET_TX_CLK_OUTPUT +#define ENET_TX_CLK_OUTPUT true +#endif + +#define TRACE_ASYNC_EV (0x0001) +#define TRACE_ETH_TX (0x0002) +#define TRACE_ETH_RX (0x0004) +#define TRACE_ETH_FULL (0x0008) + + +STATIC void eth_trace(eth_t *self, size_t len, const void *data, unsigned int flags) { + if (((flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_TX)) + || (!(flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_RX))) { + const uint8_t *buf; + if (len == (size_t)-1) { + // data is a pbuf + const struct pbuf *pbuf = data; + buf = pbuf->payload; + len = pbuf->len; // restricted to print only the first chunk of the pbuf + } else { + // data is actual data buffer + buf = data; + } + if (self->trace_flags & TRACE_ETH_FULL) { + flags |= NETUTILS_TRACE_PAYLOAD; + } + netutils_ethernet_trace(MP_PYTHON_PRINTER, len, buf, flags); + } +} + +STATIC void eth_process_frame(eth_t *self, uint8_t *buf, size_t length) { + + struct netif *netif = &self->netif; + if (netif->flags & NETIF_FLAG_LINK_UP) { + struct pbuf *p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL); + if (p != NULL) { + // Need to create a local copy first, since ENET_ReadFrame does not + // provide a pointer to the buffer. + pbuf_take(p, buf, length); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } + } +} + +void eth_irq_handler(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *userData) { + eth_t *self = (eth_t *)userData; + uint8_t g_rx_frame[ENET_FRAME_MAX_FRAMELEN + 14]; + uint32_t length = 0; + status_t status; + + if (event == kENET_RxEvent) { + do { + status = ENET_GetRxFrameSize(&g_handle, &length); + if (status == kStatus_Success) { + // Get the data + ENET_ReadFrame(ENET, &g_handle, g_rx_frame, length); + eth_process_frame(self, g_rx_frame, length); + } else if (status == kStatus_ENET_RxFrameError) { + ENET_ReadFrame(ENET, &g_handle, NULL, 0); + } + } while (status != kStatus_ENET_RxFrameEmpty); + } else { + ENET_ClearInterruptStatus(ENET, kENET_TxFrameInterrupt); + } +} + +// eth_init: Set up GPIO and the transceiver +void eth_init(eth_t *self, int mac_idx) { + + CLOCK_EnableClock(kCLOCK_Iomuxc); + + gpio_pin_config_t gpio_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode}; + (void)gpio_config; + + #ifdef ENET_RESET_PIN + // Configure the Reset Pin + const machine_pin_obj_t *reset_pin = &ENET_RESET_PIN; + const machine_pin_af_obj_t *af_obj = pin_find_af(reset_pin, PIN_AF_MODE_ALT5); + + IOMUXC_SetPinMux(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, 0U); + IOMUXC_SetPinConfig(reset_pin->muxRegister, af_obj->af_mode, 0, 0, reset_pin->configRegister, 0xB0A9U); + GPIO_PinInit(reset_pin->gpio, reset_pin->pin, &gpio_config); + #endif + + #ifdef ENET_INT_PIN + // Configure the Int Pin + const machine_pin_obj_t *int_pin = &ENET_INT_PIN; + af_obj = pin_find_af(int_pin, PIN_AF_MODE_ALT5); + + IOMUXC_SetPinMux(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, 0U); + IOMUXC_SetPinConfig(int_pin->muxRegister, af_obj->af_mode, 0, 0, int_pin->configRegister, 0xB0A9U); + GPIO_PinInit(int_pin->gpio, int_pin->pin, &gpio_config); + #endif + + // Configure the Transceiver Pins, Settings except for CLK: + // Slew Rate Field: Fast Slew Rate, Drive Strength, R0/5, Speed max(200MHz) + // Open Drain Disabled, Pull Enabled, Pull 100K Ohm Pull Up + // Hysteresis Disabled + + for (int i = 0; i < ARRAY_SIZE(iomux_table_enet); i++) { + IOMUXC_SetPinMux(IOTE.muxRegister, IOTE.muxMode, IOTE.inputRegister, IOTE.inputDaisy, IOTE.configRegister, IOTE.inputOnfield); + IOMUXC_SetPinConfig(IOTE.muxRegister, IOTE.muxMode, IOTE.inputRegister, IOTE.inputDaisy, IOTE.configRegister, IOTE.configValue); + } + + const clock_enet_pll_config_t config = { + .enableClkOutput = true, .enableClkOutput25M = false, .loopDivider = 1 + }; + CLOCK_InitEnetPll(&config); + + IOMUXC_EnableMode(IOMUXC_GPR, kIOMUXC_GPR_ENET1RefClkMode, false); // Drive ENET_REF_CLK from PAD + IOMUXC_EnableMode(IOMUXC_GPR, kIOMUXC_GPR_ENET1TxClkOutputDir, ENET_TX_CLK_OUTPUT); // Enable output driver + + // Reset transceiver + // pull up the ENET_INT before RESET. + #ifdef ENET_INT_PIN + GPIO_WritePinOutput(int_pin->gpio, int_pin->pin, 1); + #endif + + #ifdef ENET_RESET_PIN + GPIO_WritePinOutput(reset_pin->gpio, reset_pin->pin, 0); + mp_hal_delay_us(1000); + GPIO_WritePinOutput(reset_pin->gpio, reset_pin->pin, 1); + mp_hal_delay_us(1000); + #endif + + mp_hal_get_mac(0, hw_addr); + + phyConfig.autoNeg = true; + mdioHandle.resource.base = ENET; + mdioHandle.resource.csrClock_Hz = CLOCK_GetFreq(kCLOCK_IpgClk); + + // Init the PHY interface & negotiate the speed + bool link = false; + bool autonego = false; + phy_speed_t speed = kENET_MiiSpeed100M; + phy_duplex_t duplex = kENET_MiiFullDuplex; + + phyConfig.phyAddr = ENET_PHY_ADDRESS; + + status_t status = PHY_Init(&phyHandle, &phyConfig); + if (status == kStatus_Success) { + if (phyConfig.autoNeg) { + uint64_t t = ticks_us64() + PHY_AUTONEGO_TIMEOUT_US; + // Wait for auto-negotiation success and link up + do { + PHY_GetAutoNegotiationStatus(&phyHandle, &autonego); + PHY_GetLinkStatus(&phyHandle, &link); + if (autonego && link) { + break; + } + } while (ticks_us64() < t); + if (!autonego) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("PHY Auto-negotiation failed.")); + } + PHY_GetLinkSpeedDuplex(&phyHandle, &speed, &duplex); + } else { + PHY_SetLinkSpeedDuplex(&phyHandle, speed, duplex); + } + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("PHY Init failed.")); + } + + ENET_GetDefaultConfig(&enet_config); + enet_config.miiSpeed = (enet_mii_speed_t)speed; + enet_config.miiDuplex = (enet_mii_duplex_t)duplex; + enet_config.miiMode = kENET_RmiiMode; + // Enable checksum generation by the ENET controller + // Note: Disabled due to problems with the checksum on ICMP requests + // Maybe caused by LWIP inserting 0xffff instead of 0x0000 + // Keep the code for now until it may be fixed. + // enet_config.txAccelerConfig = kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled; + // Set interrupt + enet_config.interrupt |= ENET_TX_INTERRUPT | ENET_RX_INTERRUPT; + + ENET_Init(ENET, &g_handle, &enet_config, &buffConfig[0], hw_addr, CLOCK_GetFreq(kCLOCK_IpgClk)); + ENET_SetCallback(&g_handle, eth_irq_handler, (void *)self); + ENET_EnableInterrupts(ENET, ENET_RX_INTERRUPT); + ENET_ClearInterruptStatus(ENET, ENET_TX_INTERRUPT | ENET_RX_INTERRUPT | ENET_ERR_INTERRUPT); + ENET_ActiveRead(ENET); +} + +// Initialize the phy interface +STATIC int eth_mac_init(eth_t *self) { + return 0; +} + +// Deinit the interface +STATIC void eth_mac_deinit(eth_t *self) { +} + +void eth_set_trace(eth_t *self, uint32_t value) { + self->trace_flags = value; +} + +/*******************************************************************************/ +// ETH-LwIP bindings + +STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { + // This function should always be called from a context where PendSV-level IRQs are disabled + status_t status; + + LINK_STATS_INC(link.xmit); + eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); + + if (p->next == NULL) { + status = ENET_SendFrame(ENET, &g_handle, p->payload, p->len); + } else { + // frame consists of several parts. Copy them together and send them + size_t length = 0; + uint8_t tx_frame[ENET_FRAME_MAX_FRAMELEN + 14]; + + while (p) { + memcpy(&tx_frame[length], p->payload, p->len); + length += p->len; + p = p->next; + } + status = ENET_SendFrame(ENET, &g_handle, tx_frame, length); + } + return status == kStatus_Success ? ERR_OK : ERR_BUF; +} + +STATIC err_t eth_netif_init(struct netif *netif) { + netif->linkoutput = eth_netif_output; + netif->output = etharp_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP; + // Checksums only need to be checked on incoming frames, not computed on outgoing frames + NETIF_SET_CHECKSUM_CTRL(netif, + NETIF_CHECKSUM_CHECK_IP + | NETIF_CHECKSUM_CHECK_UDP + | NETIF_CHECKSUM_CHECK_TCP + | NETIF_CHECKSUM_CHECK_ICMP + | NETIF_CHECKSUM_CHECK_ICMP6 + | NETIF_CHECKSUM_GEN_IP + | NETIF_CHECKSUM_GEN_UDP + | NETIF_CHECKSUM_GEN_TCP + | NETIF_CHECKSUM_GEN_ICMP + | NETIF_CHECKSUM_GEN_ICMP6 + ); + return ERR_OK; +} + +STATIC void eth_lwip_init(eth_t *self) { + ip_addr_t ipconfig[4]; + IP4_ADDR(&ipconfig[0], 192, 168, 0, 2); + IP4_ADDR(&ipconfig[1], 255, 255, 255, 0); + IP4_ADDR(&ipconfig[2], 192, 168, 0, 1); + IP4_ADDR(&ipconfig[3], 8, 8, 8, 8); + + self->netif.hwaddr_len = 6; + memcpy(self->netif.hwaddr, hw_addr, 6); + + MICROPY_PY_LWIP_ENTER + + struct netif *n = &self->netif; + n->name[0] = 'e'; + n->name[1] = '0'; + netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, eth_netif_init, ethernet_input); + netif_set_hostname(n, "MPY"); + netif_set_default(n); + netif_set_up(n); + + dns_setserver(0, &ipconfig[3]); + dhcp_set_struct(n, &self->dhcp_struct); + dhcp_start(n); + + netif_set_link_up(n); + + MICROPY_PY_LWIP_EXIT +} + +STATIC void eth_lwip_deinit(eth_t *self) { + MICROPY_PY_LWIP_ENTER + for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) { + if (netif == &self->netif) { + netif_remove(netif); + netif->ip_addr.addr = 0; + netif->flags = 0; + } + } + MICROPY_PY_LWIP_EXIT +} + +struct netif *eth_netif(eth_t *self) { + return &self->netif; +} + +int eth_link_status(eth_t *self) { + struct netif *netif = &self->netif; + if ((netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) + == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) { + if (netif->ip_addr.addr != 0) { + return 3; // link up + } else { + return 2; // link no-ip; + } + } else { + bool link; + PHY_GetLinkStatus(&phyHandle, &link); + if (link) { + return 1; // link up + } else { + return 0; // link down + } + } +} + +int eth_start(eth_t *self) { + eth_lwip_deinit(self); + + // Make sure Eth is Not in low power mode. + eth_low_power_mode(self, false); + + int ret = eth_mac_init(self); + if (ret < 0) { + return ret; + } + eth_lwip_init(self); + return 0; +} + +int eth_stop(eth_t *self) { + eth_lwip_deinit(self); + eth_mac_deinit(self); + return 0; +} + +void eth_low_power_mode(eth_t *self, bool enable) { + ENET_EnableSleepMode(ENET, enable); +} +#endif // defined(MICROPY_HW_ETH_MDC) diff --git a/ports/mimxrt/eth.h b/ports/mimxrt/eth.h new file mode 100644 index 0000000000..56327d08ae --- /dev/null +++ b/ports/mimxrt/eth.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * Copyright (c) 2021 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MIMXRT_ETH_H +#define MICROPY_INCLUDED_MIMXRT_ETH_H + +typedef struct _eth_t eth_t; +extern eth_t eth_instance; + +void eth_init(eth_t *self, int mac_idx); +void eth_set_trace(eth_t *self, uint32_t value); +struct netif *eth_netif(eth_t *self); +int eth_link_status(eth_t *self); +int eth_start(eth_t *self); +int eth_stop(eth_t *self); +void eth_low_power_mode(eth_t *self, bool enable); + +#endif // MICROPY_INCLUDED_MIMXRT_ETH_H diff --git a/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.c b/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.c new file mode 100644 index 0000000000..2aca609a05 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.c @@ -0,0 +1,281 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_phydp83825.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the PHY PD83825 vendor defined registers. */ +#define PHY_PHYSTS_REG 0x10U // Phy status register +#define PHY_BISCR_REG 0x16U // RMII Config register. +#define PHY_RCSR_REG 0x17U // RMII Config register. +#define PHY_CONTROL2_REG 0x1FU /*!< The PHY control register 2. */ + +/*! @brief Defines the PHY DP82825 ID number. */ +#define PHY_CONTROL_ID1 0x2000U /*!< The PHY ID1 */ + +/*! @brief Defines the mask flag of operation mode in control registers */ +#define PHY_PHYSTS_100FULLDUPLEX_MASK 0x0006U /*!< The PHY 100M full duplex mask. */ +#define PHY_PHYSTS_DUPLEX_MASK 0x0004U /*!< The PHY full duplex mask. */ +#define PHY_PHYSTS_100M_MASK 0x0002U /*!< The PHY 100M mask. */ +#define PHY_PHYSTS_LINK_MASK 0x0001U /*!< The PHY link up mask. */ + +#define PYH_RMII_CLOCK_SELECT 0x80 // Select 50MHz clock +#define PHY_BISCR_REMOTELOOP_MASK 0x1FU // !< The PHY remote loopback mask. +#define PHY_BISCR_REMOTELOOP_MODE 0x08U // !< The PHY remote loopback mode. + +/*! @brief Defines the timeout macro. */ +#define PHY_READID_TIMEOUT_COUNT 1000U + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ +const phy_operations_t phydp83825_ops = {.phyInit = PHY_DP83825_Init, + .phyWrite = PHY_DP83825_Write, + .phyRead = PHY_DP83825_Read, + .getAutoNegoStatus = PHY_DP83825_GetAutoNegotiationStatus, + .getLinkStatus = PHY_DP83825_GetLinkStatus, + .getLinkSpeedDuplex = PHY_DP83825_GetLinkSpeedDuplex, + .setLinkSpeedDuplex = PHY_DP83825_SetLinkSpeedDuplex, + .enableLoopback = PHY_DP83825_EnableLoopback}; + +/******************************************************************************* + * Code + ******************************************************************************/ + +status_t PHY_DP83825_Init(phy_handle_t *handle, const phy_config_t *config) { + uint32_t counter = PHY_READID_TIMEOUT_COUNT; + status_t result = kStatus_Success; + uint32_t regValue = 0; + + /* Init MDIO interface. */ + MDIO_Init(handle->mdioHandle); + + /* Assign phy address. */ + handle->phyAddr = config->phyAddr; + + /* Check PHY ID. */ + do + { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_ID1_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + counter--; + } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U)); + + if (counter == 0U) { + return kStatus_Fail; + } + + /* Reset PHY. */ + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK); + if (result == kStatus_Success) { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_RCSR_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_RCSR_REG, (regValue | PYH_RMII_CLOCK_SELECT)); + if (result != kStatus_Success) { + return result; + } + + if (config->autoNeg) { + /* Set the auto-negotiation then start it. */ + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_AUTONEG_ADVERTISE_REG, + (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | + PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | PHY_IEEE802_3_SELECTOR_MASK)); + if (result == kStatus_Success) { + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } else { + /* This PHY only supports 10/100M speed. */ + assert(config->speed <= kPHY_Speed100M); + + /* Disable isolate mode */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + regValue &= ~PHY_BCTL_ISOLATE_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + + /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */ + result = PHY_DP83825_SetLinkSpeedDuplex(handle, config->speed, config->duplex); + } + } + return result; +} + +status_t PHY_DP83825_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, phyReg, data); +} + +status_t PHY_DP83825_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr) { + return MDIO_Read(handle->mdioHandle, handle->phyAddr, phyReg, dataPtr); +} + +status_t PHY_DP83825_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + *status = false; + + /* Check auto negotiation complete. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((regValue & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0U) { + *status = true; + } + } + return result; +} + +status_t PHY_DP83825_GetLinkStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + /* Read the basic status register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((PHY_BSTATUS_LINKSTATUS_MASK & regValue) != 0U) { + /* Link up. */ + *status = true; + } else { + /* Link down. */ + *status = false; + } + } + return result; +} + +status_t PHY_DP83825_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex) { + assert(!((speed == NULL) && (duplex == NULL))); + + status_t result; + uint32_t regValue; + uint32_t flag; + + /* Read the control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_PHYSTS_REG, ®Value); + if (result == kStatus_Success) { + if (speed != NULL) { + flag = regValue & PHY_PHYSTS_100M_MASK; + if (flag) { + *speed = kPHY_Speed10M; + } else { + *speed = kPHY_Speed100M; + } + } + + if (duplex != NULL) { + flag = regValue & PHY_PHYSTS_DUPLEX_MASK; + if (flag) { + *duplex = kPHY_FullDuplex; + } else { + *duplex = kPHY_HalfDuplex; + } + } + } + return result; +} + +status_t PHY_DP83825_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex) { + /* This PHY only supports 10/100M speed. */ + assert(speed <= kPHY_Speed100M); + + status_t result; + uint32_t regValue; + + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + /* Disable the auto-negotiation and set according to user-defined configuration. */ + regValue &= ~PHY_BCTL_AUTONEG_MASK; + if (speed == kPHY_Speed100M) { + regValue |= PHY_BCTL_SPEED0_MASK; + } else { + regValue &= ~PHY_BCTL_SPEED0_MASK; + } + if (duplex == kPHY_FullDuplex) { + regValue |= PHY_BCTL_DUPLEX_MASK; + } else { + regValue &= ~PHY_BCTL_DUPLEX_MASK; + } + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } + return result; +} +status_t PHY_DP83825_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable) { + /* This PHY only supports local/remote loopback and 10/100M speed. */ + assert(mode <= kPHY_RemoteLoop); + assert(speed <= kPHY_Speed100M); + + status_t result = kStatus_Success; + uint32_t regValue; + + /* Set the loop mode. */ + if (enable) { + if (mode == kPHY_LocalLoop) { + if (speed == kPHY_Speed100M) { + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } else { + regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } else { + /* Remote loopback only supports 100M full-duplex. */ + assert(speed == kPHY_Speed100M); + + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + /* Set the remote loopback bit. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BISCR_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BISCR_REG, + (regValue & ~PHY_BISCR_REMOTELOOP_MASK) | PHY_BISCR_REMOTELOOP_MODE); + } + } + } else { + /* Disable the loop mode. */ + if (mode != kPHY_LocalLoop) { + /* First read the current status in control one register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BISCR_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BISCR_REG, + (regValue & ~PHY_BISCR_REMOTELOOP_MASK)); + } + } + /* First read the current status in control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + regValue &= ~PHY_BCTL_LOOP_MASK; + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (regValue | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } + + return result; +} diff --git a/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.h b/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.h new file mode 100644 index 0000000000..9d6b4fdf78 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phydp83825/fsl_phydp83825.h @@ -0,0 +1,163 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/***************************************************************************** + * PHY DP83825 driver change log + *****************************************************************************/ + +/*! +@page driver_log Driver Change Log + +@section phyksz8081 PHYDP83825 + The current PHYDP83825 driver version is 2.0.0. + + - 2.0.0 + - Initial version. +*/ + +#ifndef _FSL_DP83825_H_ +#define _FSL_DP83825_H_ + +#include "fsl_phy.h" + +/*! + * @addtogroup phy_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief PHY driver version */ +#define FSL_PHY_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) + +/*! @brief PHY operations structure. */ +extern const phy_operations_t phydp83825_ops; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize PHY. + * + * @param handle PHY device handle. + * @param config Pointer to structure of phy_config_t. + * @retval kStatus_Success PHY initialization succeeds + * @retval kStatus_Fail PHY initialization fails + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_Init(phy_handle_t *handle, const phy_config_t *config); + +/*! + * @brief PHY Write function. This function writes data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data); + +/*! + * @brief PHY Read function. This interface read data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr); + +/*! + * @brief Gets the PHY auto-negotiation status. + * + * @param handle PHY device handle. + * @param status The auto-negotiation status of the PHY. + * - true the auto-negotiation is over. + * - false the auto-negotiation is on-going or not started. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link status. + * + * @param handle PHY device handle. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY gets link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_GetLinkStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @brief This function gets the speed and duplex mode of PHY. User can give one of speed + * and duplex address paramter and set the other as NULL if only wants to get one of them. + * + * @param handle PHY device handle. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY gets link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex); + +/*! + * @brief Sets the PHY link speed and duplex. + * + * @param handle PHY device handle. + * @param speed Specified PHY link speed. + * @param duplex Specified PHY link duplex. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex); + +/*! + * @brief Enables/disables PHY loopback. + * + * @param handle PHY device handle. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * All loopback modes should not be set together, when one loopback mode is set + * another should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY loopback success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_DP83825_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_DP83825_H_ */ diff --git a/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.c b/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.c new file mode 100644 index 0000000000..32cabacdb6 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.c @@ -0,0 +1,284 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_phyksz8081.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the PHY KSZ8081 vendor defined registers. */ +#define PHY_CONTROL1_REG 0x1EU /*!< The PHY control one register. */ +#define PHY_CONTROL2_REG 0x1FU /*!< The PHY control two register. */ + +/*! @brief Defines the PHY KSZ8081 ID number. */ +#define PHY_CONTROL_ID1 0x22U /*!< The PHY ID1 */ + +/*! @brief Defines the mask flag of operation mode in control registers */ +#define PHY_CTL2_REMOTELOOP_MASK 0x0004U /*!< The PHY remote loopback mask. */ +#define PHY_CTL2_REFCLK_SELECT_MASK 0x0080U /*!< The PHY RMII reference clock select. */ +#define PHY_CTL1_10HALFDUPLEX_MASK 0x0001U /*!< The PHY 10M half duplex mask. */ +#define PHY_CTL1_100HALFDUPLEX_MASK 0x0002U /*!< The PHY 100M half duplex mask. */ +#define PHY_CTL1_10FULLDUPLEX_MASK 0x0005U /*!< The PHY 10M full duplex mask. */ +#define PHY_CTL1_100FULLDUPLEX_MASK 0x0006U /*!< The PHY 100M full duplex mask. */ +#define PHY_CTL1_SPEEDUPLX_MASK 0x0007U /*!< The PHY speed and duplex mask. */ +#define PHY_CTL1_ENERGYDETECT_MASK 0x10U /*!< The PHY signal present on rx differential pair. */ +#define PHY_CTL1_LINKUP_MASK 0x100U /*!< The PHY link up. */ +#define PHY_LINK_READY_MASK (PHY_CTL1_ENERGYDETECT_MASK | PHY_CTL1_LINKUP_MASK) + +/*! @brief Defines the timeout macro. */ +#define PHY_READID_TIMEOUT_COUNT 1000U + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ +const phy_operations_t phyksz8081_ops = {.phyInit = PHY_KSZ8081_Init, + .phyWrite = PHY_KSZ8081_Write, + .phyRead = PHY_KSZ8081_Read, + .getAutoNegoStatus = PHY_KSZ8081_GetAutoNegotiationStatus, + .getLinkStatus = PHY_KSZ8081_GetLinkStatus, + .getLinkSpeedDuplex = PHY_KSZ8081_GetLinkSpeedDuplex, + .setLinkSpeedDuplex = PHY_KSZ8081_SetLinkSpeedDuplex, + .enableLoopback = PHY_KSZ8081_EnableLoopback}; + +/******************************************************************************* + * Code + ******************************************************************************/ + +status_t PHY_KSZ8081_Init(phy_handle_t *handle, const phy_config_t *config) { + uint32_t counter = PHY_READID_TIMEOUT_COUNT; + status_t result = kStatus_Success; + uint32_t regValue = 0; + + /* Init MDIO interface. */ + MDIO_Init(handle->mdioHandle); + + /* Assign phy address. */ + handle->phyAddr = config->phyAddr; + + /* Check PHY ID. */ + do + { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_ID1_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + counter--; + } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U)); + + if (counter == 0U) { + return kStatus_Fail; + } + + /* Reset PHY. */ + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK); + if (result == kStatus_Success) { + #if defined(FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE) + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, (regValue | PHY_CTL2_REFCLK_SELECT_MASK)); + if (result != kStatus_Success) { + return result; + } + #endif /* FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE */ + + if (config->autoNeg) { + /* Set the auto-negotiation then start it. */ + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_AUTONEG_ADVERTISE_REG, + (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | + PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | PHY_IEEE802_3_SELECTOR_MASK)); + if (result == kStatus_Success) { + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } else { + /* This PHY only supports 10/100M speed. */ + assert(config->speed <= kPHY_Speed100M); + + /* Disable isolate mode */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + regValue &= ~PHY_BCTL_ISOLATE_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + + /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */ + result = PHY_KSZ8081_SetLinkSpeedDuplex(handle, config->speed, config->duplex); + } + } + return result; +} + +status_t PHY_KSZ8081_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, phyReg, data); +} + +status_t PHY_KSZ8081_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr) { + return MDIO_Read(handle->mdioHandle, handle->phyAddr, phyReg, dataPtr); +} + +status_t PHY_KSZ8081_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + *status = false; + + /* Check auto negotiation complete. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((regValue & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0U) { + *status = true; + } + } + return result; +} + +status_t PHY_KSZ8081_GetLinkStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + /* Read the basic status register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((PHY_BSTATUS_LINKSTATUS_MASK & regValue) != 0U) { + /* Link up. */ + *status = true; + } else { + /* Link down. */ + *status = false; + } + } + return result; +} + +status_t PHY_KSZ8081_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex) { + assert(!((speed == NULL) && (duplex == NULL))); + + status_t result; + uint32_t regValue; + uint32_t flag; + + /* Read the control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_CONTROL1_REG, ®Value); + if (result == kStatus_Success) { + if (speed != NULL) { + flag = regValue & PHY_CTL1_SPEEDUPLX_MASK; + if ((PHY_CTL1_100HALFDUPLEX_MASK == flag) || (PHY_CTL1_100FULLDUPLEX_MASK == flag)) { + *speed = kPHY_Speed100M; + } else { + *speed = kPHY_Speed10M; + } + } + + if (duplex != NULL) { + flag = regValue & PHY_CTL1_SPEEDUPLX_MASK; + if ((PHY_CTL1_10FULLDUPLEX_MASK == flag) || (PHY_CTL1_100FULLDUPLEX_MASK == flag)) { + *duplex = kPHY_FullDuplex; + } else { + *duplex = kPHY_HalfDuplex; + } + } + } + return result; +} + +status_t PHY_KSZ8081_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex) { + /* This PHY only supports 10/100M speed. */ + assert(speed <= kPHY_Speed100M); + + status_t result; + uint32_t regValue; + + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + /* Disable the auto-negotiation and set according to user-defined configuration. */ + regValue &= ~PHY_BCTL_AUTONEG_MASK; + if (speed == kPHY_Speed100M) { + regValue |= PHY_BCTL_SPEED0_MASK; + } else { + regValue &= ~PHY_BCTL_SPEED0_MASK; + } + if (duplex == kPHY_FullDuplex) { + regValue |= PHY_BCTL_DUPLEX_MASK; + } else { + regValue &= ~PHY_BCTL_DUPLEX_MASK; + } + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } + return result; +} + +status_t PHY_KSZ8081_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable) { + /* This PHY only supports local/remote loopback and 10/100M speed. */ + assert(mode <= kPHY_RemoteLoop); + assert(speed <= kPHY_Speed100M); + + status_t result; + uint32_t regValue; + + /* Set the loop mode. */ + if (enable) { + if (mode == kPHY_LocalLoop) { + if (speed == kPHY_Speed100M) { + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } else { + regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } else { + /* Remote loopback only supports 100M full-duplex. */ + assert(speed == kPHY_Speed100M); + + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + /* Set the remote loopback bit. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, + (regValue | PHY_CTL2_REMOTELOOP_MASK)); + } + } + } else { + /* Disable the loop mode. */ + if (mode == kPHY_LocalLoop) { + /* First read the current status in control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + regValue &= ~PHY_BCTL_LOOP_MASK; + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (regValue | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } else { + /* First read the current status in control one register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_CONTROL2_REG, + (regValue & ~PHY_CTL2_REMOTELOOP_MASK)); + } + } + } + return result; +} diff --git a/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.h b/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.h new file mode 100644 index 0000000000..5b93c5698a --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phyksz8081/fsl_phyksz8081.h @@ -0,0 +1,163 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/***************************************************************************** + * PHY KSZ8081 driver change log + *****************************************************************************/ + +/*! +@page driver_log Driver Change Log + +@section phyksz8081 PHYKSZ8081 + The current PHYKSZ8081 driver version is 2.0.0. + + - 2.0.0 + - Initial version. +*/ + +#ifndef _FSL_PHYKSZ8081_H_ +#define _FSL_PHYKSZ8081_H_ + +#include "fsl_phy.h" + +/*! + * @addtogroup phy_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief PHY driver version */ +#define FSL_PHY_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) + +/*! @brief PHY operations structure. */ +extern const phy_operations_t phyksz8081_ops; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize PHY. + * + * @param handle PHY device handle. + * @param config Pointer to structure of phy_config_t. + * @retval kStatus_Success PHY initialization succeeds + * @retval kStatus_Fail PHY initialization fails + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_Init(phy_handle_t *handle, const phy_config_t *config); + +/*! + * @brief PHY Write function. This function writes data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data); + +/*! + * @brief PHY Read function. This interface read data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr); + +/*! + * @brief Gets the PHY auto-negotiation status. + * + * @param handle PHY device handle. + * @param status The auto-negotiation status of the PHY. + * - true the auto-negotiation is over. + * - false the auto-negotiation is on-going or not started. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link status. + * + * @param handle PHY device handle. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY gets link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_GetLinkStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @brief This function gets the speed and duplex mode of PHY. User can give one of speed + * and duplex address paramter and set the other as NULL if only wants to get one of them. + * + * @param handle PHY device handle. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY gets link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex); + +/*! + * @brief Sets the PHY link speed and duplex. + * + * @param handle PHY device handle. + * @param speed Specified PHY link speed. + * @param duplex Specified PHY link duplex. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex); + +/*! + * @brief Enables/disables PHY loopback. + * + * @param handle PHY device handle. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * All loopback modes should not be set together, when one loopback mode is set + * another should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY loopback success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_KSZ8081_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_PHYKSZ8081_H_ */ diff --git a/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.c b/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.c new file mode 100644 index 0000000000..b7435c2d05 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.c @@ -0,0 +1,271 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_phylan8720.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the PHY LAN8720 vendor defined registers. */ +#define PHY_PHYSTS_REG 0x1FU // Phy status register +#define PHY_MCSR_REG 0x11U // Mode Control/Status Register for loopback + +/*! @brief Defines the PHY LAN8720 ID number. */ +#define PHY_CONTROL_ID1 0x07U /*!< The PHY ID1 */ + +/*! @brief Defines the mask flag of operation mode in control registers */ +#define PHY_PHYSTS_MASK 0x001CU /*!< The PHY Status mask. */ +#define PHY_PHYSTS_DUPLEX_MASK 0x0010U /*!< The PHY full duplex mask. */ +#define PHY_PHYSTS_100M_MASK 0x000CU /*!< The PHY 100M mask. */ +#define PHY_PHYSTS_100M_FLAG 0x0008U /*!< The PHY 100M flag. */ +#define PHY_PHYSTS_LINK_MASK 0x0001U /*!< The PHY link up mask. */ + +#define PHY_MCSR_REMOTELOOP_MASK 0x100U // !< The PHY remote loopback mask. +#define PHY_MCSR_REMOTELOOP_MODE 0x100U // !< The PHY remote loopback mode. + +/*! @brief Defines the timeout macro. */ +#define PHY_READID_TIMEOUT_COUNT 1000U + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +/******************************************************************************* + * Variables + ******************************************************************************/ +const phy_operations_t phylan8720_ops = {.phyInit = PHY_LAN8720_Init, + .phyWrite = PHY_LAN8720_Write, + .phyRead = PHY_LAN8720_Read, + .getAutoNegoStatus = PHY_LAN8720_GetAutoNegotiationStatus, + .getLinkStatus = PHY_LAN8720_GetLinkStatus, + .getLinkSpeedDuplex = PHY_LAN8720_GetLinkSpeedDuplex, + .setLinkSpeedDuplex = PHY_LAN8720_SetLinkSpeedDuplex, + .enableLoopback = PHY_LAN8720_EnableLoopback}; + +/******************************************************************************* + * Code + ******************************************************************************/ + +status_t PHY_LAN8720_Init(phy_handle_t *handle, const phy_config_t *config) { + uint32_t counter = PHY_READID_TIMEOUT_COUNT; + status_t result = kStatus_Success; + uint32_t regValue = 0; + + /* Init MDIO interface. */ + MDIO_Init(handle->mdioHandle); + + /* Assign phy address. */ + handle->phyAddr = config->phyAddr; + + /* Check PHY ID. */ + do + { + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_ID1_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + counter--; + } while ((regValue != PHY_CONTROL_ID1) && (counter != 0U)); + + if (counter == 0U) { + return kStatus_Fail; + } + + /* Reset PHY. */ + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK); + if (result == kStatus_Success) { + if (config->autoNeg) { + /* Set the auto-negotiation then start it. */ + result = + MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_AUTONEG_ADVERTISE_REG, + (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK | + PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | PHY_IEEE802_3_SELECTOR_MASK)); + if (result == kStatus_Success) { + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } else { + /* This PHY only supports 10/100M speed. */ + assert(config->speed <= kPHY_Speed100M); + + /* Disable isolate mode */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result != kStatus_Success) { + return result; + } + regValue &= ~PHY_BCTL_ISOLATE_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + + /* Disable the auto-negotiation and set user-defined speed/duplex configuration. */ + result = PHY_LAN8720_SetLinkSpeedDuplex(handle, config->speed, config->duplex); + } + } + return result; +} + +status_t PHY_LAN8720_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, phyReg, data); +} + +status_t PHY_LAN8720_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr) { + return MDIO_Read(handle->mdioHandle, handle->phyAddr, phyReg, dataPtr); +} + +status_t PHY_LAN8720_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + *status = false; + + /* Check auto negotiation complete. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((regValue & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0U) { + *status = true; + } + } + return result; +} + +status_t PHY_LAN8720_GetLinkStatus(phy_handle_t *handle, bool *status) { + assert(status); + + status_t result; + uint32_t regValue; + + /* Read the basic status register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICSTATUS_REG, ®Value); + if (result == kStatus_Success) { + if ((PHY_BSTATUS_LINKSTATUS_MASK & regValue) != 0U) { + /* Link up. */ + *status = true; + } else { + /* Link down. */ + *status = false; + } + } + return result; +} + +status_t PHY_LAN8720_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex) { + assert(!((speed == NULL) && (duplex == NULL))); + + status_t result; + uint32_t regValue; + uint32_t flag; + + /* Read the control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_PHYSTS_REG, ®Value); + if (result == kStatus_Success) { + regValue &= PHY_PHYSTS_MASK; + if (speed != NULL) { + flag = regValue & PHY_PHYSTS_100M_MASK; + if (flag == PHY_PHYSTS_100M_FLAG) { + *speed = kPHY_Speed100M; + } else { + *speed = kPHY_Speed10M; + } + } + + if (duplex != NULL) { + flag = regValue & PHY_PHYSTS_DUPLEX_MASK; + if (flag) { + *duplex = kPHY_FullDuplex; + } else { + *duplex = kPHY_HalfDuplex; + } + } + } + return result; +} + +status_t PHY_LAN8720_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex) { + /* This PHY only supports 10/100M speed. */ + assert(speed <= kPHY_Speed100M); + + status_t result; + uint32_t regValue; + + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + /* Disable the auto-negotiation and set according to user-defined configuration. */ + regValue &= ~PHY_BCTL_AUTONEG_MASK; + if (speed == kPHY_Speed100M) { + regValue |= PHY_BCTL_SPEED0_MASK; + } else { + regValue &= ~PHY_BCTL_SPEED0_MASK; + } + if (duplex == kPHY_FullDuplex) { + regValue |= PHY_BCTL_DUPLEX_MASK; + } else { + regValue &= ~PHY_BCTL_DUPLEX_MASK; + } + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } + return result; +} + +status_t PHY_LAN8720_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable) { + /* This PHY only supports local/remote loopback and 10/100M speed. */ + assert(mode <= kPHY_RemoteLoop); + assert(speed <= kPHY_Speed100M); + + status_t result = kStatus_Success; + uint32_t regValue; + + /* Set the loop mode. */ + if (enable) { + if (mode == kPHY_LocalLoop) { + if (speed == kPHY_Speed100M) { + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } else { + regValue = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + } + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + } else { + /* Remote loopback only supports 100M full-duplex. */ + assert(speed == kPHY_Speed100M); + + regValue = PHY_BCTL_SPEED0_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK; + result = MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, regValue); + if (result != kStatus_Success) { + return result; + } + /* Set the remote loopback bit. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_MCSR_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_MCSR_REG, + (regValue & ~PHY_MCSR_REMOTELOOP_MASK) | PHY_MCSR_REMOTELOOP_MODE); + } + } + } else { + /* Disable the loop mode. */ + if (mode != kPHY_LocalLoop) { + /* First read the current status in control one register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_MCSR_REG, ®Value); + if (result == kStatus_Success) { + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_MCSR_REG, + (regValue & ~PHY_MCSR_REMOTELOOP_MASK)); + } + } + /* First read the current status in control register. */ + result = MDIO_Read(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, ®Value); + if (result == kStatus_Success) { + regValue &= ~PHY_BCTL_LOOP_MASK; + return MDIO_Write(handle->mdioHandle, handle->phyAddr, PHY_BASICCONTROL_REG, + (regValue | PHY_BCTL_RESTART_AUTONEG_MASK)); + } + } + + return result; +} diff --git a/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.h b/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.h new file mode 100644 index 0000000000..2e8b4e3631 --- /dev/null +++ b/ports/mimxrt/hal/phy/device/phylan8720/fsl_phylan8720.h @@ -0,0 +1,163 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/***************************************************************************** + * PHY KSZ8081 driver change log + *****************************************************************************/ + +/*! +@page driver_log Driver Change Log + +@section phylan8720 PHYLAN8720 + The current PHYLAN8720 driver version is 2.0.0. + + - 2.0.0 + - Initial version. +*/ + +#ifndef _FSL_PHYLAN8720_H_ +#define _FSL_PHYLAN8720_H_ + +#include "fsl_phy.h" + +/*! + * @addtogroup phy_driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief PHY driver version */ +#define FSL_PHY_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) + +/*! @brief PHY operations structure. */ +extern const phy_operations_t phylan8720_ops; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize PHY. + * + * @param handle PHY device handle. + * @param config Pointer to structure of phy_config_t. + * @retval kStatus_Success PHY initialization succeeds + * @retval kStatus_Fail PHY initialization fails + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_Init(phy_handle_t *handle, const phy_config_t *config); + +/*! + * @brief PHY Write function. This function writes data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data); + +/*! + * @brief PHY Read function. This interface read data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr); + +/*! + * @brief Gets the PHY auto-negotiation status. + * + * @param handle PHY device handle. + * @param status The auto-negotiation status of the PHY. + * - true the auto-negotiation is over. + * - false the auto-negotiation is on-going or not started. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link status. + * + * @param handle PHY device handle. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY gets link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_GetLinkStatus(phy_handle_t *handle, bool *status); + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @brief This function gets the speed and duplex mode of PHY. User can give one of speed + * and duplex address paramter and set the other as NULL if only wants to get one of them. + * + * @param handle PHY device handle. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY gets link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex); + +/*! + * @brief Sets the PHY link speed and duplex. + * + * @param handle PHY device handle. + * @param speed Specified PHY link speed. + * @param duplex Specified PHY link duplex. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex); + +/*! + * @brief Enables/disables PHY loopback. + * + * @param handle PHY device handle. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * All loopback modes should not be set together, when one loopback mode is set + * another should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY loopback success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +status_t PHY_LAN8720_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable); + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ + +#endif /* _FSL_PHYLAN8720_H_ */ diff --git a/ports/mimxrt/hal/phy/fsl_mdio.h b/ports/mimxrt/hal/phy/fsl_mdio.h new file mode 100644 index 0000000000..6bfee2a244 --- /dev/null +++ b/ports/mimxrt/hal/phy/fsl_mdio.h @@ -0,0 +1,125 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_MDIO_H_ +#define _FSL_MDIO_H_ + +#include "fsl_common.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief Defines the timeout macro. */ +#if defined(MDIO_TIMEOUT_COUNT_NUMBER) && MDIO_TIMEOUT_COUNT_NUMBER +#define MDIO_TIMEOUT_COUNT MDIO_TIMEOUT_COUNT_NUMBER +#endif + +/*! @brief Defines the PHY status. */ +enum _mdio_status +{ + kStatus_PHY_SMIVisitTimeout = MAKE_STATUS(kStatusGroup_PHY, 0), /*!< ENET PHY SMI visit timeout. */ +}; + +typedef struct _mdio_operations mdio_operations_t; + +/*! @brief MDIO resource. */ +typedef struct _mdio_resource +{ + void *base; /*!< ENET Ip register base. */ + uint32_t csrClock_Hz; /*!< ENET CSR clock. */ +} mdio_resource_t; + +/*! @brief MDIO handle. */ +typedef struct _mdio_handle +{ + mdio_resource_t resource; + const mdio_operations_t *ops; +} mdio_handle_t; + +/*! @brief Camera receiver operations. */ +struct _mdio_operations +{ + void (*mdioInit)(mdio_handle_t *handle); /*!< MDIO interface init. */ + status_t (*mdioWrite)(mdio_handle_t *handle, + uint32_t phyAddr, + uint32_t devAddr, + uint32_t data); /*!< MDIO write data. */ + status_t (*mdioRead)(mdio_handle_t *handle, + uint32_t phyAddr, + uint32_t devAddr, + uint32_t *dataPtr); /*!< MDIO read data. */ + status_t (*mdioWriteExt)(mdio_handle_t *handle, + uint32_t phyAddr, + uint32_t devAddr, + uint32_t data); /*!< MDIO write data. */ + status_t (*mdioReadExt)(mdio_handle_t *handle, + uint32_t phyAddr, + uint32_t devAddr, + uint32_t *dataPtr); /*!< MDIO read data. */ +}; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif +/*! + * @name MDIO Driver + * @{ + */ + +/*! + * @brief MDIO Write function. This function write data over the SMI to + * the specified MDIO register. This function is called by all MDIO interfaces. + * + * @param handle MDIO device handle. + * @retval kStatus_Success MDIO write success + * @retval kStatus_MDIO_SMIVisitTimeout MDIO SMI visit time out + */ +static inline void MDIO_Init(mdio_handle_t *handle) { + handle->ops->mdioInit(handle); +} + +/*! + * @brief MDIO Write function. This function write data over the SMI to + * the specified MDIO register. This function is called by all MDIO interfaces. + * + * @param handle MDIO device handle. + * @param phyAddr MDIO PHY address handle. + * @param devAddr The PHY device register. + * @param data The data written to the MDIO register. + * @retval kStatus_Success MDIO write success + * @retval kStatus_MDIO_SMIVisitTimeout MDIO SMI visit time out + */ +static inline status_t MDIO_Write(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t data) { + return handle->ops->mdioWrite(handle, phyAddr, devAddr, data); +} + +/*! + * @brief MDIO Read function. This interface read data over the SMI from the + * specified MDIO register. This function is called by all MDIO interfaces. + * + * @param handle MDIO device handle. + * @param phyAddr MDIO PHY address handle. + * @param devAddr The PHY device register. + * @param dataPtr The address to store the data read from the MDIO register. + * @retval kStatus_Success MDIO read success + * @retval kStatus_MDIO_SMIVisitTimeout MDIO SMI visit time out + */ +static inline status_t MDIO_Read(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t *dataPtr) { + return handle->ops->mdioRead(handle, phyAddr, devAddr, dataPtr); +} + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/ports/mimxrt/hal/phy/fsl_phy.h b/ports/mimxrt/hal/phy/fsl_phy.h new file mode 100644 index 0000000000..6a022d4eae --- /dev/null +++ b/ports/mimxrt/hal/phy/fsl_phy.h @@ -0,0 +1,258 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_PHY_H_ +#define _FSL_PHY_H_ + +#include "fsl_mdio.h" + +/*! @note The following PHY registers are the IEEE802.3 standard definition, same register and bit field may + have different names in various PHYs, but the feature they represent should be same or very similar. */ + +/*! @brief Defines the IEEE802.3 standard PHY registers. */ +#define PHY_BASICCONTROL_REG 0x00U /*!< The PHY basic control register. */ +#define PHY_BASICSTATUS_REG 0x01U /*!< The PHY basic status register. */ +#define PHY_ID1_REG 0x02U /*!< The PHY ID one register. */ +#define PHY_ID2_REG 0x03U /*!< The PHY ID two register. */ +#define PHY_AUTONEG_ADVERTISE_REG 0x04U /*!< The PHY auto-negotiate advertise register. */ +#define PHY_AUTONEG_LINKPARTNER_REG 0x05U /*!< The PHY auto negotiation link partner ability register. */ +#define PHY_AUTONEG_EXPANSION_REG 0x06U /*!< The PHY auto negotiation expansion register. */ +#define PHY_1000BASET_CONTROL_REG 0x09U /*!< The PHY 1000BASE-T control register. */ +#define PHY_MMD_ACCESS_CONTROL_REG 0x0DU /*!< The PHY MMD access control register. */ +#define PHY_MMD_ACCESS_DATA_REG 0x0EU /*!< The PHY MMD access data register. */ + +/*! @brief Defines the mask flag in basic control register(Address 0x00). */ +#define PHY_BCTL_SPEED1_MASK 0x0040U /*!< The PHY speed bit mask(MSB).*/ +#define PHY_BCTL_ISOLATE_MASK 0x0400U /*!< The PHY isolate mask.*/ +#define PHY_BCTL_DUPLEX_MASK 0x0100U /*!< The PHY duplex bit mask. */ +#define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */ +#define PHY_BCTL_AUTONEG_MASK 0x1000U /*!< The PHY auto negotiation bit mask. */ +#define PHY_BCTL_SPEED0_MASK 0x2000U /*!< The PHY speed bit mask(LSB). */ +#define PHY_BCTL_LOOP_MASK 0x4000U /*!< The PHY loop bit mask. */ +#define PHY_BCTL_RESET_MASK 0x8000U /*!< The PHY reset bit mask. */ + +/*! @brief Defines the mask flag in basic status register(Address 0x01). */ +#define PHY_BSTATUS_LINKSTATUS_MASK 0x0004U /*!< The PHY link status mask. */ +#define PHY_BSTATUS_AUTONEGABLE_MASK 0x0008U /*!< The PHY auto-negotiation ability mask. */ +#define PHY_BSTATUS_SPEEDUPLX_MASK 0x001CU /*!< The PHY speed and duplex mask. */ +#define PHY_BSTATUS_AUTONEGCOMP_MASK 0x0020U /*!< The PHY auto-negotiation complete mask. */ + +/*! @brief Defines the mask flag in PHY auto-negotiation advertise register(Address 0x04). */ +#define PHY_100BaseT4_ABILITY_MASK 0x200U /*!< The PHY have the T4 ability. */ +#define PHY_100BASETX_FULLDUPLEX_MASK 0x100U /*!< The PHY has the 100M full duplex ability.*/ +#define PHY_100BASETX_HALFDUPLEX_MASK 0x080U /*!< The PHY has the 100M full duplex ability.*/ +#define PHY_10BASETX_FULLDUPLEX_MASK 0x040U /*!< The PHY has the 10M full duplex ability.*/ +#define PHY_10BASETX_HALFDUPLEX_MASK 0x020U /*!< The PHY has the 10M full duplex ability.*/ +#define PHY_IEEE802_3_SELECTOR_MASK 0x001U /*!< The message type being sent by Auto-Nego.*/ + +/*! @brief Defines the mask flag in the 1000BASE-T control register(Address 0x09). */ +#define PHY_1000BASET_FULLDUPLEX_MASK 0x200U /*!< The PHY has the 1000M full duplex ability.*/ +#define PHY_1000BASET_HALFDUPLEX_MASK 0x100U /*!< The PHY has the 1000M half duplex ability.*/ + +/******************************************************************************* + * Definitions + ******************************************************************************/ +typedef struct _phy_handle phy_handle_t; +/*! @brief Defines the PHY link speed. */ +typedef enum _phy_speed +{ + kPHY_Speed10M = 0U, /*!< ENET PHY 10M speed. */ + kPHY_Speed100M, /*!< ENET PHY 100M speed. */ + kPHY_Speed1000M /*!< ENET PHY 1000M speed. */ +} phy_speed_t; + +/*! @brief Defines the PHY link duplex. */ +typedef enum _phy_duplex +{ + kPHY_HalfDuplex = 0U, /*!< ENET PHY half duplex. */ + kPHY_FullDuplex /*!< ENET PHY full duplex. */ +} phy_duplex_t; + +/*! @brief Defines the PHY loopback mode. */ +typedef enum _phy_loop +{ + kPHY_LocalLoop = 0U, /*!< ENET PHY local/digital loopback. */ + kPHY_RemoteLoop, /*!< ENET PHY remote loopback. */ + kPHY_ExternalLoop, /*!< ENET PHY external loopback. */ +} phy_loop_t; + +/*! @brief Defines the PHY MMD data access mode. */ +typedef enum _phy_mmd_access_mode +{ + kPHY_MMDAccessNoPostIncrement = (1U << 14), /*!< ENET PHY MMD access data with no address post increment. */ + kPHY_MMDAccessRdWrPostIncrement = + (2U << 14), /*!< ENET PHY MMD access data with Read/Write address post increment. */ + kPHY_MMDAccessWrPostIncrement = (3U << 14), /*!< ENET PHY MMD access data with Write address post increment. */ +} phy_mmd_access_mode_t; + +/*! @brief Defines PHY configuration. */ +typedef struct _phy_config +{ + uint32_t phyAddr; /*!< PHY address. */ + phy_speed_t speed; /*!< PHY speed configuration. */ + phy_duplex_t duplex; /*!< PHY duplex configuration. */ + bool autoNeg; /*!< PHY auto-negotiation, true: enable, false: disable. */ + bool enableEEE; /*!< PHY Energy Efficient Ethernet. */ +} phy_config_t; + +/*! @brief PHY device operations. */ +typedef struct _phy_operations +{ + status_t (*phyInit)(phy_handle_t *handle, const phy_config_t *config); + status_t (*phyWrite)(phy_handle_t *handle, uint32_t phyReg, uint32_t data); + status_t (*phyRead)(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr); + status_t (*getAutoNegoStatus)(phy_handle_t *handle, bool *status); + status_t (*getLinkStatus)(phy_handle_t *handle, bool *status); + status_t (*getLinkSpeedDuplex)(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex); + status_t (*setLinkSpeedDuplex)(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex); + status_t (*enableLoopback)(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable); +} phy_operations_t; + +/*! @brief PHY device handle. */ + +struct _phy_handle +{ + uint32_t phyAddr; /*!< PHY address. */ + mdio_handle_t *mdioHandle; /*!< The MDIO handle used by the phy device, it is specified by device. */ + const phy_operations_t *ops; /*!< The device related operations. */ +}; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name PHY Driver + * @{ + */ + +/*! + * @brief Initializes PHY. + * + * This function initialize PHY. + * + * @param handle PHY device handle. + * @param config Pointer to structure of phy_config_t. + * @retval kStatus_Success PHY initialization succeeds + * @retval kStatus_Fail PHY initialization fails + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_Init(phy_handle_t *handle, const phy_config_t *config) { + return handle->ops->phyInit(handle, config); +} +/*! + * @brief PHY Write function. This function write data over the SMI to + * the specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param data The data written to the PHY register. + * @retval kStatus_Success PHY write success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_Write(phy_handle_t *handle, uint32_t phyReg, uint32_t data) { + return handle->ops->phyWrite(handle, phyReg, data); +} + +/*! + * @brief PHY Read function. This interface read data over the SMI from the + * specified PHY register. This function is called by all PHY interfaces. + * + * @param handle PHY device handle. + * @param phyReg The PHY register. + * @param dataPtr The address to store the data read from the PHY register. + * @retval kStatus_Success PHY read success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_Read(phy_handle_t *handle, uint32_t phyReg, uint32_t *dataPtr) { + return handle->ops->phyRead(handle, phyReg, dataPtr); +} + +/*! + * @brief Gets the PHY auto-negotiation status. + * + * @param handle PHY device handle. + * @param status The auto-negotiation status of the PHY. + * - true the auto-negotiation is over. + * - false the auto-negotiation is on-going or not started. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_GetAutoNegotiationStatus(phy_handle_t *handle, bool *status) { + return handle->ops->getAutoNegoStatus(handle, status); +} + +/*! + * @brief Gets the PHY link status. + * + * @param handle PHY device handle. + * @param status The link up or down status of the PHY. + * - true the link is up. + * - false the link is down. + * @retval kStatus_Success PHY get link status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_GetLinkStatus(phy_handle_t *handle, bool *status) { + return handle->ops->getLinkStatus(handle, status); +} + +/*! + * @brief Gets the PHY link speed and duplex. + * + * @brief This function gets the speed and duplex mode of PHY. User can give one of speed + * and duplex address paramter and set the other as NULL if only wants to get one of them. + * + * @param handle PHY device handle. + * @param speed The address of PHY link speed. + * @param duplex The link duplex of PHY. + * @retval kStatus_Success PHY get link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_GetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t *speed, phy_duplex_t *duplex) { + return handle->ops->getLinkSpeedDuplex(handle, speed, duplex); +} + +/*! + * @brief Sets the PHY link speed and duplex. + * + * @param handle PHY device handle. + * @param speed Specified PHY link speed. + * @param duplex Specified PHY link duplex. + * @retval kStatus_Success PHY gets status success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_SetLinkSpeedDuplex(phy_handle_t *handle, phy_speed_t speed, phy_duplex_t duplex) { + return handle->ops->setLinkSpeedDuplex(handle, speed, duplex); +} + +/*! + * @brief Enable PHY loopcback mode. + * + * @param handle PHY device handle. + * @param mode The loopback mode to be enabled, please see "phy_loop_t". + * All loopback modes should not be set together, when one loopback mode is set + * another should be disabled. + * @param speed PHY speed for loopback mode. + * @param enable True to enable, false to disable. + * @retval kStatus_Success PHY get link speed and duplex success + * @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out + */ +static inline status_t PHY_EnableLoopback(phy_handle_t *handle, phy_loop_t mode, phy_speed_t speed, bool enable) { + return handle->ops->enableLoopback(handle, mode, speed, enable); +} + +/* @} */ + +#if defined(__cplusplus) +} +#endif + +/*! @}*/ +#endif diff --git a/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.c b/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.c new file mode 100644 index 0000000000..8e679cd989 --- /dev/null +++ b/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.c @@ -0,0 +1,128 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_enet_mdio.h" +#include "fsl_enet.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ + +static void ENET_MDIO_Init(mdio_handle_t *handle); +static status_t ENET_MDIO_Write(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t data); +static status_t ENET_MDIO_Read(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t *dataPtr); + +uint32_t ENET_GetInstance(ENET_Type *base); +extern clock_ip_name_t s_enetClock[]; + +/******************************************************************************* + * Variables + ******************************************************************************/ + +const mdio_operations_t enet_ops = {.mdioInit = ENET_MDIO_Init, + .mdioWrite = ENET_MDIO_Write, + .mdioRead = ENET_MDIO_Read, + .mdioWriteExt = NULL, + .mdioReadExt = NULL}; + +/******************************************************************************* + * Code + ******************************************************************************/ + +static void ENET_MDIO_Init(mdio_handle_t *handle) { + mdio_resource_t *resource = (mdio_resource_t *)&handle->resource; + ENET_Type *base = (ENET_Type *)resource->base; + uint32_t instance = ENET_GetInstance(base); + + #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Set SMI first. */ + (void)CLOCK_EnableClock(s_enetClock[instance]); + #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + ENET_SetSMI(base, resource->csrClock_Hz, false); +} + +static status_t ENET_MDIO_Write(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t data) { + mdio_resource_t *resource = (mdio_resource_t *)&handle->resource; + ENET_Type *base = (ENET_Type *)resource->base; + status_t result = kStatus_Success; + #ifdef MDIO_TIMEOUT_COUNT + uint32_t counter; + #endif + + /* Clear the SMI interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + /* Starts a SMI write command. */ + ENET_StartSMIWrite(base, phyAddr, devAddr, kENET_MiiWriteValidFrame, data); + + /* Wait for SMI complete. */ + #ifdef MDIO_TIMEOUT_COUNT + for (counter = MDIO_TIMEOUT_COUNT; counter > 0U; counter--) + { + if (ENET_EIR_MII_MASK == (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)) { + break; + } + } + /* Check for timeout. */ + if (0U == counter) { + result = kStatus_PHY_SMIVisitTimeout; + } + #else + while (ENET_EIR_MII_MASK != (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)) { + } + #endif + + /* Clear SMI interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + return result; +} + +static status_t ENET_MDIO_Read(mdio_handle_t *handle, uint32_t phyAddr, uint32_t devAddr, uint32_t *dataPtr) { + assert(dataPtr); + + mdio_resource_t *resource = (mdio_resource_t *)&handle->resource; + ENET_Type *base = (ENET_Type *)resource->base; + #ifdef MDIO_TIMEOUT_COUNT + uint32_t counter; + #endif + + /* Clear the SMI interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + /* Starts a SMI read command operation. */ + ENET_StartSMIRead(base, phyAddr, devAddr, kENET_MiiReadValidFrame); + + /* Wait for SMI complete. */ + #ifdef MDIO_TIMEOUT_COUNT + for (counter = MDIO_TIMEOUT_COUNT; counter > 0U; counter--) + { + if (ENET_EIR_MII_MASK == (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)) { + break; + } + } + /* Check for timeout. */ + if (0U == counter) { + return kStatus_PHY_SMIVisitTimeout; + } + #else + while (ENET_EIR_MII_MASK != (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)) { + } + #endif + + /* Get data from SMI register. */ + *dataPtr = ENET_ReadSMIData(base); + + /* Clear SMI interrupt event. */ + ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK); + + return kStatus_Success; +} diff --git a/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.h b/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.h new file mode 100644 index 0000000000..500985d8fd --- /dev/null +++ b/ports/mimxrt/hal/phy/mdio/enet/fsl_enet_mdio.h @@ -0,0 +1,21 @@ +/* + * Copyright 2020 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_ENET_MDIO_H_ +#define _FSL_ENET_MDIO_H_ + +#include "fsl_enet.h" +#include "fsl_mdio.h" + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @brief ENET MDIO operations structure. */ +extern const mdio_operations_t enet_ops; + +#endif diff --git a/ports/mimxrt/lwip_inc/arch/cc.h b/ports/mimxrt/lwip_inc/arch/cc.h new file mode 100644 index 0000000000..c37e62b839 --- /dev/null +++ b/ports/mimxrt/lwip_inc/arch/cc.h @@ -0,0 +1,10 @@ +#ifndef MICROPY_INCLUDED_MIMXRT_LWIP_ARCH_CC_H +#define MICROPY_INCLUDED_MIMXRT_LWIP_ARCH_CC_H + +#include +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) { assert(1); } + +#define LWIP_NO_CTYPE_H 1 + +#endif // MICROPY_INCLUDED_MIMXRT_LWIP_ARCH_CC_H diff --git a/ports/mimxrt/lwip_inc/arch/sys_arch.h b/ports/mimxrt/lwip_inc/arch/sys_arch.h new file mode 100644 index 0000000000..8b1a393741 --- /dev/null +++ b/ports/mimxrt/lwip_inc/arch/sys_arch.h @@ -0,0 +1 @@ +// empty diff --git a/ports/mimxrt/lwip_inc/lwipopts.h b/ports/mimxrt/lwip_inc/lwipopts.h new file mode 100644 index 0000000000..42dc9fc5db --- /dev/null +++ b/ports/mimxrt/lwip_inc/lwipopts.h @@ -0,0 +1,59 @@ +#ifndef MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H +#define MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H + +#include + +// This protection is not needed, instead we execute all lwIP code at PendSV priority +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +// Chksum generaration by HW fails for ICMP +// Maybe caused by LWIP inserting ffff instead of 0000 +// The checksum flags are set in eth.c +#define LWIP_CHECKSUM_CTRL_PER_NETIF 0 +#define LWIP_CHECKSUM_ON_COPY 0 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#define LWIP_IPV6 0 +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define DHCP_DOES_ARP_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 + +#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER +#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +extern uint32_t trng_random_u32(void); +#define LWIP_RAND() trng_random_u32() + +// lwip takes 26700 bytes +#define MEM_SIZE (8000) +#define TCP_MSS (800) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (32) + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_MIMXRT_LWIP_LWIPOPTS_H diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index e81974afaa..f921f50a0c 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -36,8 +36,16 @@ #include "ticks.h" #include "tusb.h" #include "led.h" +#include "pendsv.h" #include "modmachine.h" +#if MICROPY_PY_LWIP +#include "lwip/init.h" +#include "lwip/apps/mdns.h" +#endif +#include "systick.h" +#include "extmod/modnetwork.h" + extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; void board_init(void); @@ -47,10 +55,22 @@ int main(void) { ticks_init(); tusb_init(); led_init(); + pendsv_init(); mp_stack_set_top(&_estack); mp_stack_set_limit(&_estack - &_sstack - 1024); + #if MICROPY_PY_LWIP + // lwIP doesn't allow to reinitialise itself by subsequent calls to this function + // because the system timeout list (next_timeout) is only ever reset by BSS clearing. + // So for now we only init the lwIP stack once on power-up. + lwip_init(); + #if LWIP_MDNS_RESPONDER + mdns_resp_init(); + #endif + systick_enable_dispatch(SYSTICK_DISPATCH_LWIP, mod_network_lwip_poll_wrapper); + #endif + for (;;) { gc_init(&_gc_heap_start, &_gc_heap_end); mp_init(); @@ -58,6 +78,9 @@ int main(void) { mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0); + #if MICROPY_PY_NETWORK + mod_network_init(); + #endif // Initialise sub-systems. readline_init0(); @@ -93,6 +116,9 @@ int main(void) { soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); machine_pin_irq_deinit(); + #if MICROPY_PY_NETWORK + mod_network_deinit(); + #endif gc_sweep_all(); mp_deinit(); } diff --git a/ports/mimxrt/mbedtls/mbedtls_config.h b/ports/mimxrt/mbedtls/mbedtls_config.h new file mode 100644 index 0000000000..1ab748d8e5 --- /dev/null +++ b/ports/mimxrt/mbedtls/mbedtls_config.h @@ -0,0 +1,99 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H + +// Set mbedtls configuration +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS +#define MBEDTLS_DEPRECATED_REMOVED +#define MBEDTLS_ENTROPY_HARDWARE_ALT +#define MBEDTLS_AES_ROM_TABLES +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SSL_PROTO_TLS1 +#define MBEDTLS_SSL_PROTO_TLS1_1 +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_SERVER_NAME_INDICATION + +// Use a smaller output buffer to reduce size of SSL context +#define MBEDTLS_SSL_MAX_CONTENT_LEN (16384) +#define MBEDTLS_SSL_IN_CONTENT_LEN (MBEDTLS_SSL_MAX_CONTENT_LEN) +#define MBEDTLS_SSL_OUT_CONTENT_LEN (4096) + +// Enable mbedtls modules +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +// #define MBEDTLS_ECP_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_ERROR_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS5_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_RSA_C +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_USE_C + +// Memory allocation hooks +#include +#include +void *m_calloc_mbedtls(size_t nmemb, size_t size); +void m_free_mbedtls(void *ptr); +#define MBEDTLS_PLATFORM_STD_CALLOC m_calloc_mbedtls +#define MBEDTLS_PLATFORM_STD_FREE m_free_mbedtls +#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf + +#include "mbedtls/check_config.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/mimxrt/mbedtls/mbedtls_port.c b/ports/mimxrt/mbedtls/mbedtls_port.c new file mode 100644 index 0000000000..eb11f5141b --- /dev/null +++ b/ports/mimxrt/mbedtls/mbedtls_port.c @@ -0,0 +1,93 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef MICROPY_SSL_MBEDTLS + +#include "py/runtime.h" +#include "py/gc.h" +#include "fsl_trng.h" +#include "mbedtls_config.h" + +#define DEBUG (0) + +#if DEBUG +static size_t count_links(uint32_t *nb) { + void **p = MP_STATE_PORT(mbedtls_memory); + size_t n = 0; + *nb = 0; + while (p != NULL) { + ++n; + *nb += gc_nbytes(p); + p = (void **)p[1]; + } + return n; +} +#endif + +void *m_calloc_mbedtls(size_t nmemb, size_t size) { + void **ptr = m_malloc0(nmemb * size + 2 * sizeof(uintptr_t)); + #if DEBUG + uint32_t nb; + size_t n = count_links(&nb); + printf("mbed_alloc(%u, %u) -> (%u;%u) %p\n", nmemb, size, n, (uint)nb, ptr); + #endif + if (MP_STATE_PORT(mbedtls_memory) != NULL) { + MP_STATE_PORT(mbedtls_memory)[0] = ptr; + } + ptr[0] = NULL; + ptr[1] = MP_STATE_PORT(mbedtls_memory); + MP_STATE_PORT(mbedtls_memory) = ptr; + return &ptr[2]; +} + +void m_free_mbedtls(void *ptr_in) { + void **ptr = &((void **)ptr_in)[-2]; + #if DEBUG + uint32_t nb; + size_t n = count_links(&nb); + printf("mbed_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", ptr, ptr[0], ptr[1], gc_nbytes(ptr), n, (uint)nb); + #endif + if (ptr[1] != NULL) { + ((void **)ptr[1])[0] = ptr[0]; + } + if (ptr[0] != NULL) { + ((void **)ptr[0])[1] = ptr[1]; + } else { + MP_STATE_PORT(mbedtls_memory) = ptr[1]; + } + m_free(ptr); +} + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { + + // assumes that TRNG_Init was called during startup + *olen = len; + TRNG_GetRandomData(TRNG, output, len); + + return 0; +} + +#endif diff --git a/ports/mimxrt/moduos.c b/ports/mimxrt/moduos.c index ff0f0824c4..b59a5f4483 100644 --- a/ports/mimxrt/moduos.c +++ b/ports/mimxrt/moduos.c @@ -30,6 +30,8 @@ #include "py/objstr.h" #include "py/runtime.h" +#include "py/mphal.h" +#include "extmod/misc.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" #include "extmod/vfs_lfs.h" @@ -96,6 +98,21 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +#if MICROPY_PY_OS_DUPTERM +STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { + (void)obj_in; + for (;;) { + int c = mp_uos_dupterm_rx_chr(); + if (c < 0) { + break; + } + ringbuf_put(&stdin_ringbuf, c); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +#endif + STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, @@ -118,6 +135,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #if MICROPY_PY_OS_DUPTERM { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, #endif #if MICROPY_VFS diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 0fe6cd1291..f1c38b1fad 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -44,7 +44,6 @@ uint32_t trng_random_u32(void); #define MICROPY_EMIT_INLINE_THUMB (1) // Compiler configuration -#define MICROPY_COMP_CONST (0) // Python internal features #define MICROPY_READER_VFS (1) @@ -118,6 +117,7 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_UBINASCII (1) #define MICROPY_PY_UBINASCII_CRC32 (1) #define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_OS_DUPTERM (3) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32()) @@ -139,12 +139,38 @@ uint32_t trng_random_u32(void); #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +// If MICROPY_PY_LWIP is defined, add network support +#if MICROPY_PY_LWIP + +#define MICROPY_PY_NETWORK (1) +#define MICROPY_PY_USOCKET (1) +#define MICROPY_PY_UWEBSOCKET (1) +#define MICROPY_PY_WEBREPL (1) +#define MICROPY_PY_UHASHLIB_SHA1 (1) +#define MICROPY_PY_LWIP_SOCK_RAW (1) +#define MICROPY_HW_ETH_MDC (1) + +// Prevent the "LWIP task" from running. +#define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER +#define MICROPY_PY_LWIP_REENTER MICROPY_PY_PENDSV_REENTER +#define MICROPY_PY_LWIP_EXIT MICROPY_PY_PENDSV_EXIT + +#endif + +// For regular code that wants to prevent "background tasks" from running. +// These background tasks (LWIP, Bluetooth) run in PENDSV context. +// TODO: Check for the settings of the STM32 port in irq.h +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = disable_irq(); +#define MICROPY_PY_PENDSV_REENTER atomic_state = disable_irq(); +#define MICROPY_PY_PENDSV_EXIT enable_irq(atomic_state); + // Use VfsLfs2's types for fileio/textio #define mp_type_fileio mp_type_vfs_lfs2_fileio #define mp_type_textio mp_type_vfs_lfs2_textio // Use VFS's functions for import stat and builtin open #define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open mp_vfs_open #define mp_builtin_open_obj mp_vfs_open_obj // Hooks to add builtins @@ -170,6 +196,38 @@ extern const struct _mp_obj_module_t mp_module_mimxrt; extern const struct _mp_obj_module_t mp_module_onewire; extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_utime; +extern const struct _mp_obj_module_t mp_module_usocket; +extern const struct _mp_obj_module_t mp_module_network; + +#if MICROPY_PY_NETWORK +#define NETWORK_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_network), MP_ROM_PTR(&mp_module_network) }, +#else +#define NETWORK_BUILTIN_MODULE +#endif + +#if MICROPY_PY_USOCKET && MICROPY_PY_LWIP +// usocket implementation provided by lwIP +#define SOCKET_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_lwip) }, +#elif MICROPY_PY_USOCKET +// usocket implementation provided by skeleton wrapper +#define SOCKET_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_usocket) }, +#else +// no usocket module +#define SOCKET_BUILTIN_MODULE +#endif + +#if MICROPY_SSL_MBEDTLS +#define MICROPY_PORT_ROOT_POINTER_MBEDTLS void **mbedtls_memory; +#else +#define MICROPY_PORT_ROOT_POINTER_MBEDTLS +#endif + +#if defined(MICROPY_HW_ETH_MDC) +extern const struct _mp_obj_type_t network_lan_type; +#define MICROPY_HW_NIC_ETH { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&network_lan_type) }, +#else +#define MICROPY_HW_NIC_ETH +#endif #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ @@ -177,6 +235,11 @@ extern const struct _mp_obj_module_t mp_module_utime; { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \ + SOCKET_BUILTIN_MODULE \ + NETWORK_BUILTIN_MODULE \ + +#define MICROPY_PORT_NETWORK_INTERFACES \ + MICROPY_HW_NIC_ETH \ #define MICROPY_HW_PIT_NUM_CHANNELS 3 @@ -184,6 +247,10 @@ extern const struct _mp_obj_module_t mp_module_utime; const char *readline_hist[8]; \ struct _machine_timer_obj_t *timer_table[MICROPY_HW_PIT_NUM_CHANNELS]; \ void *machine_pin_irq_objects[MICROPY_HW_NUM_PIN_IRQS]; \ + /* list of registered NICs */ \ + mp_obj_list_t mod_network_nic_list; \ + /* root pointers for sub-systems */ \ + MICROPY_PORT_ROOT_POINTER_MBEDTLS \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index ff7e988dff..66498d7b27 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -29,14 +29,21 @@ #include "py/stream.h" #include "py/mphal.h" #include "shared/timeutils/timeutils.h" +#include "extmod/misc.h" #include "ticks.h" #include "tusb.h" #include "fsl_snvs_lp.h" +#include "fsl_ocotp.h" #include CPU_HEADER_H +STATIC uint8_t stdin_ringbuf_array[260]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; + #if MICROPY_KBD_EXCEPTION +int mp_interrupt_char = -1; + void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { (void)itf; (void)wanted_char; @@ -45,6 +52,7 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { } void mp_hal_set_interrupt_char(int c) { + mp_interrupt_char = c; tud_cdc_set_wanted_char(c); } @@ -52,6 +60,9 @@ void mp_hal_set_interrupt_char(int c) { uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; + if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { + ret |= MP_STREAM_POLL_RD; + } if (tud_cdc_connected() && tud_cdc_available()) { ret |= MP_STREAM_POLL_RD; } @@ -64,6 +75,10 @@ int mp_hal_stdin_rx_chr(void) { // if (USARTx->USART.INTFLAG.bit.RXC) { // return USARTx->USART.DATA.bit.DATA; // } + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } if (tud_cdc_connected() && tud_cdc_available()) { uint8_t buf[1]; uint32_t count = tud_cdc_read(buf, sizeof(buf)); @@ -90,6 +105,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { i += n2; } } + mp_uos_dupterm_tx_strn(str, len); // TODO // while (len--) { // while (!(USARTx->USART.INTFLAG.bit.DRE)) { } @@ -103,3 +119,29 @@ uint64_t mp_hal_time_ns(void) { uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second); return s * 1000000000ULL; } + +/*******************************************************************************/ +// MAC address + +// Generate a random locally administered MAC address (LAA) +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) { + // Take the MAC addr from the OTP's Configuration and Manufacturing Info + OCOTP_Init(OCOTP, CLOCK_GetFreq(kCLOCK_IpgClk)); + buf[0] = 0x02; // Locally Administered MAC + *(uint32_t *)&buf[1] = OCOTP->CFG0 ^ (OCOTP->CFG0 >> 8); + *(uint16_t *)&buf[4] = (uint16_t)(OCOTP->CFG1 ^ (OCOTP->CFG1 >> 16)); +} + +// A board can override this if needed +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + mp_hal_generate_laa_mac(idx, buf); +} + +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { + static const char hexchr[16] = "0123456789ABCDEF"; + uint8_t mac[6]; + mp_hal_get_mac(idx, mac); + for (; chr_len; ++chr_off, --chr_len) { + *dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf]; + } +} diff --git a/ports/mimxrt/mphalport.h b/ports/mimxrt/mphalport.h index 3f0ae51bb6..a98ae5ed79 100644 --- a/ports/mimxrt/mphalport.h +++ b/ports/mimxrt/mphalport.h @@ -29,10 +29,12 @@ #include #include "ticks.h" +#include "py/ringbuf.h" #include "pin.h" #include "fsl_clock.h" #define MP_HAL_PIN_FMT "%q" +extern ringbuf_t stdin_ringbuf; #define mp_hal_pin_obj_t const machine_pin_obj_t * #define mp_hal_get_pin_obj(o) pin_find(o) @@ -85,5 +87,15 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) { return CLOCK_GetCpuClkFreq(); } +enum { + MP_HAL_MAC_WLAN0 = 0, + MP_HAL_MAC_WLAN1, + MP_HAL_MAC_BDADDR, + MP_HAL_MAC_ETH0, +}; + +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac(int idx, uint8_t buf[6]); +void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); #endif // MICROPY_INCLUDED_MIMXRT_MPHALPORT_H diff --git a/ports/mimxrt/mpnetworkport.c b/ports/mimxrt/mpnetworkport.c new file mode 100644 index 0000000000..2e6a49b90e --- /dev/null +++ b/ports/mimxrt/mpnetworkport.c @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "shared/netutils/netutils.h" +#include "systick.h" +#include "pendsv.h" +#include "extmod/modnetwork.h" + +#if MICROPY_PY_LWIP +#include "lwip/netif.h" +#include "lwip/timeouts.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "lwip/apps/mdns.h" + +// Poll lwIP every 128ms +#define LWIP_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0) + +u32_t sys_now(void) { + return mp_hal_ticks_ms(); +} + +STATIC void pyb_lwip_poll(void) { + // Run the lwIP internal updates + sys_check_timeouts(); +} + +void mod_network_lwip_poll_wrapper(uint32_t ticks_ms) { + if (LWIP_TICK(ticks_ms)) { + pendsv_schedule_dispatch(PENDSV_DISPATCH_LWIP, pyb_lwip_poll); + } +} + +#endif // MICROPY_PY_LWIP diff --git a/ports/mimxrt/network_lan.c b/ports/mimxrt/network_lan.c new file mode 100644 index 0000000000..c0d6e41362 --- /dev/null +++ b/ports/mimxrt/network_lan.c @@ -0,0 +1,171 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "extmod/modnetwork.h" +#include "eth.h" + +#if defined(MICROPY_HW_ETH_MDC) + +#include "lwip/netif.h" + +typedef struct _network_lan_obj_t { + mp_obj_base_t base; + eth_t *eth; +} network_lan_obj_t; + +STATIC const network_lan_obj_t network_lan_eth0 = { { &network_lan_type }, ð_instance }; + +STATIC void network_lan_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(self_in); + struct netif *netif = eth_netif(self->eth); + int status = eth_link_status(self->eth); + mp_printf(print, "", + status, + netif->ip_addr.addr & 0xff, + netif->ip_addr.addr >> 8 & 0xff, + netif->ip_addr.addr >> 16 & 0xff, + netif->ip_addr.addr >> 24 + ); +} + +STATIC mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + const network_lan_obj_t *self = &network_lan_eth0; + eth_init(self->eth, MP_HAL_MAC_ETH0); + // register with network module + mod_network_register_nic((mp_obj_t *)self); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t network_lan_active(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + return mp_obj_new_bool(eth_link_status(self->eth)); + } else { + int ret; + if (mp_obj_is_true(args[1])) { + ret = eth_start(self->eth); + } else { + ret = eth_stop(self->eth); + } + if (ret < 0) { + mp_raise_OSError(-ret); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_active_obj, 1, 2, network_lan_active); + +STATIC mp_obj_t network_lan_isconnected(mp_obj_t self_in) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(eth_link_status(self->eth) == 3); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(network_lan_isconnected_obj, network_lan_isconnected); + +STATIC mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ifconfig(eth_netif(self->eth), n_args - 1, args + 1); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig); + +STATIC mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + (void)self; + + if (n_args == 1) { + // No arguments: return link status + return MP_OBJ_NEW_SMALL_INT(eth_link_status(self->eth)); + } + + mp_raise_ValueError(MP_ERROR_TEXT("unknown status param")); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_status_obj, 1, 2, network_lan_status); + +STATIC mp_obj_t network_lan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("must query one param")); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_mac: { + return mp_obj_new_bytes(ð_netif(self->eth)->hwaddr[0], 6); + } + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_trace: { + eth_set_trace(self->eth, mp_obj_get_int(e->value)); + break; + } + case MP_QSTR_low_power: { + eth_low_power_mode(self->eth, mp_obj_get_int(e->value)); + break; + } + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + } + } + + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_config_obj, 1, network_lan_config); + +STATIC const mp_rom_map_elem_t network_lan_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(network_lan_locals_dict, network_lan_locals_dict_table); + +const mp_obj_type_t network_lan_type = { + { &mp_type_type }, + .name = MP_QSTR_LAN, + .print = network_lan_print, + .make_new = network_lan_make_new, + .locals_dict = (mp_obj_dict_t *)&network_lan_locals_dict, +}; + +#endif // defined(MICROPY_HW_ETH_MDC) diff --git a/ports/mimxrt/pendsv.c b/ports/mimxrt/pendsv.c new file mode 100644 index 0000000000..7638b160d8 --- /dev/null +++ b/ports/mimxrt/pendsv.c @@ -0,0 +1,73 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" +#include "shared/runtime/interrupt_char.h" +#include "pendsv.h" +#include "lib/nxp_driver/sdk/CMSIS/Include/core_cm7.h" + +#define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) +#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0) + +#if defined(PENDSV_DISPATCH_NUM_SLOTS) +uint32_t pendsv_dispatch_active; +pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; +#endif + +void pendsv_init(void) { + #if defined(PENDSV_DISPATCH_NUM_SLOTS) + pendsv_dispatch_active = false; + #endif + + // set PendSV interrupt at lowest priority + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); +} + +#if defined(PENDSV_DISPATCH_NUM_SLOTS) +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { + pendsv_dispatch_table[slot] = f; + pendsv_dispatch_active = true; + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void pendsv_dispatch_handler(void) { + for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) { + if (pendsv_dispatch_table[i] != NULL) { + pendsv_dispatch_t f = pendsv_dispatch_table[i]; + pendsv_dispatch_table[i] = NULL; + f(); + } + } +} + +void PendSV_Handler(void) { + if (pendsv_dispatch_active) { + pendsv_dispatch_handler(); + } +} +#endif diff --git a/ports/mimxrt/pendsv.h b/ports/mimxrt/pendsv.h new file mode 100644 index 0000000000..64883c9031 --- /dev/null +++ b/ports/mimxrt/pendsv.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_PENDSV_H +#define MICROPY_INCLUDED_STM32_PENDSV_H + +enum { + PENDSV_DISPATCH_SOFT_TIMER, // For later & for having at least one entry + #if MICROPY_PY_NETWORK && MICROPY_PY_LWIP + PENDSV_DISPATCH_LWIP, + #endif + PENDSV_DISPATCH_MAX +}; + +#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX + +typedef void (*pendsv_dispatch_t)(void); + +void pendsv_init(void); +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); + +#endif // MICROPY_INCLUDED_STM32_PENDSV_H diff --git a/ports/mimxrt/pin.c b/ports/mimxrt/pin.c index 7a437661e3..964fb537b8 100644 --- a/ports/mimxrt/pin.c +++ b/ports/mimxrt/pin.c @@ -116,6 +116,21 @@ const machine_pin_af_obj_t *pin_find_af(const machine_pin_obj_t *pin, uint8_t fn return NULL; } +const machine_pin_af_obj_t *pin_find_af_by_base(const machine_pin_obj_t *pin, void *base_ptr[], size_t base_size) { + const machine_pin_af_obj_t *af_obj = NULL; + + for (int i = 0; i < pin->af_list_len; ++i) { + af_obj = &pin->af_list[i]; + for (int j = 0; j < base_size; ++j) { + if (af_obj->instance == base_ptr[j]) { + return af_obj; + } + } + } + + return NULL; +} + const machine_pin_af_obj_t *pin_find_af_by_index(const machine_pin_obj_t *pin, mp_uint_t af_idx) { // TODO: Implement pin_find_af_by_index function return NULL; diff --git a/ports/mimxrt/systick.c b/ports/mimxrt/systick.c new file mode 100644 index 0000000000..086bf16701 --- /dev/null +++ b/ports/mimxrt/systick.c @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "systick.h" + +volatile uint32_t systick_ms = 0; + +systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS]; + +void SysTick_Handler(void) { + // Instead of calling HAL_IncTick we do the increment here of the counter. + // This is purely for efficiency, since SysTick is called 1000 times per + // second at the highest interrupt priority. + uint32_t uw_tick = systick_ms + 1; + systick_ms = uw_tick; + + // Dispatch to any registered handlers in a cycle + systick_dispatch_t f = systick_dispatch_table[uw_tick & (SYSTICK_DISPATCH_NUM_SLOTS - 1)]; + if (f != NULL) { + f(uw_tick); + } +} + +bool systick_has_passed(uint32_t start_tick, uint32_t delay_ms) { + return systick_ms - start_tick >= delay_ms; +} + +// waits until at least delay_ms milliseconds have passed from the sampling of +// startTick. Handles overflow properly. Assumes stc was taken from +// HAL_GetTick() some time before calling this function. +void systick_wait_at_least(uint32_t start_tick, uint32_t delay_ms) { + while (!systick_has_passed(start_tick, delay_ms)) { + __WFI(); // enter sleep mode, waiting for interrupt + } +} diff --git a/ports/mimxrt/systick.h b/ports/mimxrt/systick.h new file mode 100644 index 0000000000..2638905cd6 --- /dev/null +++ b/ports/mimxrt/systick.h @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_MIMXRT_SYSTICK_H +#define MICROPY_INCLUDED_MIMXRT_SYSTICK_H + +// Works for x between 0 and 16 inclusive +#define POW2_CEIL(x) ((((x) - 1) | ((x) - 1) >> 1 | ((x) - 1) >> 2 | ((x) - 1) >> 3) + 1) + +enum { + SYSTICK_DISPATCH_DMA = 0, + #if MICROPY_HW_ENABLE_STORAGE + SYSTICK_DISPATCH_STORAGE, + #endif + #if MICROPY_PY_NETWORK && MICROPY_PY_LWIP + SYSTICK_DISPATCH_LWIP, + #endif + SYSTICK_DISPATCH_MAX +}; + +#define SYSTICK_DISPATCH_NUM_SLOTS POW2_CEIL(SYSTICK_DISPATCH_MAX) + +typedef void (*systick_dispatch_t)(uint32_t); + +extern systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS]; + +static inline void systick_enable_dispatch(size_t slot, systick_dispatch_t f) { + systick_dispatch_table[slot] = f; +} + +static inline void systick_disable_dispatch(size_t slot) { + systick_dispatch_table[slot] = NULL; +} + +void systick_wait_at_least(uint32_t stc, uint32_t delay_ms); +bool systick_has_passed(uint32_t stc, uint32_t delay_ms); + +#endif // MICROPY_INCLUDED_MIMXRT_SYSTICK_H