summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Shawcroft <scott@adafruit.com>2020-06-29 16:49:58 -0700
committerGitHub <noreply@github.com>2020-06-29 16:49:58 -0700
commit111f7ddf695acbebeca7d33fe54926827b3fe5f3 (patch)
tree9e4afb502d29b780a0a4464176cad26f67000b86
parent7750eb0dcc3ae79b18d092b4f65ddfa76e9df2c8 (diff)
parent472a04ba4bd107575b42cab102e4849d6943779e (diff)
Merge pull request #2863 from jepler/sdcard-in-core
Sdcard in core
-rw-r--r--locale/circuitpython.pot51
-rw-r--r--ports/atmel-samd/Makefile15
m---------ports/atmel-samd/asf40
-rw-r--r--ports/atmel-samd/asf4_conf/samd51/hpl_sdhc_config.h24
-rw-r--r--ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h164
-rw-r--r--ports/atmel-samd/asf4_conf/same54/hpl_sdhc_config.h24
-rw-r--r--ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h164
-rw-r--r--ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk2
-rw-r--r--ports/atmel-samd/boards/grandcentral_m4_express/pins.c17
-rw-r--r--ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk3
-rw-r--r--ports/atmel-samd/boards/same54_xplained/pins.c17
-rw-r--r--ports/atmel-samd/common-hal/sdioio/SDCard.c272
-rw-r--r--ports/atmel-samd/common-hal/sdioio/SDCard.h40
-rw-r--r--ports/atmel-samd/common-hal/sdioio/__init__.c0
-rw-r--r--ports/atmel-samd/common-hal/sdioio/__init__.h0
-rw-r--r--ports/atmel-samd/mpconfigport.mk2
-rw-r--r--ports/atmel-samd/sd_mmc/conf_sd_mmc.h79
-rw-r--r--ports/atmel-samd/sd_mmc/sd_mmc.c1671
-rw-r--r--ports/atmel-samd/sd_mmc/sd_mmc.h310
-rw-r--r--ports/atmel-samd/sd_mmc/sd_mmc_protocol.h1001
-rw-r--r--ports/litex/mpconfigport.mk3
-rw-r--r--ports/nrf/boards/pca10100/mpconfigboard.mk1
-rw-r--r--py/circuitpy_defns.mk10
-rw-r--r--py/circuitpy_mpconfig.h16
-rw-r--r--py/circuitpy_mpconfig.mk6
-rw-r--r--shared-bindings/busio/SPI.c7
-rw-r--r--shared-bindings/busio/SPI.h2
-rw-r--r--shared-bindings/microcontroller/Pin.c12
-rw-r--r--shared-bindings/microcontroller/Pin.h1
-rw-r--r--shared-bindings/rgbmatrix/RGBMatrix.c11
-rw-r--r--shared-bindings/sdcardio/SDCard.c183
-rw-r--r--shared-bindings/sdcardio/SDCard.h30
-rw-r--r--shared-bindings/sdcardio/__init__.c47
-rw-r--r--shared-bindings/sdcardio/__init__.h0
-rw-r--r--shared-bindings/sdioio/SDCard.c296
-rw-r--r--shared-bindings/sdioio/SDCard.h66
-rw-r--r--shared-bindings/sdioio/__init__.c47
-rw-r--r--shared-bindings/sdioio/__init__.h0
-rw-r--r--shared-module/sdcardio/SDCard.c466
-rw-r--r--shared-module/sdcardio/SDCard.h51
-rw-r--r--shared-module/sdcardio/__init__.c0
-rw-r--r--shared-module/sdcardio/__init__.h0
-rwxr-xr-xtools/mksdiodata.py40
43 files changed, 5139 insertions, 12 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot
index 1582b746c..3a3b159a0 100644
--- a/locale/circuitpython.pot
+++ b/locale/circuitpython.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-06-25 11:44-0500\n"
+"POT-Creation-Date: 2020-06-26 11:50-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -58,6 +58,10 @@ msgstr ""
msgid "%d address pins and %d rgb pins indicate a height of %d, not %d"
msgstr ""
+#: ports/atmel-samd/common-hal/sdioio/SDCard.c
+msgid "%q failure: %d"
+msgstr ""
+
#: shared-bindings/microcontroller/Pin.c
msgid "%q in use"
msgstr ""
@@ -85,6 +89,10 @@ msgstr ""
msgid "%q must be a tuple of length 2"
msgstr ""
+#: ports/atmel-samd/common-hal/sdioio/SDCard.c
+msgid "%q pin invalid"
+msgstr ""
+
#: shared-bindings/fontio/BuiltinFont.c
msgid "%q should be an int"
msgstr ""
@@ -338,7 +346,7 @@ msgstr ""
msgid "Array values should be single bytes."
msgstr ""
-#: shared-bindings/rgbmatrix/RGBMatrix.c
+#: shared-bindings/microcontroller/Pin.c
msgid "At most %d %q may be specified (not %d)"
msgstr ""
@@ -417,6 +425,10 @@ msgstr ""
msgid "Buffer length %d too big. It must be less than %d"
msgstr ""
+#: ports/atmel-samd/common-hal/sdioio/SDCard.c shared-module/sdcardio/SDCard.c
+msgid "Buffer length must be a multiple of 512"
+msgstr ""
+
#: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c
msgid "Buffer must be at least length 1"
msgstr ""
@@ -698,7 +710,8 @@ msgstr ""
msgid "Error in regex"
msgstr ""
-#: shared-bindings/aesio/aes.c shared-bindings/microcontroller/Pin.c
+#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c
+#: shared-bindings/microcontroller/Pin.c
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
#: shared-bindings/terminalio/Terminal.c
msgid "Expected a %q"
@@ -858,6 +871,10 @@ msgstr ""
msgid "Internal error #%d"
msgstr ""
+#: shared-bindings/sdioio/SDCard.c
+msgid "Invalid %q"
+msgstr ""
+
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c
msgid "Invalid %q pin"
@@ -1358,6 +1375,10 @@ msgstr ""
msgid "Running in safe mode! Not running saved code.\n"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "SD card CSD format not supported"
+msgstr ""
+
#: ports/atmel-samd/common-hal/busio/I2C.c
#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nrf/common-hal/busio/I2C.c
msgid "SDA or SCL needs a pull up"
@@ -1979,6 +2000,10 @@ msgstr ""
msgid "can't send non-None value to a just-started generator"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "can't set 512 block size"
+msgstr ""
+
#: py/objnamedtuple.c
msgid "can't set attribute"
msgstr ""
@@ -2105,6 +2130,10 @@ msgstr ""
msgid "could not invert Vandermonde matrix"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "couldn't determine SD card version"
+msgstr ""
+
#: extmod/ulab/code/approx.c
msgid "data must be iterable"
msgstr ""
@@ -2662,6 +2691,10 @@ msgstr ""
msgid "negative shift count"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "no SD card"
+msgstr ""
+
#: py/vm.c
msgid "no active exception to reraise"
msgstr ""
@@ -2683,6 +2716,10 @@ msgstr ""
msgid "no reset pin available"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "no response from SD card"
+msgstr ""
+
#: py/runtime.c
msgid "no such attribute"
msgstr ""
@@ -3073,6 +3110,14 @@ msgstr ""
msgid "timeout must be >= 0.0"
msgstr ""
+#: shared-module/sdcardio/SDCard.c
+msgid "timeout waiting for v1 card"
+msgstr ""
+
+#: shared-module/sdcardio/SDCard.c
+msgid "timeout waiting for v2 card"
+msgstr ""
+
#: shared-bindings/time/__init__.c
msgid "timestamp out of range for platform time_t"
msgstr ""
diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile
index 5f901a199..c308105a3 100644
--- a/ports/atmel-samd/Makefile
+++ b/ports/atmel-samd/Makefile
@@ -246,6 +246,14 @@ SRC_ASF += \
endif
+ifeq ($(CIRCUITPY_SDIOIO),1)
+SRC_ASF += \
+ hal/src/hal_mci_sync.c \
+ hpl/sdhc/hpl_sdhc.c \
+
+$(BUILD)/asf4/$(CHIP_FAMILY)/hpl/sdhc/hpl_sdhc.o: CFLAGS += -Wno-cast-align
+endif
+
SRC_ASF := $(addprefix asf4/$(CHIP_FAMILY)/, $(SRC_ASF))
SRC_C = \
@@ -290,6 +298,9 @@ SRC_C = \
supervisor/shared/memory.c \
timer_handler.c \
+ifeq ($(CIRCUITPY_SDIOIO),1)
+SRC_C += ports/atmel-samd/sd_mmc/sd_mmc.c
+endif
ifeq ($(CIRCUITPY_NETWORK),1)
CFLAGS += -DMICROPY_PY_NETWORK=1
@@ -346,6 +357,10 @@ endif
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o))
+SRC_QSTR += $(HEADER_BUILD)/sdiodata.h
+$(HEADER_BUILD)/sdiodata.h: $(TOP)/tools/mksdiodata.py | $(HEADER_BUILD)
+ $(Q)$(PYTHON3) $< > $@
+
SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_MODULE_EXPANDED)
# Sources that only hold QSTRs after pre-processing.
SRC_QSTR_PREPROCESSOR += peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/clocks.c
diff --git a/ports/atmel-samd/asf4 b/ports/atmel-samd/asf4
-Subproject c0eef7b75124fc946af5f75e12d82d6d01315ab
+Subproject 35a1525796c7ef8a3893d90befdad2f267fca20
diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_sdhc_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_sdhc_config.h
new file mode 100644
index 000000000..daa662051
--- /dev/null
+++ b/ports/atmel-samd/asf4_conf/samd51/hpl_sdhc_config.h
@@ -0,0 +1,24 @@
+/* Auto-generated config file hpl_sdhc_config.h */
+#ifndef HPL_SDHC_CONFIG_H
+#define HPL_SDHC_CONFIG_H
+
+// <<< Use Configuration Wizard in Context Menu >>>
+
+#include "peripheral_clk_config.h"
+
+#ifndef CONF_BASE_FREQUENCY
+#define CONF_BASE_FREQUENCY CONF_SDHC0_FREQUENCY
+#endif
+
+// <o> Clock Generator Select
+// <0=> Divided Clock mode
+// <1=> Programmable Clock mode
+// <i> This defines the clock generator mode in the SDCLK Frequency Select field
+// <id> sdhc_clk_gsel
+#ifndef CONF_SDHC0_CLK_GEN_SEL
+#define CONF_SDHC0_CLK_GEN_SEL 0
+#endif
+
+// <<< end of configuration section >>>
+
+#endif // HPL_SDHC_CONFIG_H
diff --git a/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h
index 030a90a7a..59fe8730e 100644
--- a/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h
+++ b/ports/atmel-samd/asf4_conf/samd51/peripheral_clk_config.h
@@ -1001,6 +1001,170 @@
#define CONF_GCLK_USB_FREQUENCY 48000000
#endif
+// <h> SDHC Clock Settings
+// <y> SDHC Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_SDHC0_SRC
+#define CONF_GCLK_SDHC0_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+
+// <y> SDHC clock slow source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_slow_gclk_selection
+#ifndef CONF_GCLK_SDHC0_SLOW_SRC
+#define CONF_GCLK_SDHC0_SLOW_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+// </h>
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock frequency
+ */
+#ifndef CONF_SDHC0_FREQUENCY
+#define CONF_SDHC0_FREQUENCY 12000000
+#endif
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock slow frequency
+ */
+#ifndef CONF_SDHC0_SLOW_FREQUENCY
+#define CONF_SDHC0_SLOW_FREQUENCY 12000000
+#endif
+
+// <h> SDHC Clock Settings
+// <y> SDHC Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_SDHC1_SRC
+#define CONF_GCLK_SDHC1_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+
+// <y> SDHC clock slow source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_slow_gclk_selection
+#ifndef CONF_GCLK_SDHC1_SLOW_SRC
+#define CONF_GCLK_SDHC1_SLOW_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+// </h>
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock frequency
+ */
+#ifndef CONF_SDHC1_FREQUENCY
+#define CONF_SDHC1_FREQUENCY 12000000
+#endif
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock slow frequency
+ */
+#ifndef CONF_SDHC1_SLOW_FREQUENCY
+#define CONF_SDHC1_SLOW_FREQUENCY 12000000
+#endif
+
// <<< end of configuration section >>>
#endif // PERIPHERAL_CLK_CONFIG_H
diff --git a/ports/atmel-samd/asf4_conf/same54/hpl_sdhc_config.h b/ports/atmel-samd/asf4_conf/same54/hpl_sdhc_config.h
new file mode 100644
index 000000000..daa662051
--- /dev/null
+++ b/ports/atmel-samd/asf4_conf/same54/hpl_sdhc_config.h
@@ -0,0 +1,24 @@
+/* Auto-generated config file hpl_sdhc_config.h */
+#ifndef HPL_SDHC_CONFIG_H
+#define HPL_SDHC_CONFIG_H
+
+// <<< Use Configuration Wizard in Context Menu >>>
+
+#include "peripheral_clk_config.h"
+
+#ifndef CONF_BASE_FREQUENCY
+#define CONF_BASE_FREQUENCY CONF_SDHC0_FREQUENCY
+#endif
+
+// <o> Clock Generator Select
+// <0=> Divided Clock mode
+// <1=> Programmable Clock mode
+// <i> This defines the clock generator mode in the SDCLK Frequency Select field
+// <id> sdhc_clk_gsel
+#ifndef CONF_SDHC0_CLK_GEN_SEL
+#define CONF_SDHC0_CLK_GEN_SEL 0
+#endif
+
+// <<< end of configuration section >>>
+
+#endif // HPL_SDHC_CONFIG_H
diff --git a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
index 030a90a7a..59fe8730e 100644
--- a/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
+++ b/ports/atmel-samd/asf4_conf/same54/peripheral_clk_config.h
@@ -1001,6 +1001,170 @@
#define CONF_GCLK_USB_FREQUENCY 48000000
#endif
+// <h> SDHC Clock Settings
+// <y> SDHC Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_SDHC0_SRC
+#define CONF_GCLK_SDHC0_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+
+// <y> SDHC clock slow source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_slow_gclk_selection
+#ifndef CONF_GCLK_SDHC0_SLOW_SRC
+#define CONF_GCLK_SDHC0_SLOW_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+// </h>
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock frequency
+ */
+#ifndef CONF_SDHC0_FREQUENCY
+#define CONF_SDHC0_FREQUENCY 12000000
+#endif
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock slow frequency
+ */
+#ifndef CONF_SDHC0_SLOW_FREQUENCY
+#define CONF_SDHC0_SLOW_FREQUENCY 12000000
+#endif
+
+// <h> SDHC Clock Settings
+// <y> SDHC Clock source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_gclk_selection
+#ifndef CONF_GCLK_SDHC1_SRC
+#define CONF_GCLK_SDHC1_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+
+// <y> SDHC clock slow source
+
+// <GCLK_PCHCTRL_GEN_GCLK0_Val"> Generic clock generator 0
+
+// <GCLK_PCHCTRL_GEN_GCLK1_Val"> Generic clock generator 1
+
+// <GCLK_PCHCTRL_GEN_GCLK2_Val"> Generic clock generator 2
+
+// <GCLK_PCHCTRL_GEN_GCLK3_Val"> Generic clock generator 3
+
+// <GCLK_PCHCTRL_GEN_GCLK4_Val"> Generic clock generator 4
+
+// <GCLK_PCHCTRL_GEN_GCLK5_Val"> Generic clock generator 5
+
+// <GCLK_PCHCTRL_GEN_GCLK6_Val"> Generic clock generator 6
+
+// <GCLK_PCHCTRL_GEN_GCLK7_Val"> Generic clock generator 7
+
+// <GCLK_PCHCTRL_GEN_GCLK8_Val"> Generic clock generator 8
+
+// <GCLK_PCHCTRL_GEN_GCLK9_Val"> Generic clock generator 9
+
+// <GCLK_PCHCTRL_GEN_GCLK10_Val"> Generic clock generator 10
+
+// <GCLK_PCHCTRL_GEN_GCLK11_Val"> Generic clock generator 11
+
+// <i> Select the clock source for SDHC.
+// <id> sdhc_slow_gclk_selection
+#ifndef CONF_GCLK_SDHC1_SLOW_SRC
+#define CONF_GCLK_SDHC1_SLOW_SRC GCLK_GENCTRL_SRC_DFLL_Val
+#endif
+// </h>
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock frequency
+ */
+#ifndef CONF_SDHC1_FREQUENCY
+#define CONF_SDHC1_FREQUENCY 12000000
+#endif
+
+/**
+ * \def SDHC FREQUENCY
+ * \brief SDHC's Clock slow frequency
+ */
+#ifndef CONF_SDHC1_SLOW_FREQUENCY
+#define CONF_SDHC1_SLOW_FREQUENCY 12000000
+#endif
+
// <<< end of configuration section >>>
#endif // PERIPHERAL_CLK_CONFIG_H
diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk
index ab81ec5a2..08eb5c98b 100644
--- a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk
+++ b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk
@@ -10,3 +10,5 @@ QSPI_FLASH_FILESYSTEM = 1
EXTERNAL_FLASH_DEVICE_COUNT = 2
EXTERNAL_FLASH_DEVICES = "W25Q64JV_IQ, GD25Q64C"
LONGINT_IMPL = MPZ
+
+CIRCUITPY_SDIOIO = 1
diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/pins.c b/ports/atmel-samd/boards/grandcentral_m4_express/pins.c
index 6b09c62bf..b125aca08 100644
--- a/ports/atmel-samd/boards/grandcentral_m4_express/pins.c
+++ b/ports/atmel-samd/boards/grandcentral_m4_express/pins.c
@@ -1,5 +1,18 @@
+#include "py/objtuple.h"
#include "shared-bindings/board/__init__.h"
+STATIC const mp_rom_obj_tuple_t sdio_data_tuple = {
+ {&mp_type_tuple},
+ 4,
+ {
+ MP_ROM_PTR(&pin_PB18),
+ MP_ROM_PTR(&pin_PB19),
+ MP_ROM_PTR(&pin_PB20),
+ MP_ROM_PTR(&pin_PB21),
+ }
+};
+
+
// This mapping only includes functional names because pins broken
// out on connectors are labeled with their MCU name available from
// microcontroller.pin.
@@ -129,5 +142,9 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_SDIO_CLOCK), MP_ROM_PTR(&pin_PA21) },
+ { MP_ROM_QSTR(MP_QSTR_SDIO_COMMAND), MP_ROM_PTR(&pin_PA20) },
+ { MP_ROM_QSTR(MP_QSTR_SDIO_DATA), MP_ROM_PTR(&sdio_data_tuple) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
diff --git a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
index ffc4f567f..7ac126514 100644
--- a/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
+++ b/ports/atmel-samd/boards/same54_xplained/mpconfigboard.mk
@@ -10,4 +10,5 @@ QSPI_FLASH_FILESYSTEM = 1
EXTERNAL_FLASH_DEVICE_COUNT = 2
EXTERNAL_FLASH_DEVICES = "N25Q256A"
LONGINT_IMPL = MPZ
-LONGINT_IMPL = MPZ
+
+CIRCUITPY_SDIOIO = 1
diff --git a/ports/atmel-samd/boards/same54_xplained/pins.c b/ports/atmel-samd/boards/same54_xplained/pins.c
index 5b9373724..8a864ab97 100644
--- a/ports/atmel-samd/boards/same54_xplained/pins.c
+++ b/ports/atmel-samd/boards/same54_xplained/pins.c
@@ -1,5 +1,18 @@
+#include "py/objtuple.h"
#include "shared-bindings/board/__init__.h"
+STATIC const mp_rom_obj_tuple_t sdio_data_tuple = {
+ {&mp_type_tuple},
+ 4,
+ {
+ MP_ROM_PTR(&pin_PB18),
+ MP_ROM_PTR(&pin_PB19),
+ MP_ROM_PTR(&pin_PB20),
+ MP_ROM_PTR(&pin_PB21),
+ }
+};
+
+
// This mapping only includes functional names because pins broken
// out on connectors are labeled with their MCU name available from
// microcontroller.pin.
@@ -95,5 +108,9 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_SDIO_CLOCK), MP_ROM_PTR(&pin_PA21) },
+ { MP_ROM_QSTR(MP_QSTR_SDIO_COMMAND), MP_ROM_PTR(&pin_PA20) },
+ { MP_ROM_QSTR(MP_QSTR_SDIO_DATA), MP_ROM_PTR(&sdio_data_tuple) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c
new file mode 100644
index 000000000..8cd077ecd
--- /dev/null
+++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <stdbool.h>
+
+#include "py/mperrno.h"
+#include "py/runtime.h"
+
+#include "boards/board.h"
+#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/sdioio/SDCard.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "supervisor/shared/translate.h"
+
+#include "genhdr/sdiodata.h"
+
+#include "sd_mmc/sd_mmc.h"
+#include "sd_mmc/conf_sd_mmc.h"
+#include "peripheral_clk_config.h"
+
+#ifndef DEBUG_SDIO
+#define DEBUG_SDIO (0)
+#endif
+
+#if DEBUG_SDIO
+#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, __VA_ARGS__))
+#define DEBUG_PRINT_OBJ(o) ((void)mp_obj_print_helper(&mp_plat_print, (mp_obj_t)o, PRINT_REPR))
+#else
+#define DEBUG_PRINT(...) ((void)0)
+#define DEBUG_PRINT_OBJ(...) ((void)0)
+#endif
+#define DEBUG_PRINT_OBJ_NL(o) (DEBUG_PRINT_OBJ(o), DEBUG_PRINT("\n"))
+
+#define GPIO_PIN_FUNCTION_SDIO (GPIO_PIN_FUNCTION_I)
+
+static Sdhc *sdhc_insts[] = SDHC_INSTS;
+
+STATIC pin_function_t *find_pin_function(pin_function_t *table, const mcu_pin_obj_t *pin, int instance, uint16_t name) {
+ DEBUG_PRINT("\n\n[inst=% 2d] %q: ", instance, name);
+ DEBUG_PRINT_OBJ_NL(pin);
+
+ for(; table->obj; table++) {
+ DEBUG_PRINT("[inst=% 2d] considering table @%p: ");
+ DEBUG_PRINT_OBJ(table->obj);
+ DEBUG_PRINT(" %d %d\n", table->instance, table->pin);
+ if (instance != -1 && instance != table->instance) {
+ continue;
+ }
+ if (pin == table->obj) {
+ return table;
+ }
+ }
+ mp_raise_ValueError_varg(translate("%q pin invalid"), name);
+}
+
+void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self,
+ const mcu_pin_obj_t * clock, const mcu_pin_obj_t * command,
+ uint8_t num_data, mcu_pin_obj_t ** data, uint32_t frequency) {
+ /*
+SD breakout as assembled ("*" = minimum viable set)
+
+PURPLE 9 DAT2 SDA
+BLUE 1 DAT3 SCL
+GREEN 2 CMD * D32
+YELLOW 3 VSS1
+RED 4 VDD * 3.3V
+BROWN 5 CLK * BROWN
+BLACK 6 VSS2 * GND
+WHITE 7 DAT0 * D8
+GREY 8 DAT1 D29
+
+DAT0..3 PB18..21 (D8 D29 D20 D21) WHITE GREY PURPLE BLUE
+CMD PA20 PCC_D? (D33) GREEN
+CLK PA21 PCC_D? (D32) BROWN
+
+*/
+
+ pin_function_t *functions[6] = {};
+ functions[0] = find_pin_function(sdio_cmd, command, -1, MP_QSTR_command);
+ int instance = functions[0]->instance;
+ functions[1] = find_pin_function(sdio_ck, clock, instance, MP_QSTR_clock);
+ functions[2] = find_pin_function(sdio_dat0, data[0], instance, MP_QSTR_data0);
+ if(num_data == 4) {
+ functions[3] = find_pin_function(sdio_dat1, data[1], instance, MP_QSTR_data1);
+ functions[4] = find_pin_function(sdio_dat2, data[2], instance, MP_QSTR_data2);
+ functions[5] = find_pin_function(sdio_dat3, data[3], instance, MP_QSTR_data3);
+ }
+
+ // We've verified all pins, now set their special functions
+ self->command_pin = common_hal_mcu_pin_number(functions[0]->obj);
+ self->clock_pin = common_hal_mcu_pin_number(functions[1]->obj);
+
+ for(int i=0; i<num_data; i++) {
+ pin_function_t *function = functions[2+i];
+ if (function) {
+ self->data_pins[i] = common_hal_mcu_pin_number(function->obj);
+ } else {
+ self->data_pins[i] = COMMON_HAL_MCU_NO_PIN;
+ }
+ }
+
+ for(size_t i=0; i<MP_ARRAY_SIZE(functions); i++) {
+ if (!functions[i]->obj) {
+ break;
+ }
+
+ gpio_set_pin_direction(functions[i]->pin, GPIO_DIRECTION_OUT);
+ gpio_set_pin_level(functions[i]->pin, false);
+ // Enable pullups on all pins except CLK and DAT3
+ gpio_set_pin_pull_mode(functions[i]->pin,
+ (i == 1 || i == 5) ? GPIO_PULL_OFF : GPIO_PULL_UP);
+ gpio_set_pin_function(functions[i]->pin, GPIO_PIN_FUNCTION_SDIO);
+
+ common_hal_never_reset_pin(functions[i]->obj);
+ }
+
+ self->num_data = num_data;
+ self->frequency = frequency;
+
+ if(instance == 0) {
+ hri_mclk_set_AHBMASK_SDHC0_bit(MCLK);
+ hri_gclk_write_PCHCTRL_reg(GCLK, SDHC0_GCLK_ID, CONF_GCLK_SDHC0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+ hri_gclk_write_PCHCTRL_reg(GCLK, SDHC0_GCLK_ID_SLOW, CONF_GCLK_SDHC0_SLOW_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+ } else {
+#ifdef SDHC1_GCLK_ID
+ hri_mclk_set_AHBMASK_SDHC1_bit(MCLK);
+ hri_gclk_write_PCHCTRL_reg(GCLK, SDHC1_GCLK_ID, CONF_GCLK_SDHC1_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+ hri_gclk_write_PCHCTRL_reg(GCLK, SDHC1_GCLK_ID_SLOW, CONF_GCLK_SDHC1_SLOW_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
+#endif
+ }
+
+ DEBUG_PRINT("instance %d @%p\n", instance, sdhc_insts[instance]);
+ mci_sync_init(&self->IO_BUS, sdhc_insts[instance]);
+ sd_mmc_init(&self->IO_BUS, NULL, NULL);
+
+ sd_mmc_err_t result = SD_MMC_INIT_ONGOING;
+
+ for (int i=0; result == SD_MMC_INIT_ONGOING && i<100; i++) {
+ result = sd_mmc_check(0);
+ DEBUG_PRINT("sd_mmc_check(0) -> %d\n", result);
+ }
+
+ if (result != SD_MMC_OK) {
+ mp_raise_OSError_msg_varg(translate("%q failure: %d"), MP_QSTR_sd_mmc_check, (int)result);
+ }
+ // sd_mmc_get_capacity() is in KiB, but our "capacity" is in 512-byte blocks
+ self->capacity = sd_mmc_get_capacity(0) * 2;
+
+ DEBUG_PRINT("capacity=%u\n", self->capacity);
+}
+
+uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self) {
+ return self->capacity;
+}
+
+uint32_t common_hal_sdioio_sdcard_get_frequency(sdioio_sdcard_obj_t *self) {
+ return self->frequency; // self->frequency;
+}
+
+uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self) {
+ return self->num_data; // self->width;
+}
+
+STATIC void check_for_deinit(sdioio_sdcard_obj_t *self) {
+}
+
+STATIC void check_whole_block(mp_buffer_info_t *bufinfo) {
+ if (bufinfo->len % 512) {
+ mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
+ }
+}
+
+STATIC void wait_write_complete(sdioio_sdcard_obj_t *self) {
+ if (self->state_programming) {
+ sd_mmc_wait_end_of_write_blocks(0);
+ self->state_programming = 0;
+ }
+}
+
+STATIC void debug_print_state(sdioio_sdcard_obj_t *self, const char *what, sd_mmc_err_t r) {
+#if DEBUG_SDIO
+ DEBUG_PRINT("%s: %d\n", what, r);
+#endif
+}
+
+int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
+ check_for_deinit(self);
+ check_whole_block(bufinfo);
+ wait_write_complete(self);
+ self->state_programming = true;
+ sd_mmc_err_t r = sd_mmc_init_write_blocks(0, start_block, bufinfo->len / 512);
+ if (r != SD_MMC_OK) {
+ debug_print_state(self, "sd_mmc_init_write_blocks", r);
+ return -EIO;
+ }
+ r = sd_mmc_start_write_blocks(bufinfo->buf, bufinfo->len / 512);
+ if (r != SD_MMC_OK) {
+ debug_print_state(self, "sd_mmc_start_write_blocks", r);
+ return -EIO;
+ }
+ // debug_print_state(self, "after writeblocks OK");
+ return 0;
+}
+
+int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
+ check_for_deinit(self);
+ check_whole_block(bufinfo);
+ wait_write_complete(self);
+ sd_mmc_err_t r = sd_mmc_init_read_blocks(0, start_block, bufinfo->len / 512);
+ if (r != SD_MMC_OK) {
+ debug_print_state(self, "sd_mmc_init_read_blocks", r);
+ return -EIO;
+ }
+ r = sd_mmc_start_read_blocks(bufinfo->buf, bufinfo->len / 512);
+ if (r != SD_MMC_OK) {
+ debug_print_state(self, "sd_mmc_start_read_blocks", r);
+ return -EIO;
+ }
+ sd_mmc_wait_end_of_write_blocks(0);
+ return 0;
+}
+
+bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) {
+ check_for_deinit(self);
+ return true;
+}
+
+bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self) {
+ return self->command_pin == COMMON_HAL_MCU_NO_PIN;
+}
+
+void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self) {
+ reset_pin_number(self->command_pin);
+ reset_pin_number(self->clock_pin);
+ reset_pin_number(self->data_pins[0]);
+ reset_pin_number(self->data_pins[1]);
+ reset_pin_number(self->data_pins[2]);
+ reset_pin_number(self->data_pins[3]);
+
+ self->command_pin = COMMON_HAL_MCU_NO_PIN;
+ self->clock_pin = COMMON_HAL_MCU_NO_PIN;
+ self->data_pins[0] = COMMON_HAL_MCU_NO_PIN;
+ self->data_pins[1] = COMMON_HAL_MCU_NO_PIN;
+ self->data_pins[2] = COMMON_HAL_MCU_NO_PIN;
+ self->data_pins[3] = COMMON_HAL_MCU_NO_PIN;
+}
+
+void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) {
+}
diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.h b/ports/atmel-samd/common-hal/sdioio/SDCard.h
new file mode 100644
index 000000000..2baba38f0
--- /dev/null
+++ b/ports/atmel-samd/common-hal/sdioio/SDCard.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "hal_mci_sync.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ struct mci_sync_desc IO_BUS;
+ uint32_t frequency;
+ uint32_t capacity;
+ uint8_t num_data:3, state_programming:1, has_lock:1;
+ uint8_t command_pin;
+ uint8_t clock_pin;
+ uint8_t data_pins[4];
+} sdioio_sdcard_obj_t;
diff --git a/ports/atmel-samd/common-hal/sdioio/__init__.c b/ports/atmel-samd/common-hal/sdioio/__init__.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ports/atmel-samd/common-hal/sdioio/__init__.c
diff --git a/ports/atmel-samd/common-hal/sdioio/__init__.h b/ports/atmel-samd/common-hal/sdioio/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ports/atmel-samd/common-hal/sdioio/__init__.h
diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk
index 92d47b186..face79fad 100644
--- a/ports/atmel-samd/mpconfigport.mk
+++ b/ports/atmel-samd/mpconfigport.mk
@@ -37,6 +37,8 @@ ifndef CIRCUITPY_TOUCHIO_USE_NATIVE
CIRCUITPY_TOUCHIO_USE_NATIVE = 1
endif
+CIRCUITPY_SDCARDIO ?= 0
+
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
USB_MSC_EP_NUM_OUT = 1
diff --git a/ports/atmel-samd/sd_mmc/conf_sd_mmc.h b/ports/atmel-samd/sd_mmc/conf_sd_mmc.h
new file mode 100644
index 000000000..735dd3783
--- /dev/null
+++ b/ports/atmel-samd/sd_mmc/conf_sd_mmc.h
@@ -0,0 +1,79 @@
+/* Auto-generated config file conf_sd_mmc.h */
+#ifndef CONF_SD_MMC_H
+#define CONF_SD_MMC_H
+
+// <<< Use Configuration Wizard in Context Menu >>>
+
+// <q> Enable the SDIO support
+// <id> conf_sdio_support
+#ifndef CONF_SDIO_SUPPORT
+#define CONF_SDIO_SUPPORT 0
+#endif
+
+// <q> Enable the MMC card support
+// <id> conf_mmc_support
+#ifndef CONF_MMC_SUPPORT
+#define CONF_MMC_SUPPORT 0
+#endif
+
+// <q> Enable the OS support
+// <id> conf_sd_mmc_os_support
+#ifndef CONF_OS_SUPPORT
+#define CONF_OS_SUPPORT 0
+#endif
+
+// Detection (card/write protect) timeout (ms/ticks)
+// conf_sd_mmc_debounce
+#ifndef CONF_SD_MMC_DEBOUNCE
+#define CONF_SD_MMC_DEBOUNCE 1000
+#endif
+
+#ifndef CONF_SD_MMC_MEM_CNT
+#define CONF_SD_MMC_MEM_CNT 1
+#endif
+
+// <e> SD/MMC Slot 0
+// <id> conf_sd_mmc_0_enable
+#ifndef CONF_SD_MMC_0_ENABLE
+#define CONF_SD_MMC_0_ENABLE 1
+#endif
+
+// <e> Card Detect (CD) 0 Enable
+// <id> conf_sd_mmc_0_cd_detect_en
+#ifndef CONF_SD_MMC_0_CD_DETECT_EN
+#define CONF_SD_MMC_0_CD_DETECT_EN 0
+#endif
+
+// <o> Card Detect (CD) detection level
+// <1=> High
+// <0=> Low
+// <id> conf_sd_mmc_0_cd_detect_value
+#ifndef CONF_SD_MMC_0_CD_DETECT_VALUE
+#define CONF_SD_MMC_0_CD_DETECT_VALUE 0
+#endif
+// </e>
+
+// <e> Write Protect (WP) 0 Enable
+// <id> conf_sd_mmc_0_wp_detect_en
+#ifndef CONF_SD_MMC_0_WP_DETECT_EN
+#define CONF_SD_MMC_0_WP_DETECT_EN 0
+#endif
+
+// <o> Write Protect (WP) detection level
+// <1=> High
+// <0=> Low
+// <id> conf_sd_mmc_0_wp_detect_value
+#ifndef CONF_SD_MMC_0_WP_DETECT_VALUE
+#define CONF_SD_MMC_0_WP_DETECT_VALUE 1
+#endif
+// </e>
+
+// </e>
+
+#ifndef CONF_MCI_OS_SUPPORT
+#define CONF_MCI_OS_SUPPORT 0
+#endif
+
+// <<< end of configuration section >>>
+
+#endif // CONF_SD_MMC_H
diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.c b/ports/atmel-samd/sd_mmc/sd_mmc.c
new file mode 100644
index 000000000..5ea9d830e
--- /dev/null
+++ b/ports/atmel-samd/sd_mmc/sd_mmc.c
@@ -0,0 +1,1671 @@
+/**
+ * \file
+ *
+ * \brief Common SD/MMC stack
+ *
+ * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
+ */
+
+#include <string.h>
+
+#include "conf_sd_mmc.h"
+
+#include "sd_mmc_protocol.h"
+#include "sd_mmc.h"
+
+#if CONF_OS_SUPPORT
+#include <hal_rtos.h>
+#endif
+#include <hal_gpio.h>
+#include <utils.h>
+#include <utils_assert.h>
+#include <utils_repeat_macro.h>
+
+#define TPASTE2(a, b) a##b
+#define ATPASTE2(a, b) TPASTE2(a, b)
+
+/**
+ * \ingroup sd_mmc_stack
+ * \defgroup sd_mmc_stack_internal Implementation of SD/MMC/SDIO Stack
+ * @{
+ */
+
+#ifndef CONF_SD_MMC_MEM_CNT
+#define CONF_SD_MMC_MEM_CNT 0
+#endif
+
+/* Macros to switch SD MMC stack to the correct driver (MCI or SPI) */
+#if (CONF_SD_MMC_MEM_CNT != 0)
+#if CONF_MCI_OS_SUPPORT
+#include "hal_mci_os.h"
+#else
+#include "hal_mci_sync.h"
+#endif
+#define driver mci
+#else
+#error No MCI or HSMCI interfaces are defined for SD MMC stack. \
+ CONF_SD_MMC_MEM_CNT must be added in board.h file.
+#endif
+
+#if CONF_MCI_OS_SUPPORT
+#define driver_select_device ATPASTE2(driver, _os_select_device)
+#define driver_deselect_device ATPASTE2(driver, _os_deselect_device)
+#define driver_get_bus_width ATPASTE2(driver, _os_get_bus_width)
+#define driver_is_high_speed_capable ATPASTE2(driver, _os_is_high_speed_capable)
+#define driver_send_clock ATPASTE2(driver, _os_send_init_sequence)
+#define driver_send_cmd ATPASTE2(driver, _os_send_cmd)
+#define driver_get_response ATPASTE2(driver, _os_get_response)
+#define driver_get_response_128 ATPASTE2(driver, _os_get_response_128)
+#define driver_adtc_start ATPASTE2(driver, _os_adtc_start)
+#define driver_adtc_stop ATPASTE2(driver, _os_send_cmd)
+#define driver_read_word ATPASTE2(driver, _os_read_bytes)
+#define driver_write_word ATPASTE2(driver, _os_write_bytes)
+#define driver_start_read_blocks ATPASTE2(driver, _os_start_read_blocks)
+#define driver_wait_end_of_read_blocks ATPASTE2(driver, _os_wait_end_of_read_blocks)
+#define driver_start_write_blocks ATPASTE2(driver, _os_start_write_blocks)
+#define driver_wait_end_of_write_blocks ATPASTE2(driver, _os_wait_end_of_write_blocks)
+#else
+#define driver_select_device ATPASTE2(driver, _sync_select_device)
+#define driver_deselect_device ATPASTE2(driver, _sync_deselect_device)
+#define driver_get_bus_width ATPASTE2(driver, _sync_get_bus_width)
+#define driver_is_high_speed_capable ATPASTE2(driver, _sync_is_high_speed_capable)
+#define driver_send_clock ATPASTE2(driver, _sync_send_clock)
+#define driver_send_cmd ATPASTE2(driver, _sync_send_cmd)
+#define driver_get_response ATPASTE2(driver, _sync_get_response)
+#define driver_get_response_128 ATPASTE2(driver, _sync_get_response_128)
+#define driver_adtc_start ATPASTE2(driver, _sync_adtc_start)
+#define driver_adtc_stop ATPASTE2(driver, _sync_send_cmd)
+#define driver_read_word ATPASTE2(driver, _sync_read_word)
+#define driver_write_word ATPASTE2(driver, _sync_write_word)
+#define driver_start_read_blocks ATPASTE2(driver, _sync_start_read_blocks)
+#define driver_wait_end_of_read_blocks ATPASTE2(driver, _sync_wait_end_of_read_blocks)
+#define driver_start_write_blocks ATPASTE2(driver, _sync_start_write_blocks)
+#define driver_wait_end_of_write_blocks ATPASTE2(driver, _sync_wait_end_of_write_blocks)
+#endif
+
+#if (CONF_SDIO_SUPPORT == 1)
+#define IS_SDIO() (sd_mmc_card->type & CARD_TYPE_SDIO)
+#else
+#define IS_SDIO() false
+#endif
+
+/** This SD MMC stack supports only the high voltage */
+#define SD_MMC_VOLTAGE_SUPPORT \
+ (OCR_VDD_27_28 | OCR_VDD_28_29 | OCR_VDD_29_30 | OCR_VDD_30_31 | OCR_VDD_31_32 | OCR_VDD_32_33)
+
+/** SD/MMC card states */
+enum card_state {
+ SD_MMC_CARD_STATE_READY = 0, /**< Ready to use */
+ SD_MMC_CARD_STATE_DEBOUNCE = 1, /**< Debounce on going */
+ SD_MMC_CARD_STATE_INIT = 2, /**< Initialization on going */
+ SD_MMC_CARD_STATE_UNUSABLE = 3, /**< Unusable card */
+ SD_MMC_CARD_STATE_NO_CARD = 4 /**< No SD/MMC card inserted */
+};
+
+/** SD/MMC card information structure */
+struct sd_mmc_card {
+ uint32_t clock; /**< Card access clock */
+ uint32_t capacity; /**< Card capacity in KBytes */
+ uint16_t rca; /**< Relative card address */
+ enum card_state state; /**< Card state */
+ card_type_t type; /**< Card type */
+ card_version_t version; /**< Card version */
+ uint8_t bus_width; /**< Number of DATA lin on bus (MCI only) */
+ uint8_t csd[CSD_REG_BSIZE]; /**< CSD register */
+ uint8_t high_speed; /**< High speed card (1) */
+};
+
+/** Card detect pin */
+static sd_mmc_detect_t *_cd;
+/** Write protect pin */
+static sd_mmc_detect_t *_wp;
+
+/** SD/MMC card list */
+/** Note: Initialize card detect pin fields if present */
+static struct sd_mmc_card sd_mmc_cards[CONF_SD_MMC_MEM_CNT];
+
+/** HAL driver instance */
+static void *sd_mmc_hal;
+/** Index of current slot configurated */
+static uint8_t sd_mmc_slot_sel;
+/** Pointer on current slot configurated */
+static struct sd_mmc_card *sd_mmc_card;
+/** Number of block to read or write on the current transfer */
+static uint16_t sd_mmc_nb_block_to_tranfer = 0;
+/** Number of block remaining to read or write on the current transfer */
+static uint16_t sd_mmc_nb_block_remaining = 0;
+
+/** SD/MMC transfer rate unit codes (10K) list */
+const uint32_t sd_mmc_trans_units[7] = {10, 100, 1000, 10000, 0, 0, 0};
+/** SD transfer multiplier factor codes (1/10) list */
+const uint32_t sd_trans_multipliers[16] = {0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80};
+/** MMC transfer multiplier factor codes (1/10) list */
+const uint32_t mmc_trans_multipliers[16] = {0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80};
+
+/** \name MMC, SD and SDIO commands process */
+/** @{ */
+#if CONF_MMC_SUPPORT
+static bool mmc_mci_op_cond(void);
+static bool mmc_cmd6_set_bus_width(uint8_t bus_width);
+static bool mmc_cmd6_set_high_speed(void);
+static bool mmc_cmd8(uint8_t *b_authorize_high_speed);
+static void mmc_decode_csd(void);
+#endif
+static bool sd_mci_op_cond(uint8_t v2);
+static bool sdio_op_cond(void);
+static bool sdio_get_max_speed(void);
+static bool sdio_cmd52_set_bus_width(void);
+static bool sdio_cmd52_set_high_speed(void);
+static bool sd_cm6_set_high_speed(void);
+static bool sd_cmd8(uint8_t *v2);
+static bool sd_mmc_cmd9_mci(void);
+static void sd_decode_csd(void);
+static bool sd_mmc_cmd13(void);
+#if (CONF_SDIO_SUPPORT == 1)
+static bool sdio_cmd52(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr, uint8_t rd_after_wr, uint8_t *io_data);
+static bool sdio_cmd53(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr, uint8_t inc_addr, uint32_t size,
+ bool access_block);
+#endif
+static bool sd_acmd6(void);
+static bool sd_acmd51(void);
+/** @} */
+
+/** \name Internal function to process the initialization and install */
+/** @{ */
+static sd_mmc_err_t sd_mmc_select_slot(uint8_t slot);
+static void sd_mmc_configure_slot(void);
+static void sd_mmc_deselect_slot(void);
+static bool sd_mmc_mci_card_init(void);
+#if CONF_MMC_SUPPORT
+static bool sd_mmc_mci_install_mmc(void);
+#endif
+/** @} */
+
+/** \name Internal functions to manage a large timeout after a card insertion */
+/** @{ */
+#if CONF_OS_SUPPORT
+#define SD_MMC_START_TIMEOUT() os_sleep(CONF_SD_MMC_DEBOUNCE)
+#else
+#define SD_MMC_START_TIMEOUT()
+#endif
+#define SD_MMC_IS_TIMEOUT() true
+#define SD_MMC_STOP_TIMEOUT()
+/** @} */
+
+#if CONF_MMC_SUPPORT
+/**
+ * \brief Sends operation condition command and read OCR (MCI only)
+ * - CMD1 sends operation condition command
+ * - CMD1 reads OCR
+ *
+ * \return true if success, otherwise false
+ */
+static bool mmc_mci_op_cond(void)
+{
+ uint32_t retry, resp;
+
+ /*
+ * Timeout 1s = 400KHz / ((6+6)*8) cylces = 4200 retry
+ * 6 = cmd byte size
+ * 6 = response byte size
+ */
+ retry = 4200;
+ do {
+ if (!driver_send_cmd(sd_mmc_hal, MMC_MCI_CMD1_SEND_OP_COND, SD_MMC_VOLTAGE_SUPPORT | OCR_ACCESS_MODE_SECTOR)) {
+ return false;
+ }
+ /* Check busy flag */
+ resp = driver_get_response(sd_mmc_hal);
+ if (resp & OCR_POWER_UP_BUSY) {
+ /* Check OCR value */
+ if ((resp & OCR_ACCESS_MODE_MASK) == OCR_ACCESS_MODE_SECTOR) {
+ sd_mmc_card->type |= CARD_TYPE_HC;
+ }
+ break;
+ }
+ if (retry-- == 0) {
+ return false;
+ }
+ } while (1);
+ return true;
+}
+#endif
+
+/**
+ * \brief Ask to all cards to send their operations conditions (MCI only).
+ * - ACMD41 sends operation condition command.
+ * - ACMD41 reads OCR
+ *
+ * \param v2 Shall be 1 if it is a SD card V2
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_mci_op_cond(uint8_t v2)
+{
+ uint32_t arg, retry, resp;
+
+ /*
+ * Timeout 1s = 400KHz / ((6+6+6+6)*8) cylces = 2100 retry
+ * 6 = cmd byte size
+ * 6 = response byte size
+ * 6 = cmd byte size
+ * 6 = response byte size
+ */
+ retry = 2100;
+ do {
+ /* CMD55 - Indicate to the card that the next command is an
+ * application specific command rather than a standard command.*/
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD55_APP_CMD, 0)) {
+ return false;
+ }
+
+ /* (ACMD41) Sends host OCR register */
+ arg = SD_MMC_VOLTAGE_SUPPORT;
+ if (v2) {
+ arg |= SD_ACMD41_HCS;
+ }
+ /* Check response */
+ if (!driver_send_cmd(sd_mmc_hal, SD_MCI_ACMD41_SD_SEND_OP_COND, arg)) {
+ return false;
+ }
+ resp = driver_get_response(sd_mmc_hal);
+ if (resp & OCR_POWER_UP_BUSY) {
+ /* Card is ready */
+ if ((resp & OCR_CCS) != 0) {
+ sd_mmc_card->type |= CARD_TYPE_HC;
+ }
+ break;
+ }
+ if (retry-- == 0) {
+ return false;
+ }
+ } while (1);
+ return true;
+}
+
+#if (CONF_SDIO_SUPPORT == 1)
+/**
+ * \brief Try to get the SDIO card's operating condition
+ * - CMD5 to read OCR NF field
+ * - CMD5 to wait OCR power up busy
+ * - CMD5 to read OCR MP field
+ * sd_mmc_card->type is updated
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_op_cond(void)
+{
+ uint32_t resp;
+
+ /* CMD5 - SDIO send operation condition (OCR) command. */
+ if (!driver_send_cmd(sd_mmc_hal, SDIO_CMD5_SEND_OP_COND, 0)) {
+ return true; /* No error but card type not updated */
+ }
+ resp = driver_get_response(sd_mmc_hal);
+ if ((resp & OCR_SDIO_NF) == 0) {
+ return true; /* No error but card type not updated */
+ }
+
+ /*
+ * Wait card ready
+ * Timeout 1s = 400KHz / ((6+4)*8) cylces = 5000 retry
+ * 6 = cmd byte size
+ * 4(SPI) 6(MCI) = response byte size
+ */
+ uint32_t cmd5_retry = 5000;
+ while (1) {
+ /* CMD5 - SDIO send operation condition (OCR) command.*/
+ if (!driver_send_cmd(sd_mmc_hal, SDIO_CMD5_SEND_OP_COND, resp & SD_MMC_VOLTAGE_SUPPORT)) {
+ return false;
+ }
+ resp = driver_get_response(sd_mmc_hal);
+ if ((resp & OCR_POWER_UP_BUSY) == OCR_POWER_UP_BUSY) {
+ break;
+ }
+ if (cmd5_retry-- == 0) {
+ return false;
+ }
+ }
+ /* Update card type at the end of busy */
+ if ((resp & OCR_SDIO_MP) > 0) {
+ sd_mmc_card->type = CARD_TYPE_SD_COMBO;
+ } else {
+ sd_mmc_card->type = CARD_TYPE_SDIO;
+ }
+ return true; /* No error and card type updated with SDIO type */
+}
+
+/**
+ * \brief Get SDIO max transfer speed in Hz.
+ * - CMD53 reads CIS area address in CCCR area.
+ * - Nx CMD53 search Fun0 tuple in CIS area
+ * - CMD53 reads TPLFE_MAX_TRAN_SPEED in Fun0 tuple
+ * - Compute maximum speed of SDIO
+ * and update sd_mmc_card->clock
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_get_max_speed(void)
+{
+ uint32_t addr_new, addr_old;
+ uint8_t buf[6];
+ uint32_t unit;
+ uint32_t mul;
+ uint8_t tplfe_max_tran_speed, i;
+ uint8_t addr_cis[4];
+
+ /* Read CIS area address in CCCR area */
+ addr_old = SDIO_CCCR_CIS_PTR;
+ for (i = 0; i < 4; i++) {
+ sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_old, 0, &addr_cis[i]);
+ addr_old++;
+ }
+ addr_old = addr_cis[0] + (addr_cis[1] << 8) + (addr_cis[2] << 16) + (addr_cis[3] << 24);
+ addr_new = addr_old;
+
+ while (1) {
+ /* Read a sample of CIA area */
+ for (i = 0; i < 3; i++) {
+ sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_new, 0, &buf[i]);
+ addr_new++;
+ }
+ if (buf[0] == SDIO_CISTPL_END) {
+ return false; /* Tuple error */
+ }
+ if (buf[0] == SDIO_CISTPL_FUNCE && buf[2] == 0x00) {
+ break; /* Fun0 tuple found */
+ }
+ if (buf[1] == 0) {
+ return false; /* Tuple error */
+ }
+ /* Next address */
+ addr_new += buf[1] - 1;
+ if (addr_new > (addr_old + 256)) {
+ return false; /* Outoff CIS area */
+ }
+ }
+
+ /* Read all Fun0 tuple fields: fn0_blk_siz & max_tran_speed */
+ addr_new -= 3;
+ for (i = 0; i < 6; i++) {
+ sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, addr_new, 0, &buf[i]);
+ addr_new++;
+ }
+
+ tplfe_max_tran_speed = buf[5];
+ if (tplfe_max_tran_speed > 0x32) {
+ /* Error on SDIO register, the high speed is not activated
+ * and the clock can not be more than 25MHz.
+ * This error is present on specific SDIO card
+ * (H&D wireless card - HDG104 WiFi SIP).
+ */
+ tplfe_max_tran_speed = 0x32; /* 25Mhz */
+ }
+
+ /* Decode transfer speed in Hz.*/
+ unit = sd_mmc_trans_units[tplfe_max_tran_speed & 0x7];
+ mul = sd_trans_multipliers[(tplfe_max_tran_speed >> 3) & 0xF];
+ sd_mmc_card->clock = unit * mul * 1000;
+ /**
+ * Note: A combo card shall be a Full-Speed SDIO card
+ * which supports upto 25MHz.
+ * A SDIO card alone can be:
+ * - a Low-Speed SDIO card which supports 400Khz minimum
+ * - a Full-Speed SDIO card which supports upto 25MHz
+ */
+ return true;
+}
+
+/**
+ * \brief CMD52 for SDIO - Switches the bus width mode to 4
+ *
+ * \note sd_mmc_card->bus_width is updated.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_cmd52_set_bus_width(void)
+{
+ /**
+ * A SD memory card always supports bus 4bit
+ * A SD COMBO card always supports bus 4bit
+ * A SDIO Full-Speed alone always supports 4bit
+ * A SDIO Low-Speed alone can supports 4bit (Optional)
+ */
+ uint8_t u8_value;
+
+ /* Check 4bit support in 4BLS of "Card Capability" register */
+ if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, SDIO_CCCR_CAP, 0, &u8_value)) {
+ return false;
+ }
+ if ((u8_value & SDIO_CAP_4BLS) != SDIO_CAP_4BLS) {
+ /* No supported, it is not a protocol error */
+ return true;
+ }
+ /* HS mode possible, then enable */
+ u8_value = SDIO_BUSWIDTH_4B;
+ if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, SDIO_CIA, SDIO_CCCR_BUS_CTRL, 1, &u8_value)) {
+ return false;
+ }
+ sd_mmc_card->bus_width = 4;
+ return true;
+}
+
+/**
+ * \brief CMD52 for SDIO - Enable the high speed mode
+ *
+ * \note sd_mmc_card->high_speed is updated.
+ * \note sd_mmc_card->clock is updated.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_cmd52_set_high_speed(void)
+{
+ uint8_t u8_value;
+
+ /* Check CIA.HS */
+ if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, SDIO_CIA, SDIO_CCCR_HS, 0, &u8_value)) {
+ return false;
+ }
+ if ((u8_value & SDIO_SHS) != SDIO_SHS) {
+ /* No supported, it is not a protocol error */
+ return true;
+ }
+ /* HS mode possible, then enable */
+ u8_value = SDIO_EHS;
+ if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, SDIO_CIA, SDIO_CCCR_HS, 1, &u8_value)) {
+ return false;
+ }
+ sd_mmc_card->high_speed = 1;
+ sd_mmc_card->clock *= 2;
+ return true;
+}
+
+#else
+static bool sdio_op_cond(void)
+{
+ return true; /* No error but card type not updated */
+}
+static bool sdio_get_max_speed(void)
+{
+ return false;
+}
+static bool sdio_cmd52_set_bus_width(void)
+{
+ return false;
+}
+static bool sdio_cmd52_set_high_speed(void)
+{
+ return false;
+}
+#endif
+
+/**
+ * \brief CMD6 for SD - Switch card in high speed mode
+ *
+ * \note CMD6 for SD is valid under the "trans" state.
+ * \note sd_mmc_card->high_speed is updated.
+ * \note sd_mmc_card->clock is updated.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_cm6_set_high_speed(void)
+{
+ uint8_t switch_status[SD_SW_STATUS_BSIZE] = {0};
+
+ if (!driver_adtc_start(sd_mmc_hal,
+ SD_CMD6_SWITCH_FUNC,
+ SD_CMD6_MODE_SWITCH | SD_CMD6_GRP6_NO_INFLUENCE | SD_CMD6_GRP5_NO_INFLUENCE
+ | SD_CMD6_GRP4_NO_INFLUENCE | SD_CMD6_GRP3_NO_INFLUENCE | SD_CMD6_GRP2_DEFAULT
+ | SD_CMD6_GRP1_HIGH_SPEED,
+ SD_SW_STATUS_BSIZE,
+ 1,
+ true)) {
+ return false;
+ }
+ if (!driver_start_read_blocks(sd_mmc_hal, switch_status, 1)) {
+ return false;
+ }
+ if (!driver_wait_end_of_read_blocks(sd_mmc_hal)) {
+ return false;
+ }
+
+ if (driver_get_response(sd_mmc_hal) & CARD_STATUS_SWITCH_ERROR) {
+ return false;
+ }
+ if (SD_SW_STATUS_FUN_GRP1_RC(switch_status) == SD_SW_STATUS_FUN_GRP_RC_ERROR) {
+ /* No supported, it is not a protocol error */
+ return true;
+ }
+ if (SD_SW_STATUS_FUN_GRP1_BUSY(switch_status)) {
+ return false;
+ }
+ /* CMD6 function switching period is within 8 clocks
+ * after the end bit of status data.*/
+ driver_send_clock(sd_mmc_hal);
+ sd_mmc_card->high_speed = 1;
+ sd_mmc_card->clock *= 2;
+ return true;
+}
+
+#if CONF_MMC_SUPPORT
+
+/**
+ * \brief CMD6 for MMC - Switches the bus width mode
+ *
+ * \note CMD6 is valid under the "trans" state.
+ * \note sd_mmc_card->bus_width is updated.
+ *
+ * \param bus_width Bus width to set
+ *
+ * \return true if success, otherwise false
+ */
+static bool mmc_cmd6_set_bus_width(uint8_t bus_width)
+{
+ uint32_t arg;
+
+ switch (bus_width) {
+ case 8:
+ arg = MMC_CMD6_ACCESS_SET_BITS | MMC_CMD6_INDEX_BUS_WIDTH | MMC_CMD6_VALUE_BUS_WIDTH_8BIT;
+ break;
+ case 4:
+ arg = MMC_CMD6_ACCESS_SET_BITS | MMC_CMD6_INDEX_BUS_WIDTH | MMC_CMD6_VALUE_BUS_WIDTH_4BIT;
+ break;
+ default:
+ arg = MMC_CMD6_ACCESS_SET_BITS | MMC_CMD6_INDEX_BUS_WIDTH | MMC_CMD6_VALUE_BUS_WIDTH_1BIT;
+ break;
+ }
+ if (!driver_send_cmd(sd_mmc_hal, MMC_CMD6_SWITCH, arg)) {
+ return false;
+ }
+ if (driver_get_response(sd_mmc_hal) & CARD_STATUS_SWITCH_ERROR) {
+ /* No supported, it is not a protocol error */
+ return false;
+ }
+ sd_mmc_card->bus_width = bus_width;
+ return true;
+}
+
+/**
+ * \brief CMD6 for MMC - Switches in high speed mode
+ *
+ * \note CMD6 is valid under the "trans" state.
+ * \note sd_mmc_card->high_speed is updated.
+ * \note sd_mmc_card->clock is updated.
+ *
+ * \return true if success, otherwise false
+ */
+static bool mmc_cmd6_set_high_speed(void)
+{
+ if (!driver_send_cmd(sd_mmc_hal,
+ MMC_CMD6_SWITCH,
+ MMC_CMD6_ACCESS_WRITE_BYTE | MMC_CMD6_INDEX_HS_TIMING | MMC_CMD6_VALUE_HS_TIMING_ENABLE)) {
+ return false;
+ }
+ if (driver_get_response(sd_mmc_hal) & CARD_STATUS_SWITCH_ERROR) {
+ /* No supported, it is not a protocol error */
+ return false;
+ }
+ sd_mmc_card->high_speed = 1;
+ sd_mmc_card->clock = 52000000lu;
+ return true;
+}
+#endif
+
+/**
+ * \brief CMD8 for SD card - Send Interface Condition Command.
+ *
+ * \note
+ * Send SD Memory Card interface condition, which includes host supply
+ * voltage information and asks the card whether card supports voltage.
+ * Should be performed at initialization time to detect the card type.
+ *
+ * \param v2 Pointer to v2 flag to update
+ *
+ * \return true if success, otherwise false
+ * with a update of \ref sd_mmc_err.
+ */
+static bool sd_cmd8(uint8_t *v2)
+{
+ uint32_t resp;
+
+ *v2 = 0;
+ /* Test for SD version 2 */
+ if (!driver_send_cmd(sd_mmc_hal, SD_CMD8_SEND_IF_COND, SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
+ return true; /* It is not a V2 */
+ }
+ /* Check R7 response */
+ resp = driver_get_response(sd_mmc_hal);
+ if (resp == 0xFFFFFFFF) {
+ /* No compliance R7 value */
+ return true; /* It is not a V2 */
+ }
+ if ((resp & (SD_CMD8_MASK_PATTERN | SD_CMD8_MASK_VOLTAGE)) != (SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
+ return false;
+ }
+ *v2 = 1;
+ return true;
+}
+
+#if CONF_MMC_SUPPORT
+/**
+ * \brief CMD8 - The card sends its EXT_CSD register as a block of data.
+ *
+ * \param b_authorize_high_speed Pointer to update with the high speed
+ * support information
+ *
+ * \return true if success, otherwise false
+ */
+static bool mmc_cmd8(uint8_t *b_authorize_high_speed)
+{
+ uint16_t i;
+ uint32_t ext_csd;
+ uint32_t sec_count;
+
+ if (!driver_adtc_start(sd_mmc_hal, MMC_CMD8_SEND_EXT_CSD, 0, EXT_CSD_BSIZE, 1, false)) {
+ return false;
+ }
+ /* Read and decode Extended Extended CSD
+ * Note: The read access is done in byte to avoid a buffer
+ * of EXT_CSD_BSIZE Byte in stack.*/
+
+ /* Read card type */
+ for (i = 0; i < (EXT_CSD_CARD_TYPE_INDEX + 4) / 4; i++) {
+ if (!driver_read_word(sd_mmc_hal, &ext_csd)) {
+ return false;
+ }
+ }
+ *b_authorize_high_speed = (ext_csd >> ((EXT_CSD_CARD_TYPE_INDEX % 4) * 8)) & MMC_CTYPE_52MHZ;
+
+ if (MMC_CSD_C_SIZE(sd_mmc_card->csd) == 0xFFF) {
+ /* For high capacity SD/MMC card,
+ * memory capacity = SEC_COUNT * 512 byte */
+ for (; i < (EXT_CSD_SEC_COUNT_INDEX + 4) / 4; i++) {
+ if (!driver_read_word(sd_mmc_hal, &sec_count)) {
+ return false;
+ }
+ }
+ sd_mmc_card->capacity = sec_count / 2;
+ }
+ for (; i < EXT_CSD_BSIZE / 4; i++) {
+ if (!driver_read_word(sd_mmc_hal, &sec_count)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#endif
+
+/**
+ * \brief CMD9: Addressed card sends its card-specific
+ * data (CSD) on the CMD line mci.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_mmc_cmd9_mci(void)
+{
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_MCI_CMD9_SEND_CSD, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ driver_get_response_128(sd_mmc_hal, sd_mmc_card->csd);
+ return true;
+}
+
+#if CONF_MMC_SUPPORT
+/**
+ * \brief Decodes MMC CSD register
+ */
+static void mmc_decode_csd(void)
+{
+ uint32_t unit;
+ uint32_t mul;
+ uint32_t tran_speed;
+
+ /* Get MMC System Specification version supported by the card */
+ switch (MMC_CSD_SPEC_VERS(sd_mmc_card->csd)) {
+ default:
+ case 0:
+ sd_mmc_card->version = CARD_VER_MMC_1_2;
+ break;
+
+ case 1:
+ sd_mmc_card->version = CARD_VER_MMC_1_4;
+ break;
+
+ case 2:
+ sd_mmc_card->version = CARD_VER_MMC_2_2;
+ break;
+
+ case 3:
+ sd_mmc_card->version = CARD_VER_MMC_3;
+ break;
+
+ case 4:
+ sd_mmc_card->version = CARD_VER_MMC_4;
+ break;
+ }
+
+ /* Get MMC memory max transfer speed in Hz.*/
+ tran_speed = CSD_TRAN_SPEED(sd_mmc_card->csd);
+ unit = sd_mmc_trans_units[tran_speed & 0x7];
+ mul = mmc_trans_multipliers[(tran_speed >> 3) & 0xF];
+ sd_mmc_card->clock = unit * mul * 1000;
+
+ /*
+ * Get card capacity.
+ * ----------------------------------------------------
+ * For normal SD/MMC card:
+ * memory capacity = BLOCKNR * BLOCK_LEN
+ * Where
+ * BLOCKNR = (C_SIZE+1) * MULT
+ * MULT = 2 ^ (C_SIZE_MULT+2) (C_SIZE_MULT < 8)
+ * BLOCK_LEN = 2 ^ READ_BL_LEN (READ_BL_LEN < 12)
+ * ----------------------------------------------------
+ * For high capacity SD/MMC card:
+ * memory capacity = SEC_COUNT * 512 byte
+ */
+ if (MMC_CSD_C_SIZE(sd_mmc_card->csd) != 0xFFF) {
+ uint32_t blocknr
+ = ((MMC_CSD_C_SIZE(sd_mmc_card->csd) + 1) * (1 << (MMC_CSD_C_SIZE_MULT(sd_mmc_card->csd) + 2)));
+ sd_mmc_card->capacity = blocknr * (1 << MMC_CSD_READ_BL_LEN(sd_mmc_card->csd)) / 1024;
+ }
+}
+#endif
+
+/**
+ * \brief Decodes SD CSD register
+ */
+static void sd_decode_csd(void)
+{
+ uint32_t unit;
+ uint32_t mul;
+ uint32_t tran_speed;
+
+ /* Get SD memory maximum transfer speed in Hz. */
+ tran_speed = CSD_TRAN_SPEED(sd_mmc_card->csd);
+ unit = sd_mmc_trans_units[tran_speed & 0x7];
+ mul = sd_trans_multipliers[(tran_speed >> 3) & 0xF];
+ sd_mmc_card->clock = unit * mul * 1000;
+
+ /*
+ * Get card capacity.
+ * ----------------------------------------------------
+ * For normal SD/MMC card:
+ * memory capacity = BLOCKNR * BLOCK_LEN
+ * Where
+ * BLOCKNR = (C_SIZE+1) * MULT
+ * MULT = 2 ^ (C_SIZE_MULT+2) (C_SIZE_MULT < 8)
+ * BLOCK_LEN = 2 ^ READ_BL_LEN (READ_BL_LEN < 12)
+ * ----------------------------------------------------
+ * For high capacity SD card:
+ * memory capacity = (C_SIZE+1) * 512K byte
+ */
+ if (CSD_STRUCTURE_VERSION(sd_mmc_card->csd) >= SD_CSD_VER_2_0) {
+ sd_mmc_card->capacity = (SD_CSD_2_0_C_SIZE(sd_mmc_card->csd) + 1) * 512;
+ } else {
+ uint32_t blocknr
+ = ((SD_CSD_1_0_C_SIZE(sd_mmc_card->csd) + 1) * (1 << (SD_CSD_1_0_C_SIZE_MULT(sd_mmc_card->csd) + 2)));
+ sd_mmc_card->capacity = blocknr * (1 << SD_CSD_1_0_READ_BL_LEN(sd_mmc_card->csd)) / 1024;
+ }
+}
+
+/**
+ * \brief CMD13 - Addressed card sends its status register.
+ * This function waits the clear of the busy flag
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_mmc_cmd13(void)
+{
+ uint32_t nec_timeout;
+
+ /* Wait for data ready status.
+ * Nec timing: 0 to unlimited
+ * However a timeout is used.
+ * 200 000 * 8 cycles
+ */
+ nec_timeout = 200000;
+ do {
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_MCI_CMD13_SEND_STATUS, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ /* Check busy flag */
+ if (driver_get_response(sd_mmc_hal) & CARD_STATUS_READY_FOR_DATA) {
+ break;
+ }
+ if (nec_timeout-- == 0) {
+ return false;
+ }
+ } while (1);
+
+ return true;
+}
+
+#if (CONF_SDIO_SUPPORT == 1)
+/**
+ * \brief CMD52 - SDIO IO_RW_DIRECT command
+ *
+ * \param rw_flag Direction, 1:write, 0:read.
+ * \param func_nb Number of the function.
+ * \param rd_after_wr Read after Write flag.
+ * \param reg_addr register address.
+ * \param io_data Pointer to input argument and response buffer.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_cmd52(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr, uint8_t rd_after_wr, uint8_t *io_data)
+{
+ ASSERT(io_data != NULL);
+ if (!driver_send_cmd(sd_mmc_hal,
+ SDIO_CMD52_IO_RW_DIRECT,
+ ((uint32_t)*io_data << SDIO_CMD52_WR_DATA) | ((uint32_t)rw_flag << SDIO_CMD52_RW_FLAG)
+ | ((uint32_t)func_nb << SDIO_CMD52_FUNCTION_NUM)
+ | ((uint32_t)rd_after_wr << SDIO_CMD52_RAW_FLAG)
+ | ((uint32_t)reg_addr << SDIO_CMD52_REG_ADRR))) {
+ return false;
+ }
+ *io_data = driver_get_response(sd_mmc_hal) & 0xFF;
+ return true;
+}
+
+/**
+ * \brief CMD53 - SDIO IO_RW_EXTENDED command
+ * This implementation support only the SDIO multi-byte transfer mode which is
+ * similar to the single block transfer on memory.
+ * Note: The SDIO block transfer mode is optional for SDIO card.
+ *
+ * \param rw_flag Direction, 1:write, 0:read.
+ * \param func_nb Number of the function.
+ * \param reg_addr Register address.
+ * \param inc_addr 1:Incrementing address, 0: fixed.
+ * \param size Transfer data size.
+ * \param access_block true, if the block access (DMA) is used
+ *
+ * \return true if success, otherwise false
+ */
+static bool sdio_cmd53(uint8_t rw_flag, uint8_t func_nb, uint32_t reg_addr, uint8_t inc_addr, uint32_t size,
+ bool access_block)
+{
+ ASSERT(size != 0);
+ ASSERT(size <= 512);
+
+ return driver_adtc_start(
+ sd_mmc_hal,
+ (rw_flag == SDIO_CMD53_READ_FLAG) ? SDIO_CMD53_IO_R_BYTE_EXTENDED : SDIO_CMD53_IO_W_BYTE_EXTENDED,
+ ((size % 512) << SDIO_CMD53_COUNT) | ((uint32_t)reg_addr << SDIO_CMD53_REG_ADDR)
+ | ((uint32_t)inc_addr << SDIO_CMD53_OP_CODE) | ((uint32_t)0 << SDIO_CMD53_BLOCK_MODE)
+ | ((uint32_t)func_nb << SDIO_CMD53_FUNCTION_NUM) | ((uint32_t)rw_flag << SDIO_CMD53_RW_FLAG),
+ size,
+ 1,
+ access_block);
+}
+#endif
+
+/**
+ * \brief ACMD6 - Define the data bus width to 4 bits bus
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_acmd6(void)
+{
+ /* CMD55 - Indicate to the card that the next command is an
+ * application specific command rather than a standard command.*/
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ /* 10b = 4 bits bus */
+ if (!driver_send_cmd(sd_mmc_hal, SD_ACMD6_SET_BUS_WIDTH, 0x2)) {
+ return false;
+ }
+ sd_mmc_card->bus_width = 4;
+ return true;
+}
+
+/**
+ * \brief ACMD51 - Read the SD Configuration Register.
+ *
+ * \note
+ * SD Card Configuration Register (SCR) provides information on the SD Memory
+ * Card's special features that were configured into the given card. The size
+ * of SCR register is 64 bits.
+ *
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_acmd51(void)
+{
+ uint8_t scr[SD_SCR_REG_BSIZE];
+
+ /* CMD55 - Indicate to the card that the next command is an
+ * application specific command rather than a standard command.*/
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD55_APP_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ if (!driver_adtc_start(sd_mmc_hal, SD_ACMD51_SEND_SCR, 0, SD_SCR_REG_BSIZE, 1, true)) {
+ return false;
+ }
+ if (!driver_start_read_blocks(sd_mmc_hal, scr, 1)) {
+ return false;
+ }
+ if (!driver_wait_end_of_read_blocks(sd_mmc_hal)) {
+ return false;
+ }
+
+ /* Get SD Memory Card - Spec. Version */
+ switch (SD_SCR_SD_SPEC(scr)) {
+ case SD_SCR_SD_SPEC_1_0_01:
+ sd_mmc_card->version = CARD_VER_SD_1_0;
+ break;
+
+ case SD_SCR_SD_SPEC_1_10:
+ sd_mmc_card->version = CARD_VER_SD_1_10;
+ break;
+
+ case SD_SCR_SD_SPEC_2_00:
+ if (SD_SCR_SD_SPEC3(scr) == SD_SCR_SD_SPEC_3_00) {
+ sd_mmc_card->version = CARD_VER_SD_3_0;
+ } else {
+ sd_mmc_card->version = CARD_VER_SD_2_0;
+ }
+ break;
+
+ default:
+ sd_mmc_card->version = CARD_VER_SD_1_0;
+ break;
+ }
+ return true;
+}
+
+/**
+ * \brief Select a card slot and initialize the associated driver
+ *
+ * \param slot Card slot number
+ *
+ * \retval SD_MMC_ERR_SLOT Wrong slot number
+ * \retval SD_MMC_ERR_NO_CARD No card present on slot
+ * \retval SD_MMC_ERR_UNUSABLE Unusable card
+ * \retval SD_MMC_INIT_ONGOING Card initialization requested
+ * \retval SD_MMC_OK Card present
+ */
+static sd_mmc_err_t sd_mmc_select_slot(uint8_t slot)
+{
+ if (slot >= CONF_SD_MMC_MEM_CNT) {
+ return SD_MMC_ERR_SLOT;
+ }
+
+ if (_cd && _cd[slot].pin != -1) {
+ /** Card Detect pins */
+ if (gpio_get_pin_level(_cd[slot].pin) != _cd[slot].val) {
+ if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_DEBOUNCE) {
+ SD_MMC_STOP_TIMEOUT();
+ }
+ sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD;
+ return SD_MMC_ERR_NO_CARD;
+ }
+ if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_NO_CARD) {
+ /* A card plug on going, but this is not initialized */
+ sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_DEBOUNCE;
+ /* Debounce + Power On Setup */
+ SD_MMC_START_TIMEOUT();
+ return SD_MMC_ERR_NO_CARD;
+ }
+ if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_DEBOUNCE) {
+ if (!SD_MMC_IS_TIMEOUT()) {
+ /* Debounce on going */
+ return SD_MMC_ERR_NO_CARD;
+ }
+ /* Card is not initialized */
+ sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_INIT;
+ /* Set 1-bit bus width and low clock for initialization */
+ sd_mmc_cards[slot].clock = SDMMC_CLOCK_INIT;
+ sd_mmc_cards[slot].bus_width = 1;
+ sd_mmc_cards[slot].high_speed = 0;
+ }
+ if (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_UNUSABLE) {
+ return SD_MMC_ERR_UNUSABLE;
+ }
+ } else {
+ /* No pin card detection, then always try to install it */
+ if ((sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_NO_CARD)
+ || (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_UNUSABLE)) {
+ /* Card is not initialized */
+ sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_INIT;
+ /* Set 1-bit bus width and low clock for initialization */
+ sd_mmc_cards[slot].clock = SDMMC_CLOCK_INIT;
+ sd_mmc_cards[slot].bus_width = 1;
+ sd_mmc_cards[slot].high_speed = 0;
+ }
+ }
+
+ ASSERT(!(sd_mmc_slot_sel != slot && sd_mmc_nb_block_remaining != 0));
+
+ /* Initialize interface */
+ sd_mmc_slot_sel = slot;
+ sd_mmc_card = &sd_mmc_cards[slot];
+ sd_mmc_configure_slot();
+ return (sd_mmc_cards[slot].state == SD_MMC_CARD_STATE_INIT) ? SD_MMC_INIT_ONGOING : SD_MMC_OK;
+}
+
+/**
+ * \brief Configures the driver with the selected card configuration
+ */
+static void sd_mmc_configure_slot(void)
+{
+ driver_select_device(
+ sd_mmc_hal, sd_mmc_slot_sel, sd_mmc_card->clock, sd_mmc_card->bus_width, sd_mmc_card->high_speed);
+}
+
+/**
+ * \brief Deselect the current card slot
+ */
+static void sd_mmc_deselect_slot(void)
+{
+ if (sd_mmc_slot_sel < CONF_SD_MMC_MEM_CNT) {
+ driver_deselect_device(sd_mmc_hal, sd_mmc_slot_sel);
+ }
+}
+
+/**
+ * \brief Initialize the SD card in MCI mode.
+ *
+ * \note
+ * This function runs the initialization procedure and the identification
+ * process, then it sets the SD/MMC card in transfer state.
+ * At last, it will automaticly enable maximum bus width and transfer speed.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_mmc_mci_card_init(void)
+{
+ uint8_t v2 = 0;
+#if (CONF_SDIO_SUPPORT == 1)
+ uint8_t data = 0x08;
+#endif
+
+ /* In first, try to install SD/SDIO card */
+ sd_mmc_card->type = CARD_TYPE_SD;
+ sd_mmc_card->version = CARD_VER_UNKNOWN;
+ sd_mmc_card->rca = 0;
+
+ /* Card need of 74 cycles clock minimum to start */
+ driver_send_clock(sd_mmc_hal);
+
+#if (CONF_SDIO_SUPPORT == 1)
+ /* CMD52 Reset SDIO */
+ sdio_cmd52(SDIO_CMD52_WRITE_FLAG, SDIO_CIA, SDIO_CCCR_IOA, 0, &data);
+#endif
+
+ /* CMD0 - Reset all cards to idle state.*/
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
+ return false;
+ }
+ if (!sd_cmd8(&v2)) {
+ return false;
+ }
+ /* Try to get the SDIO card's operating condition */
+ if (!sdio_op_cond()) {
+ return false;
+ }
+
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ /* Try to get the SD card's operating condition */
+ if (!sd_mci_op_cond(v2)) {
+ /* It is not a SD card */
+#if CONF_MMC_SUPPORT
+ sd_mmc_card->type = CARD_TYPE_MMC;
+ return sd_mmc_mci_install_mmc();
+#else
+ sd_mmc_card->type = CARD_TYPE_UNKNOWN;
+ return false;
+#endif
+ }
+ }
+
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ /* SD MEMORY, Put the Card in Identify Mode
+ * Note: The CID is not used in this stack */
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD2_ALL_SEND_CID, 0)) {
+ return false;
+ }
+ }
+ /* Ask the card to publish a new relative address (RCA).*/
+ if (!driver_send_cmd(sd_mmc_hal, SD_CMD3_SEND_RELATIVE_ADDR, 0)) {
+ return false;
+ }
+ sd_mmc_card->rca = (driver_get_response(sd_mmc_hal) >> 16) & 0xFFFF;
+
+ /* SD MEMORY, Get the Card-Specific Data */
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ if (!sd_mmc_cmd9_mci()) {
+ return false;
+ }
+ sd_decode_csd();
+ }
+ /* Select the and put it into Transfer Mode */
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD7_SELECT_CARD_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ /* SD MEMORY, Read the SCR to get card version */
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ if (!sd_acmd51()) {
+ return false;
+ }
+ }
+ if (IS_SDIO()) {
+ if (!sdio_get_max_speed()) {
+ return false;
+ }
+ }
+ if ((4 <= driver_get_bus_width(sd_mmc_hal, sd_mmc_slot_sel))) {
+ /* TRY to enable 4-bit mode */
+ if (IS_SDIO()) {
+ if (!sdio_cmd52_set_bus_width()) {
+ return false;
+ }
+ }
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ if (!sd_acmd6()) {
+ return false;
+ }
+ }
+ /* Switch to selected bus mode */
+ sd_mmc_configure_slot();
+ }
+ if (driver_is_high_speed_capable(sd_mmc_hal)) {
+ /* TRY to enable High-Speed Mode */
+ if (IS_SDIO()) {
+ if (!sdio_cmd52_set_high_speed()) {
+ return false;
+ }
+ }
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ if (sd_mmc_card->version > CARD_VER_SD_1_0) {
+ if (!sd_cm6_set_high_speed()) {
+ return false;
+ }
+ }
+ }
+ /* Valid new configuration */
+ sd_mmc_configure_slot();
+ }
+ /* SD MEMORY, Set default block size */
+ if (sd_mmc_card->type & CARD_TYPE_SD) {
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#if CONF_MMC_SUPPORT
+/**
+ * \brief Initialize the MMC card in MCI mode.
+ *
+ * \note
+ * This function runs the initialization procedure and the identification
+ * process, then it sets the SD/MMC card in transfer state.
+ * At last, it will automaticly enable maximum bus width and transfer speed.
+ *
+ * \return true if success, otherwise false
+ */
+static bool sd_mmc_mci_install_mmc(void)
+{
+ uint8_t b_authorize_high_speed;
+
+ /* CMD0 - Reset all cards to idle state. */
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
+ return false;
+ }
+
+ if (!mmc_mci_op_cond()) {
+ return false;
+ }
+
+ /* Put the Card in Identify Mode
+ * Note: The CID is not used in this stack*/
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD2_ALL_SEND_CID, 0)) {
+ return false;
+ }
+ /* Assign relative address to the card.*/
+ sd_mmc_card->rca = 1;
+ if (!driver_send_cmd(sd_mmc_hal, MMC_CMD3_SET_RELATIVE_ADDR, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ /* Get the Card-Specific Data */
+ if (!sd_mmc_cmd9_mci()) {
+ return false;
+ }
+ mmc_decode_csd();
+ /* Select the and put it into Transfer Mode */
+ if (!driver_send_cmd(sd_mmc_hal, SDMMC_CMD7_SELECT_CARD_CMD, (uint32_t)sd_mmc_card->rca << 16)) {
+ return false;
+ }
+ if (sd_mmc_card->version >= CARD_VER_MMC_4) {
+ /* For MMC 4.0 Higher version
+ * Get EXT_CSD */
+ if (!mmc_cmd8(&b_authorize_high_speed)) {
+ return false;
+ }
+ if (4 <= driver_get_bus_width(sd_mmc_hal, sd_mmc_slot_sel)) {
+ /* Enable more bus width */
+ if (!mmc_cmd6_set_bus_width(driver_get_bus_width(sd_mmc_hal, sd_mmc_slot_sel))) {
+ return false;
+ }
+ /* Reinitialize the slot with the bus width */
+ sd_mmc_configure_slot();
+ }
+ if (driver_is_high_speed_capable(sd_mmc_hal) && b_authorize_high_speed) {
+ /* Enable HS */
+ if (!mmc_cmd6_set_high_speed()) {
+ return false;
+ }
+ /* Reinitialize the slot with the new speed */
+ sd_mmc_configure_slot();
+ }
+ } else {
+ /* Reinitialize the slot with the new speed */
+ sd_mmc_configure_slot();
+ }
+
+ uint8_t retry = 10;
+ while (retry--) {
+ /* Retry is a WORKAROUND for no compliance card (Atmel Internal ref. MMC19):
+ * These cards seem not ready immediatly
+ * after the end of busy of mmc_cmd6_set_high_speed()*/
+
+ /* Set default block size */
+ if (driver_send_cmd(sd_mmc_hal, SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+/*--------------------- PUBLIC FUNCTIONS ----------------------------*/
+
+void sd_mmc_init(void *hal, sd_mmc_detect_t *card_detects, sd_mmc_detect_t *wp_detects)
+{
+ /* GPIO will be used to detect card and write protect.
+ * The related clocks and pinmux must be configurated in good
+ * condition. */
+
+ for (uint8_t slot = 0; slot < CONF_SD_MMC_MEM_CNT; slot++) {
+ sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD;
+ }
+ sd_mmc_slot_sel = 0xFF; /* No slot configurated */
+ sd_mmc_hal = hal;
+ _cd = card_detects;
+ _wp = wp_detects;
+}
+
+uint8_t sd_mmc_nb_slot(void)
+{
+ return CONF_SD_MMC_MEM_CNT;
+}
+
+sd_mmc_err_t sd_mmc_check(uint8_t slot)
+{
+ sd_mmc_err_t sd_mmc_err;
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_INIT_ONGOING) {
+ sd_mmc_deselect_slot();
+ return sd_mmc_err;
+ }
+
+ /* Initialization of the card requested */
+ if (sd_mmc_mci_card_init()) {
+ sd_mmc_card->state = SD_MMC_CARD_STATE_READY;
+ sd_mmc_deselect_slot();
+ /* To notify that the card has been just initialized
+ * It is necessary for USB Device MSC */
+ return SD_MMC_INIT_ONGOING;
+ }
+ sd_mmc_card->state = SD_MMC_CARD_STATE_UNUSABLE;
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_UNUSABLE;
+}
+
+card_type_t sd_mmc_get_type(uint8_t slot)
+{
+ if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
+ return CARD_TYPE_UNKNOWN;
+ }
+ sd_mmc_deselect_slot();
+ return sd_mmc_card->type;
+}
+
+card_version_t sd_mmc_get_version(uint8_t slot)
+{
+ if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
+ return CARD_VER_UNKNOWN;
+ }
+ sd_mmc_deselect_slot();
+ return sd_mmc_card->version;
+}
+
+uint32_t sd_mmc_get_capacity(uint8_t slot)
+{
+ if (SD_MMC_OK != sd_mmc_select_slot(slot)) {
+ return 0;
+ }
+ sd_mmc_deselect_slot();
+ return sd_mmc_card->capacity;
+}
+
+bool sd_mmc_is_write_protected(uint8_t slot)
+{
+ /* No detection, always writable */
+ if (!_wp || _wp[slot].pin == -1) {
+ return false;
+ }
+ /* Write Protect Detect */
+ if (gpio_get_pin_level(_wp[slot].pin) == _wp[slot].val) {
+ return true;
+ }
+ return false;
+}
+
+sd_mmc_err_t sd_mmc_init_read_blocks(uint8_t slot, uint32_t start, uint16_t nb_block)
+{
+ sd_mmc_err_t sd_mmc_err;
+ uint32_t cmd, arg, resp;
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+
+ /* Wait for data ready status */
+ if (!sd_mmc_cmd13()) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ if (nb_block > 1) {
+ cmd = SDMMC_CMD18_READ_MULTIPLE_BLOCK;
+ } else {
+ cmd = SDMMC_CMD17_READ_SINGLE_BLOCK;
+ }
+ /*
+ * SDSC Card (CCS=0) uses byte unit address,
+ * SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
+ */
+ if (sd_mmc_card->type & CARD_TYPE_HC) {
+ arg = start;
+ } else {
+ arg = (start * SD_MMC_BLOCK_SIZE);
+ }
+
+ if (!driver_adtc_start(sd_mmc_hal, cmd, arg, SD_MMC_BLOCK_SIZE, nb_block, true)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ /* Check response */
+ resp = driver_get_response(sd_mmc_hal);
+ if (resp & CARD_STATUS_ERR_RD_WR) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_nb_block_remaining = nb_block;
+ sd_mmc_nb_block_to_tranfer = nb_block;
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sd_mmc_start_read_blocks(void *dest, uint16_t nb_block)
+{
+ ASSERT(sd_mmc_nb_block_remaining >= nb_block);
+
+ if (!driver_start_read_blocks(sd_mmc_hal, dest, nb_block)) {
+ sd_mmc_nb_block_remaining = 0;
+ return SD_MMC_ERR_COMM;
+ }
+ sd_mmc_nb_block_remaining -= nb_block;
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sd_mmc_wait_end_of_read_blocks(bool abort)
+{
+ if (!driver_wait_end_of_read_blocks(sd_mmc_hal)) {
+ return SD_MMC_ERR_COMM;
+ }
+ if (abort) {
+ sd_mmc_nb_block_remaining = 0;
+ } else if (sd_mmc_nb_block_remaining) {
+ return SD_MMC_OK;
+ }
+
+ /* All blocks are transfered then stop read operation */
+ if (sd_mmc_nb_block_to_tranfer == 1) {
+ /* Single block transfer, then nothing to do */
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+ }
+ /* WORKAROUND for no compliance card (Atmel Internal ref. !MMC7 !SD19):
+ * The errors on this command must be ignored
+ * and one retry can be necessary in SPI mode for no compliance card.*/
+ if (!driver_adtc_stop(sd_mmc_hal, SDMMC_CMD12_STOP_TRANSMISSION, 0)) {
+ driver_adtc_stop(sd_mmc_hal, SDMMC_CMD12_STOP_TRANSMISSION, 0);
+ }
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sd_mmc_init_write_blocks(uint8_t slot, uint32_t start, uint16_t nb_block)
+{
+ sd_mmc_err_t sd_mmc_err;
+ uint32_t cmd, arg, resp;
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+ if (sd_mmc_is_write_protected(slot)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_WP;
+ }
+
+ if (nb_block > 1) {
+ cmd = SDMMC_CMD25_WRITE_MULTIPLE_BLOCK;
+ } else {
+ cmd = SDMMC_CMD24_WRITE_BLOCK;
+ }
+ /*
+ * SDSC Card (CCS=0) uses byte unit address,
+ * SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit).
+ */
+ if (sd_mmc_card->type & CARD_TYPE_HC) {
+ arg = start;
+ } else {
+ arg = (start * SD_MMC_BLOCK_SIZE);
+ }
+ if (!driver_adtc_start(sd_mmc_hal, cmd, arg, SD_MMC_BLOCK_SIZE, nb_block, true)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ /* Check response */
+ resp = driver_get_response(sd_mmc_hal);
+ if (resp & CARD_STATUS_ERR_RD_WR) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_nb_block_remaining = nb_block;
+ sd_mmc_nb_block_to_tranfer = nb_block;
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sd_mmc_start_write_blocks(const void *src, uint16_t nb_block)
+{
+ ASSERT(sd_mmc_nb_block_remaining >= nb_block);
+ if (!driver_start_write_blocks(sd_mmc_hal, src, nb_block)) {
+ sd_mmc_nb_block_remaining = 0;
+ return SD_MMC_ERR_COMM;
+ }
+ sd_mmc_nb_block_remaining -= nb_block;
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sd_mmc_wait_end_of_write_blocks(bool abort)
+{
+ if (!driver_wait_end_of_write_blocks(sd_mmc_hal)) {
+ return SD_MMC_ERR_COMM;
+ }
+ if (abort) {
+ sd_mmc_nb_block_remaining = 0;
+ } else if (sd_mmc_nb_block_remaining) {
+ return SD_MMC_OK;
+ }
+
+ /* All blocks are transfered then stop write operation */
+ if (sd_mmc_nb_block_to_tranfer == 1) {
+ /* Single block transfer, then nothing to do */
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+ }
+
+ /* Note: SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.*/
+ if (!driver_adtc_stop(sd_mmc_hal, SDMMC_CMD12_STOP_TRANSMISSION, 0)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+
+#if (CONF_SDIO_SUPPORT == 1)
+sd_mmc_err_t sdio_read_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t *dest)
+{
+ sd_mmc_err_t sd_mmc_err;
+
+ if (dest == NULL) {
+ return SD_MMC_ERR_PARAM;
+ }
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+
+ if (!sdio_cmd52(SDIO_CMD52_READ_FLAG, func_num, addr, 0, dest)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sdio_write_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t data)
+{
+ sd_mmc_err_t sd_mmc_err;
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+
+ if (!sdio_cmd52(SDIO_CMD52_WRITE_FLAG, func_num, addr, 0, &data)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sdio_read_extended(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t inc_addr, uint8_t *dest,
+ uint16_t size)
+{
+ sd_mmc_err_t sd_mmc_err;
+
+ if ((size == 0) || (size > 512)) {
+ return SD_MMC_ERR_PARAM;
+ }
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+
+ if (!sdio_cmd53(SDIO_CMD53_READ_FLAG, func_num, addr, inc_addr, size, true)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ if (!driver_start_read_blocks(sd_mmc_hal, dest, 1)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ if (!driver_wait_end_of_read_blocks(sd_mmc_hal)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+
+sd_mmc_err_t sdio_write_extended(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t inc_addr, uint8_t *src,
+ uint16_t size)
+{
+ sd_mmc_err_t sd_mmc_err;
+
+ if ((size == 0) || (size > 512)) {
+ return SD_MMC_ERR_PARAM;
+ }
+
+ sd_mmc_err = sd_mmc_select_slot(slot);
+ if (sd_mmc_err != SD_MMC_OK) {
+ return sd_mmc_err;
+ }
+
+ if (!sdio_cmd53(SDIO_CMD53_WRITE_FLAG, func_num, addr, inc_addr, size, true)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ if (!driver_start_write_blocks(sd_mmc_hal, src, 1)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+ if (!driver_wait_end_of_write_blocks(sd_mmc_hal)) {
+ sd_mmc_deselect_slot();
+ return SD_MMC_ERR_COMM;
+ }
+
+ sd_mmc_deselect_slot();
+ return SD_MMC_OK;
+}
+#endif
+
+/** @} */
diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.h b/ports/atmel-samd/sd_mmc/sd_mmc.h
new file mode 100644
index 000000000..1437e5efd
--- /dev/null
+++ b/ports/atmel-samd/sd_mmc/sd_mmc.h
@@ -0,0 +1,310 @@
+/**
+ * \file
+ *
+ * \brief Common SD/MMC stack header file
+ *
+ * Copyright (c) 2012-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
+ */
+
+#ifndef SD_MMC_H_INCLUDED
+#define SD_MMC_H_INCLUDED
+
+#include "compiler.h"
+#include "conf_sd_mmc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \ingroup common_memory
+ * \defgroup sd_mmc_stack_group SD/MMC/SDIO common stack
+ *
+ * SD/MMC/SDIO basic APIs used by SD/MMC/SDIO memory
+ * APIs (\ref sd_mmc_stack_mem_group).
+ * Also, it can be used by application which use the SDIO card
+ * or specific application which does not need of File System.
+ *
+ * For usual application which use the SD/MMC card in
+ * memory mode with a file system, please refer to
+ * \ref sd_mmc_stack_mem_group.
+ * @{
+ */
+
+typedef uint8_t sd_mmc_err_t; /**< Type of return error code */
+
+/** \name Return error codes */
+/** @{ */
+#define SD_MMC_OK 0 /**< No error */
+#define SD_MMC_INIT_ONGOING 1 /**< Card not initialized */
+#define SD_MMC_ERR_NO_CARD 2 /**< No SD/MMC card inserted */
+#define SD_MMC_ERR_UNUSABLE 3 /**< Unusable card */
+#define SD_MMC_ERR_SLOT 4 /**< Slot unknow */
+#define SD_MMC_ERR_COMM 5 /**< General communication error */
+#define SD_MMC_ERR_PARAM 6 /**< Illeage input parameter */
+#define SD_MMC_ERR_WP 7 /**< Card write protected */
+/** @} */
+
+typedef uint8_t card_type_t; /**< Type of card type */
+
+/** \name Card Types */
+/** @{ */
+#define CARD_TYPE_UNKNOWN (0) /**< Unknown type card */
+#define CARD_TYPE_SD (1 << 0) /**< SD card */
+#define CARD_TYPE_MMC (1 << 1) /**< MMC card */
+#define CARD_TYPE_SDIO (1 << 2) /**< SDIO card */
+#define CARD_TYPE_HC (1 << 3) /**< High capacity card */
+/** SD combo card (io + memory) */
+#define CARD_TYPE_SD_COMBO (CARD_TYPE_SD | CARD_TYPE_SDIO)
+/** @} */
+
+typedef uint8_t card_version_t; /**< Type of card version */
+
+/** \name Card Versions */
+/** @{ */
+#define CARD_VER_UNKNOWN (0) /**< Unknown card version */
+#define CARD_VER_SD_1_0 (0x10) /**< SD version 1.0 and 1.01 */
+#define CARD_VER_SD_1_10 (0x1A) /**< SD version 1.10 */
+#define CARD_VER_SD_2_0 (0X20) /**< SD version 2.00 */
+#define CARD_VER_SD_3_0 (0X30) /**< SD version 3.0X */
+#define CARD_VER_MMC_1_2 (0x12) /**< MMC version 1.2 */
+#define CARD_VER_MMC_1_4 (0x14) /**< MMC version 1.4 */
+#define CARD_VER_MMC_2_2 (0x22) /**< MMC version 2.2 */
+#define CARD_VER_MMC_3 (0x30) /**< MMC version 3 */
+#define CARD_VER_MMC_4 (0x40) /**< MMC version 4 */
+/** @} */
+
+/** Card detect setting */
+typedef struct sd_mmc_detect {
+ int16_t pin; /**< Detection pin, -1 if no such pin */
+ uint16_t val; /**< Detection value */
+} sd_mmc_detect_t;
+
+/** This SD MMC stack uses the maximum block size autorized (512 bytes) */
+#define SD_MMC_BLOCK_SIZE 512
+
+/**
+ * \brief Initialize the SD/MMC stack and low level driver required
+ * \param[in] hal Pointer to HAL instance
+ * \param[in] card_detects Pointer to list of card detect settings,
+ * list size should be \ref CONF_SD_MMC_MEM_CNT
+ * \param[in] wp_detects Pointer to list of write protect detect settings
+ * list size should be \ref CONF_SD_MMC_MEM_CNT
+ */
+void sd_mmc_init(void *hal, sd_mmc_detect_t *card_detects, sd_mmc_detect_t *wp_detects);
+
+/** \brief Return the number of slot available
+ *
+ * \return Number of card slot available
+ */
+uint8_t sd_mmc_nb_slot(void);
+
+/** \brief Performs a card checks
+ *
+ * \param[in] slot Card slot to use
+ *
+ * \retval SD_MMC_OK Card ready
+ * \retval SD_MMC_INIT_ONGOING Initialization on going
+ * \retval SD_MMC_ERR_NO_CARD Card not present in slot
+ * \retval Other value for error cases, see \ref sd_mmc_err_t
+ */
+sd_mmc_err_t sd_mmc_check(uint8_t slot);
+
+/** \brief Get the card type
+ *
+ * \param[in] slot Card slot
+ *
+ * \return Card type (\ref card_type_t)
+ */
+card_type_t sd_mmc_get_type(uint8_t slot);
+
+/** \brief Get the card version
+ *
+ * \param[in] slot Card slot
+ *
+ * \return Card version (\ref card_version_t)
+ */
+card_version_t sd_mmc_get_version(uint8_t slot);
+
+/** \brief Get the memory capacity
+ *
+ * \param[in] slot Card slot
+ *
+ * \return Capacity (unit KB)
+ */
+uint32_t sd_mmc_get_capacity(uint8_t slot);
+
+/** \brief Get the card write protection status
+ *
+ * \param[in] slot Card slot
+ *
+ * \return true, if write portected
+ */
+bool sd_mmc_is_write_protected(uint8_t slot);
+
+/**
+ * \brief Initialize the read blocks of data from the card.
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] start Start block number to to read.
+ * \param[in] nb_block Total number of blocks to be read.
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_init_read_blocks(uint8_t slot, uint32_t start, uint16_t nb_block);
+
+/**
+ * \brief Start the read blocks of data from the card.
+ *
+ * \param[out] dest Pointer to read buffer.
+ * \param[in] nb_block Number of blocks to be read.
+ *
+ * \return return SD_MMC_OK if started,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_start_read_blocks(void *dest, uint16_t nb_block);
+
+/**
+ * \brief Wait the end of read blocks of data from the card.
+ *
+ * \param[in] abort Abort reading process initialized by
+ * \ref sd_mmc_init_read_blocks() after the reading issued by
+ * \ref sd_mmc_start_read_blocks() is done
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_wait_end_of_read_blocks(bool abort);
+
+/**
+ * \brief Initialize the write blocks of data
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] start Start block number to be written.
+ * \param[in] nb_block Total number of blocks to be written.
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_init_write_blocks(uint8_t slot, uint32_t start, uint16_t nb_block);
+
+/**
+ * \brief Start the write blocks of data
+ *
+ * \param[in] src Pointer to write buffer.
+ * \param[in] nb_block Number of blocks to be written.
+ *
+ * \return return SD_MMC_OK if started,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_start_write_blocks(const void *src, uint16_t nb_block);
+
+/**
+ * \brief Wait the end of write blocks of data
+ *
+ * \param[in] abort Abort writing process initialized by
+ * \ref sd_mmc_init_write_blocks() after the writing issued by
+ * \ref sd_mmc_start_write_blocks() is done
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sd_mmc_wait_end_of_write_blocks(bool abort);
+
+#if (CONF_SDIO_SUPPORT == 1)
+/**
+ * \brief Read one byte from SDIO using RW_DIRECT command.
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] func_num Function number.
+ * \param[in] addr Register address to read from.
+ * \param[out] dest Pointer to read buffer.
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sdio_read_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t *dest);
+/**
+ * \brief Write one byte to SDIO using RW_DIRECT command.
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] func_num Function number.
+ * \param[in] addr Register address to read from.
+ * \param[in] data Data to be written.
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sdio_write_direct(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t data);
+
+/**
+ * \brief Read bytes from SDIO using RW_EXTENDED command.
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] func_num Function number.
+ * \param[in] addr First register address to read from.
+ * \param[in] inc_addr 0 - The data address is fixed.
+ * 1 - The data address increase automatically.
+ * \param[out] dest Pointer to read buffer.
+ * \param[in] size Number of bytes to read (1 ~ 512).
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sdio_read_extended(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t inc_addr, uint8_t *dest,
+ uint16_t size);
+
+/**
+ * \brief Write bytes to SDIO using RW_EXTENDED command.
+ *
+ * \param[in] slot Card slot to use
+ * \param[in] func_num Function number.
+ * \param[in] addr First register address to write to.
+ * \param[in] inc_addr 0 - The data address is fixed.
+ * 1 - The data address increase automatically.
+ * \param[in] src Pointer to write buffer.
+ * \param[in] size Number of bytes to read (1 ~ 512).
+ *
+ * \return return SD_MMC_OK if success,
+ * otherwise return an error code (\ref sd_mmc_err_t).
+ */
+sd_mmc_err_t sdio_write_extended(uint8_t slot, uint8_t func_num, uint32_t addr, uint8_t inc_addr, uint8_t *src,
+ uint16_t size);
+#endif /* SDIO_SUPPORT_ENABLE */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SD_MMC_H_INCLUDED */
diff --git a/ports/atmel-samd/sd_mmc/sd_mmc_protocol.h b/ports/atmel-samd/sd_mmc/sd_mmc_protocol.h
new file mode 100644
index 000000000..0516e4fd1
--- /dev/null
+++ b/ports/atmel-samd/sd_mmc/sd_mmc_protocol.h
@@ -0,0 +1,1001 @@
+/**
+ * \file
+ *
+ * \brief SD/MMC protocol definitions.
+ *
+ * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
+ */
+
+#ifndef SD_MMC_PROTOCOL_H_INCLUDED
+#define SD_MMC_PROTOCOL_H_INCLUDED
+
+#include "compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup sd_mmc_protocol SD/MMC Protocol Definition
+ * \ingroup sd_mmc_stack_group
+ * @{
+ */
+
+// SD/MMC/SDIO default clock frequency for initialization (400KHz)
+#define SDMMC_CLOCK_INIT 400000
+
+/**
+ * \name Macros for command definition
+ *
+ * Commands types:
+ * - broadcast commands (bc), no response
+ * - broadcast commands with response (bcr) (Note: No open drain on SD card)
+ * - addressed (point-to-point) commands (ac), no data transfer on DAT lines
+ * - addressed (point-to-point) data transfer commands (adtc), data transfer
+ * on DAT lines
+ *
+ * Specific MMC norms:
+ * CMD1, CMD2 & CMD3 are processed in the open-drain mode.
+ * The CMD line is driven with push-pull drivers.
+ *
+ * Specific SD norms:
+ * There is no open drain mode in SD memory card.
+ *
+ ***************************************
+ * Responses types:
+ *
+ * R1, R3, R4 & R5 use a 48 bits response protected by a 7bit CRC checksum
+ * - R1 receiv data not specified
+ * - R3 receiv OCR
+ * - R4, R5 RCA management (MMC only)
+ * - R6, R7 RCA management (SD only)
+ *
+ * R1b assert the BUSY signal and respond with R1.
+ * If the busy signal is asserted, it is done two clock cycles (Nsr time)
+ * after the end bit of the command. The DAT0 line is driven low.
+ * DAT1-DAT7 lines are driven by the card though their values are not relevant.
+ *
+ * R2 use a 136 bits response protected by a 7bit CRC checksum
+ * The content is CID or CSD
+ *
+ * Specific MMC norms:
+ * - R4 (Fast I/O) return RCA
+ * - R5 (interrupt request) return RCA null
+ *
+ * Specific SD norms:
+ * - R6 (Published RCA) return RCA
+ * - R7 (Card interface condition) return RCA null
+ *
+ * @{
+ */
+
+//! Value to define a SD/MMC/SDIO command
+typedef uint32_t sdmmc_cmd_def_t;
+
+//! \name Flags used to define a SD/MMC/SDIO command
+//! @{
+#define SDMMC_CMD_GET_INDEX(cmd) (cmd & 0x3F)
+//! Have response (MCI only)
+#define SDMMC_RESP_PRESENT (1lu << 8)
+//! 8 bit response (SPI only)
+#define SDMMC_RESP_8 (1lu << 9)
+//! 32 bit response (SPI only)
+#define SDMMC_RESP_32 (1lu << 10)
+//! 136 bit response (MCI only)
+#define SDMMC_RESP_136 (1lu << 11)
+//! Expect valid crc (MCI only)
+#define SDMMC_RESP_CRC (1lu << 12)
+//! Card may send busy
+#define SDMMC_RESP_BUSY (1lu << 13)
+// Open drain for a braodcast command (bc)
+// or to enter in inactive state (MCI only)
+#define SDMMC_CMD_OPENDRAIN (1lu << 14)
+//! To signal a data write operation
+#define SDMMC_CMD_WRITE (1lu << 15)
+//! To signal a SDIO tranfer in multi byte mode
+#define SDMMC_CMD_SDIO_BYTE (1lu << 16)
+//! To signal a SDIO tranfer in block mode
+#define SDMMC_CMD_SDIO_BLOCK (1lu << 17)
+//! To signal a data transfer in stream mode
+#define SDMMC_CMD_STREAM (1lu << 18)
+//! To signal a data transfer in single block mode
+#define SDMMC_CMD_SINGLE_BLOCK (1lu << 19)
+//! To signal a data transfer in multi block mode
+#define SDMMC_CMD_MULTI_BLOCK (1lu << 20)
+//! @}
+
+//! \name Set of flags to define a reponse type
+//! @{
+#define SDMMC_CMD_NO_RESP (0)
+#define SDMMC_CMD_R1 (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC)
+#define SDMMC_CMD_R1B (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC | SDMMC_RESP_BUSY)
+#define SDMMC_CMD_R2 (SDMMC_RESP_PRESENT | SDMMC_RESP_8 | SDMMC_RESP_136 | SDMMC_RESP_CRC)
+#define SDMMC_CMD_R3 (SDMMC_RESP_PRESENT | SDMMC_RESP_32)
+#define SDMMC_CMD_R4 (SDMMC_RESP_PRESENT | SDMMC_RESP_32)
+#define SDMMC_CMD_R5 (SDMMC_RESP_PRESENT | SDMMC_RESP_8 | SDMMC_RESP_CRC)
+#define SDMMC_CMD_R6 (SDMMC_RESP_PRESENT | SDMMC_RESP_CRC)
+#define SDMMC_CMD_R7 (SDMMC_RESP_PRESENT | SDMMC_RESP_32 | SDMMC_RESP_CRC)
+//! @}
+
+//! \name SD/MMC/SDIO command definitions
+//! SDMMC_CMDx are include in SD and MMC norms
+//! MMC_CMDx are include in MMC norms only
+//! SD_CMDx are include in SD norms only
+//! SDIO_CMDx are include in SDIO norms only
+//! @{
+
+/*
+ * --- Basic commands and read-stream command (class 0 and class 1) ---
+ */
+
+/** Cmd0(bc): Reset all cards to idle state */
+#define SDMMC_SPI_CMD0_GO_IDLE_STATE (0 | SDMMC_CMD_R1)
+#define SDMMC_MCI_CMD0_GO_IDLE_STATE (0 | SDMMC_CMD_NO_RESP | SDMMC_CMD_OPENDRAIN)
+/** MMC Cmd1(bcr, R3): Ask the card to send its Operating Conditions */
+#define MMC_SPI_CMD1_SEND_OP_COND (1 | SDMMC_CMD_R1)
+#define MMC_MCI_CMD1_SEND_OP_COND (1 | SDMMC_CMD_R3 | SDMMC_CMD_OPENDRAIN)
+/** Cmd2(bcr, R2): Ask the card to send its CID number (stuff but arg 0 used) */
+#define SDMMC_CMD2_ALL_SEND_CID (2 | SDMMC_CMD_R2 | SDMMC_CMD_OPENDRAIN)
+/** SD Cmd3(bcr, R6): Ask the card to publish a new relative address (RCA) */
+#define SD_CMD3_SEND_RELATIVE_ADDR (3 | SDMMC_CMD_R6 | SDMMC_CMD_OPENDRAIN)
+/** MMC Cmd3(ac, R1): Assigns relative address to the card */
+#define MMC_CMD3_SET_RELATIVE_ADDR (3 | SDMMC_CMD_R1)
+/** Cmd4(bc): Program the DSR of all cards (MCI only) */
+#define SDMMC_CMD4_SET_DSR (4 | SDMMC_CMD_NO_RESP)
+/** MMC Cmd5(ac, R1b): Toggle the card between Sleep state and Standby state. */
+#define MMC_CMD5_SLEEP_AWAKE (5 | SDMMC_CMD_R1B)
+/** Cmd7(ac, R1/R1b): Select/Deselect card
+ * For SD: R1b only from the selected card.
+ * For MMC: R1 while selecting from Stand-By State to Transfer State;
+ * R1b while selecting from Disconnected State to Programming State.
+ */
+#define SDMMC_CMD7_SELECT_CARD_CMD (7 | SDMMC_CMD_R1B)
+#define SDMMC_CMD7_DESELECT_CARD_CMD (7 | SDMMC_CMD_R1)
+/** MMC Cmd8(adtc, R1): Send EXT_CSD register as a block of data */
+#define MMC_CMD8_SEND_EXT_CSD (8 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK)
+/** SD Cmd8(bcr, R7) : Send SD Memory Card interface condition */
+#define SD_CMD8_SEND_IF_COND (8 | SDMMC_CMD_R7 | SDMMC_CMD_OPENDRAIN)
+/** Cmd9 SPI (R1): Addressed card sends its card-specific data (CSD) */
+#define SDMMC_SPI_CMD9_SEND_CSD (9 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK)
+/** Cmd9 MCI (ac, R2): Addressed card sends its card-specific data (CSD) */
+#define SDMMC_MCI_CMD9_SEND_CSD (9 | SDMMC_CMD_R2)
+/** Cmd10(ac, R2): Addressed card sends its card identification (CID) */
+#define SDMMC_CMD10_SEND_CID (10 | SDMMC_CMD_R2)
+/**
+ * MMC Cmd11(adtc, R1): Read data stream from the card, starting at the given
+ * address, until a STOP_TRANSMISSION follows.
+ */
+#define MMC_CMD11_READ_DAT_UNTIL_STOP (11 | SDMMC_CMD_R1)
+/* SD Cmd11 MCI (ac, R1): Voltage switching */
+#define SD_CMD11_READ_DAT_UNTIL_STOP (11 | SDMMC_CMD_R1)
+/** Cmd12(ac, R1b): Force the card to stop transmission */
+#define SDMMC_CMD12_STOP_TRANSMISSION (12 | SDMMC_CMD_R1B)
+/** Cmd13(R2): Addressed card sends its status register. */
+#define SDMMC_SPI_CMD13_SEND_STATUS (13 | SDMMC_CMD_R2)
+/** Cmd13(ac, R1): Addressed card sends its status register. */
+#define SDMMC_MCI_CMD13_SEND_STATUS (13 | SDMMC_CMD_R1)
+/** MMC Cmd14(adtc, R1): Read the reversed bus testing data pattern from a card. */
+#define MMC_CMD14_BUSTEST_R (14 | SDMMC_CMD_R1)
+/** Cmd15(ac): Send an addressed card into the Inactive State. */
+// Note: It is a ac cmd, but it must be send like bc cmd to open drain
+#define SDMMC_CMD15_GO_INACTIVE_STATE (15 | SDMMC_CMD_NO_RESP | SDMMC_CMD_OPENDRAIN)
+/** MMC Cmd19(adtc, R1): Send the bus test data pattern */
+#define MMC_CMD19_BUSTEST_W (19 | SDMMC_CMD_R1)
+/** Cmd58(R3): Reads the OCR register of a card */
+#define SDMMC_SPI_CMD58_READ_OCR (58 | SDMMC_CMD_R3)
+/** Cmd59(R1): Turns the CRC option on or off */
+#define SDMMC_SPI_CMD59_CRC_ON_OFF (59 | SDMMC_CMD_R1)
+
+/*
+ * --- Block-oriented read commands (class 2) ---
+ */
+/** Cmd16(ac, R1): Set the block length (in bytes) */
+#define SDMMC_CMD16_SET_BLOCKLEN (16 | SDMMC_CMD_R1)
+/** Cmd17(adtc, R1): Read single block */
+#define SDMMC_CMD17_READ_SINGLE_BLOCK (17 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK)
+/** Cmd18(adtc, R1): Read multiple block */
+#define SDMMC_CMD18_READ_MULTIPLE_BLOCK (18 | SDMMC_CMD_R1 | SDMMC_CMD_MULTI_BLOCK)
+
+/*
+ * --- Sequential write commands (class 3) ---
+ */
+
+/**
+ * MMC Cmd20(adtc, R1): Write a data stream from the host, starting at the
+ * given address, until a STOP_TRANSMISSION follows.
+ */
+#define MMC_CMD20_WRITE_DAT_UNTIL_STOP (20 | SDMMC_CMD_R1)
+
+/*
+ * --- Block-oriented write commands (class 4) ---
+ */
+/** MMC Cmd23(ac, R1): Set block count */
+#define MMC_CMD23_SET_BLOCK_COUNT (23 | SDMMC_CMD_R1)
+/** Cmd24(adtc, R1): Write block */
+#define SDMMC_CMD24_WRITE_BLOCK (24 | SDMMC_CMD_R1 | SDMMC_CMD_WRITE | SDMMC_CMD_SINGLE_BLOCK)
+/** Cmd25(adtc, R1): Write multiple block */
+#define SDMMC_CMD25_WRITE_MULTIPLE_BLOCK (25 | SDMMC_CMD_R1 | SDMMC_CMD_WRITE | SDMMC_CMD_MULTI_BLOCK)
+/** MMC Cmd26(adtc, R1): Programming of the card identification register. */
+#define MMC_CMD26_PROGRAM_CID (26 | SDMMC_CMD_R1)
+/** Cmd27(adtc, R1): Programming of the programmable bits of the CSD. */
+#define SDMMC_CMD27_PROGRAM_CSD (27 | SDMMC_CMD_R1)
+
+/*
+ * --- Erase commands (class 5) ---
+ */
+/** SD Cmd32(ac, R1): */
+#define SD_CMD32_ERASE_WR_BLK_START (32 | SDMMC_CMD_R1)
+/** SD Cmd33(ac, R1): */
+#define SD_CMD33_ERASE_WR_BLK_END (33 | SDMMC_CMD_R1)
+/** MMC Cmd35(ac, R1): */
+#define MMC_CMD35_ERASE_GROUP_START (35 | SDMMC_CMD_R1)
+/** MMC Cmd36(ac, R1): */
+#define MMC_CMD36_ERASE_GROUP_END (36 | SDMMC_CMD_R1)
+/** Cmd38(ac, R1B): */
+#define SDMMC_CMD38_ERASE (38 | SDMMC_CMD_R1B)
+
+/*
+ * --- Block Oriented Write Protection Commands (class 6) ---
+ */
+/** Cmd28(ac, R1b): Set write protection */
+#define SDMMC_CMD28_SET_WRITE_PROT (28 | SDMMC_CMD_R1B)
+/** Cmd29(ac, R1b): Clr write protection */
+#define SDMMC_CMD29_CLR_WRITE_PROT (29 | SDMMC_CMD_R1B)
+/** Cmd30(adtc, R1b): Send write protection */
+#define SDMMC_CMD30_SEND_WRITE_PROT (30 | SDMMC_CMD_R1)
+
+/*
+ * --- Lock Card (class 7) ---
+ */
+/** Cmd42(adtc, R1): Used to set/reset the password or lock/unlock the card. */
+#define SDMMC_CMD42_LOCK_UNLOCK (42 | SDMMC_CMD_R1)
+
+/*
+ * --- Application-specific commands (class 8) ---
+ */
+/**
+ * Cmd55(ac, R1): Indicate to the card that the next command is an application
+ * specific command rather than a standard command.
+ */
+#define SDMMC_CMD55_APP_CMD (55 | SDMMC_CMD_R1)
+/**
+ * Cmd 56(adtc, R1): Used either to transfer a data block to the card or to get
+ * a data block from the card for general purpose/application specific commands.
+ */
+#define SDMMC_CMD56_GEN_CMD (56 | SDMMC_CMD_R1)
+
+/**
+ * MMC Cmd6(ac, R1b) : Switche the mode of operation of the selected card
+ * or modifies the EXT_CSD registers.
+ */
+#define MMC_CMD6_SWITCH (6 | SDMMC_CMD_R1B)
+/**
+ * SD Cmd6(adtc, R1) : Check switchable function (mode 0)
+ * and switch card function (mode 1).
+ */
+#define SD_CMD6_SWITCH_FUNC (6 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK)
+/** ACMD6(ac, R1): Define the data bus width */
+#define SD_ACMD6_SET_BUS_WIDTH (6 | SDMMC_CMD_R1)
+/** ACMD13(adtc, R1): Send the SD Status. */
+#define SD_ACMD13_SD_STATUS (13 | SDMMC_CMD_R1)
+/**
+ * ACMD22(adtc, R1): Send the number of the written (with-out errors) write
+ * blocks.
+ */
+#define SD_ACMD22_SEND_NUM_WR_BLOCKS (22 | SDMMC_CMD_R1)
+/**
+ * ACMD23(ac, R1): Set the number of write blocks to be pre-erased before
+ * writing
+ */
+#define SD_ACMD23_SET_WR_BLK_ERASE_COUNT (23 | SDMMC_CMD_R1)
+/**
+ * ACMD41(bcr, R3): Send host capacity support information (HCS) and asks the
+ * accessed card to send its operating condition register (OCR) content
+ * in the response
+ */
+#define SD_MCI_ACMD41_SD_SEND_OP_COND (41 | SDMMC_CMD_R3 | SDMMC_CMD_OPENDRAIN)
+/**
+ * ACMD41(R1): Send host capacity support information (HCS) and activates the
+ * card's initilization process
+ */
+#define SD_SPI_ACMD41_SD_SEND_OP_COND (41 | SDMMC_CMD_R1)
+/**
+ * ACMD42(ac, R1): Connect[1]/Disconnect[0] the 50 KOhm pull-up resistor on
+ * CD/DAT3 (pin 1) of the card.
+ */
+#define SD_ACMD42_SET_CLR_CARD_DETECT (42 | SDMMC_CMD_R1)
+/** ACMD51(adtc, R1): Read the SD Configuration Register (SCR). */
+#define SD_ACMD51_SEND_SCR (51 | SDMMC_CMD_R1 | SDMMC_CMD_SINGLE_BLOCK)
+
+/*
+ * --- I/O mode commands (class 9) ---
+ */
+/** MMC Cmd39(ac, R4): Used to write and read 8 bit (register) data fields. */
+#define MMC_CMD39_FAST_IO (39 | SDMMC_CMD_R4)
+/** MMC Cmd40(bcr, R5): Set the system into interrupt mode */
+#define MMC_CMD40_GO_IRQ_STATE (40 | SDMMC_CMD_R5 | SDMMC_CMD_OPENDRAIN)
+/** SDIO Cmd5(R4): Send operation condition */
+#define SDIO_CMD5_SEND_OP_COND (5 | SDMMC_CMD_R4 | SDMMC_CMD_OPENDRAIN)
+/** SDIO CMD52(R5): Direct IO read/write */
+#define SDIO_CMD52_IO_RW_DIRECT (52 | SDMMC_CMD_R5)
+/** SDIO CMD53(R5): Extended IO read/write */
+#define SDIO_CMD53_IO_R_BYTE_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BYTE)
+#define SDIO_CMD53_IO_W_BYTE_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BYTE | SDMMC_CMD_WRITE)
+#define SDIO_CMD53_IO_R_BLOCK_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BLOCK)
+#define SDIO_CMD53_IO_W_BLOCK_EXTENDED (53 | SDMMC_CMD_R5 | SDMMC_CMD_SDIO_BLOCK | SDMMC_CMD_WRITE)
+//! @}
+//! @}
+
+//! \name Macros for command argument definition
+//! @{
+
+//! \name MMC CMD6 argument structure
+//! @{
+//! [31:26] Set to 0
+//! [25:24] Access
+#define MMC_CMD6_ACCESS_COMMAND_SET (0lu << 24)
+#define MMC_CMD6_ACCESS_SET_BITS (1lu << 24)
+#define MMC_CMD6_ACCESS_CLEAR_BITS (2lu << 24)
+#define MMC_CMD6_ACCESS_WRITE_BYTE (3lu << 24)
+//! [23:16] Index for Mode Segment
+#define MMC_CMD6_INDEX_CMD_SET (EXT_CSD_CMD_SET_INDEX << 16)
+#define MMC_CMD6_INDEX_CMD_SET_REV (EXT_CSD_CMD_SET_REV_INDEX << 16)
+#define MMC_CMD6_INDEX_POWER_CLASS (EXT_CSD_POWER_CLASS_INDEX << 16)
+#define MMC_CMD6_INDEX_HS_TIMING (EXT_CSD_HS_TIMING_INDEX << 16)
+#define MMC_CMD6_INDEX_BUS_WIDTH (EXT_CSD_BUS_WIDTH_INDEX << 16)
+#define MMC_CMD6_INDEX_ERASED_MEM_CONT (EXT_CSD_ERASED_MEM_CONT_INDEX << 16)
+#define MMC_CMD6_INDEX_BOOT_CONFIG (EXT_CSD_BOOT_CONFIG_INDEX << 16)
+#define MMC_CMD6_INDEX_BOOT_BUS_WIDTH (EXT_CSD_BOOT_BUS_WIDTH_INDEX << 16)
+#define MMC_CMD6_INDEX_ERASE_GROUP_DEF (EXT_CSD_ERASE_GROUP_DEF_INDEX << 16)
+//! [15:8] Value
+#define MMC_CMD6_VALUE_BUS_WIDTH_1BIT (0x0lu << 8)
+#define MMC_CMD6_VALUE_BUS_WIDTH_4BIT (0x1lu << 8)
+#define MMC_CMD6_VALUE_BUS_WIDTH_8BIT (0x2lu << 8)
+#define MMC_CMD6_VALUE_HS_TIMING_ENABLE (0x1lu << 8)
+#define MMC_CMD6_VALUE_HS_TIMING_DISABLE (0x0lu << 8)
+//! [7:3] Set to 0
+//! [2:0] Cmd Set
+//! @}
+
+//! \name SD CMD6 argument structure
+//! @{
+//! CMD6 arg[ 3: 0] function group 1, access mode
+#define SD_CMD6_GRP1_HIGH_SPEED (0x1lu << 0)
+#define SD_CMD6_GRP1_DEFAULT (0x0lu << 0)
+//! CMD6 arg[ 7: 4] function group 2, command system
+#define SD_CMD6_GRP2_NO_INFLUENCE (0xFlu << 4)
+#define SD_CMD6_GRP2_DEFAULT (0x0lu << 4)
+//! CMD6 arg[11: 8] function group 3, 0xF or 0x0
+#define SD_CMD6_GRP3_NO_INFLUENCE (0xFlu << 8)
+#define SD_CMD6_GRP3_DEFAULT (0x0lu << 8)
+//! CMD6 arg[15:12] function group 4, 0xF or 0x0
+#define SD_CMD6_GRP4_NO_INFLUENCE (0xFlu << 12)
+#define SD_CMD6_GRP4_DEFAULT (0x0lu << 12)
+//! CMD6 arg[19:16] function group 5, 0xF or 0x0
+#define SD_CMD6_GRP5_NO_INFLUENCE (0xFlu << 16)
+#define SD_CMD6_GRP5_DEFAULT (0x0lu << 16)
+//! CMD6 arg[23:20] function group 6, 0xF or 0x0
+#define SD_CMD6_GRP6_NO_INFLUENCE (0xFlu << 20)
+#define SD_CMD6_GRP6_DEFAULT (0x0lu << 20)
+//! CMD6 arg[30:24] reserved 0
+//! CMD6 arg[31 ] Mode, 0: Check, 1: Switch
+#define SD_CMD6_MODE_CHECK (0lu << 31)
+#define SD_CMD6_MODE_SWITCH (1lu << 31)
+//! @}
+
+//! \name SD CMD8 argument structure
+//! @{
+#define SD_CMD8_PATTERN 0xAA
+#define SD_CMD8_MASK_PATTERN 0xFF
+#define SD_CMD8_HIGH_VOLTAGE 0x100
+#define SD_CMD8_MASK_VOLTAGE 0xF00
+//! @}
+
+//! \name SD ACMD41 arguments
+//! @{
+#define SD_ACMD41_HCS (1lu << 30) //!< (SD) Host Capacity Support
+ //! @}
+//! @}
+
+//! \name SDIO definitions
+//! @{
+
+//! \name SDIO state (in R5)
+//! @{
+#define SDIO_R5_COM_CRC_ERROR (1lu << 15) /**< CRC check error */
+#define SDIO_R5_ILLEGAL_COMMAND (1lu << 14) /**< Illegal command */
+#define SDIO_R5_STATE (3lu << 12) /**< SDIO R5 state mask */
+#define SDIO_R5_STATE_DIS (0lu << 12) /**< Disabled */
+#define SDIO_R5_STATE_CMD (1lu << 12) /**< DAT lines free */
+#define SDIO_R5_STATE_TRN (2lu << 12) /**< Transfer */
+#define SDIO_R5_STATE_RFU (3lu << 12) /**< Reserved */
+#define SDIO_R5_ERROR (1lu << 11) /**< General error */
+#define SDIO_R5_FUNC_NUM (1lu << 9) /**< Invalid function number */
+#define SDIO_R5_OUT_OF_RANGE (1lu << 8) /**< Argument out of range */
+#define SDIO_R5_STATUS_ERR (SDIO_R5_ERROR | SDIO_R5_FUNC_NUM | SDIO_R5_OUT_OF_RANGE) //!< Errro status bits mask
+ //! @}
+
+//! \name SDIO state (in R6)
+//! @{
+/** The CRC check of the previous command failed. */
+#define SDIO_R6_COM_CRC_ERROR (1lu << 15)
+/** Command not legal for the card state. */
+#define SDIO_R6_ILLEGAL_COMMAND (1lu << 14)
+/** A general or an unknown error occurred during the operation. */
+#define SDIO_R6_ERROR (1lu << 13)
+/** Status bits mask for SDIO R6 */
+#define SDIO_STATUS_R6 (SDIO_R6_COM_CRC_ERROR | SDIO_R6_ILLEGAL_COMMAND | SDIO_R6_ERROR)
+//! @}
+
+//! \name SDIO CMD52 argument bit offset
+//! @{
+//! CMD52 arg[ 7: 0] Write data or stuff bits
+#define SDIO_CMD52_WR_DATA 0
+//! CMD52 arg[ 8] Reserved
+#define SDIO_CMD52_STUFF0 8
+//! CMD52 arg[25: 9] Register address
+#define SDIO_CMD52_REG_ADRR 9
+//! CMD52 arg[ 26] Reserved
+#define SDIO_CMD52_STUFF1 26
+//! CMD52 arg[ 27] Read after Write flag
+#define SDIO_CMD52_RAW_FLAG 27
+//! CMD52 arg[30:28] Number of the function
+#define SDIO_CMD52_FUNCTION_NUM 28
+//! CMD52 arg[ 31] Direction, 1:write, 0:read.
+#define SDIO_CMD52_RW_FLAG 31
+#define SDIO_CMD52_READ_FLAG 0
+#define SDIO_CMD52_WRITE_FLAG 1
+//! @}
+
+//! \name SDIO CMD53 argument structure
+//! @{
+/**
+ * [ 8: 0] Byte mode: number of bytes to transfer,
+ * 0 cause 512 bytes transfer.
+ * Block mode: number of blocks to transfer,
+ * 0 set count to infinite.
+ */
+#define SDIO_CMD53_COUNT 0
+//! CMD53 arg[25: 9] Start Address I/O register
+#define SDIO_CMD53_REG_ADDR 9
+//! CMD53 arg[ 26] 1:Incrementing address, 0: fixed
+#define SDIO_CMD53_OP_CODE 26
+//! CMD53 arg[ 27] (Optional) 1:block mode
+#define SDIO_CMD53_BLOCK_MODE 27
+//! CMD53 arg[30:28] Number of the function
+#define SDIO_CMD53_FUNCTION_NUM 28
+//! CMD53 arg[ 31] Direction, 1:WR, 0:RD
+#define SDIO_CMD53_RW_FLAG 31
+#define SDIO_CMD53_READ_FLAG 0
+#define SDIO_CMD53_WRITE_FLAG 1
+//! @}
+
+//! \name SDIO Functions
+//! @{
+#define SDIO_CIA 0 /**< SDIO Function 0 (CIA) */
+#define SDIO_FN0 0 /**< SDIO Function 0 */
+#define SDIO_FN1 1 /**< SDIO Function 1 */
+#define SDIO_FN2 2 /**< SDIO Function 2 */
+#define SDIO_FN3 3 /**< SDIO Function 3 */
+#define SDIO_FN4 4 /**< SDIO Function 4 */
+#define SDIO_FN5 5 /**< SDIO Function 5 */
+#define SDIO_FN6 6 /**< SDIO Function 6 */
+#define SDIO_FN7 7 /**< SDIO Function 7 */
+ //! @}
+
+//! \name SDIO Card Common Control Registers (CCCR)
+//! @{
+#define SDIO_CCCR_SDIO_REV 0x00 /**< CCCR/SDIO revision (RO) */
+#define SDIO_CCCR_REV (0xFlu << 0) /**< CCCR/FBR Version */
+#define SDIO_CCCR_REV_1_00 (0x0lu << 0) /**< CCCR/FBR Version 1.00 */
+#define SDIO_CCCR_REV_1_10 (0x1lu << 0) /**< CCCR/FBR Version 1.10 */
+#define SDIO_CCCR_REV_2_00 (0x2lu << 0) /**< CCCR/FBR Version 2.00 */
+#define SDIO_CCCR_REV_3_00 (0x3lu << 0) /**< CCCR/FBR Version 3.00 */
+#define SDIO_SDIO_REV (0xFlu << 4) /**< SDIO Spec */
+#define SDIO_SDIO_REV_1_00 (0x0lu << 4) /**< SDIO Spec 1.00 */
+#define SDIO_SDIO_REV_1_10 (0x1lu << 4) /**< SDIO Spec 1.10 */
+#define SDIO_SDIO_REV_1_20 (0x2lu << 4) /**< SDIO Spec 1.20(unreleased) */
+#define SDIO_SDIO_REV_2_00 (0x3lu << 4) /**< SDIO Spec Version 2.00 */
+#define SDIO_SDIO_REV_3_00 (0x4lu << 4) /**< SDIO Spec Version 3.00 */
+#define SDIO_CCCR_SD_REV 0x01 /**< SD Spec Revision (RO) */
+#define SDIO_SD_REV (0xFlu << 0) /**< SD Physical Spec */
+#define SDIO_SD_REV_1_01 (0x0lu << 0) /**< SD 1.01 (Mar 2000) */
+#define SDIO_SD_REV_1_10 (0x1lu << 0) /**< SD 1.10 (Oct 2004) */
+#define SDIO_SD_REV_2_00 (0x2lu << 0) /**< SD 2.00 (May 2006) */
+#define SDIO_SD_REV_3_00 (0x3lu << 0) /**< SD 3.00 */
+#define SDIO_CCCR_IOE 0x02 /**< I/O Enable (R/W) */
+#define SDIO_IOE (0xFElu << 1) /**< Functions Enable/Disable */
+#define SDIO_IOE_FN1 (0x1lu << 1) /**< Function 1 Enable/Disable */
+#define SDIO_IOE_FN2 (0x1lu << 2) /**< Function 2 Enable/Disable */
+#define SDIO_IOE_FN3 (0x1lu << 3) /**< Function 3 Enable/Disable */
+#define SDIO_IOE_FN4 (0x1lu << 4) /**< Function 4 Enable/Disable */
+#define SDIO_IOE_FN5 (0x1lu << 5) /**< Function 5 Enable/Disable */
+#define SDIO_IOE_FN6 (0x1lu << 6) /**< Function 6 Enable/Disable */
+#define SDIO_IOE_FN7 (0x1lu << 7) /**< Function 7 Enable/Disable */
+#define SDIO_CCCR_IOR 0x03 /**< I/O Ready (RO) */
+#define SDIO_IOR (0xFElu << 1) /**< Functions ready */
+#define SDIO_IOR_FN1 (0x1lu << 1) /**< Function 1 ready */
+#define SDIO_IOR_FN2 (0x1lu << 2) /**< Function 2 ready */
+#define SDIO_IOR_FN3 (0x1lu << 3) /**< Function 3 ready */
+#define SDIO_IOR_FN4 (0x1lu << 4) /**< Function 4 ready */
+#define SDIO_IOR_FN5 (0x1lu << 5) /**< Function 5 ready */
+#define SDIO_IOR_FN6 (0x1lu << 6) /**< Function 6 ready */
+#define SDIO_IOR_FN7 (0x1lu << 7) /**< Function 7 ready */
+#define SDIO_CCCR_IEN 0x04 /**< Int Enable */
+#define SDIO_IENM (0x1lu << 0) /**< Int Enable Master (R/W) */
+#define SDIO_IEN (0xFElu << 1) /**< Functions Int Enable */
+#define SDIO_IEN_FN1 (0x1lu << 1) /**< Function 1 Int Enable */
+#define SDIO_IEN_FN2 (0x1lu << 2) /**< Function 2 Int Enable */
+#define SDIO_IEN_FN3 (0x1lu << 3) /**< Function 3 Int Enable */
+#define SDIO_IEN_FN4 (0x1lu << 4) /**< Function 4 Int Enable */
+#define SDIO_IEN_FN5 (0x1lu << 5) /**< Function 5 Int Enable */
+#define SDIO_IEN_FN6 (0x1lu << 6) /**< Function 6 Int Enable */
+#define SDIO_IEN_FN7 (0x1lu << 7) /**< Function 7 Int Enable */
+#define SDIO_CCCR_INT 0x05 /**< Int Pending */
+#define SDIO_INT (0xFElu << 1) /**< Functions Int pending */
+#define SDIO_INT_FN1 (0x1lu << 1) /**< Function 1 Int pending */
+#define SDIO_INT_FN2 (0x1lu << 2) /**< Function 2 Int pending */
+#define SDIO_INT_FN3 (0x1lu << 3) /**< Function 3 Int pending */
+#define SDIO_INT_FN4 (0x1lu << 4) /**< Function 4 Int pending */
+#define SDIO_INT_FN5 (0x1lu << 5) /**< Function 5 Int pending */
+#define SDIO_INT_FN6 (0x1lu << 6) /**< Function 6 Int pending */
+#define SDIO_INT_FN7 (0x1lu << 7) /**< Function 7 Int pending */
+#define SDIO_CCCR_IOA 0x06 /**< I/O Abort */
+#define SDIO_AS (0x7lu << 0) /**< Abort Select In Order (WO) */
+#define SDIO_AS_FN1 (0x1lu << 0) /**< Abort function 1 IO */
+#define SDIO_AS_FN2 (0x2lu << 0) /**< Abort function 2 IO */
+#define SDIO_AS_FN3 (0x3lu << 0) /**< Abort function 3 IO */
+#define SDIO_AS_FN4 (0x4lu << 0) /**< Abort function 4 IO */
+#define SDIO_AS_FN5 (0x5lu << 0) /**< Abort function 5 IO */
+#define SDIO_AS_FN6 (0x6lu << 0) /**< Abort function 6 IO */
+#define SDIO_AS_FN7 (0x7lu << 0) /**< Abort function 7 IO */
+#define SDIO_RES (0x1lu << 3) /**< IO CARD RESET (WO) */
+#define SDIO_CCCR_BUS_CTRL 0x07 /**< Bus Interface Control */
+#define SDIO_BUSWIDTH (0x3lu << 0) /**< Data bus width (R/W) */
+#define SDIO_BUSWIDTH_1B (0x0lu << 0) /**< 1-bit data bus */
+#define SDIO_BUSWIDTH_4B (0x2lu << 0) /**< 4-bit data bus */
+/** Enable Continuous SPI interrupt (R/W) */
+#define SDIO_BUS_ECSI (0x1lu << 5)
+/** Support Continuous SPI interrupt (RO) */
+#define SDIO_BUS_SCSI (0x1lu << 6)
+/** Connect(0)/Disconnect(1) pull-up on CD/DAT[3] (R/W) */
+#define SDIO_BUS_CD_DISABLE (0x1lu << 7)
+#define SDIO_CCCR_CAP 0x08 /**< Card Capability */
+/** Support Direct Commands during data transfer (RO) */
+#define SDIO_CAP_SDC (0x1lu << 0)
+/** Support Multi-Block (RO) */
+#define SDIO_CAP_SMB (0x1lu << 1)
+/** Support Read Wait (RO) */
+#define SDIO_CAP_SRW (0x1lu << 2)
+/** Support Suspend/Resume (RO) */
+#define SDIO_CAP_SBS (0x1lu << 3)
+/** Support interrupt between blocks of data in 4-bit SD mode (RO) */
+#define SDIO_CAP_S4MI (0x1lu << 4)
+/** Enable interrupt between blocks of data in 4-bit SD mode (R/W) */
+#define SDIO_CAP_E4MI (0x1lu << 5)
+/** Low-Speed Card (RO) */
+#define SDIO_CAP_LSC (0x1lu << 6)
+/** 4-bit support for Low-Speed Card (RO) */
+#define SDIO_CAP_4BLS (0x1lu << 7)
+/** Pointer to CIS (3B, LSB first) */
+#define SDIO_CCCR_CIS_PTR 0x09
+/** Bus Suspend */
+#define SDIO_CCCR_BUS_SUSPEND 0x0C
+/** Bus Status (transfer on DAT[x] lines) (RO) */
+#define SDIO_BS (0x1lu << 0)
+/** Bus Release Request/Status (R/W) */
+#define SDIO_BR (0x1lu << 1)
+#define SDIO_CCCR_FUN_SEL 0x0D /**< Function select */
+#define SDIO_DF (0x1lu << 7) /**< Resume Data Flag (RO) */
+#define SDIO_FS (0xFlu << 0) /**< Select Function (R/W) */
+#define SDIO_FS_CIA (0x0lu << 0) /**< Select CIA (function 0) */
+#define SDIO_FS_FN1 (0x1lu << 0) /**< Select Function 1 */
+#define SDIO_FS_FN2 (0x2lu << 0) /**< Select Function 2 */
+#define SDIO_FS_FN3 (0x3lu << 0) /**< Select Function 3 */
+#define SDIO_FS_FN4 (0x4lu << 0) /**< Select Function 4 */
+#define SDIO_FS_FN5 (0x5lu << 0) /**< Select Function 5 */
+#define SDIO_FS_FN6 (0x6lu << 0) /**< Select Function 6 */
+#define SDIO_FS_FN7 (0x7lu << 0) /**< Select Function 7 */
+#define SDIO_FS_MEM (0x8lu << 0) /**< Select memory in combo card */
+#define SDIO_CCCR_EXEC 0x0E /**< Exec Flags (RO) */
+#define SDIO_EXM (0x1lu << 0) /**< Executing status of memory */
+#define SDIO_EX (0xFElu << 1) /**< Executing functions status */
+#define SDIO_EX_FN1 (0x1lu << 1) /**< Executing status of func 1 */
+#define SDIO_EX_FN2 (0x1lu << 2) /**< Executing status of func 2 */
+#define SDIO_EX_FN3 (0x1lu << 3) /**< Executing status of func 3 */
+#define SDIO_EX_FN4 (0x1lu << 4) /**< Executing status of func 4 */
+#define SDIO_EX_FN5 (0x1lu << 5) /**< Executing status of func 5 */
+#define SDIO_EX_FN6 (0x1lu << 6) /**< Executing status of func 6 */
+#define SDIO_EX_FN7 (0x1lu << 7) /**< Executing status of func 7 */
+#define SDIO_CCCR_READY 0x0F /**< Ready Flags (RO) */
+#define SDIO_RFM (0x1lu << 0) /**< Ready Flag for memory */
+#define SDIO_RF (0xFElu) /**< Ready Flag for functions */
+#define SDIO_RF_FN1 (0x1lu << 1) /**< Ready Flag for function 1 */
+#define SDIO_RF_FN2 (0x1lu << 2) /**< Ready Flag for function 2 */
+#define SDIO_RF_FN3 (0x1lu << 3) /**< Ready Flag for function 3 */
+#define SDIO_RF_FN4 (0x1lu << 4) /**< Ready Flag for function 4 */
+#define SDIO_RF_FN5 (0x1lu << 5) /**< Ready Flag for function 5 */
+#define SDIO_RF_FN6 (0x1lu << 6) /**< Ready Flag for function 6 */
+#define SDIO_RF_FN7 (0x1lu << 7) /**< Ready Flag for function 7 */
+#define SDIO_CCCR_FN0_BLKSIZ 0x10 /**< FN0 Block Size (2B, LSB first) (R/W) */
+#define SDIO_CCCR_POWER 0x12 /**< Power Control */
+#define SDIO_SMPC (0x1lu << 0) /**< Support Master Power Control*/
+#define SDIO_EMPC (0x1lu << 1) /**< Enable Master Power Control */
+#define SDIO_CCCR_HS 0x13 /**< High-Speed */
+#define SDIO_SHS (0x1lu << 0) /**< Support High-Speed (RO) */
+#define SDIO_EHS (0x1lu << 1) /**< Enable High-Speed (R/W) */
+ //! @}
+
+//! \name SDIO Function Basic Registers (FBR)
+//! @{
+#define SDIO_FBR_ADDR(fn, x) (0x100 * (fn) + (x))
+#define SDIO_FBR_CSA_IF 0x0 /**< CSA and function interface code (RO) */
+#define SDIO_IFC (0xFUL << 0) /**< Standard SDIO Fun Interface Code */
+#define SDIO_IFC_NO_IF (0x0UL << 0) /**< No SDIO standard interface */
+#define SDIO_IFC_UART (0x1UL << 0) /**< UART */
+#define SDIO_IFC_TA_BT (0x2UL << 0) /**< Type-A Bluetooth */
+#define SDIO_IFC_TB_BT (0x3UL << 0) /**< Type-B Bluetooth */
+#define SDIO_IFC_GPS (0x4UL << 0) /**< GPS */
+#define SDIO_IFC_CAMERA (0x5UL << 0) /**< Camera */
+#define SDIO_IFC_PHS (0x6UL << 0) /**< PHS */
+#define SDIO_IFC_WLAN (0x7UL << 0) /**< WLAN */
+#define SDIO_IFC_ATA (0x8UL << 0) /**< Embedded SDIO-ATA */
+#define SDIO_IFC_EXT (0xFUL << 0) /**< Check EXT interface code */
+#define SDIO_SCSA (0x1UL << 6) /**< Function supports Code Storage Area (CSA) */
+#define SDIO_FBR_CSA (0x1UL << 7) /**< Function CSA enable */
+#define SDIO_FBR_EXT_IF 0x1 /**< Extended function interface code (RO) */
+#define SDIO_FBR_PWR 0x2 /**< function power control */
+#define SDIO_SPS (0x1UL << 0) /**< function support power selection (RO) */
+#define SDIO_EPS (0x1UL << 1) /**< Low Current Mode/High Current Mode (R/W) */
+#define SDIO_FBR_CIS_PTR 0x9 /**< Address pointer to function CIS (3B, LSB first) (RO) */
+#define SDIO_FBR_CSA_PTR 0xC /**< Address pointer to CSA (3B, LSB first) (R/W) */
+#define SDIO_FBR_CSA_DATA 0xF /**< Read/Write fifo to CSA (R/W) */
+#define SDIO_FBR_BLK_SIZ 0x10 /**< Block size (2B, LSB first) (R/W) */
+ //! @}
+
+//! \name SDIO Card Metaformat
+//! @{
+/** Null tuple (PCMCIA 3.1.9) */
+#define SDIO_CISTPL_NULL 0x00
+/** Device tuple (PCMCIA 3.2.2) */
+#define SDIO_CISTPL_DEVICE 0x01
+/** Checksum control (PCMCIA 3.1.1) */
+#define SDIO_CISTPL_CHECKSUM 0x10
+/** Level 1 version (PCMCIA 3.2.10) */
+#define SDIO_CISTPL_VERS_1 0x15
+/** Alternate Language String (PCMCIA 3.2.1) */
+#define SDIO_CISTPL_ALTSTR 0x16
+/** Manufacturer Identification String (PCMCIA 3.2.9) */
+#define SDIO_CISTPL_MANFID 0x20
+/** Function Identification (PCMCIA 3.2.7) */
+#define SDIO_CISTPL_FUNCID 0x21
+/** Function Extensions (PCMCIA 3.2.6) */
+#define SDIO_CISTPL_FUNCE 0x22
+/** Additional information for SDIO (PCMCIA 6.1.2) */
+#define SDIO_CISTPL_SDIO_STD 0x91
+/** Reserved for future SDIO (PCMCIA 6.1.3) */
+#define SDIO_CISTPL_SDIO_EXT 0x92
+/** The End-of-chain Tuple (PCMCIA 3.1.2) */
+#define SDIO_CISTPL_END 0xFF
+//! @}
+
+//! @}
+
+//! \name CSD, OCR, SCR, Switch status, extend CSD definitions
+//! @{
+
+/**
+ * \brief Macro function to extract a bits field from a large SD MMC register
+ * Used by : CSD, SCR, Switch status
+ */
+static inline uint32_t SDMMC_UNSTUFF_BITS(uint8_t *reg, uint16_t reg_size, uint16_t pos, uint8_t size)
+{
+ uint32_t value;
+ value = reg[((reg_size - pos + 7) / 8) - 1] >> (pos % 8);
+ if (((pos % 8) + size) > 8) {
+ value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 2] << (8 - (pos % 8));
+ }
+ if (((pos % 8) + size) > 16) {
+ value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 3] << (16 - (pos % 8));
+ }
+ if (((pos % 8) + size) > 24) {
+ value |= (uint32_t)reg[((reg_size - pos + 7) / 8) - 3] << (24 - (pos % 8));
+ }
+ value &= ((uint32_t)1 << size) - 1;
+ return value;
+}
+
+ //! \name CSD Fields
+ //! @{
+#define CSD_REG_BIT_SIZE 128 //!< 128 bits
+#define CSD_REG_BSIZE (CSD_REG_BIT_SIZE / 8) //!< 16 bytes
+#define CSD_STRUCTURE(csd, pos, size) SDMMC_UNSTUFF_BITS(csd, CSD_REG_BIT_SIZE, pos, size)
+#define CSD_STRUCTURE_VERSION(csd) CSD_STRUCTURE(csd, 126, 2)
+#define SD_CSD_VER_1_0 0
+#define SD_CSD_VER_2_0 1
+#define MMC_CSD_VER_1_0 0
+#define MMC_CSD_VER_1_1 1
+#define MMC_CSD_VER_1_2 2
+#define CSD_TRAN_SPEED(csd) CSD_STRUCTURE(csd, 96, 8)
+#define SD_CSD_1_0_C_SIZE(csd) CSD_STRUCTURE(csd, 62, 12)
+#define SD_CSD_1_0_C_SIZE_MULT(csd) CSD_STRUCTURE(csd, 47, 3)
+#define SD_CSD_1_0_READ_BL_LEN(csd) CSD_STRUCTURE(csd, 80, 4)
+#define SD_CSD_2_0_C_SIZE(csd) CSD_STRUCTURE(csd, 48, 22)
+#define MMC_CSD_C_SIZE(csd) CSD_STRUCTURE(csd, 62, 12)
+#define MMC_CSD_C_SIZE_MULT(csd) CSD_STRUCTURE(csd, 47, 3)
+#define MMC_CSD_READ_BL_LEN(csd) CSD_STRUCTURE(csd, 80, 4)
+#define MMC_CSD_SPEC_VERS(csd) CSD_STRUCTURE(csd, 122, 4)
+ //! @}
+
+ //! \name OCR Register Fields
+ //! @{
+#define OCR_REG_BSIZE (32 / 8) /**< 32 bits, 4 bytes */
+#define OCR_VDD_170_195 (1lu << 7)
+#define OCR_VDD_20_21 (1lu << 8)
+#define OCR_VDD_21_22 (1lu << 9)
+#define OCR_VDD_22_23 (1lu << 10)
+#define OCR_VDD_23_24 (1lu << 11)
+#define OCR_VDD_24_25 (1lu << 12)
+#define OCR_VDD_25_26 (1lu << 13)
+#define OCR_VDD_26_27 (1lu << 14)
+#define OCR_VDD_27_28 (1lu << 15)
+#define OCR_VDD_28_29 (1lu << 16)
+#define OCR_VDD_29_30 (1lu << 17)
+#define OCR_VDD_30_31 (1lu << 18)
+#define OCR_VDD_31_32 (1lu << 19)
+#define OCR_VDD_32_33 (1lu << 20)
+#define OCR_VDD_33_34 (1lu << 21)
+#define OCR_VDD_34_35 (1lu << 22)
+#define OCR_VDD_35_36 (1lu << 23)
+#define OCR_SDIO_S18R (1lu << 24) /**< Switching to 1.8V Accepted */
+#define OCR_SDIO_MP (1lu << 27) /**< Memory Present */
+#define OCR_SDIO_NF (7lu << 28) /**< Number of I/O Functions */
+#define OCR_ACCESS_MODE_MASK (3lu << 29) /**< (MMC) Access mode mask */
+#define OCR_ACCESS_MODE_BYTE (0lu << 29) /**< (MMC) Byte access mode */
+#define OCR_ACCESS_MODE_SECTOR (2lu << 29) /**< (MMC) Sector access mode */
+#define OCR_CCS (1lu << 30) /**< (SD) Card Capacity Status */
+#define OCR_POWER_UP_BUSY (1lu << 31) /**< Card power up status bit */
+ //! @}
+
+ //! \name SD SCR Register Fields
+ //! @{
+#define SD_SCR_REG_BIT_SIZE 64 //!< 64 bits
+#define SD_SCR_REG_BSIZE (SD_SCR_REG_BIT_SIZE / 8) //!< 8 bytes
+#define SD_SCR_STRUCTURE(scr, pos, size) SDMMC_UNSTUFF_BITS(scr, SD_SCR_REG_BIT_SIZE, pos, size)
+#define SD_SCR_SCR_STRUCTURE(scr) SD_SCR_STRUCTURE(scr, 60, 4)
+#define SD_SCR_SCR_STRUCTURE_1_0 0
+#define SD_SCR_SD_SPEC(scr) SD_SCR_STRUCTURE(scr, 56, 4)
+#define SD_SCR_SD_SPEC_1_0_01 0
+#define SD_SCR_SD_SPEC_1_10 1
+#define SD_SCR_SD_SPEC_2_00 2
+#define SD_SCR_DATA_STATUS_AFTER_ERASE(scr) SD_SCR_STRUCTURE(scr, 55, 1)
+#define SD_SCR_SD_SECURITY(scr) SD_SCR_STRUCTURE(scr, 52, 3)
+#define SD_SCR_SD_SECURITY_NO 0
+#define SD_SCR_SD_SECURITY_NOTUSED 1
+#define SD_SCR_SD_SECURITY_1_01 2
+#define SD_SCR_SD_SECURITY_2_00 3
+#define SD_SCR_SD_SECURITY_3_00 4
+#define SD_SCR_SD_BUS_WIDTHS(scr) SD_SCR_STRUCTURE(scr, 48, 4)
+#define SD_SCR_SD_BUS_WIDTH_1BITS (1lu << 0)
+#define SD_SCR_SD_BUS_WIDTH_4BITS (1lu << 2)
+#define SD_SCR_SD_SPEC3(scr) SD_SCR_STRUCTURE(scr, 47, 1)
+#define SD_SCR_SD_SPEC_3_00 1
+#define SD_SCR_SD_EX_SECURITY(scr) SD_SCR_STRUCTURE(scr, 43, 4)
+#define SD_SCR_SD_CMD_SUPPORT(scr) SD_SCR_STRUCTURE(scr, 32, 2)
+ //! @}
+
+ //! \name SD Switch Status Fields
+ //! @{
+#define SD_SW_STATUS_BIT_SIZE 512 //!< 512 bits
+#define SD_SW_STATUS_BSIZE (SD_SW_STATUS_BIT_SIZE / 8) //!< 64 bytes
+#define SD_SW_STATUS_STRUCTURE(sd_sw_status, pos, size) \
+ SDMMC_UNSTUFF_BITS(sd_sw_status, SD_SW_STATUS_BIT_SIZE, pos, size)
+#define SD_SW_STATUS_MAX_CURRENT_CONSUMPTION(status) SD_SW_STATUS_STRUCTURE(status, 496, 16)
+#define SD_SW_STATUS_FUN_GRP6_INFO(status) SD_SW_STATUS_STRUCTURE(status, 480, 16)
+#define SD_SW_STATUS_FUN_GRP5_INFO(status) SD_SW_STATUS_STRUCTURE(status, 464, 16)
+#define SD_SW_STATUS_FUN_GRP4_INFO(status) SD_SW_STATUS_STRUCTURE(status, 448, 16)
+#define SD_SW_STATUS_FUN_GRP3_INFO(status) SD_SW_STATUS_STRUCTURE(status, 432, 16)
+#define SD_SW_STATUS_FUN_GRP2_INFO(status) SD_SW_STATUS_STRUCTURE(status, 416, 16)
+#define SD_SW_STATUS_FUN_GRP1_INFO(status) SD_SW_STATUS_STRUCTURE(status, 400, 16)
+#define SD_SW_STATUS_FUN_GRP6_RC(status) SD_SW_STATUS_STRUCTURE(status, 396, 4)
+#define SD_SW_STATUS_FUN_GRP5_RC(status) SD_SW_STATUS_STRUCTURE(status, 392, 4)
+#define SD_SW_STATUS_FUN_GRP4_RC(status) SD_SW_STATUS_STRUCTURE(status, 388, 4)
+#define SD_SW_STATUS_FUN_GRP3_RC(status) SD_SW_STATUS_STRUCTURE(status, 384, 4)
+#define SD_SW_STATUS_FUN_GRP2_RC(status) SD_SW_STATUS_STRUCTURE(status, 380, 4)
+#define SD_SW_STATUS_FUN_GRP1_RC(status) SD_SW_STATUS_STRUCTURE(status, 376, 4)
+#define SD_SW_STATUS_FUN_GRP_RC_ERROR 0xFU
+#define SD_SW_STATUS_DATA_STRUCT_VER(status) SD_SW_STATUS_STRUCTURE(status, 368, 8)
+#define SD_SW_STATUS_FUN_GRP6_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 352, 16)
+#define SD_SW_STATUS_FUN_GRP5_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 336, 16)
+#define SD_SW_STATUS_FUN_GRP4_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 320, 16)
+#define SD_SW_STATUS_FUN_GRP3_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 304, 16)
+#define SD_SW_STATUS_FUN_GRP2_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 288, 16)
+#define SD_SW_STATUS_FUN_GRP1_BUSY(status) SD_SW_STATUS_STRUCTURE(status, 272, 16)
+ //! @}
+
+ //! \name Card Status Fields
+ //! @{
+#define CARD_STATUS_APP_CMD (1lu << 5)
+#define CARD_STATUS_SWITCH_ERROR (1lu << 7)
+#define CARD_STATUS_READY_FOR_DATA (1lu << 8)
+#define CARD_STATUS_STATE_IDLE (0lu << 9)
+#define CARD_STATUS_STATE_READY (1lu << 9)
+#define CARD_STATUS_STATE_IDENT (2lu << 9)
+#define CARD_STATUS_STATE_STBY (3lu << 9)
+#define CARD_STATUS_STATE_TRAN (4lu << 9)
+#define CARD_STATUS_STATE_DATA (5lu << 9)
+#define CARD_STATUS_STATE_RCV (6lu << 9)
+#define CARD_STATUS_STATE_PRG (7lu << 9)
+#define CARD_STATUS_STATE_DIS (8lu << 9)
+#define CARD_STATUS_STATE (0xFlu << 9)
+#define CARD_STATUS_ERASE_RESET (1lu << 13)
+#define CARD_STATUS_WP_ERASE_SKIP (1lu << 15)
+#define CARD_STATUS_CIDCSD_OVERWRITE (1lu << 16)
+#define CARD_STATUS_OVERRUN (1lu << 17)
+#define CARD_STATUS_UNERRUN (1lu << 18)
+#define CARD_STATUS_ERROR (1lu << 19)
+#define CARD_STATUS_CC_ERROR (1lu << 20)
+#define CARD_STATUS_CARD_ECC_FAILED (1lu << 21)
+#define CARD_STATUS_ILLEGAL_COMMAND (1lu << 22)
+#define CARD_STATUS_COM_CRC_ERROR (1lu << 23)
+#define CARD_STATUS_UNLOCK_FAILED (1lu << 24)
+#define CARD_STATUS_CARD_IS_LOCKED (1lu << 25)
+#define CARD_STATUS_WP_VIOLATION (1lu << 26)
+#define CARD_STATUS_ERASE_PARAM (1lu << 27)
+#define CARD_STATUS_ERASE_SEQ_ERROR (1lu << 28)
+#define CARD_STATUS_BLOCK_LEN_ERROR (1lu << 29)
+#define CARD_STATUS_ADDRESS_MISALIGN (1lu << 30)
+#define CARD_STATUS_ADDR_OUT_OF_RANGE (1lu << 31)
+
+#define CARD_STATUS_ERR_RD_WR \
+ (CARD_STATUS_ADDR_OUT_OF_RANGE | CARD_STATUS_ADDRESS_MISALIGN | CARD_STATUS_BLOCK_LEN_ERROR \
+ | CARD_STATUS_WP_VIOLATION | CARD_STATUS_ILLEGAL_COMMAND | CARD_STATUS_CC_ERROR | CARD_STATUS_ERROR)
+ //! @}
+
+ //! \name SD Status Field
+ //! @{
+#define SD_STATUS_BSIZE (512 / 8) /**< 512 bits, 64bytes */
+ //! @}
+
+ //! \name MMC Extended CSD Register Field
+ //! @{
+#define EXT_CSD_BSIZE 512 /**< 512 bytes. */
+/* Below belongs to Properties Segment */
+#define EXT_CSD_S_CMD_SET_INDEX 504lu
+#define EXT_CSD_BOOT_INFO_INDEX 228lu
+#define EXT_CSD_BOOT_SIZE_MULTI_INDEX 226lu
+#define EXT_CSD_ACC_SIZE_INDEX 225lu
+#define EXT_CSD_HC_ERASE_GRP_SIZE_INDEX 224lu
+#define EXT_CSD_ERASE_TIMEOUT_MULT_INDEX 223lu
+#define EXT_CSD_REL_WR_SEC_C_INDEX 222lu
+#define EXT_CSD_HC_WP_GRP_SIZE_INDEX 221lu
+#define EXT_CSD_S_C_VCC_INDEX 220lu
+#define EXT_CSD_S_C_VCCQ_INDEX 219lu
+#define EXT_CSD_S_A_TIMEOUT_INDEX 217lu
+#define EXT_CSD_SEC_COUNT_INDEX 212lu
+#define EXT_CSD_MIN_PERF_W_8_52_INDEX 210lu
+#define EXT_CSD_MIN_PERF_R_8_52_INDEX 209lu
+#define EXT_CSD_MIN_PERF_W_8_26_4_52_INDEX 208lu
+#define EXT_CSD_MIN_PERF_R_8_26_4_52_INDEX 207lu
+#define EXT_CSD_MIN_PERF_W_4_26_INDEX 206lu
+#define EXT_CSD_MIN_PERF_R_4_26_INDEX 205lu
+#define EXT_CSD_PWR_CL_26_360_INDEX 203lu
+#define EXT_CSD_PWR_CL_52_360_INDEX 202lu
+#define EXT_CSD_PWR_CL_26_195_INDEX 201lu
+#define EXT_CSD_PWR_CL_52_195_INDEX 200lu
+#define EXT_CSD_CARD_TYPE_INDEX 196lu
+/* MMC card type */
+#define MMC_CTYPE_26MHZ 0x1
+#define MMC_CTYPE_52MHZ 0x2
+#define EXT_CSD_CSD_STRUCTURE_INDEX 194lu
+#define EXT_CSD_EXT_CSD_REV_INDEX 192lu
+
+/* Below belongs to Mode Segment */
+#define EXT_CSD_CMD_SET_INDEX 191lu
+#define EXT_CSD_CMD_SET_REV_INDEX 189lu
+#define EXT_CSD_POWER_CLASS_INDEX 187lu
+#define EXT_CSD_HS_TIMING_INDEX 185lu
+#define EXT_CSD_BUS_WIDTH_INDEX 183lu
+#define EXT_CSD_ERASED_MEM_CONT_INDEX 181lu
+#define EXT_CSD_BOOT_CONFIG_INDEX 179lu
+#define EXT_CSD_BOOT_BUS_WIDTH_INDEX 177lu
+#define EXT_CSD_ERASE_GROUP_DEF_INDEX 175lu
+ //! @}
+//! @}
+
+//! \name Definition for SPI mode only
+//! @{
+
+//! SPI commands start with a start bit "0" and a transmit bit "1"
+#define SPI_CMD_ENCODE(x) (0x40 | (x & 0x3F))
+
+//! \name Register R1 definition for SPI mode
+//! The R1 register is always send after a command.
+//! @{
+#define R1_SPI_IDLE (1lu << 0)
+#define R1_SPI_ERASE_RESET (1lu << 1)
+#define R1_SPI_ILLEGAL_COMMAND (1lu << 2)
+#define R1_SPI_COM_CRC (1lu << 3)
+#define R1_SPI_ERASE_SEQ (1lu << 4)
+#define R1_SPI_ADDRESS (1lu << 5)
+#define R1_SPI_PARAMETER (1lu << 6)
+// R1 bit 7 is always zero, reuse this bit for error
+#define R1_SPI_ERROR (1lu << 7)
+//! @}
+
+//! \name Register R2 definition for SPI mode
+//! The R2 register can be send after R1 register.
+//! @{
+#define R2_SPI_CARD_LOCKED (1lu << 0)
+#define R2_SPI_WP_ERASE_SKIP (1lu << 1)
+#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP
+#define R2_SPI_ERROR (1lu << 2)
+#define R2_SPI_CC_ERROR (1lu << 3)
+#define R2_SPI_CARD_ECC_ERROR (1lu << 4)
+#define R2_SPI_WP_VIOLATION (1lu << 5)
+#define R2_SPI_ERASE_PARAM (1lu << 6)
+#define R2_SPI_OUT_OF_RANGE (1lu << 7)
+#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE
+//! @}
+
+//! \name Control Tokens in SPI Mode
+//! @{
+//! \name Tokens used for a read operation
+//! @{
+#define SPI_TOKEN_SINGLE_MULTI_READ 0xFE
+#define SPI_TOKEN_DATA_ERROR_VALID(token) (((token)&0xF0) == 0)
+#define SPI_TOKEN_DATA_ERROR_ERRORS (0x0F)
+#define SPI_TOKEN_DATA_ERROR_ERROR (1lu << 0)
+#define SPI_TOKEN_DATA_ERROR_CC_ERROR (1lu << 1)
+#define SPI_TOKEN_DATA_ERROR_ECC_ERROR (1lu << 2)
+#define SPI_TOKEN_DATA_ERROR_OUT_RANGE (1lu << 3)
+ //! @}
+ //! \name Tokens used for a write operation
+ //! @{
+#define SPI_TOKEN_SINGLE_WRITE 0xFE
+#define SPI_TOKEN_MULTI_WRITE 0xFC
+#define SPI_TOKEN_STOP_TRAN 0xFD
+#define SPI_TOKEN_DATA_RESP_VALID(token) ((((token) & (1 << 4)) == 0) && (((token) & (1 << 0)) == 1))
+#define SPI_TOKEN_DATA_RESP_CODE(token) ((token)&0x1E)
+#define SPI_TOKEN_DATA_RESP_ACCEPTED (2lu << 1)
+#define SPI_TOKEN_DATA_RESP_CRC_ERR (5lu << 1)
+#define SPI_TOKEN_DATA_RESP_WRITE_ERR (6lu << 1)
+ //! @}
+ //! @}
+ //! @}
+
+ //! @} end of sd_mmc_protocol
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SD_MMC_PROTOCOL_H_INCLUDED */
diff --git a/ports/litex/mpconfigport.mk b/ports/litex/mpconfigport.mk
index 311c87b5d..427e9ea84 100644
--- a/ports/litex/mpconfigport.mk
+++ b/ports/litex/mpconfigport.mk
@@ -18,6 +18,7 @@ CIRCUITPY_AUDIOIO = 0
CIRCUITPY_BITBANGIO = 0
CIRCUITPY_BOARD = 0
CIRCUITPY_BUSIO = 0
+CIRCUITPY_COUNTIO = 0
CIRCUITPY_DISPLAYIO = 0
CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_I2CPERIPHERAL = 0
@@ -25,7 +26,7 @@ CIRCUITPY_NVM = 0
CIRCUITPY_PULSEIO = 0
CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 0
-CIRCUITPY_COUNTIO = 0
+CIRCUITPY_SDCARDIO = 0
# Enable USB support
CIRCUITPY_USB_HID = 1
CIRCUITPY_USB_MIDI = 1
diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk
index 385870a65..318bc0206 100644
--- a/ports/nrf/boards/pca10100/mpconfigboard.mk
+++ b/ports/nrf/boards/pca10100/mpconfigboard.mk
@@ -21,6 +21,7 @@ CIRCUITPY_PIXELBUF = 0
CIRCUITPY_RGBMATRIX = 0
CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 1
+CIRCUITPY_SDCARDIO = 0
CIRCUITPY_TOUCHIO = 0
CIRCUITPY_ULAB = 0
diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk
index 74d8f548d..32d364006 100644
--- a/py/circuitpy_defns.mk
+++ b/py/circuitpy_defns.mk
@@ -213,6 +213,12 @@ endif
ifeq ($(CIRCUITPY_SAMD),1)
SRC_PATTERNS += samd/%
endif
+ifeq ($(CIRCUITPY_SDCARDIO),1)
+SRC_PATTERNS += sdcardio/%
+endif
+ifeq ($(CIRCUITPY_SDIOIO),1)
+SRC_PATTERNS += sdioio/%
+endif
ifeq ($(CIRCUITPY_STAGE),1)
SRC_PATTERNS += _stage/%
endif
@@ -311,6 +317,8 @@ SRC_COMMON_HAL_ALL = \
rotaryio/__init__.c \
rtc/RTC.c \
rtc/__init__.c \
+ sdioio/SDCard.c \
+ sdioio/__init__.c \
supervisor/Runtime.c \
supervisor/__init__.c \
watchdog/WatchDogMode.c \
@@ -384,6 +392,8 @@ SRC_SHARED_MODULE_ALL = \
fontio/__init__.c \
framebufferio/FramebufferDisplay.c \
framebufferio/__init__.c \
+ sdcardio/SDCard.c \
+ sdcardio/__init__.c \
gamepad/GamePad.c \
gamepad/__init__.c \
gamepadshift/GamePadShift.c \
diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h
index c92cb1b66..d05a246fc 100644
--- a/py/circuitpy_mpconfig.h
+++ b/py/circuitpy_mpconfig.h
@@ -537,6 +537,20 @@ extern const struct _mp_obj_module_t samd_module;
#define SAMD_MODULE
#endif
+#if CIRCUITPY_SDCARDIO
+extern const struct _mp_obj_module_t sdcardio_module;
+#define SDCARDIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_sdcardio), (mp_obj_t)&sdcardio_module },
+#else
+#define SDCARDIO_MODULE
+#endif
+
+#if CIRCUITPY_SDIOIO
+extern const struct _mp_obj_module_t sdioio_module;
+#define SDIOIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_sdioio), (mp_obj_t)&sdioio_module },
+#else
+#define SDIOIO_MODULE
+#endif
+
#if CIRCUITPY_STAGE
extern const struct _mp_obj_module_t stage_module;
#define STAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module },
@@ -709,6 +723,8 @@ extern const struct _mp_obj_module_t watchdog_module;
ROTARYIO_MODULE \
RTC_MODULE \
SAMD_MODULE \
+ SDCARDIO_MODULE \
+ SDIOIO_MODULE \
STAGE_MODULE \
STORAGE_MODULE \
STRUCT_MODULE \
diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk
index 3459bff6d..302368f74 100644
--- a/py/circuitpy_mpconfig.mk
+++ b/py/circuitpy_mpconfig.mk
@@ -166,6 +166,12 @@ CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC)
CIRCUITPY_SAMD ?= 0
CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD)
+CIRCUITPY_SDCARDIO ?= $(CIRCUITPY_FULL_BUILD)
+CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO)
+
+CIRCUITPY_SDIOIO ?= 0
+CFLAGS += -DCIRCUITPY_SDIOIO=$(CIRCUITPY_SDIOIO)
+
# Currently always off.
CIRCUITPY_STAGE ?= 0
CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE)
diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c
index 22f001d2d..793ab4f67 100644
--- a/shared-bindings/busio/SPI.c
+++ b/shared-bindings/busio/SPI.c
@@ -418,3 +418,10 @@ const mp_obj_type_t busio_spi_type = {
.make_new = busio_spi_make_new,
.locals_dict = (mp_obj_dict_t*)&busio_spi_locals_dict,
};
+
+busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj) {
+ if (!MP_OBJ_IS_TYPE(obj, &busio_spi_type)) {
+ mp_raise_TypeError_varg(translate("Expected a %q"), busio_spi_type.name);
+ }
+ return MP_OBJ_TO_PTR(obj);
+}
diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h
index b7b0715d1..aa7d715b9 100644
--- a/shared-bindings/busio/SPI.h
+++ b/shared-bindings/busio/SPI.h
@@ -70,4 +70,6 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self);
// This is used by the supervisor to claim SPI devices indefinitely.
extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self);
+extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in);
+
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
diff --git a/shared-bindings/microcontroller/Pin.c b/shared-bindings/microcontroller/Pin.c
index 765e602e5..d5b971ae5 100644
--- a/shared-bindings/microcontroller/Pin.c
+++ b/shared-bindings/microcontroller/Pin.c
@@ -101,6 +101,18 @@ mcu_pin_obj_t *validate_obj_is_free_pin(mp_obj_t obj) {
return pin;
}
+// Validate every element in the list to be a free pin.
+void validate_list_is_free_pins(qstr what, mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) {
+ mp_int_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq));
+ if (len > max_pins) {
+ mp_raise_ValueError_varg(translate("At most %d %q may be specified (not %d)"), max_pins, what, len);
+ }
+ *count_out = len;
+ for (mp_int_t i=0; i<len; i++) {
+ pins_out[i] = validate_obj_is_free_pin(mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL));
+ }
+}
+
// Validate that the obj is a free pin or None. Return an mcu_pin_obj_t* or NULL, correspondingly.
mcu_pin_obj_t *validate_obj_is_free_pin_or_none(mp_obj_t obj) {
if (obj == mp_const_none) {
diff --git a/shared-bindings/microcontroller/Pin.h b/shared-bindings/microcontroller/Pin.h
index bb5256b55..f6659db7d 100644
--- a/shared-bindings/microcontroller/Pin.h
+++ b/shared-bindings/microcontroller/Pin.h
@@ -37,6 +37,7 @@ mcu_pin_obj_t *validate_obj_is_pin(mp_obj_t obj);
mcu_pin_obj_t *validate_obj_is_pin_or_none(mp_obj_t obj);
mcu_pin_obj_t *validate_obj_is_free_pin(mp_obj_t obj);
mcu_pin_obj_t *validate_obj_is_free_pin_or_none(mp_obj_t obj);
+void validate_list_is_free_pins(qstr what, mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out);
void assert_pin_free(const mcu_pin_obj_t* pin);
diff --git a/shared-bindings/rgbmatrix/RGBMatrix.c b/shared-bindings/rgbmatrix/RGBMatrix.c
index cbf570958..c833faa6e 100644
--- a/shared-bindings/rgbmatrix/RGBMatrix.c
+++ b/shared-bindings/rgbmatrix/RGBMatrix.c
@@ -50,13 +50,10 @@ STATIC uint8_t validate_pin(mp_obj_t obj) {
}
STATIC void validate_pins(qstr what, uint8_t* pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) {
- mp_int_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq));
- if (len > max_pins) {
- mp_raise_ValueError_varg(translate("At most %d %q may be specified (not %d)"), max_pins, what, len);
- }
- *count_out = len;
- for (mp_int_t i=0; i<len; i++) {
- pin_nos[i] = validate_pin(mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL));
+ mcu_pin_obj_t *pins[max_pins];
+ validate_list_is_free_pins(what, pins, max_pins, seq, count_out);
+ for (mp_int_t i=0; i<*count_out; i++) {
+ pin_nos[i] = common_hal_mcu_pin_number(pins[i]);
}
}
diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c
new file mode 100644
index 000000000..1d8c4bafb
--- /dev/null
+++ b/shared-bindings/sdcardio/SDCard.c
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+#include "py/objarray.h"
+
+#include "shared-bindings/sdcardio/SDCard.h"
+#include "shared-module/sdcardio/SDCard.h"
+#include "common-hal/busio/SPI.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "supervisor/flash.h"
+
+//| class SDCard:
+//| """SD Card Block Interface
+//|
+//| Controls an SD card over SPI. This built-in module has higher read
+//| performance than the library adafruit_sdcard, but it is only compatible with
+//| `busio.SPI`, not `bitbangio.SPI`. Usually an SDCard object is used
+//| with ``storage.VfsFat`` to allow file I/O to an SD card."""
+//|
+//| def __init__(bus:busio.SPI, cs=digitalio.DigitalInOut, baudrate=8000000):
+//| """Construct an SPI SD Card object with the given properties
+//|
+//| :param busio.SPI spi: The SPI bus
+//| :param microcontroller.Pin cs: The chip select connected to the card
+//| :param int baudrate: The SPI data rate to use after card setup
+//|
+//| Note that during detection and configuration, a hard-coded low baudrate is used.
+//| Data transfers use the specified baurate (rounded down to one that is supported by
+//| the microcontroller)
+//|
+//| Example usage:
+//|
+//| .. code-block:: python
+//|
+//| import os
+//|
+//| import board
+//| import sdcardio
+//| import storage
+//|
+//| sd = sdcardio.SDCard(board.SPI(), board.SD_CS)
+//| vfs = storage.VfsFat(sd)
+//| storage.mount(vfs, '/sd')
+//| os.listdir('/sd')"""
+
+STATIC mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_spi, ARG_cs, ARG_baudrate, ARG_sdio, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_spi, MP_ARG_OBJ, {.u_obj = mp_const_none } },
+ { MP_QSTR_cs, MP_ARG_OBJ, {.u_obj = mp_const_none } },
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 8000000} },
+ { MP_QSTR_sdio, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 8000000} },
+ };
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj);
+ mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj);
+
+ sdcardio_sdcard_obj_t *self = m_new_obj(sdcardio_sdcard_obj_t);
+ self->base.type = &sdcardio_SDCard_type;
+
+ common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int);
+
+ return self;
+}
+
+
+//| def count() -> int:
+//| """Returns the total number of sectors
+//|
+//| Due to technical limitations, this is a function and not a property.
+//|
+//| :return: The number of 512-byte blocks, as a number"""
+//|
+mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) {
+ sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
+ return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count);
+
+//| def deinit() -> None:
+//| """Disable permanently.
+//|
+//| :return: None"""
+//|
+mp_obj_t sdcardio_sdcard_deinit(mp_obj_t self_in) {
+ sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
+ common_hal_sdcardio_sdcard_deinit(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit);
+
+
+//| def readblocks(start_block: int, buf: bytearray) -> None:
+//|
+//| """Read one or more blocks from the card
+//|
+//| :param int start_block: The block to start reading from
+//| :param bytearray buf: The buffer to write into. Length must be multiple of 512.
+//|
+//| :return: None"""
+//|
+
+mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
+ uint32_t start_block = mp_obj_get_int(start_block_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
+ sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
+ int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo);
+ if (result < 0) {
+ mp_raise_OSError(-result);
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks);
+
+//| def writeblocks(start_block: int, buf: bytearray) -> None:
+//|
+//| """Write one or more blocks to the card
+//|
+//| :param int start_block: The block to start writing from
+//| :param bytearray buf: The buffer to read from. Length must be multiple of 512.
+//|
+//| :return: None"""
+//|
+
+mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
+ uint32_t start_block = mp_obj_get_int(start_block_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+ sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
+ int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo);
+ if (result < 0) {
+ mp_raise_OSError(-result);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks);
+
+STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(sdcardio_sdcard_locals_dict, sdcardio_sdcard_locals_dict_table);
+
+const mp_obj_type_t sdcardio_SDCard_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_SDCard,
+ .make_new = sdcardio_sdcard_make_new,
+ .locals_dict = (mp_obj_dict_t*)&sdcardio_sdcard_locals_dict,
+};
diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h
new file mode 100644
index 000000000..5986d5b81
--- /dev/null
+++ b/shared-bindings/sdcardio/SDCard.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+extern const mp_obj_type_t sdcardio_SDCard_type;
diff --git a/shared-bindings/sdcardio/__init__.c b/shared-bindings/sdcardio/__init__.c
new file mode 100644
index 000000000..746aa5588
--- /dev/null
+++ b/shared-bindings/sdcardio/__init__.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <stdint.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/sdcardio/SDCard.h"
+
+//| """Interface to an SD card via the SPI bus"""
+
+STATIC const mp_rom_map_elem_t sdcardio_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdcardio) },
+ { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdcardio_SDCard_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(sdcardio_module_globals, sdcardio_module_globals_table);
+
+const mp_obj_module_t sdcardio_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&sdcardio_module_globals,
+};
diff --git a/shared-bindings/sdcardio/__init__.h b/shared-bindings/sdcardio/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-bindings/sdcardio/__init__.h
diff --git a/shared-bindings/sdioio/SDCard.c b/shared-bindings/sdioio/SDCard.c
new file mode 100644
index 000000000..77b41ce12
--- /dev/null
+++ b/shared-bindings/sdioio/SDCard.c
@@ -0,0 +1,296 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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.
+ */
+
+// This file contains all of the Python API definitions for the
+// sdioio.SDCard class.
+
+#include <string.h>
+
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/sdioio/SDCard.h"
+#include "shared-bindings/util.h"
+
+#include "lib/utils/buffer_helper.h"
+#include "lib/utils/context_manager_helpers.h"
+#include "py/mperrno.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+#include "supervisor/shared/translate.h"
+
+//| class SDCard:
+//| """SD Card Block Interface with SDIO
+//|
+//| Controls an SD card over SDIO. SDIO is a parallel protocol designed
+//| for SD cards. It uses a clock pin, a command pin, and 1 or 4
+//| data pins. It can be operated at a high frequency such as
+//| 25MHz. Usually an SDCard object is used with ``storage.VfsFat``
+//| to allow file I/O to an SD card."""
+//|
+//| def __init__(*, clock: digitalio.DigitalInOut, command: digitalio.DigitalInOut, data: List[digitalio.DigitalInOut], frequency: int):
+//| """Construct an SDIO SD Card object with the given properties
+//|
+//| :param ~microcontroller.Pin clock: the pin to use for the clock.
+//| :param ~microcontroller.Pin command: the pin to use for the command.
+//| :param data: A sequence of pins to use for data.
+//| :param frequency: The frequency of the bus in Hz
+//|
+//| Example usage:
+//|
+//| .. code-block:: python
+//|
+//| import os
+//|
+//| import board
+//| import sdioio
+//| import storage
+//|
+//| sd = sdioio.SDCard(
+//| clock=board.SDIO_CLOCK,
+//| command=board.SDIO_COMMAND,
+//| data=board.SDIO_DATA,
+//| frequency=25000000)
+//| vfs = storage.VfsFat(sd)
+//| storage.mount(vfs, '/sd')
+//| os.listdir('/sd')"""
+//| ...
+//|
+
+STATIC mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ sdioio_sdcard_obj_t *self = m_new_obj(sdioio_sdcard_obj_t);
+ self->base.type = &sdioio_SDCard_type;
+ enum { ARG_clock, ARG_command, ARG_data, ARG_frequency, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ },
+ { MP_QSTR_command, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ },
+ { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ },
+ { MP_QSTR_frequency, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT },
+ };
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ const mcu_pin_obj_t* clock = validate_obj_is_free_pin(args[ARG_clock].u_obj);
+ const mcu_pin_obj_t* command = validate_obj_is_free_pin(args[ARG_command].u_obj);
+ mcu_pin_obj_t *data_pins[4];
+ uint8_t num_data;
+ validate_list_is_free_pins(MP_QSTR_data, data_pins, MP_ARRAY_SIZE(data_pins), args[ARG_data].u_obj, &num_data);
+
+ common_hal_sdioio_sdcard_construct(self, clock, command, num_data, data_pins, args[ARG_frequency].u_int);
+ return MP_OBJ_FROM_PTR(self);
+}
+
+STATIC void check_for_deinit(sdioio_sdcard_obj_t *self) {
+ if (common_hal_sdioio_sdcard_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+//| def configure(*, frequency=0, width=0) -> None:
+//| """Configures the SDIO bus.
+//|
+//| :param int frequency: the desired clock rate in Hertz. The actual clock rate may be higher or lower due to the granularity of available clock settings. Check the `frequency` attribute for the actual clock rate.
+//| :param int width: the number of data lines to use. Must be 1 or 4 and must also not exceed the number of data lines at construction
+//|
+//| .. note:: Leaving a value unspecified or 0 means the current setting is kept"""
+//|
+STATIC mp_obj_t sdioio_sdcard_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_frequency, ARG_width, NUM_ARGS };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ };
+ sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_int_t frequency = args[ARG_frequency].u_int;
+ if (frequency < 0) {
+ mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_baudrate);
+ }
+
+ uint8_t width = args[ARG_width].u_int;
+ if (width != 0 && width != 1 && width != 4) {
+ mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_width);
+ }
+
+ if (!common_hal_sdioio_sdcard_configure(self, frequency, width)) {
+ mp_raise_OSError(MP_EIO);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(sdioio_sdcard_configure_obj, 1, sdioio_sdcard_configure);
+
+//| def count() -> int:
+//| """Returns the total number of sectors
+//|
+//| Due to technical limitations, this is a function and not a property.
+//|
+//| :return: The number of 512-byte blocks, as a number"""
+//|
+STATIC mp_obj_t sdioio_sdcard_count(mp_obj_t self_in) {
+ sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_count(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_count_obj, sdioio_sdcard_count);
+
+//| def readblocks(start_block: int, buf: bytearray) -> None:
+//|
+//| """Read one or more blocks from the card
+//|
+//| :param int start_block: The block to start reading from
+//| :param bytearray buf: The buffer to write into. Length must be multiple of 512.
+//|
+//| :return: None"""
+mp_obj_t sdioio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
+ uint32_t start_block = mp_obj_get_int(start_block_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
+ sdioio_sdcard_obj_t *self = (sdioio_sdcard_obj_t*)self_in;
+ int result = common_hal_sdioio_sdcard_readblocks(self, start_block, &bufinfo);
+ if (result < 0) {
+ mp_raise_OSError(-result);
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_readblocks_obj, sdioio_sdcard_readblocks);
+
+//| def writeblocks(start_block: int, buf: bytearray) -> None:
+//|
+//| """Write one or more blocks to the card
+//|
+//| :param int start_block: The block to start writing from
+//| :param bytearray buf: The buffer to read from. Length must be multiple of 512.
+//|
+//| :return: None"""
+//|
+mp_obj_t sdioio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
+ uint32_t start_block = mp_obj_get_int(start_block_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
+ sdioio_sdcard_obj_t *self = (sdioio_sdcard_obj_t*)self_in;
+ int result = common_hal_sdioio_sdcard_writeblocks(self, start_block, &bufinfo);
+ if (result < 0) {
+ mp_raise_OSError(-result);
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_writeblocks_obj, sdioio_sdcard_writeblocks);
+
+//| @property
+//| def frequency(self) -> int:
+//| """The actual SDIO bus frequency. This may not match the frequency
+//| requested due to internal limitations."""
+//| ...
+//|
+STATIC mp_obj_t sdioio_sdcard_obj_get_frequency(mp_obj_t self_in) {
+ sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_frequency(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_get_frequency_obj, sdioio_sdcard_obj_get_frequency);
+
+const mp_obj_property_t sdioio_sdcard_frequency_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&sdioio_sdcard_get_frequency_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| @property
+//| def width(self) -> int:
+//| """The actual SDIO bus width, in bits"""
+//| ...
+//|
+STATIC mp_obj_t sdioio_sdcard_obj_get_width(mp_obj_t self_in) {
+ sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_width(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_get_width_obj, sdioio_sdcard_obj_get_width);
+
+const mp_obj_property_t sdioio_sdcard_width_obj = {
+ .base.type = &mp_type_property,
+ .proxy = {(mp_obj_t)&sdioio_sdcard_get_width_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj},
+};
+
+//| def deinit() -> None:
+//| """Disable permanently.
+//|
+//| :return: None"""
+STATIC mp_obj_t sdioio_sdcard_obj_deinit(mp_obj_t self_in) {
+ sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_sdioio_sdcard_deinit(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_deinit_obj, sdioio_sdcard_obj_deinit);
+
+//| def __enter__(self, ) -> Any:
+//| """No-op used by Context Managers.
+//| Provided by context manager helper."""
+//| ...
+//|
+
+//| def __exit__(self, ) -> Any:
+//| """Automatically deinitializes the hardware when exiting a context. See
+//| :ref:`lifetime-and-contextmanagers` for more info."""
+//| ...
+//|
+STATIC mp_obj_t sdioio_sdcard_obj___exit__(size_t n_args, const mp_obj_t *args) {
+ (void)n_args;
+ common_hal_sdioio_sdcard_deinit(args[0]);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sdioio_sdcard_obj___exit___obj, 4, 4, sdioio_sdcard_obj___exit__);
+
+STATIC const mp_rom_map_elem_t sdioio_sdcard_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdioio_sdcard_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&sdioio_sdcard_obj___exit___obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&sdioio_sdcard_configure_obj) },
+ { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&sdioio_sdcard_frequency_obj) },
+ { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&sdioio_sdcard_width_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdioio_sdcard_count_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdioio_sdcard_readblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdioio_sdcard_writeblocks_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(sdioio_sdcard_locals_dict, sdioio_sdcard_locals_dict_table);
+
+const mp_obj_type_t sdioio_SDCard_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_SDCard,
+ .make_new = sdioio_sdcard_make_new,
+ .locals_dict = (mp_obj_dict_t*)&sdioio_sdcard_locals_dict,
+};
diff --git a/shared-bindings/sdioio/SDCard.h b/shared-bindings/sdioio/SDCard.h
new file mode 100644
index 000000000..7f62ee7a6
--- /dev/null
+++ b/shared-bindings/sdioio/SDCard.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * 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_SHARED_BINDINGS_BUSIO_SDIO_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SDIO_H
+
+#include "py/obj.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/sdioio/SDCard.h"
+
+// Type object used in Python. Should be shared between ports.
+extern const mp_obj_type_t sdioio_SDCard_type;
+
+// Construct an underlying SDIO object.
+extern void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self,
+ const mcu_pin_obj_t * clock, const mcu_pin_obj_t * command,
+ uint8_t num_data, mcu_pin_obj_t ** data, uint32_t frequency);
+
+extern void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self);
+extern bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self);
+
+extern bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t baudrate, uint8_t width);
+
+extern void common_hal_sdioio_sdcard_unlock(sdioio_sdcard_obj_t *self);
+
+// Return actual SDIO bus frequency.
+uint32_t common_hal_sdioio_sdcard_get_frequency(sdioio_sdcard_obj_t* self);
+
+// Return SDIO bus width.
+uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t* self);
+
+// Return number of device blocks
+uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t* self);
+
+// Read or write blocks
+int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t* self, uint32_t start_block, mp_buffer_info_t *bufinfo);
+int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t* self, uint32_t start_block, mp_buffer_info_t *bufinfo);
+
+// This is used by the supervisor to claim SDIO devices indefinitely.
+extern void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SDIO_H
diff --git a/shared-bindings/sdioio/__init__.c b/shared-bindings/sdioio/__init__.c
new file mode 100644
index 000000000..b88e5c3a9
--- /dev/null
+++ b/shared-bindings/sdioio/__init__.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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 <stdint.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/sdioio/SDCard.h"
+
+//| """Interface to an SD card via the SDIO bus"""
+
+STATIC const mp_rom_map_elem_t sdioio_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdio) },
+ { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdioio_SDCard_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(sdioio_module_globals, sdioio_module_globals_table);
+
+const mp_obj_module_t sdioio_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&sdioio_module_globals,
+};
diff --git a/shared-bindings/sdioio/__init__.h b/shared-bindings/sdioio/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-bindings/sdioio/__init__.h
diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c
new file mode 100644
index 000000000..9e861279d
--- /dev/null
+++ b/shared-module/sdcardio/SDCard.c
@@ -0,0 +1,466 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+// This implementation largely follows the structure of adafruit_sdcard.py
+
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/time/__init__.h"
+#include "shared-bindings/util.h"
+#include "shared-module/sdcardio/SDCard.h"
+
+#include "py/mperrno.h"
+
+#if 0
+#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, ## __VA_ARGS__))
+#else
+#define DEBUG_PRINT(...) ((void)0)
+#endif
+
+#define CMD_TIMEOUT (200)
+
+#define R1_IDLE_STATE (1<<0)
+#define R1_ILLEGAL_COMMAND (1<<2)
+
+#define TOKEN_CMD25 (0xFC)
+#define TOKEN_STOP_TRAN (0xFD)
+#define TOKEN_DATA (0xFE)
+
+STATIC bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) {
+ if (!common_hal_busio_spi_try_lock(self->bus)) {
+ return false;
+ }
+ common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8);
+ common_hal_digitalio_digitalinout_set_value(&self->cs, false);
+ return true;
+}
+
+STATIC void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) {
+ if (!lock_and_configure_bus(self)) {
+ mp_raise_OSError(EAGAIN);
+ }
+}
+
+STATIC void clock_card(sdcardio_sdcard_obj_t *self, int bytes) {
+ uint8_t buf[] = {0xff};
+ common_hal_digitalio_digitalinout_set_value(&self->cs, true);
+ for (int i=0; i<bytes; i++) {
+ common_hal_busio_spi_write(self->bus, buf, 1);
+ }
+}
+
+STATIC void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) {
+ clock_card(self, 1);
+ common_hal_busio_spi_unlock(self->bus);
+}
+
+static uint8_t CRC7(const uint8_t* data, uint8_t n) {
+ uint8_t crc = 0;
+ for (uint8_t i = 0; i < n; i++) {
+ uint8_t d = data[i];
+ for (uint8_t j = 0; j < 8; j++) {
+ crc <<= 1;
+ if ((d & 0x80) ^ (crc & 0x80)) {
+ crc ^= 0x09;
+ }
+ d <<= 1;
+ }
+ }
+ return (crc << 1) | 1;
+}
+
+#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms
+STATIC void wait_for_ready(sdcardio_sdcard_obj_t *self) {
+ uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS;
+ while (common_hal_time_monotonic_ns() < deadline) {
+ uint8_t b;
+ common_hal_busio_spi_read(self->bus, &b, 1, 0xff);
+ if (b == 0xff) {
+ break;
+ }
+ }
+}
+
+// In Python API, defaults are response=None, data_block=True, wait=True
+STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) {
+ DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : "");
+ uint8_t cmdbuf[6];
+ cmdbuf[0] = cmd | 0x40;
+ cmdbuf[1] = (arg >> 24) & 0xff;
+ cmdbuf[2] = (arg >> 16) & 0xff;
+ cmdbuf[3] = (arg >> 8) & 0xff;
+ cmdbuf[4] = arg & 0xff;
+ cmdbuf[5] = CRC7(cmdbuf, 5);
+
+ if (wait) {
+ wait_for_ready(self);
+ }
+
+ common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
+
+ // Wait for the response (response[7] == 0)
+ bool response_received = false;
+ for (int i=0; i<CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
+ if ((cmdbuf[0] & 0x80) == 0) {
+ response_received = true;
+ break;
+ }
+ }
+
+ if (!response_received) {
+ return -EIO;
+ }
+
+ if (response_buf) {
+
+ if (data_block) {
+ cmdbuf[1] = 0xff;
+ do {
+ // Wait for the start block byte
+ common_hal_busio_spi_read(self->bus, cmdbuf+1, 1, 0xff);
+ } while (cmdbuf[1] != 0xfe);
+ }
+
+ common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff);
+
+ if (data_block) {
+ // Read and discard the CRC-CCITT checksum
+ common_hal_busio_spi_read(self->bus, cmdbuf+1, 2, 0xff);
+ }
+
+ }
+
+ return cmdbuf[0];
+}
+
+STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) {
+ return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true);
+}
+
+STATIC bool cmd_nodata(sdcardio_sdcard_obj_t* self, int cmd, int response) {
+ uint8_t cmdbuf[2] = {cmd, 0xff};
+
+ common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
+
+ // Wait for the response (response[7] == response)
+ for (int i=0; i<CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
+ if (cmdbuf[0] == response) {
+ return 0;
+ }
+ }
+ return -EIO;
+}
+
+STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) {
+ for (int i=0; i<CMD_TIMEOUT; i++) {
+ if (cmd(self, 41, 0, NULL, 0, true, true) == 0) {
+ return NULL;
+ }
+ }
+ return translate("timeout waiting for v1 card");
+}
+
+STATIC const compressed_string_t *init_card_v2(sdcardio_sdcard_obj_t *self) {
+ for (int i=0; i<CMD_TIMEOUT; i++) {
+ uint8_t ocr[4];
+ common_hal_time_delay_ms(50);
+ cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
+ cmd(self, 55, 0, NULL, 0, true, true);
+ if (cmd(self, 41, 0x40000000, NULL, 0, true, true) == 0) {
+ cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
+ if ((ocr[0] & 0x40) != 0) {
+ self->cdv = 1;
+ }
+ return NULL;
+ }
+ }
+ return translate("timeout waiting for v2 card");
+}
+
+STATIC const compressed_string_t *init_card(sdcardio_sdcard_obj_t *self) {
+ clock_card(self, 10);
+
+ common_hal_digitalio_digitalinout_set_value(&self->cs, false);
+
+ // CMD0: init card: should return _R1_IDLE_STATE (allow 5 attempts)
+ {
+ bool reached_idle_state = false;
+ for (int i=0; i<5; i++) {
+ if (cmd(self, 0, 0, NULL, 0, true, true) == R1_IDLE_STATE) {
+ reached_idle_state = true;
+ break;
+ }
+ }
+ if (!reached_idle_state) {
+ return translate("no SD card");
+ }
+ }
+
+ // CMD8: determine card version
+ {
+ uint8_t rb7[4];
+ int response = cmd(self, 8, 0x1AA, rb7, sizeof(rb7), false, true);
+ if (response == R1_IDLE_STATE) {
+ const compressed_string_t *result =init_card_v2(self);
+ if (result != NULL) {
+ return result;
+ }
+ } else if (response == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) {
+ const compressed_string_t *result =init_card_v1(self);
+ if (result != NULL) {
+ return result;
+ }
+ } else {
+ return translate("couldn't determine SD card version");
+ }
+ }
+
+ // CMD9: get number of sectors
+ {
+ uint8_t csd[16];
+ int response = cmd(self, 9, 0, csd, sizeof(csd), true, true);
+ if (response != 0) {
+ return translate("no response from SD card");
+ }
+ int csd_version = (csd[0] & 0xC0) >> 6;
+ if (csd_version >= 2) {
+ return translate("SD card CSD format not supported");
+ }
+
+ if (csd_version == 1) {
+ self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024;
+ } else {
+ uint32_t block_length = 1 << (csd[5] & 0xF);
+ uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6);
+ uint32_t mult = 1 << (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2);
+ self->sectors = block_length / 512 * mult * (c_size + 1);
+ }
+ }
+
+ // CMD16: set block length to 512 bytes
+ {
+ int response = cmd(self, 16, 512, NULL, 0, true, true);
+ if (response != 0) {
+ return translate("can't set 512 block size");
+ }
+ }
+
+ return NULL;
+}
+
+void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, mcu_pin_obj_t *cs, int baudrate) {
+ self->bus = bus;
+ common_hal_digitalio_digitalinout_construct(&self->cs, cs);
+ common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL);
+
+ self->cdv = 512;
+ self->sectors = 0;
+ self->baudrate = 250000;
+
+ lock_bus_or_throw(self);
+ const compressed_string_t *result = init_card(self);
+ extraclock_and_unlock_bus(self);
+
+ if (result != NULL) {
+ common_hal_digitalio_digitalinout_deinit(&self->cs);
+ mp_raise_OSError_msg(result);
+ }
+
+ self->baudrate = baudrate;
+}
+
+void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) {
+ if (!self->bus) {
+ return;
+ }
+ self->bus = 0;
+ common_hal_digitalio_digitalinout_deinit(&self->cs);
+}
+
+void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) {
+ if (!self->bus) {
+ raise_deinited_error();
+ }
+}
+
+int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) {
+ common_hal_sdcardio_check_for_deinit(self);
+ return self->sectors;
+}
+
+int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) {
+ uint8_t aux[2] = {0, 0};
+ while (aux[0] != 0xfe) {
+ common_hal_busio_spi_read(self->bus, aux, 1, 0xff);
+ }
+
+ common_hal_busio_spi_read(self->bus, buf, size, 0xff);
+
+ // Read checksum and throw it away
+ common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff);
+ return 0;
+}
+
+int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ uint32_t nblocks = buf->len / 512;
+ if (nblocks == 1) {
+ // Use CMD17 to read a single block
+ return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true);
+ } else {
+ // Use CMD18 to read multiple blocks
+ int r = block_cmd(self, 18, start_block, NULL, 0, true, true);
+ if (r < 0) {
+ return r;
+ }
+
+ uint8_t *ptr = buf->buf;
+ while (nblocks--) {
+ r = readinto(self, ptr, 512);
+ if (r < 0) {
+ return r;
+ }
+ ptr += 512;
+ }
+
+ // End the multi-block read
+ r = cmd(self, 12, 0, NULL, 0, true, false);
+
+ // Return first status 0 or last before card ready (0xff)
+ while (r != 0) {
+ uint8_t single_byte;
+ common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff);
+ if (single_byte & 0x80) {
+ return r;
+ }
+ r = single_byte;
+ }
+ }
+ return 0;
+}
+
+int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ if (buf->len % 512 != 0) {
+ mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
+ }
+
+ lock_and_configure_bus(self);
+ int r = readblocks(self, start_block, buf);
+ extraclock_and_unlock_bus(self);
+ return r;
+}
+
+int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) {
+ wait_for_ready(self);
+
+ uint8_t cmd[2];
+ cmd[0] = token;
+
+ common_hal_busio_spi_write(self->bus, cmd, 1);
+ common_hal_busio_spi_write(self->bus, buf, size);
+
+ cmd[0] = cmd[1] = 0xff;
+ common_hal_busio_spi_write(self->bus, cmd, 2);
+
+ // Check the response
+ // This differs from the traditional adafruit_sdcard handling,
+ // but adafruit_sdcard also ignored the return value of SDCard._write(!)
+ // so nobody noticed
+ //
+ //
+ // Response is as follows:
+ // x x x 0 STAT 1
+ // 7 6 5 4 3..1 0
+ // with STATUS 010 indicating "data accepted", and other status bit
+ // combinations indicating failure.
+ // In practice, I was seeing cmd[0] as 0xe5, indicating success
+ for (int i=0; i<CMD_TIMEOUT; i++) {
+ common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
+ DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]);
+ if ((cmd[0] & 0b00010001) == 0b00000001) {
+ if ((cmd[0] & 0x1f) != 0x5) {
+ return -EIO;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Wait for the write to finish
+ do {
+ common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
+ } while (cmd[0] == 0);
+
+ // Success
+ return 0;
+}
+
+int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ uint32_t nblocks = buf->len / 512;
+ if (nblocks == 1) {
+ // Use CMD24 to write a single block
+ int r = block_cmd(self, 24, start_block, NULL, 0, true, true);
+ if (r < 0) {
+ return r;
+ }
+ r = _write(self, TOKEN_DATA, buf->buf, buf->len);
+ if (r < 0) {
+ return r;
+ }
+ } else {
+ // Use CMD25 to write multiple block
+ int r = block_cmd(self, 25, start_block, NULL, 0, true, true);
+ if (r < 0) {
+ return r;
+ }
+
+ uint8_t *ptr = buf->buf;
+ while (nblocks--) {
+ r = _write(self, TOKEN_CMD25, ptr, 512);
+ if (r < 0) {
+ return r;
+ }
+ ptr += 512;
+ }
+
+ cmd_nodata(self, TOKEN_STOP_TRAN, 0);
+ }
+ return 0;
+}
+
+int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
+ common_hal_sdcardio_check_for_deinit(self);
+ if (buf->len % 512 != 0) {
+ mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
+ }
+ lock_and_configure_bus(self);
+ int r = writeblocks(self, start_block, buf);
+ extraclock_and_unlock_bus(self);
+ return r;
+}
diff --git a/shared-module/sdcardio/SDCard.h b/shared-module/sdcardio/SDCard.h
new file mode 100644
index 000000000..76c906029
--- /dev/null
+++ b/shared-module/sdcardio/SDCard.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "py/obj.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+#include "py/objarray.h"
+
+#include "common-hal/busio/SPI.h"
+#include "common-hal/digitalio/DigitalInOut.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ busio_spi_obj_t *bus;
+ digitalio_digitalinout_obj_t cs;
+ int cdv;
+ int baudrate;
+ uint32_t sectors;
+} sdcardio_sdcard_obj_t;
+
+void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *cs, int baudrate);
+void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self);
+void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self);
+int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self);
+int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
+int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/sdcardio/__init__.c
diff --git a/shared-module/sdcardio/__init__.h b/shared-module/sdcardio/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/sdcardio/__init__.h
diff --git a/tools/mksdiodata.py b/tools/mksdiodata.py
new file mode 100755
index 000000000..0cce3819f
--- /dev/null
+++ b/tools/mksdiodata.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python3
+
+def defines(name, function):
+ print(f'pin_function_t {name} [] = {{')
+ for instance in (0, 1):
+ for port in 'ABCD':
+ for idx in range(32):
+ pin = f'P{port}{idx:02d}'
+ pinmux = f'PINMUX_{pin}I_SDHC{instance}_{function}'
+ print(f'''\
+#if defined({pinmux}) && ! defined(IGNORE_PIN_{pin})
+ {{&pin_{pin}, {instance}, PIN_{pin}, {pinmux} & 0xffff}},
+#endif''')
+ print(f'{{NULL, 0, 0}}')
+ print(f'}};')
+ print()
+
+print('''\
+#include <stdint.h>
+#include "py/obj.h"
+#include "sam.h"
+#include "samd/pins.h"
+#include "mpconfigport.h"
+#include "atmel_start_pins.h"
+#include "hal/include/hal_gpio.h"
+
+typedef struct {
+ const mcu_pin_obj_t *obj;
+ uint8_t instance;
+ uint8_t pin;
+ uint16_t function;
+} pin_function_t;
+''')
+
+defines('sdio_ck', 'SDCK')
+defines('sdio_cmd', 'SDCMD')
+defines('sdio_dat0', 'SDDAT0')
+defines('sdio_dat1', 'SDDAT1')
+defines('sdio_dat2', 'SDDAT2')
+defines('sdio_dat3', 'SDDAT3')