From 87f97e490c4fe12ab5f06e154dc76f0dd27091a9 Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Sun, 1 Aug 2021 11:20:39 +0200 Subject: [PATCH] mimxrt/sdcard: Implement SDCard driver. - Configures `PLL2->PFD0` with **198MHz** as base clock of `USDHCx` peripheral. - Adds guards for SDCard related files via `MICROPY_PY_MACHINE_SDCARD` - Adds creation of pin defines for SDCard to make-pins.py - Adds new configuration option for SDCard peripheral pinout to mpconfigport.h - Adds interrupt handling support instead of polling - Adds support for `ADMA2` powered data transfer - Configures SDCard to run in HS (high-speed mode) with **50MHz** only! SDCard support is optional and requires `USDHC` peripheral. Thus this driver is not available on `MIMXRT1010_EVK`. SDCard support is enabled by setting `MICROPY_PY_MACHINE_SDCARD = 1` in mpconfigboard.mk. Signed-off-by: Philipp Ebensberger --- ports/mimxrt/Makefile | 77 +- ports/mimxrt/board_init.c | 6 +- .../boards/MIMXRT1010_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1020_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 6 +- .../boards/MIMXRT1050_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 7 +- .../boards/MIMXRT1050_EVKB/mpconfigboard.h | 1 - .../boards/MIMXRT1050_EVKB/mpconfigboard.mk | 7 +- .../boards/MIMXRT1060_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 6 +- .../boards/MIMXRT1064_EVK/mpconfigboard.h | 12 + .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 7 +- ports/mimxrt/boards/TEENSY40/mpconfigboard.h | 12 + ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 7 +- ports/mimxrt/boards/TEENSY40/pins.csv | 10 +- ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 12 + ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 7 +- ports/mimxrt/boards/make-pins.py | 88 +- ports/mimxrt/boards/mimxrt_prefix.c | 5 +- ports/mimxrt/fatfs_port.c | 39 + ports/mimxrt/machine_sdcard.c | 222 ++++ ports/mimxrt/modmachine.c | 3 + ports/mimxrt/modmachine.h | 2 + ports/mimxrt/modules/_boot.py | 23 +- ports/mimxrt/moduos.c | 1 + ports/mimxrt/mpconfigport.h | 6 + ports/mimxrt/pin.h | 5 +- ports/mimxrt/sdcard.c | 1023 +++++++++++++++++ ports/mimxrt/sdcard.h | 111 ++ 30 files changed, 1672 insertions(+), 70 deletions(-) create mode 100644 ports/mimxrt/fatfs_port.c create mode 100644 ports/mimxrt/machine_sdcard.c create mode 100644 ports/mimxrt/sdcard.c create mode 100644 ports/mimxrt/sdcard.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 818a2a3c84..58fbe2df33 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -20,6 +20,7 @@ QSTR_GLOBAL_DEPENDENCIES = $(BOARD_DIR)/mpconfigboard.h # MicroPython feature configurations FROZEN_MANIFEST ?= boards/manifest.py MICROPY_VFS_LFS2 ?= 1 +MICROPY_VFS_FAT ?= 1 # Include py core make definitions include $(TOP)/py/py.mk @@ -44,27 +45,32 @@ GEN_PINS_AF_PY = $(BUILD)/pins_af.py CFLAGS += -Wno-error=unused-parameter INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) INC += -I$(BOARD_DIR) -INC += -I$(TOP)/lib/cmsis/inc +INC += -I$(BUILD) +INC += -I$(TOP) INC += -I$(TOP)/$(MCU_DIR) INC += -I$(TOP)/$(MCU_DIR)/drivers INC += -I$(TOP)/$(MCU_DIR)/project_template -INC += -I$(TOP)/lib/tinyusb/src +INC += -I$(TOP)/lib/cmsis/inc +INC += -I$(TOP)/lib/oofatfs INC += -I$(TOP)/lib/tinyusb/hw INC += -I$(TOP)/lib/tinyusb/hw/bsp/teensy_40 +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 += -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ + -DFSL_SDK_ENABLE_DRIVER_CACHE_CONTROL=1 \ -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ -D__STARTUP_CLEAR_BSS \ -D__STARTUP_INITIALIZE_RAMFUNCTION \ -D__START=main \ -DCPU_HEADER_H='<$(MCU_SERIES).h>' +ifeq ($(MICROPY_PY_MACHINE_SDCARD),1) +CFLAGS += -DMICROPY_PY_MACHINE_SDCARD=1 +endif CFLAGS += $(CFLAGS_MOD) $(CFLAGS_EXTRA) # Configure floating point support @@ -98,64 +104,71 @@ endif # TinyUSB Stack source SRC_TINYUSB_C += \ - lib/tinyusb/src/tusb.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - lib/tinyusb/src/class/msc/msc_device.c \ lib/tinyusb/src/class/cdc/cdc_device.c \ lib/tinyusb/src/class/dfu/dfu_rt_device.c \ lib/tinyusb/src/class/hid/hid_device.c \ lib/tinyusb/src/class/midi/midi_device.c \ + lib/tinyusb/src/class/msc/msc_device.c \ lib/tinyusb/src/class/usbtmc/usbtmc_device.c \ lib/tinyusb/src/class/vendor/vendor_device.c \ - lib/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + lib/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c \ + lib/tinyusb/src/tusb.c SRC_HAL_IMX_C += \ - $(MCU_DIR)/system_$(MCU_SERIES).c \ - $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \ - $(MCU_DIR)/project_template/clock_config.c \ $(MCU_DIR)/drivers/fsl_adc.c \ $(MCU_DIR)/drivers/fsl_cache.c \ $(MCU_DIR)/drivers/fsl_clock.c \ + $(MCU_DIR)/drivers/fsl_common.c \ $(MCU_DIR)/drivers/fsl_dmamux.c \ $(MCU_DIR)/drivers/fsl_edma.c \ + $(MCU_DIR)/drivers/fsl_flexram.c \ + $(MCU_DIR)/drivers/fsl_flexspi.c \ $(MCU_DIR)/drivers/fsl_gpio.c \ $(MCU_DIR)/drivers/fsl_gpt.c \ - $(MCU_DIR)/drivers/fsl_common.c \ $(MCU_DIR)/drivers/fsl_lpi2c.c \ $(MCU_DIR)/drivers/fsl_lpspi.c \ $(MCU_DIR)/drivers/fsl_lpspi_edma.c \ $(MCU_DIR)/drivers/fsl_lpuart.c \ - $(MCU_DIR)/drivers/fsl_flexram.c \ - $(MCU_DIR)/drivers/fsl_flexspi.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_trng.c \ + $(MCU_DIR)/project_template/clock_config.c \ + $(MCU_DIR)/system_$(MCU_SERIES).c \ + $(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \ + +ifeq ($(MICROPY_PY_MACHINE_SDCARD),1) +SRC_HAL_IMX_C += $(MCU_DIR)/drivers/fsl_usdhc.c +endif SRC_C += \ - main.c \ - led.c \ - pin.c \ - ticks.c \ - tusb_port.c \ + $(BOARD_DIR)/flash_config.c \ board_init.c \ dma_channel.c \ - $(BOARD_DIR)/flash_config.c \ + drivers/bus/softspi.c \ + extmod/modonewire.c \ + fatfs_port.c \ + led.c \ machine_adc.c \ machine_i2c.c \ machine_led.c \ machine_pin.c \ machine_rtc.c \ + machine_sdcard.c \ machine_spi.c \ machine_timer.c \ machine_uart.c \ + main.c \ mimxrt_flash.c \ - modutime.c \ modmachine.c \ modmimxrt.c \ moduos.c \ + modutime.c \ mphalport.c \ + pin.c \ + sdcard.c \ shared/libc/printf.c \ shared/libc/string0.c \ shared/readline/readline.c \ @@ -165,8 +178,8 @@ SRC_C += \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ - drivers/bus/softspi.c \ - extmod/modonewire.c \ + ticks.c \ + tusb_port.c \ $(SRC_TINYUSB_C) \ $(SRC_HAL_IMX_C) \ @@ -188,9 +201,9 @@ LIBM_SRC_C += $(addprefix lib/libm_dbl/,\ atan2.c \ atanh.c \ ceil.c \ + copysign.c \ cos.c \ cosh.c \ - copysign.c \ erf.c \ exp.c \ expm1.c \ @@ -222,7 +235,6 @@ LIBM_SRC_C += lib/libm_dbl/sqrt.c endif else LIBM_SRC_C += $(addprefix lib/libm/,\ - math.c \ acoshf.c \ asinfacosf.c \ asinhf.c \ @@ -237,6 +249,7 @@ LIBM_SRC_C += $(addprefix lib/libm/,\ kf_sin.c \ kf_tan.c \ log1pf.c \ + math.c \ nearbyintf.c \ roundf.c \ sf_cos.c \ @@ -263,28 +276,29 @@ ifeq ($(MICROPY_FLOAT_IMPL),double) $(LIBM_O): CFLAGS := $(filter-out -Wdouble-promotion -Wfloat-conversion, $(CFLAGS)) endif -SRC_SS = $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S +SRC_SS += $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S -SRC_S = shared/runtime/gchelper_m3.s \ +SRC_S += shared/runtime/gchelper_m3.s \ # List of sources for qstr extraction SRC_QSTR += \ + extmod/modonewire.c \ machine_adc.c \ machine_led.c \ machine_pin.c \ machine_rtc.c \ + machine_sdcard.c \ machine_spi.c \ machine_timer.c \ machine_uart.c \ mimxrt_flash.c \ - modutime.c \ modmachine.c \ modmimxrt.c \ moduos.c \ + modutime.c \ pin.c \ shared/runtime/mpirq.c \ shared/runtime/sys_stdio_mphal.c \ - extmod/modonewire.c \ $(GEN_PINS_SRC) \ OBJ += $(PY_O) @@ -327,6 +341,7 @@ $(HEADER_BUILD)/qstrdefs.generated.h: $(BOARD_DIR)/mpconfigboard.h $(BUILD)/%_gen.c $(HEADER_BUILD)/%.h: $(BOARD_PINS) $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) $(ECHO) "Create $@" $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE)\ + --iomux $(abspath $(TOP)/$(MCU_DIR)/drivers/fsl_iomuxc.h) \ --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) > $(GEN_PINS_SRC) $(BUILD)/pins_gen.o: $(BUILD)/pins_gen.c diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index 65024ff3e2..091e23eb5d 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -40,7 +40,6 @@ #include "clock_config.h" #include "modmachine.h" -volatile uint32_t systick_ms = 0; const uint8_t dcd_data[] = { 0x00 }; @@ -85,6 +84,11 @@ void board_init(void) { // PIT machine_timer_init_PIT(); + + // SDCard + #if MICROPY_PY_MACHINE_SDCARD + machine_sdcard_init0(); + #endif } void USB_OTG1_IRQHandler(void) { diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index ccc8ffeb41..f616d5afd4 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -2,6 +2,7 @@ MCU_SERIES = MIMXRT1011 MCU_VARIANT = MIMXRT1011DAE5A MICROPY_FLOAT_IMPL = single +MICROPY_PY_MACHINE_SDCARD = 0 SRC_C += \ hal/flexspi_nor_flash.c \ diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index d2a2cbbdbd..d7fe575abc 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -64,3 +64,15 @@ { IOMUXC_GPIO_AD_B1_08_LPI2C2_SCL }, { IOMUXC_GPIO_AD_B1_09_LPI2C2_SDA }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_SD_B1_02_LPI2C4_SCL }, { IOMUXC_GPIO_SD_B1_03_LPI2C4_SDA }, + +#define USDHC_DUMMY_PIN NULL , 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_02_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_03_USDHC1_CLK }, \ + .cd_b = { GPIO_SD_B0_06_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_04_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_05_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_00_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_01_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index f8f66b0dff..6dd1686521 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -2,9 +2,7 @@ MCU_SERIES = MIMXRT1021 MCU_VARIANT = MIMXRT1021DAG5A MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_nor_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 JLINK_PATH ?= /media/RT1020-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink @@ -16,6 +14,8 @@ else JLINK_CONNECTION_SETTINGS = endif +SRC_C += \ + hal/flexspi_nor_flash.c deploy_jlink: $(BUILD)/firmware.hex $(ECHO) "ExitOnError 1" > $(JLINK_COMMANDER_SCRIPT) diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index af0975bc17..976de9c07d 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -54,3 +54,15 @@ { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, + +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { GPIO_B1_12_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 61826b6f60..fdbf47f01e 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -2,11 +2,12 @@ MCU_SERIES = MIMXRT1052 MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_nor_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 JLINK_PATH ?= /media/RT1050-EVK/ deploy: $(BUILD)/firmware.bin cp $< $(JLINK_PATH) + +SRC_C += \ + hal/flexspi_nor_flash.c diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h index 518240b03f..963c42cb6a 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h @@ -56,7 +56,6 @@ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, #define USDHC_DUMMY_PIN NULL , 0 - #define MICROPY_USDHC1 \ { \ .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ diff --git a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk index c9e32612ca..e6cd1a63e8 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk @@ -2,11 +2,12 @@ MCU_SERIES = MIMXRT1052 MCU_VARIANT = MIMXRT1052DVL6B MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_hyper_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 JLINK_PATH ?= /media/RT1050-EVKB/ +SRC_C += \ + hal/flexspi_hyper_flash.c + deploy: $(BUILD)/firmware.bin cp $< $(JLINK_PATH) diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h index c207da832e..c26364f265 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h @@ -54,3 +54,15 @@ { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, + +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { GPIO_B1_12_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index f5eaf3eab5..623e2617fd 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -2,9 +2,7 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_hyper_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 JLINK_PATH ?= /media/RT1060-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink @@ -16,6 +14,8 @@ else JLINK_CONNECTION_SETTINGS = -USB endif +SRC_C += \ + hal/flexspi_hyper_flash.c deploy_jlink: $(BUILD)/firmware.hex $(Q)$(TOUCH) $(JLINK_COMMANDER_SCRIPT) diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h index 59f28b0e2a..4534caa296 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h @@ -52,3 +52,15 @@ { IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL }, { IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA }, \ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, + +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { GPIO_B1_12_USDHC1_CD_B },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index 444fe99672..fe3c442faa 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -2,9 +2,7 @@ MCU_SERIES = MIMXRT1064 MCU_VARIANT = MIMXRT1064DVL6A MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_hyper_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 JLINK_PATH ?= /media/RT1064-EVK/ @@ -12,3 +10,6 @@ CFLAGS += -DBOARD_FLASH_SIZE=0x400000 deploy: $(BUILD)/firmware.bin cp $< $(JLINK_PATH) + +SRC_C += \ + hal/flexspi_hyper_flash.c diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h index aacee4623c..4a69303b56 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.h @@ -57,3 +57,15 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, + +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { USDHC_DUMMY_PIN },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index a15dd78128..bd70fd0925 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -2,9 +2,10 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_nor_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< + +SRC_C += \ + hal/flexspi_nor_flash.c diff --git a/ports/mimxrt/boards/TEENSY40/pins.csv b/ports/mimxrt/boards/TEENSY40/pins.csv index c9d7c9856d..0ea4f13731 100644 --- a/ports/mimxrt/boards/TEENSY40/pins.csv +++ b/ports/mimxrt/boards/TEENSY40/pins.csv @@ -32,12 +32,12 @@ D30,GPIO_EMC_37 D31,GPIO_EMC_36 D32,GPIO_B0_12 D33,GPIO_EMC_07 -DAT1,GPIO_AD_B0_03 -DAT0,GPIO_AD_B0_02 -CLK,GPIO_AD_B0_01 -CMD,GPIO_ASD_B0_00 -DAT3,GPIO_SD_B0_05 +CMD,GPIO_SD_B0_00 +CLK,GPIO_SD_B0_01 +DAT0,GPIO_SD_B0_02 +DAT1,GPIO_SD_B0_03 DAT2,GPIO_SD_B0_04 +DAT3,GPIO_SD_B0_05 A0,GPIO_AD_B1_02 A1,GPIO_AD_B1_03 A2,GPIO_AD_B1_07 diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index a72bd127b6..587bf9c620 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -57,3 +57,15 @@ { 0 }, { 0 }, \ { IOMUXC_GPIO_AD_B1_07_LPI2C3_SCL }, { IOMUXC_GPIO_AD_B1_06_LPI2C3_SDA }, \ { IOMUXC_GPIO_AD_B0_12_LPI2C4_SCL }, { IOMUXC_GPIO_AD_B0_13_LPI2C4_SDA }, + +#define USDHC_DUMMY_PIN NULL, 0 +#define MICROPY_USDHC1 \ + { \ + .cmd = {GPIO_SD_B0_00_USDHC1_CMD}, \ + .clk = { GPIO_SD_B0_01_USDHC1_CLK }, \ + .cd_b = { USDHC_DUMMY_PIN },\ + .data0 = { GPIO_SD_B0_02_USDHC1_DATA0 },\ + .data1 = { GPIO_SD_B0_03_USDHC1_DATA1 },\ + .data2 = { GPIO_SD_B0_04_USDHC1_DATA2 },\ + .data3 = { GPIO_SD_B0_05_USDHC1_DATA3 },\ + } diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index a15dd78128..bd70fd0925 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -2,9 +2,10 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A MICROPY_FLOAT_IMPL = double - -SRC_C += \ - hal/flexspi_nor_flash.c \ +MICROPY_PY_MACHINE_SDCARD = 1 deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< + +SRC_C += \ + hal/flexspi_nor_flash.c diff --git a/ports/mimxrt/boards/make-pins.py b/ports/mimxrt/boards/make-pins.py index e9e32a5047..d75592124a 100644 --- a/ports/mimxrt/boards/make-pins.py +++ b/ports/mimxrt/boards/make-pins.py @@ -8,11 +8,22 @@ import sys import csv import re -SUPPORTED_AFS = {"GPIO"} +SUPPORTED_AFS = {"GPIO", "USDHC"} MAX_AF = 10 # AF0 .. AF9 ADC_COL = 11 +regexes = [ + r"IOMUXC_(?PGPIO_SD_B\d_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_B\d_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_EMC_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_B\d_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_SD_\d\d)_(?P\w+) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", +] + + def parse_pad(pad_str): """Parses a string and returns a (port, gpio_bit) tuple.""" if len(pad_str) < 4: @@ -127,16 +138,18 @@ class AdcFunction(object): class AlternateFunction(object): """Holds the information associated with a pins alternate function.""" - def __init__(self, idx, af_str): + def __init__(self, idx, input_reg, input_daisy, af_str): self.idx = idx self.af_str = af_str + self.input_reg = input_reg + self.input_daisy = input_daisy self.instance = self.af_str.split("_")[0] def print(self): """Prints the C representation of this AF.""" print( - " PIN_AF({0}, PIN_AF_MODE_ALT{1}, {2}, {3}),".format( - self.af_str, self.idx, self.instance, "0x10B0U" + " PIN_AF({0}, PIN_AF_MODE_ALT{1}, {2}, {3}, {4}, {5}),".format( + self.af_str, self.idx, self.input_daisy, self.instance, self.input_reg, "0x10B0U" ) ) @@ -171,8 +184,26 @@ class Pins(object): if pin and row[0]: # Only add board pins that have a name self.board_pins.append(NamedPin(row[0], pin.pad, pin.idx)) - def parse_af_file(self, filename, pad_col, af_start_col): + def parse_af_file(self, filename, iomux_filename, pad_col, af_start_col): af_end_col = af_start_col + MAX_AF + + iomux_pin_config = dict() + + with open(iomux_filename, "r") as ipt: + input_str = ipt.read() + for regex in regexes: + matches = re.finditer(regex, input_str, re.MULTILINE) + + for match in matches: + if match.group("pin") not in iomux_pin_config: + iomux_pin_config[match.group("pin")] = { + int((match.groupdict()["muxMode"].strip("U")), 16): match.groupdict() + } + else: + iomux_pin_config[match.group("pin")][ + int((match.groupdict()["muxMode"].strip("U")), 16) + ] = match.groupdict() + with open(filename, "r") as csvfile: rows = csv.reader(csvfile) header = next(rows) @@ -187,7 +218,16 @@ class Pins(object): af_idx = 0 for af_idx, af in enumerate(row[af_start_col:af_end_col]): if af and af_supported(af): - pin.add_af(AlternateFunction(af_idx, af)) + pin.add_af( + AlternateFunction( + af_idx, + iomux_pin_config[pin.name][af_idx]["inputRegister"].strip("U"), + int( + iomux_pin_config[pin.name][af_idx]["inputDaisy"].strip("U"), 16 + ), + af, + ) + ) pin.parse_adc(row[ADC_COL]) @@ -235,6 +275,33 @@ class Pins(object): hdr_file.write("extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict;\n") hdr_file.write("extern const mp_obj_dict_t machine_pin_board_pins_locals_dict;\n") + hdr_file.write("\n// Defines\n") + usdhc_instance_factory(self.cpu_pins, hdr_file) + + +def usdhc_instance_factory(pins, output_file): + usdhc_pins = filter(lambda p: any([af for af in p.alt_fn if "USDHC" in af.af_str]), pins) + + usdhc_instances = dict() + for pin in usdhc_pins: + for idx, alt_fn in enumerate(pin.alt_fn): + if "USDHC" in alt_fn.instance: + format_string = "#define {0}_{1} &pin_{0}, {2}" + if alt_fn.instance not in usdhc_instances: + usdhc_instances[alt_fn.instance] = [ + format_string.format(pin.name, alt_fn.af_str, idx) + ] + else: + usdhc_instances[alt_fn.instance].append( + format_string.format(pin.name, alt_fn.af_str, idx) + ) + + for k, v in usdhc_instances.items(): + output_file.write(f"// {k}\n") + output_file.write(f"#define {k}_AVAIL (1)\n") + for i in v: + output_file.write(i + "\n") + def main(): parser = argparse.ArgumentParser( @@ -249,6 +316,13 @@ def main(): help="Specifies the alternate function file for the chip", default="mimxrt1021_af.csv", ) + parser.add_argument( + "-i", + "--iomux", + dest="iomux_filename", + help="Specifies the fsl_iomux.h file for the chip", + default="fsl_iomuxc.h", + ) parser.add_argument( "-b", "--board", @@ -279,7 +353,7 @@ def main(): if args.af_filename: print("// --af {:s}".format(args.af_filename)) - pins.parse_af_file(args.af_filename, 0, 1) + pins.parse_af_file(args.af_filename, args.iomux_filename, 0, 1) if args.board_filename: print("// --board {:s}".format(args.board_filename)) diff --git a/ports/mimxrt/boards/mimxrt_prefix.c b/ports/mimxrt/boards/mimxrt_prefix.c index 96ba6a0d5e..938efc8104 100644 --- a/ports/mimxrt/boards/mimxrt_prefix.c +++ b/ports/mimxrt/boards/mimxrt_prefix.c @@ -4,12 +4,14 @@ #include "py/mphal.h" #include "pin.h" -#define PIN_AF(_name, _af_mode, _instance, _pad_config) \ +#define PIN_AF(_name, _af_mode, _input_daisy, _instance, _input_register, _pad_config) \ { \ .base = { &machine_pin_af_type }, \ .name = MP_QSTR_##_name, \ .af_mode = (uint32_t)(_af_mode), \ + .input_daisy = (uint8_t)(_input_daisy), \ .instance = (void *)(_instance), \ + .input_register = (uint32_t)(_input_register), \ .pad_config = (uint32_t)(_pad_config), \ } \ @@ -32,3 +34,4 @@ .af_list = (_af_list), \ .adc_list = (_adc_list), \ } \ + diff --git a/ports/mimxrt/fatfs_port.c b/ports/mimxrt/fatfs_port.c new file mode 100644 index 0000000000..7b7008667f --- /dev/null +++ b/ports/mimxrt/fatfs_port.c @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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 "lib/oofatfs/ff.h" +#include "fsl_snvs_lp.h" + + +MP_WEAK DWORD get_fattime(void) { + snvs_lp_srtc_datetime_t srtcDate; + + SNVS_LP_SRTC_GetDatetime(SNVS, &srtcDate); + + return ((srtcDate.year - 1980) << 25) | (srtcDate.month << 21) | (srtcDate.day << 16) | + (srtcDate.hour << 11) | ((srtcDate.minute << 5) | (srtcDate.second / 2)); +} diff --git a/ports/mimxrt/machine_sdcard.c b/ports/mimxrt/machine_sdcard.c new file mode 100644 index 0000000000..4a92aae00c --- /dev/null +++ b/ports/mimxrt/machine_sdcard.c @@ -0,0 +1,222 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * 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. + */ + +#if MICROPY_PY_MACHINE_SDCARD + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "ticks.h" +#include "fsl_cache.h" + +#include "sdcard.h" + + +enum { SDCARD_INIT_ARG_ID }; + + +STATIC const mp_arg_t sdcard_init_allowed_args[] = { + [SDCARD_INIT_ARG_ID] = { MP_QSTR_id, MP_ARG_INT, {.u_int = 1} }, +}; + + +STATIC void machine_sdcard_init_helper(mimxrt_sdcard_obj_t *self) { + sdcard_init(self, 198000000UL); // Initialize SDCard Host with 198MHz base clock + sdcard_init_pins(self); + + ticks_delay_us64(2ULL * 1000ULL); // Wait 2ms to allow USDHC signals to settle/debounce +} + +STATIC mp_obj_t sdcard_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args); + + // Parse args + mp_arg_val_t args[MP_ARRAY_SIZE(sdcard_init_allowed_args)]; + mp_arg_parse_all(n_args, all_args, &kw_args, MP_ARRAY_SIZE(sdcard_init_allowed_args), sdcard_init_allowed_args, args); + + // Extract arguments + mp_int_t sdcard_id = args[SDCARD_INIT_ARG_ID].u_int; + + if (!(1 <= sdcard_id && sdcard_id <= MP_ARRAY_SIZE(mimxrt_sdcard_objs))) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "SDCard(%d) doesn't exist", sdcard_id)); + } + + mimxrt_sdcard_obj_t *self = &mimxrt_sdcard_objs[(sdcard_id - 1)]; + + // Initialize SDCard Host + if (!sdcard_state_initialized(self)) { + machine_sdcard_init_helper(self); + } + return MP_OBJ_FROM_PTR(self); +} + +// init() +STATIC mp_obj_t machine_sdcard_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + if (!sdcard_state_initialized(self)) { + machine_sdcard_init_helper(self); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_sdcard_init_obj, 1, machine_sdcard_init); + +// deinit() +STATIC mp_obj_t machine_sdcard_deinit(mp_obj_t self_in) { + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + sdcard_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_sdcard_deinit_obj, machine_sdcard_deinit); + +// readblocks(block_num, buf) +STATIC mp_obj_t machine_sdcard_readblocks(mp_obj_t self_in, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); + + if (sdcard_read(self, bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_DEFAULT_BLOCK_SIZE)) { + return MP_OBJ_NEW_SMALL_INT(0); + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_readblocks_obj, machine_sdcard_readblocks); + +// present() +STATIC mp_obj_t machine_sdcard_present(mp_obj_t self_in) { + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(sdcard_detect(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_sdcard_present_obj, machine_sdcard_present); + +STATIC mp_obj_t machine_sdcard_info(mp_obj_t self_in) { + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (sdcard_detect(self) && sdcard_state_initialized(self)) { + uint32_t log_block_nbr = self->block_count; + uint32_t log_block_size = self->block_len; + + mp_obj_t tuple[2] = { + mp_obj_new_int_from_ull((uint64_t)log_block_nbr * (uint64_t)log_block_size), + mp_obj_new_int_from_uint(log_block_size), + }; + return mp_obj_new_tuple(2, tuple); + } else { + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_sdcard_info_obj, machine_sdcard_info); + +// writeblocks(block_num, buf) +STATIC mp_obj_t machine_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t block_num, mp_obj_t buf) { + mp_buffer_info_t bufinfo; + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE); + + if (sdcard_write(self, bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_DEFAULT_BLOCK_SIZE)) { + return MP_OBJ_NEW_SMALL_INT(0); + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_writeblocks_obj, machine_sdcard_writeblocks); + +// ioctl(op, arg) +STATIC mp_obj_t machine_sdcard_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + mimxrt_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: { + if (sdcard_detect(self)) { + if (sdcard_power_on(self)) { + return MP_OBJ_NEW_SMALL_INT(0); + } else { + sdcard_power_off(self); + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); // Initialization failed + } + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); // Initialization failed + } + } + case MP_BLOCKDEV_IOCTL_DEINIT: { + if (sdcard_power_off(self)) { + return MP_OBJ_NEW_SMALL_INT(0); + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); // Deinitialization failed + } + } + case MP_BLOCKDEV_IOCTL_SYNC: { + return MP_OBJ_NEW_SMALL_INT(0); + } + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: { + if (sdcard_state_initialized(self)) { + return MP_OBJ_NEW_SMALL_INT(self->block_count); + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); // Card not initialized + } + } + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: { + if (sdcard_state_initialized(self)) { + return MP_OBJ_NEW_SMALL_INT(self->block_len); + } else { + return MP_OBJ_NEW_SMALL_INT(-MP_EIO); // Card not initialized + } + } + default: // unknown command + { + return mp_const_none; + } + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_ioctl_obj, machine_sdcard_ioctl); + +STATIC const mp_rom_map_elem_t sdcard_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_sdcard_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_present), MP_ROM_PTR(&machine_sdcard_present_obj) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_sdcard_info_obj) }, + // block device protocol + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&machine_sdcard_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&machine_sdcard_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&machine_sdcard_ioctl_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sdcard_locals_dict, sdcard_locals_dict_table); + +const mp_obj_type_t machine_sdcard_type = { + { &mp_type_type }, + .name = MP_QSTR_SDCard, + .make_new = sdcard_obj_make_new, + .locals_dict = (mp_obj_dict_t *)&sdcard_locals_dict, +}; + +void machine_sdcard_init0(void) { + return; +} + +#endif // MICROPY_PY_MACHINE_SDCARD diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 150615ca03..a98d09bd0e 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -82,6 +82,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) }, + #if MICROPY_PY_MACHINE_SDCARD + { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&machine_sdcard_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, diff --git a/ports/mimxrt/modmachine.h b/ports/mimxrt/modmachine.h index 9732093bd8..e1a7ce0b72 100644 --- a/ports/mimxrt/modmachine.h +++ b/ports/mimxrt/modmachine.h @@ -35,9 +35,11 @@ extern const mp_obj_type_t machine_rtc_type; extern const mp_obj_type_t machine_i2c_type; extern const mp_obj_type_t machine_spi_type; extern const mp_obj_type_t machine_uart_type; +extern const mp_obj_type_t machine_sdcard_type; void machine_adc_init(void); void machine_pin_irq_deinit(void); void machine_timer_init_PIT(void); +void machine_sdcard_init0(void); #endif // MICROPY_INCLUDED_MIMXRT_MODMACHINE_H diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index 892d004735..bc8d72224a 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -3,13 +3,32 @@ # Note: the flash requires the programming size to be aligned to 256 bytes. import os +import sys import mimxrt +from machine import Pin bdev = mimxrt.Flash() - try: vfs = os.VfsLfs2(bdev, progsize=256) except: os.VfsLfs2.mkfs(bdev, progsize=256) vfs = os.VfsLfs2(bdev, progsize=256) -os.mount(vfs, "/") +os.mount(vfs, "/flash") +os.chdir("/flash") +sys.path.append("/flash") + +# do not mount the SD card if SKIPSD exists. +try: + os.stat("SKIPSD") +except: + try: + from machine import SDCard + + sdcard = SDCard(1) + + fat = os.VfsFat(sdcard) + os.mount(fat, "/sdcard") + os.chdir("/sdcard") + sys.path.append("/sdcard") + except: + pass # Fail silently diff --git a/ports/mimxrt/moduos.c b/ports/mimxrt/moduos.c index 225cd4ddd2..ff0f0824c4 100644 --- a/ports/mimxrt/moduos.c +++ b/ports/mimxrt/moduos.c @@ -31,6 +31,7 @@ #include "py/objstr.h" #include "py/runtime.h" #include "extmod/vfs.h" +#include "extmod/vfs_fat.h" #include "extmod/vfs_lfs.h" #include "genhdr/mpversion.h" #include "fsl_trng.h" diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index be2b87e6a2..9775b29efa 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -132,6 +132,12 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_FRAMEBUF (1) #define MICROPY_PY_ONEWIRE (1) +// fatfs configuration used in ffconf.h +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ + // 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 diff --git a/ports/mimxrt/pin.h b/ports/mimxrt/pin.h index 8d80490d1c..3520ed0f4f 100644 --- a/ports/mimxrt/pin.h +++ b/ports/mimxrt/pin.h @@ -58,7 +58,8 @@ enum { }; enum { - PIN_AF_MODE_ALT1 = 1, + PIN_AF_MODE_ALT0 = 0, + PIN_AF_MODE_ALT1, PIN_AF_MODE_ALT2, PIN_AF_MODE_ALT3, PIN_AF_MODE_ALT4, @@ -97,7 +98,9 @@ typedef struct { mp_obj_base_t base; qstr name; // port name uint8_t af_mode; // alternate function + uint8_t input_daisy; void *instance; // pointer to peripheral instance for alternate function + uint32_t input_register; uint32_t pad_config; // pad configuration for alternate function } machine_pin_af_obj_t; diff --git a/ports/mimxrt/sdcard.c b/ports/mimxrt/sdcard.c new file mode 100644 index 0000000000..14a78e94a5 --- /dev/null +++ b/ports/mimxrt/sdcard.c @@ -0,0 +1,1023 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * 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. + */ + +#if MICROPY_PY_MACHINE_SDCARD + +#include "sdcard.h" +#include "ticks.h" +#include "fsl_iomuxc.h" + +#define SDCARD_VOLTAGE_WINDOW_SD (0x80100000U) +#define SDCARD_HIGH_CAPACITY (0x40000000U) +#define SDCARD_SWITCH_1_8V_CAPACITY ((uint32_t)0x01000000U) +#define SDCARD_MAX_VOLT_TRIAL ((uint32_t)0x000000FFU) + +// Error +#define SDCARD_STATUS_OUT_OF_RANGE_SHIFT (31U) +#define SDCARD_STATUS_ADDRESS_ERROR_SHIFT (30U) +#define SDCARD_STATUS_BLOCK_LEN_ERROR_SHIFT (29U) +#define SDCARD_STATUS_ERASE_SEQ_ERROR_SHIFT (28U) +#define SDCARD_STATUS_ERASE_PARAM_SHIFT (27U) +#define SDCARD_STATUS_WP_VIOLATION_SHIFT (26U) +#define SDCARD_STATUS_LOCK_UNLOCK_FAILED_SHIFT (24U) +#define SDCARD_STATUS_COM_CRC_ERROR_SHIFT (23U) +#define SDCARD_STATUS_ILLEGAL_COMMAND_SHIFT (22U) +#define SDCARD_STATUS_CARD_ECC_FAILED_SHIFT (21U) +#define SDCARD_STATUS_CC_ERROR_SHIFT (20U) +#define SDCARD_STATUS_ERROR_SHIFT (19U) +#define SDCARD_STATUS_CSD_OVERWRITE_SHIFT (16U) +#define SDCARD_STATUS_WP_ERASE_SKIP_SHIFT (15U) +#define SDCARD_STATUS_AUTH_SEQ_ERR_SHIFT (3U) + +// Status Flags +#define SDCARD_STATUS_CARD_IS_LOCKED_SHIFT (25U) +#define SDCARD_STATUS_CARD_ECC_DISABLED_SHIFT (14U) +#define SDCARD_STATUS_ERASE_RESET_SHIFT (13U) +#define SDCARD_STATUS_READY_FOR_DATA_SHIFT (8U) +#define SDCARD_STATUS_FX_EVENT_SHIFT (6U) +#define SDCARD_STATUS_APP_CMD_SHIFT (5U) + +#define SDMMC_MASK(bit) (1U << (bit)) +#define SDMMC_R1_ALL_ERROR_FLAG \ + (SDMMC_MASK(SDCARD_STATUS_OUT_OF_RANGE_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_ADDRESS_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_BLOCK_LEN_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_ERASE_SEQ_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_ERASE_PARAM_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_WP_VIOLATION_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_LOCK_UNLOCK_FAILED_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_COM_CRC_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_ILLEGAL_COMMAND_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_CARD_ECC_FAILED_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_CC_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_ERROR_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_CSD_OVERWRITE_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_WP_ERASE_SKIP_SHIFT)) | \ + (SDMMC_MASK(SDCARD_STATUS_AUTH_SEQ_ERR_SHIFT)) + +#define SDMMC_R1_CURRENT_STATE(x) (((x) & 0x00001E00U) >> 9U) + +// --- +// SD Card command identifiers +// --- +enum +{ + SDCARD_CMD_GO_IDLE_STATE = 0U, + SDCARD_CMD_ALL_SEND_CID = 2U, + SDCARD_CMD_SEND_REL_ADDR = 3U, + SDCARD_CMD_SET_DSR = 4U, + SDCARD_CMD_SELECT_CARD = 7U, + SDCARD_CMD_SEND_IF_COND = 8U, + SDCARD_CMD_SEND_CSD = 9U, + SDCARD_CMD_SEND_CID = 10U, + SDCARD_CMD_STOP_TRANSMISSION = 12U, + SDCARD_CMD_SEND_STATUS = 13U, + SDCARD_CMD_GO_INACTIVE_STATE = 15U, + SDCARD_CMD_SET_BLOCKLENGTH = 16U, + SDCARD_CMD_READ_SINGLE_BLOCK = 17U, + SDCARD_CMD_READ_MULTIPLE_BLOCK = 18U, + SDCARD_CMD_SET_BLOCK_COUNT = 23U, + SDCARD_CMD_WRITE_SINGLE_BLOCK = 24U, + SDCARD_CMD_WRITE_MULTIPLE_BLOCK = 25U, + SDCARD_CMD_PROGRAM_CSD = 27U, + SDCARD_CMD_SET_WRITE_PROTECT = 28U, + SDCARD_CMD_CLEAR_WRITE_PROTECT = 29U, + SDCARD_CMD_SEND_WRITE_PROTECT = 30U, + SDCARD_CMD_ERASE = 38U, + SDCARD_CMD_LOCK_UNLOCK = 42U, + SDCARD_CMD_APP_CMD = 55U, + SDCARD_CMD_GEN_CMD = 56U, + SDCARD_CMD_READ_OCR = 58U, +}; + +// --- +// SD Card application command identifiers +// --- +enum +{ + SDCARD_ACMD_SET_BUS_WIDTH = 6U, + SDCARD_ACMD_SD_SEND_OP_COND = 41U, +}; + +// --- +// SD Card state identifiers +// --- +enum +{ + SDCARD_STATE_IDLE = 0U, + SDCARD_STATE_READY = 1U, + SDCARD_STATE_IDENTIFY = 2U, + SDCARD_STATE_STANDBY = 3U, + SDCARD_STATE_TRANSFER = 4U, + SDCARD_STATE_SENDDATA = 5U, + SDCARD_STATE_RECEIVEDATA = 6U, + SDCARD_STATE_PROGRAM = 7U, + SDCARD_STATE_DISCONNECT = 8U, +}; + +// --- +// SD Card transfer status +// --- +typedef enum +{ + SDCARD_TRANSFER_SUCCESS = 0, + SDCARD_TRANSFER_ERROR, + SDCARD_TRANSFER_PENDING +} sdcard_transfer_status_t; + +// --- +// SD Card type definitions +// --- +typedef struct _cid_t { + uint8_t reserved_0; + uint16_t mdt : 12; + uint16_t reserved_1 : 4; + uint32_t psn; + uint8_t prv; + char pnm[6]; + uint16_t oid; + uint8_t mid; +} __attribute__((packed)) cid_t; + +typedef struct _csd_t { + uint32_t data[4]; +} __attribute__((packed)) csd_t; + +typedef struct _csr_t { + uint32_t data[2]; +} __attribute__((packed)) csr_t; + +#define DMA_DESCRIPTOR_BUFFER_SIZE (32U) +#define DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE (4U) +AT_NONCACHEABLE_SECTION_ALIGN(static uint32_t sdcard_adma_descriptor_table[DMA_DESCRIPTOR_BUFFER_SIZE], DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE); + +#if defined MICROPY_USDHC1 && USDHC1_AVAIL +const mimxrt_sdcard_obj_pins_t mimxrt_sdcard_1_obj_pins = MICROPY_USDHC1; +#endif + +#if defined MICROPY_USDHC2 && USDHC2_AVAIL +const mimxrt_sdcard_obj_pins_t mimxrt_sdcard_2_obj_pins = MICROPY_USDHC2; +#endif + +#if defined MICROPY_USDHC1 && USDHC1_AVAIL +mimxrt_sdcard_status_obj_t sdcard_usdhc1_state = {.initialized = false, .inserted = false}; +#endif +#if defined MICROPY_USDHC2 && USDHC2_AVAIL +mimxrt_sdcard_status_obj_t sdcard_usdhc2_state = {.initialized = false, .inserted = false}; +#endif + +mimxrt_sdcard_obj_t mimxrt_sdcard_objs[] = +{ + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + { + .base.type = &machine_sdcard_type, + .usdhc_inst = USDHC1, + .state = &sdcard_usdhc1_state, + .rca = 0x0UL, + .block_len = SDCARD_DEFAULT_BLOCK_SIZE, + .block_count = 0UL, + .pins = &mimxrt_sdcard_1_obj_pins, + }, + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + { + .base.type = &machine_sdcard_type, + .usdhc_inst = USDHC2, + .state = &sdcard_usdhc2_state, + .rca = 0x0UL, + .block_len = SDCARD_DEFAULT_BLOCK_SIZE, + .block_count = 0UL, + .pins = &mimxrt_sdcard_2_obj_pins, + }; + #endif +}; + +volatile status_t sdcard_transfer_status; +volatile bool sdcard_transfer_done; + +// --- +// Local function declarations +// --- +static status_t sdcard_transfer_blocking(USDHC_Type *base, + usdhc_handle_t *handle, + usdhc_transfer_t *transfer, + uint32_t timeout_ms); +static void sdcard_decode_csd(mimxrt_sdcard_obj_t *sdcard, csd_t *csd); +static bool sdcard_reset(mimxrt_sdcard_obj_t *card); +static inline void sdcard_init_pin(const machine_pin_obj_t *pin, uint8_t af_idx, uint32_t config_value); + +// SD Card interrupt callbacks +void sdcard_card_inserted_callback(USDHC_Type *base, void *userData); +void sdcard_card_removed_callback(USDHC_Type *base, void *userData); +void sdcard_transfer_complete_callback(USDHC_Type *base, usdhc_handle_t *handle, status_t status, void *userData); +void sdcard_dummy_callback(USDHC_Type *base, void *userData); + +// SD Card commmands +static bool sdcard_cmd_go_idle_state(mimxrt_sdcard_obj_t *card); +static bool sdcard_cmd_oper_cond(mimxrt_sdcard_obj_t *card); +static bool sdcard_cmd_app_cmd(mimxrt_sdcard_obj_t *card); +static bool sdcard_cmd_sd_app_op_cond(mimxrt_sdcard_obj_t *card, uint32_t argument); +static bool sdcard_cmd_all_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid); +static bool sdcard_cmd_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid); +static bool sdcard_cmd_set_rel_add(mimxrt_sdcard_obj_t *card); +static bool sdcard_cmd_send_csd(mimxrt_sdcard_obj_t *card, csd_t *csd); +static bool sdcard_cmd_select_card(mimxrt_sdcard_obj_t *sdcard); +static bool sdcard_cmd_set_blocklen(mimxrt_sdcard_obj_t *sdcard); +static bool sdcard_cmd_set_bus_width(mimxrt_sdcard_obj_t *sdcard, uint8_t bus_width); + +void sdcard_card_inserted_callback(USDHC_Type *base, void *userData) { + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + if (base == USDHC1) { + sdcard_usdhc1_state.inserted = true; + } + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + if (base == USDHC2) { + sdcard_usdhc2_state.inserted = true; + + } + #endif + + USDHC_ClearInterruptStatusFlags(base, kUSDHC_CardInsertionFlag); +} + +void sdcard_card_removed_callback(USDHC_Type *base, void *userData) { + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + if (base == USDHC1) { + sdcard_usdhc1_state.inserted = false; + sdcard_usdhc1_state.initialized = false; + } + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + if (base == USDHC2) { + sdcard_usdhc2_state.inserted = false; + sdcard_usdhc2_state.initialized = false; + } + #endif + + USDHC_ClearInterruptStatusFlags(base, kUSDHC_CardRemovalFlag); +} + +void sdcard_transfer_complete_callback(USDHC_Type *base, usdhc_handle_t *handle, status_t status, void *userData) { + sdcard_transfer_status = status; + sdcard_transfer_done = true; + USDHC_ClearInterruptStatusFlags(base, kUSDHC_CommandCompleteFlag | kUSDHC_DataCompleteFlag); +} + +void sdcard_dummy_callback(USDHC_Type *base, void *userData) { + return; +} + +static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_handle_t *handle, usdhc_transfer_t *transfer, uint32_t timeout_ms) { + uint32_t retry_ctr = 0UL; + status_t status; + + usdhc_adma_config_t dma_config; + + (void)memset(&dma_config, 0, sizeof(usdhc_adma_config_t)); + dma_config.dmaMode = kUSDHC_DmaModeAdma2; + dma_config.burstLen = kUSDHC_EnBurstLenForINCR; + dma_config.admaTable = sdcard_adma_descriptor_table; + dma_config.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE; + + do { + status = USDHC_TransferNonBlocking(base, handle, &dma_config, transfer); + retry_ctr++; + } while (!(status == kStatus_Success) && (retry_ctr < 1000000UL)); + + if (status == kStatus_Success) { + for (int i = 0; i < timeout_ms * 100; i++) { + if ((sdcard_transfer_done == true) && (sdcard_transfer_status == kStatus_Success)) { + sdcard_transfer_done = false; + return kStatus_Success; + } + ticks_delay_us64(10); + } + return kStatus_Timeout; + } else { + return status; + } +} + +static void sdcard_decode_csd(mimxrt_sdcard_obj_t *card, csd_t *csd) { + uint8_t csd_structure = 0x3 & (csd->data[3] >> 30); + uint8_t read_bl_len; + uint32_t c_size; + uint8_t c_size_mult; + + switch (csd_structure) + { + case 0: { + read_bl_len = 0xF & (csd->data[2] >> 16); + c_size = ((0x3FF & csd->data[2]) << 30) | (0x3 & (csd->data[1] >> 30)); + c_size_mult = 0x7 & (csd->data[1] >> 15); + + card->block_len = (1U << (read_bl_len)); + card->block_count = ((c_size + 1U) << (c_size_mult + 2U)); + + if (card->block_len != SDCARD_DEFAULT_BLOCK_SIZE) { + card->block_count = (card->block_count * card->block_len); + card->block_len = SDCARD_DEFAULT_BLOCK_SIZE; + card->block_count = (card->block_count / card->block_len); + } + break; + } + case 1: { + c_size = ((0x3F & csd->data[2]) << 16) | (0xFFFF & (csd->data[1] >> 16)); + + card->block_len = 512UL; + card->block_count = (uint32_t)(((uint64_t)(c_size + 1U) * (uint64_t)1024UL)); + break; + } + case 2: { + c_size = ((0xFF & csd->data[2]) << 16) | (0xFFFF & (csd->data[1] >> 16)); + + card->block_len = 512UL; + card->block_count = (uint32_t)(((uint64_t)(c_size + 1U) * (uint64_t)1024UL)); + break; + } + default: { + break; + } + } +} + +static bool sdcard_cmd_go_idle_state(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_GO_IDLE_STATE, + .argument = 0UL, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeNone, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_oper_cond(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SEND_IF_COND, + .argument = 0x000001AAU, // 2.7-3.3V range and 0xAA check pattern + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR7, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->oper_cond = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_app_cmd(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_APP_CMD, + .argument = (card->rca << 16), + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_sd_app_op_cond(mimxrt_sdcard_obj_t *card, uint32_t argument) { + if (!sdcard_cmd_app_cmd(card)) { + return false; + } + + status_t status; + usdhc_command_t command = { + .index = SDCARD_ACMD_SD_SEND_OP_COND, + .argument = argument, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR3, + }; + + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->oper_cond = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_all_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_ALL_SEND_CID, + .argument = 0UL, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR2, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + cid->mdt = (uint16_t)((command.response[0] & 0xFFF00U) >> 8U); + cid->psn = (uint32_t)(((command.response[1] & 0xFFFFFFU) << 8U) | ((command.response[0] & 0xFF000000U) >> 24U)); + cid->prv = (uint8_t)((command.response[1] & 0xFF000000U) >> 24U); + cid->pnm[0] = (char)(command.response[2] & 0xFFU); + cid->pnm[1] = (char)((command.response[2] & 0xFF00U) >> 8U); + cid->pnm[2] = (char)((command.response[2] & 0xFF0000U) >> 16U); + cid->pnm[3] = (char)((command.response[2] & 0xFF000000U) >> 24U); + cid->pnm[4] = (char)(command.response[3] & 0xFFU); + cid->pnm[5] = '\0'; + cid->oid = (uint16_t)((command.response[3] & 0xFFFF00U) >> 8U); + cid->mid = (uint8_t)((command.response[3] & 0xFF000000U) >> 24U); + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_send_cid(mimxrt_sdcard_obj_t *card, cid_t *cid) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SEND_CID, + .argument = (card->rca << 16), + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR2, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + cid->mdt = (uint16_t)((command.response[0] & 0xFFF00U) >> 8U); + cid->psn = (uint32_t)(((command.response[1] & 0xFFFFFFU) << 8U) | ((command.response[0] & 0xFF000000U) >> 24U)); + cid->prv = (uint8_t)((command.response[1] & 0xFF000000U) >> 24U); + cid->pnm[0] = (char)(command.response[2] & 0xFFU); + cid->pnm[1] = (char)((command.response[2] & 0xFF00U) >> 8U); + cid->pnm[2] = (char)((command.response[2] & 0xFF0000U) >> 16U); + cid->pnm[3] = (char)((command.response[2] & 0xFF000000U) >> 24U); + cid->pnm[4] = (char)(command.response[3] & 0xFFU); + cid->pnm[5] = '\0'; + cid->oid = (uint16_t)((command.response[3] & 0xFFFF00U) >> 8U); + cid->mid = (uint8_t)((command.response[3] & 0xFF000000U) >> 24U); + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_set_rel_add(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SEND_REL_ADDR, + .argument = 0UL, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR6, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->rca = 0xFFFFFFFF & (command.response[0] >> 16); + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_send_csd(mimxrt_sdcard_obj_t *card, csd_t *csd) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SEND_CSD, + .argument = (card->rca << 16), + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR2, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + csd->data[0] = command.response[0]; + csd->data[1] = command.response[1]; + csd->data[2] = command.response[2]; + csd->data[3] = command.response[3]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_select_card(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SELECT_CARD, + .argument = (card->rca << 16), + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1b, + .responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_set_blocklen(mimxrt_sdcard_obj_t *card) { + status_t status; + usdhc_command_t command = { + .index = SDCARD_CMD_SET_BLOCKLENGTH, + .argument = card->block_len, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1, + .responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG, + }; + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_cmd_set_bus_width(mimxrt_sdcard_obj_t *card, usdhc_data_bus_width_t bus_width) { + if (!sdcard_cmd_app_cmd(card)) { + return false; + } + + status_t status; + usdhc_command_t command = { + .index = SDCARD_ACMD_SET_BUS_WIDTH, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1, + }; + + if (bus_width == kUSDHC_DataBusWidth1Bit) { + command.argument = 0U; + } else if (bus_width == kUSDHC_DataBusWidth4Bit) { + command.argument = 2U; + } else { + return false; // Invalid argument + } + + usdhc_transfer_t transfer = { + .data = NULL, + .command = &command, + }; + + status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 250); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +static bool sdcard_reset(mimxrt_sdcard_obj_t *card) { + card->block_len = SDCARD_DEFAULT_BLOCK_SIZE; + card->rca = 0UL; + card->block_count = 0UL; + card->status = 0UL; + card->oper_cond = 0UL; + card->state->initialized = false; + return USDHC_Reset(card->usdhc_inst, (USDHC_SYS_CTRL_RSTA_MASK | USDHC_SYS_CTRL_RSTC_MASK | USDHC_SYS_CTRL_RSTD_MASK), 2048); +} + +void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk) { + // Configure PFD0 of PLL2 (system PLL) fractional divider to 24 resulting in: + // with PFD0_clk = PLL2_clk * 18 / N + // PFD0_clk = 528MHz * 18 / 24 = 396MHz + CLOCK_InitSysPfd(kCLOCK_Pfd0, 24U); + + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + // Configure USDHC clock source and divider + CLOCK_SetDiv(kCLOCK_Usdhc1Div, 1U); // USDHC_input_clk = PFD0_clk / 2 = 198MHZ + CLOCK_SetMux(kCLOCK_Usdhc1Mux, 1U); // Select PFD0 as clock input for USDHC + #endif + + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + // Configure USDHC clock source and divider + CLOCK_SetDiv(kCLOCK_Usdhc2Div, 1U); // USDHC_input_clk = PFD0_clk / 2 = 198MHZ + CLOCK_SetMux(kCLOCK_Usdhc2Mux, 1U); // Select PFD0 as clock input for USDHC + #endif + + // Initialize USDHC + const usdhc_config_t config = { + .endianMode = kUSDHC_EndianModeLittle, + .dataTimeout = 0xFU, + .readWatermarkLevel = 128U, + .writeWatermarkLevel = 128U, + }; + USDHC_Init(card->usdhc_inst, &config); + + (void)sdcard_reset(card); + card->base_clk = base_clk; + + usdhc_transfer_callback_t callbacks = { + .CardInserted = sdcard_card_inserted_callback, + .CardRemoved = sdcard_card_removed_callback, + .SdioInterrupt = sdcard_dummy_callback, + .BlockGap = sdcard_dummy_callback, + .TransferComplete = sdcard_transfer_complete_callback, + .ReTuning = sdcard_dummy_callback, + }; + + USDHC_TransferCreateHandle(card->usdhc_inst, &card->handle, &callbacks, NULL); +} + +void sdcard_deinit(mimxrt_sdcard_obj_t *card) { + sdcard_power_off(card); + USDHC_Deinit(card->usdhc_inst); +} + +static inline void sdcard_init_pin(const machine_pin_obj_t *pin, uint8_t af_idx, uint32_t config_value) { + machine_pin_af_obj_t af = pin->af_list[af_idx]; + + IOMUXC_SetPinMux(pin->muxRegister, af.af_mode, af.input_register, af.input_daisy, pin->configRegister, 0U); + IOMUXC_SetPinConfig(pin->muxRegister, af.af_mode, af.input_register, af.input_daisy, pin->configRegister, config_value); +} + +void sdcard_init_pins(mimxrt_sdcard_obj_t *card) { + // speed and strength optimized for clock frequency < 100MHz + uint32_t speed = 1U; + uint32_t strength = 7U; + const mimxrt_sdcard_obj_pins_t *pins = card->pins; + + uint32_t default_config = IOMUXC_SW_PAD_CTL_PAD_SPEED(speed) | + IOMUXC_SW_PAD_CTL_PAD_SRE_MASK | + IOMUXC_SW_PAD_CTL_PAD_PKE_MASK | + IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | + IOMUXC_SW_PAD_CTL_PAD_HYS_MASK | + IOMUXC_SW_PAD_CTL_PAD_PUS(1) | + IOMUXC_SW_PAD_CTL_PAD_DSE(strength); + uint32_t no_cd_config = IOMUXC_SW_PAD_CTL_PAD_SPEED(speed) | + IOMUXC_SW_PAD_CTL_PAD_SRE_MASK | + IOMUXC_SW_PAD_CTL_PAD_PKE_MASK | + IOMUXC_SW_PAD_CTL_PAD_PUE_MASK | + IOMUXC_SW_PAD_CTL_PAD_HYS_MASK | + IOMUXC_SW_PAD_CTL_PAD_PUS(0) | + IOMUXC_SW_PAD_CTL_PAD_DSE(strength); + + sdcard_init_pin(card->pins->clk.pin, card->pins->clk.af_idx, default_config); // USDHC1_CLK + sdcard_init_pin(card->pins->cmd.pin, card->pins->cmd.af_idx, default_config); // USDHC1_CMD + sdcard_init_pin(card->pins->data0.pin, card->pins->data0.af_idx, default_config); // USDHC1_DATA0 + sdcard_init_pin(card->pins->data1.pin, card->pins->data1.af_idx, default_config); // USDHC1_DATA1 + sdcard_init_pin(card->pins->data2.pin, card->pins->data2.af_idx, default_config); // USDHC1_DATA2 + + if (pins->cd_b.pin) { + sdcard_init_pin(card->pins->data3.pin, card->pins->data3.af_idx, default_config); // USDHC1_DATA3 + sdcard_init_pin(card->pins->cd_b.pin, card->pins->cd_b.af_idx, default_config); // USDHC1_CD_B + USDHC_CardDetectByData3(card->usdhc_inst, false); + } else { + sdcard_init_pin(card->pins->data3.pin, card->pins->data3.af_idx, no_cd_config); // USDHC1_DATA3 + USDHC_CardDetectByData3(card->usdhc_inst, true); + } +} + +bool sdcard_read(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num, uint32_t block_count) { + if (!card->state->initialized) { + return false; + } + + usdhc_data_t data = { + .enableAutoCommand12 = true, + .enableAutoCommand23 = false, + .enableIgnoreError = false, + .dataType = kUSDHC_TransferDataNormal, + .blockSize = card->block_len, + .blockCount = block_count, + .rxData = (uint32_t *)buffer, + .txData = NULL, + }; + + usdhc_command_t command = { + .index = (block_count == 1U) ? (uint32_t)SDCARD_CMD_READ_SINGLE_BLOCK : (uint32_t)SDCARD_CMD_READ_MULTIPLE_BLOCK, + .argument = block_num, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1, + .responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG, + }; + + usdhc_transfer_t transfer = { + .data = &data, + .command = &command, + }; + + status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 500); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +bool sdcard_write(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num, uint32_t block_count) { + if (!card->state->initialized) { + return false; + } + + usdhc_data_t data = { + .enableAutoCommand12 = true, + .enableAutoCommand23 = false, + .enableIgnoreError = false, + .dataType = kUSDHC_TransferDataNormal, + .blockSize = card->block_len, + .blockCount = block_count, + .rxData = NULL, + .txData = (uint32_t *)buffer, + }; + + usdhc_command_t command = { + .index = (block_count == 1U) ? (uint32_t)SDCARD_CMD_WRITE_SINGLE_BLOCK : (uint32_t)SDCARD_CMD_WRITE_MULTIPLE_BLOCK, + .argument = block_num, + .type = kCARD_CommandTypeNormal, + .responseType = kCARD_ResponseTypeR1, + .responseErrorFlags = SDMMC_R1_ALL_ERROR_FLAG, + }; + + usdhc_transfer_t transfer = { + .data = &data, + .command = &command, + }; + + status_t status = sdcard_transfer_blocking(card->usdhc_inst, &card->handle, &transfer, 500); + + if (status == kStatus_Success) { + card->status = command.response[0]; + return true; + } else { + return false; + } +} + +bool sdcard_set_active(mimxrt_sdcard_obj_t *card) { + return USDHC_SetCardActive(card->usdhc_inst, 8192); +} + +bool sdcard_probe_bus_voltage(mimxrt_sdcard_obj_t *card) { + bool valid_voltage = false; + uint32_t count = 0UL; + bool status = false; + + // Perform voltage validation + while ((count < SDCARD_MAX_VOLT_TRIAL) && (valid_voltage == false)) { + status = sdcard_cmd_sd_app_op_cond(card, (uint32_t)(SDCARD_VOLTAGE_WINDOW_SD | + SDCARD_HIGH_CAPACITY | + SDCARD_SWITCH_1_8V_CAPACITY)); + if (status == false) { + return false; + } + + /* Get operating voltage*/ + valid_voltage = (((card->oper_cond >> 31U) == 1U) ? true : false); + count++; + ticks_delay_us64(1000); + } + + if (count >= SDCARD_MAX_VOLT_TRIAL) { + return false; + } else { + return true; + } +} + +bool sdcard_power_on(mimxrt_sdcard_obj_t *card) { + bool status = false; + + // Check if card is already initialized and powered on + if (card->state->initialized) { + return true; + } + + USDHC_SetDataBusWidth(card->usdhc_inst, kUSDHC_DataBusWidth1Bit); + card->bus_clk = USDHC_SetSdClock(card->usdhc_inst, card->base_clk, SDCARD_CLOCK_400KHZ); + + // Start initialization process + status = sdcard_reset(card); + if (!status) { + return false; + } + + status = sdcard_set_active(card); + if (!status) { + return false; + } + + status = sdcard_cmd_go_idle_state(card); + if (!status) { + return false; + } + + status = sdcard_cmd_oper_cond(card); + if (!status) { + return false; + } + + status = sdcard_probe_bus_voltage(card); + if (!status) { + return false; + } + + // === + // Ready State + // === + cid_t cid_all; + status = sdcard_cmd_all_send_cid(card, &cid_all); + if (!status) { + return false; + } + + // === + // Identification State + // === + status = sdcard_cmd_set_rel_add(card); + if (!status) { + return false; + } + + // === + // Standby State + // === + card->bus_clk = USDHC_SetSdClock(card->usdhc_inst, card->base_clk, SDCARD_CLOCK_50MHZ); + + csd_t csd; + status = sdcard_cmd_send_csd(card, &csd); + if (!status) { + return false; + } + sdcard_decode_csd(card, &csd); + + cid_t cid; + status = sdcard_cmd_send_cid(card, &cid); + if (!status) { + return false; + } + + // === + // Transfer State + // === + status = sdcard_cmd_select_card(card); + if (!status) { + return false; + } + + status = sdcard_cmd_set_blocklen(card); + if (!status) { + return false; + } + + status = sdcard_cmd_set_bus_width(card, kUSDHC_DataBusWidth4Bit); + if (!status) { + return false; + } + USDHC_SetDataBusWidth(card->usdhc_inst, kUSDHC_DataBusWidth4Bit); + + + status = sdcard_cmd_set_blocklen(card); + if (!status) { + return false; + } + + // Finialize initialization + card->state->initialized = true; + return true; +} + +bool sdcard_power_off(mimxrt_sdcard_obj_t *card) { + (void)sdcard_cmd_go_idle_state(card); + + // Reset card bus clock + USDHC_SetDataBusWidth(card->usdhc_inst, kUSDHC_DataBusWidth1Bit); + card->bus_clk = USDHC_SetSdClock(card->usdhc_inst, card->base_clk, SDCARD_CLOCK_400KHZ); + + (void)sdcard_reset(card); + return true; +} + +bool sdcard_detect(mimxrt_sdcard_obj_t *card) { + bool detect = false; + + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + if ((card->usdhc_inst == USDHC1) && (sdcard_usdhc1_state.inserted == true)) { + return true; + } + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + if ((card->usdhc_inst == USDHC2) && (sdcard_usdhc2_state.inserted == true)) { + return true; + } + #endif + + if (card->pins->cd_b.pin) { + detect = USDHC_DetectCardInsert(card->usdhc_inst); + } else { + USDHC_CardDetectByData3(card->usdhc_inst, true); + detect = (USDHC_GetPresentStatusFlags(card->usdhc_inst) & USDHC_PRES_STATE_DLSL(8)) != 0; + } + + // Update card state when detected via pin state + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + if (card->usdhc_inst == USDHC1) { + sdcard_usdhc1_state.inserted = detect; + sdcard_usdhc1_state.initialized = detect ? sdcard_usdhc1_state.initialized : false; + } + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + if (card->usdhc_inst == USDHC2) { + sdcard_usdhc2_state.inserted = detect; + sdcard_usdhc2_state.initialized = detect ? sdcard_usdhc1_state.initialized : false; + } + #endif + + return detect; +} + +#endif // MICROPY_PY_MACHINE_SDCARD diff --git a/ports/mimxrt/sdcard.h b/ports/mimxrt/sdcard.h new file mode 100644 index 0000000000..9219a42306 --- /dev/null +++ b/ports/mimxrt/sdcard.h @@ -0,0 +1,111 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * 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_SDCARD_H +#define MICROPY_INCLUDED_MIMXRT_SDCARD_H + +#include "modmachine.h" +#include "pin.h" +#include "fsl_usdhc.h" + +// --- +// SD Card public defines +// --- +#define SDCARD_DEFAULT_BLOCK_SIZE (512U) +#define SDCARD_CLOCK_400KHZ (400000U) +#define SDCARD_CLOCK_25MHZ (25000000U) +#define SDCARD_CLOCK_50MHZ (50000000U) + +typedef struct _mimxrt_sdcard_pin_t { + const machine_pin_obj_t *pin; + uint8_t af_idx; +} mimxrt_sdcard_pin_t; + +typedef struct _mimxrt_sdcard_obj_pins_t { + mimxrt_sdcard_pin_t cmd; + mimxrt_sdcard_pin_t clk; + mimxrt_sdcard_pin_t cd_b; + mimxrt_sdcard_pin_t data0; + mimxrt_sdcard_pin_t data1; + mimxrt_sdcard_pin_t data2; + mimxrt_sdcard_pin_t data3; +} mimxrt_sdcard_obj_pins_t; + +typedef volatile struct _mimxrt_sdcard_status_obj_t { + bool initialized; + bool inserted; +} mimxrt_sdcard_status_obj_t; + +typedef struct _mimxrt_sdcard_obj_t { + mp_obj_base_t base; + USDHC_Type *usdhc_inst; + usdhc_handle_t handle; + mimxrt_sdcard_status_obj_t *state; + uint16_t block_len; + uint32_t base_clk; + uint32_t bus_clk; + uint32_t rca; + uint32_t block_count; + uint32_t status; + uint32_t oper_cond; + const mimxrt_sdcard_obj_pins_t *pins; +} mimxrt_sdcard_obj_t; + +// --- +// SD Card object instances +// --- +#if MICROPY_PY_MACHINE_SDCARD && (defined MICROPY_USDHC1 || defined MICROPY_USDHC2) +enum { + #if defined MICROPY_USDHC1 && USDHC1_AVAIL + SDCARD_OBJ_USDHC1_IDX, + #endif + #if defined MICROPY_USDHC2 && USDHC2_AVAIL + SDCARD_OBJ_USDHC2_IDX, + #endif + SDCARD_OBJ_USDHC_N +}; +#endif +extern mimxrt_sdcard_obj_t mimxrt_sdcard_objs[SDCARD_OBJ_USDHC_N]; + +// --- +// SD Card functions +// --- +void sdcard_init(mimxrt_sdcard_obj_t *card, uint32_t base_clk); +void sdcard_deinit(mimxrt_sdcard_obj_t *card); +void sdcard_init_pins(mimxrt_sdcard_obj_t *card); +bool sdcard_read(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num, uint32_t block_count); +bool sdcard_write(mimxrt_sdcard_obj_t *card, uint8_t *buffer, uint32_t block_num, uint32_t block_count); +bool sdcard_set_active(mimxrt_sdcard_obj_t *card); +bool sdcard_volt_validation(mimxrt_sdcard_obj_t *card); +bool sdcard_power_on(mimxrt_sdcard_obj_t *self); +bool sdcard_power_off(mimxrt_sdcard_obj_t *self); +bool sdcard_detect(mimxrt_sdcard_obj_t *self); + +static inline bool sdcard_state_initialized(mimxrt_sdcard_obj_t *card) { + return card->state->initialized; +} + +#endif // MICROPY_INCLUDED_MIMXRT_SDCARD_H