summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Ebensberger <philipp.ebensberger@3bricks-software.de>2021-08-01 11:20:39 +0200
committerPhilipp Ebensberger <philipp.ebensberger@3bricks-software.de>2021-09-07 20:45:33 +0200
commit87f97e490c4fe12ab5f06e154dc76f0dd27091a9 (patch)
treee79de3516334ac7a5028ad63ef39b3e8482ff313
parentbbbdef4cc1d2cb3a3f6c2d067b6988534773b2a6 (diff)
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
-rw-r--r--ports/mimxrt/Makefile77
-rw-r--r--ports/mimxrt/board_init.c6
-rw-r--r--ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk1
-rw-r--r--ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h12
-rw-r--r--ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk6
-rw-r--r--ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h12
-rw-r--r--ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk7
-rw-r--r--ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.h1
-rw-r--r--ports/mimxrt/boards/MIMXRT1050_EVKB/mpconfigboard.mk7
-rw-r--r--ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.h12
-rw-r--r--ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk6
-rw-r--r--ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.h12
-rw-r--r--ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk7
-rw-r--r--ports/mimxrt/boards/TEENSY40/mpconfigboard.h12
-rw-r--r--ports/mimxrt/boards/TEENSY40/mpconfigboard.mk7
-rw-r--r--ports/mimxrt/boards/TEENSY40/pins.csv10
-rw-r--r--ports/mimxrt/boards/TEENSY41/mpconfigboard.h12
-rwxr-xr-xports/mimxrt/boards/TEENSY41/mpconfigboard.mk7
-rw-r--r--ports/mimxrt/boards/make-pins.py88
-rw-r--r--ports/mimxrt/boards/mimxrt_prefix.c5
-rw-r--r--ports/mimxrt/fatfs_port.c39
-rw-r--r--ports/mimxrt/machine_sdcard.c222
-rw-r--r--ports/mimxrt/modmachine.c3
-rw-r--r--ports/mimxrt/modmachine.h2
-rw-r--r--ports/mimxrt/modules/_boot.py23
-rw-r--r--ports/mimxrt/moduos.c1
-rw-r--r--ports/mimxrt/mpconfigport.h6
-rw-r--r--ports/mimxrt/pin.h5
-rw-r--r--ports/mimxrt/sdcard.c1023
-rw-r--r--ports/mimxrt/sdcard.h111
30 files changed, 1672 insertions, 70 deletions
diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile
index 818a2a3c8..58fbe2df3 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 65024ff3e..091e23eb5 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 ccc8ffeb4..f616d5afd 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 d2a2cbbdb..d7fe575ab 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 f8f66b0df..6dd168652 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 af0975bc1..976de9c07 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 61826b6f6..fdbf47f01 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 518240b03..963c42cb6 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 c9e32612c..e6cd1a63e 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 c207da832..c26364f26 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 f5eaf3eab..623e2617f 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 59f28b0e2..4534caa29 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 444fe9967..fe3c442fa 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 aacee4623..4a69303b5 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 a15dd7812..bd70fd092 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 c9d7c9856..0ea4f1373 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 a72bd127b..587bf9c62 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 a15dd7812..bd70fd092 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 e9e32a504..d75592124 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_(?P<pin>GPIO_SD_B\d_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_AD_B\d_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_EMC_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_B\d_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_AD_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\w+)",
+ r"IOMUXC_(?P<pin>GPIO_SD_\d\d)_(?P<function>\w+) (?P<muxRegister>\w+), (?P<muxMode>\w+), (?P<inputRegister>\w+), (?P<inputDaisy>\w+), (?P<configRegister>\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(
@@ -250,6 +317,13 @@ def main():
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",
dest="board_filename",
@@ -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 96ba6a0d5..938efc810 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 000000000..7b7008667
--- /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 000000000..4a92aae00
--- /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 150615ca0..a98d09bd0 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 9732093bd..e1a7ce0b7 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 892d00473..bc8d72224 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 225cd4ddd..ff0f0824c 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 be2b87e6a..9775b29ef 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 8d80490d1..3520ed0f4 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 000000000..14a78e94a
--- /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 000000000..9219a4230
--- /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