1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-24 22:13:52 +01:00
19539: drivers/periph_sdmmc: define a High-level SDIO/SD/MMC API and low-level SDMMC periperal driver interface r=benpicco a=gschorcht

### Contribution description

This PR provides a SDIO/SD/MMC Device API (SDMMC). It implements a SD host controller driver that provides a high-level functions using a low-level SDIO/SD/MMC peripheral driver for accessing

- MultiMediaCards (MMC) and Embedded MultiMediaCards (eMMC)
- SD Memory Cards (SD Cards) with Standard Capacity (SDSC), High Capacity (SDHC) or Extended Capacity (SDXC).

It supports:

- 1-bit, 4-bit and 8-bit data bus width
- Default Speed and High Speed
- Auto-CLK

The SDIO/SD/MMC device API (SDMMC) is divided into two parts:

1. The high-level API that implements the SD Host Controller driver and allows
   - to inititialize and identify different types of cards,
   - to access them either blockwise or bytewise,
   - to get information about the used card, and
   - to send single commands or application specific commands to the card.

2. The low-level SDIO/SD/MMC peripheral driver implements the low-level functions required by the high-level device API. It has to be implemented for each MCU.

### Limitations:

- Only one card per SDIO/SD/MMC device is supported.
- eMMCs specific features are not supported.
- UHS-I, UHS-II and UHS-III are not supported.

### Testing procedure

PR #19540, PR #19760 or PR #19786 is needed to test this PR.

### Issues/PRs references

Prerequisite for PR #19540
Prerequisite for PR #19760
Prerequisite for PR #19786

19815: cpu/sam0_common/periph/sdhc: busy waiting and clock fixes r=benpicco a=benpicco



19860: drivers/ft5x06: fix vendor ID for FT6xx6 and FTxxxx register addresses r=benpicco a=gschorcht

### Contribution description

This PR provides a fix of the vendor ID for FT6xx6 touch panel driver ICs and a fix of register addresses for FTxxxx.

According to the [Application Note for FT6x06 CTPM](https://cdn-shop.adafruit.com/datasheets/FT6x06_AN_public_ver0.1.3.pdf), the vendor ID of FT6x06 touch panel driver ICs is `0x11` instead of `0xcd`. Although there are no information found in the Web about the FT6x36, the FT6336U touch panel of a ESP32-S3 WT32 SC01 Plus is also working with `0x11` as vendor ID so that it seems that FT6x36 is also using `0x11` as vendor ID.

Figured out with a `stm32f723e-disco` board (revision D03). Without this PR, `tests/drivers/ft5x06` gives:
```
+------------Initializing------------+
[ft5x06] init: invalid vendor ID: '0x11' (expected: 0xcd)
[Error] Initialization failed
```
With this PR it works as expected.
```
+------------Initializing------------+
Initialization successful
main(): This is RIOT! (Version: 2023.10-devel-96-gbb9011-drivers/ft5x06_fix_vendor_id)
FT5x06 test application

+------------Initializing------------+
[ft5x06] init: configuring touchscreen interrupt
Initialization successful
1 touch detected
[ft5x06] read gesture_id '0x00'
Touch 1 - X: 151, Y:138
[ft5x06] read gesture_id '0x00'
```

Some background information found in the Web:

- According to the [STM32CubeF7](c20e6dd15b/Drivers/BSP/STM32F723E-Discovery/stm32f723e_discovery_ts.c (L24-L27)) the FRIDA LCD panel mounted on the `stm32f723e-disco` board either uses FT6x36 (prior revision D) or FT3x67 (revision D). However, the FT5x06 driver type for the card is defined as FT6x06, which does not seem correct: bb9011c3fb/boards/stm32f723e-disco/include/board.h (L59)
- According to the [STM32CubeF7](c20e6dd15b/Drivers/BSP/Components/ft6x06/ft6x06.h (L269-L270)), the vendor ID for FT6x36 should be `0xcd`. However, the FT6336U on ESP32-S3 WT32 SC01 Plus works with vendor ID `0x11`.
- The [Adafruit FT6206 library](95118cd983/Adafruit_FT6206.h (L28)) uses `0x11` as vendor id.
- The `stm32l496g-disco` board uses a FT6236 which has vendor ID `0xcd`.

So the information available on the web is confusing. Maybe, a better solution would be to accept `0x11` as well as `0xcd` as vendor ID for FT6xxx touch panels. Unfortunately, there are no documents available on the registers directly from FocalTech 😟 so it seems to be more speculation than knowledge.

### Testing procedure


### Issues/PRs references



19886: cpu/efm32: fix DAC configuration r=benpicco a=gschorcht

### Contribution description

The EFM32 MCU allows the reference voltage to be configured per DAC device, not per DAC channel. Also, the DAC reference voltage was defined in the configuration but not used anywhere.

At the moment we have only defined one board (`stwstk6220a`) that uses the DAC, so changing the configuration interface shouldn't be critical.

### Testing procedure

`tests/periph/dac` should still work for the `stwstk6220a`
```
BOARD=slwstk6220a make -j8 -C tests/periph/dac flash
```
I don't have a `stwstk6220a` board (EFM32 Series 0) so that I can't test it. I could only test it for the `sltb009a` board (EFM32 Series 1) with the change for VDAC in PR #19887.

### Issues/PRs references


Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
Co-authored-by: Benjamin Valentin <benjamin.valentin@ml-pa.com>
This commit is contained in:
bors[bot] 2023-08-23 16:55:09 +00:00 committed by GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 5031 additions and 123 deletions

View File

@ -83,6 +83,7 @@ static const adc_chan_conf_t adc_channel_config[] = {
static const dac_conf_t dac_config[] = {
{
.dev = DAC0,
.ref = dacRefVDD,
.cmu = cmuClock_DAC0,
}
};
@ -91,7 +92,6 @@ static const dac_chan_conf_t dac_channel_config[] = {
{
.dev = 0,
.index = 1,
.ref = dacRefVDD,
}
};

View File

@ -84,6 +84,7 @@ static const adc_chan_conf_t adc_channel_config[] = {
static const dac_conf_t dac_config[] = {
{
.dev = DAC0,
.ref = dacRefVDD,
.cmu = cmuClock_DAC0,
}
};
@ -92,7 +93,6 @@ static const dac_chan_conf_t dac_channel_config[] = {
{
.dev = 0,
.index = 1,
.ref = dacRefVDD,
}
};

View File

@ -84,6 +84,7 @@ static const adc_chan_conf_t adc_channel_config[] = {
static const dac_conf_t dac_config[] = {
{
.dev = DAC0,
.ref = dacRefVDD,
.cmu = cmuClock_DAC0,
}
};
@ -92,7 +93,6 @@ static const dac_chan_conf_t dac_channel_config[] = {
{
.dev = 0,
.index = 1,
.ref = dacRefVDD,
}
};

View File

@ -79,6 +79,7 @@ typedef struct {
*/
typedef struct {
DAC_TypeDef *dev; /**< DAC device used */
DAC_Ref_TypeDef ref; /**< DAC voltage reference */
CMU_Clock_TypeDef cmu; /**< the device CMU channel */
} dac_conf_t;
@ -88,7 +89,6 @@ typedef struct {
typedef struct {
uint8_t dev; /**< device index */
uint8_t index; /**< channel index */
DAC_Ref_TypeDef ref; /**< channel voltage reference */
} dac_chan_conf_t;
#endif

View File

@ -45,6 +45,7 @@ int8_t dac_init(dac_t line)
/* reset and initialize peripheral */
DAC_Init_TypeDef init = DAC_INIT_DEFAULT;
init.reference = dac_config[dev].ref;
DAC_Reset(dac_config[dev].dev);
DAC_Init(dac_config[dev].dev, &init);

View File

@ -95,6 +95,7 @@ typedef struct {
/** This SD stack uses the maximum block size authorized (512 bytes) */
#define SD_MMC_BLOCK_SIZE 512 /**< SD card block size */
#define SDHC_SLOW_CLOCK_HZ 400000 /**< Clock frequency on init */
#define SDHC_FAST_CLOCK_HZ 25000000 /**< Clock frequency after init */
/**
* @brief Initialize the SD host controller

View File

@ -50,6 +50,7 @@
#include <stdio.h>
#include "periph_cpu.h"
#include "periph/pm.h"
#include "macros/math.h"
#include "vendor/sd_mmc_protocol.h"
#include "sdhc.h"
#include "time_units.h"
@ -66,6 +67,10 @@
#define SDHC_CLOCK_SLOW SAM0_GCLK_TIMER
#endif
#ifndef SDHC_ENABLE_HS
#define SDHC_ENABLE_HS 1
#endif
/**
* @brief The board can overwrite this if only a single SDHC instance is used
* to save 80 Bytes of ROM.
@ -98,17 +103,18 @@ static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint
uint16_t num_blocks);
static bool sdio_test_type(sdhc_state_t *state);
/** SD/MMC transfer rate unit codes (10K) list */
static const uint32_t transfer_units[] = { 10, 100, 1000, 10000, 0, 0, 0, 0 };
/** SD transfer multiplier factor codes (1/10) list */
static const uint8_t transfer_multiplier[16] =
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static bool _card_detect(sdhc_state_t *state)
{
return state->dev->PSR.bit.CARDINS;
}
static inline void _clock_sdcard(sdhc_state_t *state, bool on)
{
(void)state;
SDHC_DEV->CCR.bit.SDCLKEN = on;
}
static bool _check_mask(uint32_t val, uint32_t mask)
{
return (val & mask) == mask;
@ -126,26 +132,73 @@ static void _delay(unsigned us)
}
}
static void _reset_all(sdhc_state_t *state)
/**
* @brief Reset the entire SDHC peripheral or a part of it
*
* @param state SDHC device context
* @param type Reset type
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*/
static void _reset_sdhc(sdhc_state_t *state, uint8_t type)
{
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTALL;
while (SDHC_DEV->SRR.bit.SWRSTALL) {}
state->need_init = true;
state->error = 0;
SDHC_DEV->SRR.reg = type;
while (SDHC_DEV->SRR.reg & type) {}
if (type == SDHC_SRR_SWRSTALL) {
/* trigger card_init */
state->need_init = true;
state->error = 0;
}
}
static uint32_t _wait_for_event(sdhc_state_t *state)
/**
* @brief Wait for a given event while checking for errors
*
* @param state SDHC device context
* @param event Event to wait for [SDHC_NISTR_*]
* @param error_mask Mask of errors to be checked [SDHC_EISTR_*]
* @param reset Reset type in case of errors
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*
* @return true if event occurred or false on error
*/
static bool _wait_for_event(sdhc_state_t *state,
uint16_t event, uint16_t error_mask,
uint8_t reset)
{
uint32_t res;
/* wait for the event given by event mask */
do {
/* check for any error in error mask */
if (SDHC_DEV->EISTR.reg & error_mask) {
state->error = SDHC_DEV->EISTR.reg;
SDHC_DEV->EISTR.reg = SDHC_EISTR_MASK;
if (IS_USED(ENABLE_DEBUG)) {
DEBUG("sdhc error: %x, ", state->error);
switch (reset) {
case SDHC_SRR_SWRSTCMD:
DEBUG("reset CMD\n");
break;
case SDHC_SRR_SWRSTDAT:
DEBUG("reset DAT\n");
break;
case SDHC_SRR_SWRSTALL:
DEBUG("reset ALL\n");
break;
default:
assert(false);
break;
}
}
_reset_sdhc(state, reset);
return false;
}
} while (!(SDHC_DEV->NISTR.reg & event));
/* SDHC runs off CPU clock - block IDLE so that the clock does not stop */
pm_block(3);
mutex_lock(&state->sync);
pm_unblock(3);
/* clear the event */
SDHC_DEV->NISTR.reg = event;
res = state->error;
state->error = 0;
return res;
return true;
}
static void _init_clocks(sdhc_state_t *state)
@ -207,6 +260,7 @@ int sdhc_init(sdhc_state_t *state)
{
bool f8;
uint32_t response;
int res = 0;
/* set the initial clock slow, single bit and normal speed */
state->type = CARD_TYPE_SD;
@ -220,7 +274,9 @@ int sdhc_init(sdhc_state_t *state)
state->sync = _init_locked;
_init_clocks(state);
_reset_all(state);
_reset_sdhc(state, SDHC_SRR_SWRSTALL);
SDHC_DEV->NISIER.reg = NISTR_CARD_DETECT;
SDHC_DEV->TCR.reg = 14; /* max timeout is 14 or about 1sec */
SDHC_DEV->PCR.reg = SDHC_PCR_SDBPWR | SDHC_PCR_SDBVSEL_3V3;
@ -236,14 +292,16 @@ int sdhc_init(sdhc_state_t *state)
for (int i = 0; i < 2; i++) { /* we do this step twice before failing */
if (!sdhc_send_cmd(state, SDMMC_MCI_CMD0_GO_IDLE_STATE, 0)) {
if (i == 1) {
return -EIO;
res = -EIO;
goto out;
}
}
/* Test for SD version 2 */
if (!sdhc_send_cmd(state, SD_CMD8_SEND_IF_COND, SD_CMD8_PATTERN | SD_CMD8_HIGH_VOLTAGE)) {
if (i == 1) {
/* bad card */
return -EIO;
res = -EIO;
goto out;
}
}
else {
@ -260,49 +318,62 @@ int sdhc_init(sdhc_state_t *state)
}
}
if (!sdio_test_type(state)) {
return -EIO;
res = -EIO;
goto out;
}
if (state->type & CARD_TYPE_SDIO) {
return -ENOTSUP;
res = -ENOTSUP;
goto out;
}
/* Try to get the SD card's operating condition */
if (!_test_voltage(state, f8)) {
state->type = CARD_TYPE_UNKNOWN;
return -EIO;
res = -EIO;
goto out;
}
/* SD MEMORY, Put the Card in Identify Mode
* Note: The CID is not used in this stack */
if (!sdhc_send_cmd(state, SDMMC_CMD2_ALL_SEND_CID, 0)) {
return -EIO;
res = -EIO;
goto out;
}
/* Ask the card to publish a new relative address (RCA).*/
if (!sdhc_send_cmd(state, SD_CMD3_SEND_RELATIVE_ADDR, 0)) {
return -EIO;
res = -EIO;
goto out;
}
state->rca = (uint16_t)(SDHC_DEV->RR[0].reg >> 16);
/* SD MEMORY, Get the Card-Specific Data */
if (!_test_capacity(state)) {
return -EIO;
res = -EIO;
goto out;
}
/* Put it into Transfer Mode */
if (!sdhc_send_cmd(state, SDMMC_CMD7_SELECT_CARD_CMD, (uint32_t)state->rca << 16)) {
return -EIO;
res = -EIO;
goto out;
}
/* SD MEMORY, Read the SCR to get card version */
if (!_test_version(state)) {
return -EIO;
res = -EIO;
goto out;
}
if (!_test_bus_width(state)) {
return -EIO;
res = -EIO;
goto out;
}
/* all SD Cards should support this clock at that point */
state->clock = SDHC_FAST_CLOCK_HZ;
/* update the host controller to the detected changes in bus_width and clock */
_set_hc(state);
/* if it is high speed capable, (well it is) */
if (SDHC_DEV->CA0R.bit.HSSUP) {
if (IS_USED(SDHC_ENABLE_HS) && SDHC_DEV->CA0R.bit.HSSUP) {
if (!_test_high_speed(state)) {
return -EIO;
res = -EIO;
goto out;
}
}
@ -310,11 +381,15 @@ int sdhc_init(sdhc_state_t *state)
_set_hc(state);
if (!sdhc_send_cmd(state, SDMMC_CMD16_SET_BLOCKLEN, SD_MMC_BLOCK_SIZE)) {
return -EIO;
res = -EIO;
goto out;
}
state->need_init = false;
return 0;
out:
_clock_sdcard(state, 0);
return res;
}
bool sdhc_send_cmd(sdhc_state_t *state, uint32_t cmd, uint32_t arg)
@ -355,15 +430,10 @@ bool sdhc_send_cmd(sdhc_state_t *state, uint32_t cmd, uint32_t arg)
: SDHC_EISTR_CMDTEO | SDHC_EISTR_CMDEND | SDHC_EISTR_CMDIDX | SDHC_EISTR_DATTEO |
SDHC_EISTR_DATEND | SDHC_EISTR_ADMA | SDHC_EISTR_CMDCRC | SDHC_EISTR_DATCRC;
SDHC_DEV->NISIER.reg = SDHC_NISTR_CMDC | NISTR_CARD_DETECT;
SDHC_DEV->EISIER.reg = eis;
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (_wait_for_event(state)) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.bit.SWRSTCMD) {}
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
@ -384,32 +454,23 @@ static void _set_speed(sdhc_state_t *state, uint32_t fsdhc)
{
(void)state;
uint32_t div;
if (SDHC_DEV->CCR.bit.SDCLKEN) {
/* wait for command/data to go inactive */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
/* disable the clock */
SDHC_DEV->CCR.reg &= ~SDHC_CCR_SDCLKEN;
SDHC_DEV->CCR.reg = 0;
}
/* since both examples use divided clock rather than programmable - just use divided here */
SDHC_DEV->CCR.reg &= ~SDHC_CCR_CLKGSEL; /* divided clock */
uint32_t div = DIV_ROUND_UP(sam0_gclk_freq(SDHC_CLOCK), fsdhc) - 1;
/* Fsdclk = Fsdhc_core/(2 * div) */
div = (sam0_gclk_freq(SDHC_CLOCK) / fsdhc) / 2;
/* high speed div must not be 0 */
if (SDHC_DEV->HC1R.bit.HSEN && (div == 0)) {
div = 1;
}
DEBUG("sdhc: switch to %lu Hz (div %lu) -> %lu Hz\n",
fsdhc, div, sam0_gclk_freq(SDHC_CLOCK) / (div + 1));
/* write the 10 bit clock divider */
SDHC_DEV->CCR.reg &= ~(SDHC_CCR_USDCLKFSEL_Msk | SDHC_CCR_SDCLKFSEL_Msk);
SDHC_DEV->CCR.reg |= SDHC_CCR_SDCLKFSEL(div) | SDHC_CCR_USDCLKFSEL(div >> 8);
SDHC_DEV->CCR.reg |= SDHC_CCR_INTCLKEN; /* enable internal clock */
SDHC_DEV->CCR.reg = SDHC_CCR_SDCLKFSEL(div) | SDHC_CCR_USDCLKFSEL(div >> 8)
| SDHC_CCR_CLKGSEL | SDHC_CCR_INTCLKEN;
while (!SDHC_DEV->CCR.bit.INTCLKS) {} /* wait for clock to be stable */
SDHC_DEV->CCR.reg |= SDHC_CCR_SDCLKEN; /* enable clock to card */
SDHC_DEV->CCR.bit.SDCLKEN = 1; /* enable clock to card */
}
/**
@ -503,7 +564,6 @@ static bool _test_capacity(sdhc_state_t *state)
{
alignas(uint32_t)
uint8_t csd[CSD_REG_BSIZE];
uint32_t transfer_speed;
if (!sdhc_send_cmd(state, SDMMC_MCI_CMD9_SEND_CSD, (uint32_t)state->rca << 16)) {
return false;
@ -512,9 +572,7 @@ static bool _test_capacity(sdhc_state_t *state)
uint32_t *csd32 = (void *)csd;
csd32[i] = __builtin_bswap32(SDHC_DEV->RR[3 - i].reg);
}
transfer_speed = CSD_TRAN_SPEED(&csd[1]);
state->clock = transfer_units[transfer_speed & 0x7] *
transfer_multiplier[(transfer_speed >> 3) & 0xF] * 1000;
/*
* Card Capacity.
* ----------------------------------------------------
@ -569,10 +627,24 @@ static bool _test_version(sdhc_state_t *state)
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SCR_REG_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
/* Get SD Memory Card - Spec. Version */
switch (SD_SCR_SD_SPEC(scr)) {
case SD_SCR_SD_SPEC_1_0_01:
@ -606,17 +678,16 @@ static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint
uint32_t tmr;
uint32_t command;
uint32_t eis;
uint32_t timeout = 0xFFFFFFFF;
/* wait if card is busy */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
if (cmd & MCI_CMD_WRITE) {
tmr = SDHC_TMR_DTDSEL_WRITE;
SDHC_DEV->NISIER.reg = SDHC_NISTR_BWRRDY | NISTR_CARD_DETECT;
}
else {
tmr = SDHC_TMR_DTDSEL_READ;
SDHC_DEV->NISIER.reg = SDHC_NISTR_BRDRDY | NISTR_CARD_DETECT;
}
if (cmd & MCI_CMD_SDIO_BYTE) {
@ -672,17 +743,24 @@ static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint
DEBUG("sdhc: send cmd %lx\n", command);
SDHC_DEV->EISIER.reg = eis;
SDHC_DEV->SSAR.reg = num_blocks; /* Setup block size for Auto CMD23 */
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (_wait_for_event(state)) {
DEBUG("sdhc error: %x, reset all\n", state->error);
_reset_all(state);
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
if (cmd & MCI_RESP_BUSY) {
do {
if (--timeout == 0) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.bit.SWRSTCMD) {}
return false;
}
} while (!(SDHC_DEV->PSR.reg & SDHC_PSR_DATLL(1))); /* DAT[0] is busy bit */
}
return true;
}
@ -707,6 +785,7 @@ int sdhc_read_blocks(sdhc_state_t *state, uint32_t address, void *dst, uint16_t
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
@ -741,13 +820,30 @@ int sdhc_read_blocks(sdhc_state_t *state, uint32_t address, void *dst, uint16_t
goto out;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
while (!SDHC_DEV->PSR.bit.BUFRDEN) {}
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
@ -772,6 +868,7 @@ int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
@ -808,6 +905,14 @@ int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
goto out;
}
/* wait until buffer write ready */
if (!_wait_for_event(state, SDHC_NISTR_BWRRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
/* Write data */
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
@ -815,7 +920,17 @@ int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
SDHC_DEV->BDPR.reg = *p++;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
_wait_not_busy(state);
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
@ -830,6 +945,7 @@ int sdhc_erase_blocks(sdhc_state_t *state, uint32_t start, uint16_t num_blocks)
}
mutex_lock(&state->lock);
_clock_sdcard(state, 1);
if (state->need_init) {
res = sdhc_init(state);
@ -859,6 +975,8 @@ int sdhc_erase_blocks(sdhc_state_t *state, uint32_t start, uint16_t num_blocks)
}
out:
_wait_not_busy(state);
_clock_sdcard(state, 0);
mutex_unlock(&state->lock);
return res;
}
@ -966,10 +1084,24 @@ static bool _test_high_speed(sdhc_state_t *state)
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SW_STATUS_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
if (SDHC_DEV->RR[0].reg & CARD_STATUS_SWITCH_ERROR) {
return false;
}
@ -1016,32 +1148,13 @@ static bool _test_bus_width(sdhc_state_t *state)
static void _isr(sdhc_state_t *state)
{
uint16_t events = (SDHC_DEV->NISIER.reg & ~NISTR_CARD_DETECT)
| SDHC_NISTR_ERRINT;
if (SDHC_DEV->EISTR.reg) {
state->error = SDHC_DEV->EISTR.reg;
}
DEBUG("NISTR: %x\n", SDHC_DEV->NISTR.reg);
DEBUG("EISTR: %x\n", SDHC_DEV->EISTR.reg);
DEBUG("ACESR: %x\n", SDHC_DEV->ACESR.reg);
/* we got the awaited event */
if (SDHC_DEV->NISTR.reg & events) {
DEBUG_PUTS("unlock");
mutex_unlock(&state->sync);
}
/* if card got inserted we need to re-init */
if (SDHC_DEV->NISTR.reg & NISTR_CARD_DETECT) {
SDHC_DEV->NISTR.reg = NISTR_CARD_DETECT;
DEBUG_PUTS("card presence changed");
state->need_init = true;
}
SDHC_DEV->EISTR.reg = SDHC_EISTR_MASK;
SDHC_DEV->NISTR.reg = SDHC_NISTR_MASK;
cortexm_isr_end();
}

View File

@ -255,9 +255,9 @@ void sam0_gclk_enable(uint8_t id)
gclk_connect(SAM0_GCLK_PERIPH, GCLK_SOURCE_ACTIVE_XOSC, 0);
}
break;
case SAM0_GCLK_200MHZ:
fdpll_init_nolock(1, MHZ(200), OSCCTRL_DPLLCTRLA_ONDEMAND);
gclk_connect(SAM0_GCLK_200MHZ, GCLK_SOURCE_DPLL1, 0);
case SAM0_GCLK_100MHZ:
fdpll_init_nolock(1, MHZ(100), 0 /* OSCCTRL_DPLLCTRLA_ONDEMAND */);
gclk_connect(SAM0_GCLK_100MHZ, GCLK_SOURCE_DPLL1, 0);
fdpll_lock(1);
break;
}
@ -281,8 +281,8 @@ uint32_t sam0_gclk_freq(uint8_t id)
assert(0);
return 0;
}
case SAM0_GCLK_200MHZ:
return MHZ(200);
case SAM0_GCLK_100MHZ:
return MHZ(100);
default:
return 0;
}

View File

@ -65,7 +65,7 @@ enum {
SAM0_GCLK_32KHZ, /**< 32 kHz clock */
SAM0_GCLK_TIMER, /**< 4-8 MHz clock for xTimer */
SAM0_GCLK_PERIPH, /**< 12-48 MHz (DFLL) clock */
SAM0_GCLK_200MHZ, /**< 200MHz FDPLL clock */
SAM0_GCLK_100MHZ, /**< 100MHz FDPLL clock */
};
/** @} */

View File

@ -161,6 +161,7 @@ rsource "mtd_sdcard/Kconfig"
rsource "nvram/Kconfig"
rsource "nvram_spi/Kconfig"
rsource "sdcard_spi/Kconfig"
rsource "sdmmc/Kconfig"
endmenu # Storage Device Drivers
endmenu # Drivers

View File

@ -54,17 +54,17 @@ int ft5x06_init(ft5x06_t *dev, const ft5x06_params_t *params, ft5x06_event_cb_t
return -EPROTO;
}
uint8_t expected_id;
if (dev->params.type == FT5X06_TYPE_FT6X06 || dev->params.type == FT5X06_TYPE_FT6X36) {
expected_id = FT6XX6_VENDOR_ID;
if ((vendor_id != FT5X06_VENDOR_ID_2) && (vendor_id != FT5X06_VENDOR_ID_3)) {
DEBUG("[ft5x06] init: invalid vendor ID: '0x%02x' (expected: 0x%02x or 0x%02x)\n",
vendor_id, FT5X06_VENDOR_ID_2, FT5X06_VENDOR_ID_3);
i2c_release(FT5X06_BUS);
return -ENODEV;
}
}
else {
expected_id = FT5X06_VENDOR_ID;
}
if (expected_id != vendor_id) {
else if (vendor_id != FT5X06_VENDOR_ID_1) {
DEBUG("[ft5x06] init: invalid vendor ID: '0x%02x' (expected: 0x%02x)\n",
vendor_id, expected_id);
vendor_id, FT5X06_VENDOR_ID_1);
i2c_release(FT5X06_BUS);
return -ENODEV;
}

View File

@ -31,15 +31,20 @@ extern "C" {
*/
#define FT5X06_I2C_DEFAULT_ADDRESS (0x38)
/**
* @brief Vendor ID for FT6X06 and FT6X36 models.
*/
#define FT6XX6_VENDOR_ID (0xcd)
/**
* @brief Vendor ID for FT5606, FT5X16, FT5X06I, FT5336, FT3316, FT5436I, FT5336I, FT5X46 models.
*/
#define FT5X06_VENDOR_ID (0x51)
#define FT5X06_VENDOR_ID_1 (0x51)
/**
* @brief Vendor ID used for most FT6X06 and FT6X36 as well as FT3X67 models.
*/
#define FT5X06_VENDOR_ID_2 (0x11)
/**
* @brief Vendor ID used for some FT6X06 and FT6X36 models.
*/
#define FT5X06_VENDOR_ID_3 (0xcd)
/**
* @brief Maximum touches count for FT6X06 and FT6X36 models.
@ -81,9 +86,9 @@ extern "C" {
#define FT5X06_TOUCH5_YH_REG (0x1D)
#define FT5X06_TOUCH5_YL_REG (0x1E)
#define FT5X06_G_AUTO_CLB_MODE_REG (0xA0)
#define FT5X06_G_CIPHER_REG (0xA1)
#define FT5X06_G_LIB_VERSION_H_REG (0xA2)
#define FT5X06_G_LIB_VERSION_L_REG (0xA3)
#define FT5X06_G_LIB_VERSION_H_REG (0xA1)
#define FT5X06_G_LIB_VERSION_L_REG (0xA2)
#define FT5X06_G_CIPHER_REG (0xA3)
#define FT5X06_G_MODE_REG (0xA4)
#define FT5X06_G_PMODE_REG (0xA5)
#define FT5X06_G_FIRMID_REG (0xA6)

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 HAW-Hamburg
* 2023 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_mtd_sdmmc mtd wrapper for sdmmc
* @ingroup drivers_storage
* @brief Driver for SD Memory Cards and MMCs/eMMCs using mtd interface
*
* @{
*
* @file
* @brief Interface definition for mtd_sdmmc driver
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef MTD_SDMMC_H
#define MTD_SDMMC_H
#include <stdint.h>
#include "mtd.h"
#include "sdmmc/sdmmc.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Device descriptor for a mtd_sdmmc device
*
* This is an extension of the @c mtd_dev_t struct
*/
typedef struct {
mtd_dev_t base; /**< inherit mtd_dev_t object */
sdmmc_dev_t *sdmmc; /**< SD/MMC device descriptor */
uint8_t sdmmc_idx; /**< SD/MMC peripheral index */
} mtd_sdmmc_t;
/**
* @defgroup drivers_mtd_sdmmc_config SD Memory Card driver compile configuration
* @ingroup config_drivers_storage
* @{
*/
/**
* @brief Enable SD Memory Card Erase
*
* SD Memory Cards and MMCs/eMMCs handle sector erase internally
* so it's possible to directly write to the card without erasing
* the sector first.
*
* @note An erase call will NOT touch the content if `CONFIG_MTD_SDMMC_ERASE`
* is not set, so enable this feature to ensure overriding the data.
*
* @pre This feature requires the `mtd_write_page` module.
*/
#ifdef DOXYGEN
#define CONFIG_MTD_SDMMC_ERASE
#endif
/** @} */
/**
* @brief sdcard device operations table for mtd
*/
extern const mtd_desc_t mtd_sdmmc_driver;
#ifdef __cplusplus
}
#endif
#endif /* MTD_SDMMC_H */
/** @} */

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,21 @@ config HAVE_MTD_SDCARD
help
Indicates that a sdcard MTD is present
config HAVE_MTD_SDMMC_DEFAULT
bool
depends on HAS_PERIPH_SDMMC
imply MODULE_MTD_SDMMC if MODULE_MTD
imply MODULE_MTD_SDMMC_DEFAULT if MODULE_MTD
help
Indicates that a SD/MMC MTD is present with generic configuration
config HAVE_MTD_SDMMC
bool
depends on HAS_PERIPH_SDMMC
imply MODULE_MTD_SDMMC if MODULE_MTD
help
Indicates that a SD/MMC MTD is present
config HAVE_MTD_SPI_NOR
bool
imply MODULE_MTD_SPI_NOR if MODULE_MTD
@ -46,13 +61,13 @@ config HAVE_MTD_SPI_NOR
config HAVE_SAM0_SDHC
bool
imply MODULE_SAM0_SDHC if MODULE_MTD
imply MODULE_SAM0_SDHC if MODULE_MTD && !MODULE_MTD_SDMMC
help
Indicates that a SAM0 SD Host Controller MTD is present
config HAVE_MTD_SPI_MCI
bool
imply MODULE_MTD_MCI if MODULE_MTD
imply MODULE_MTD_MCI if MODULE_MTD && !MODULE_MTD_SDMMC
help
Indicates that a Multimedia Card Interface (MCI) MTD is present
@ -62,7 +77,7 @@ menuconfig MODULE_MTD
if MODULE_MTD
menu "MTD Interefaces"
menu "MTD Interfaces"
config MODULE_MTD_SPI_NOR
bool "MTD interface for SPI NOR Flash"
@ -95,22 +110,33 @@ config MODULE_MTD_MCI
depends on CPU_FAM_LPC23XX
select MODULE_MCI
config MODULE_MTD_SDCARD
bool "MTD interface for SPI SD-Card"
depends on MODULE_SDCARD_SPI
config MODULE_MTD_SDCARD_DEFAULT
bool "Use Generic SD card configuration"
depends on MODULE_MTD_SDCARD
help
Automatically create a MTD device and mount point for the SD card.
config MODULE_MTD_SDCARD
bool "MTD interface for SPI SD-Card"
depends on MODULE_SDCARD_SPI
config MODULE_MTD_SDMMC
bool "MTD interface for SD/MMC"
depends on HAS_PERIPH_SDMMC
select MODULE_PERIPH_SDMMC
config MODULE_MTD_SDMMC_DEFAULT
bool "Use Generic SD/MMC card configuration"
depends on MODULE_MTD_SDMMC
help
Automatically create a MTD device and mount point for the SD/MMC card.
config MODULE_MTD_EMULATED
bool "MTD interface for MTD emulated in RAM"
config MODULE_SAM0_SDHC
bool "MTD interface for SAM0 SD Host Controller"
depends on CPU_COMMON_SAM0
depends on CPU_COMMON_SAM0 && HAVE_SAM0_SDHC && !MODULE_PERIPH_SDMMC
endmenu # MTD Interfacs

View File

@ -13,3 +13,11 @@ endif
ifneq (,$(filter mtd_sdcard,$(USEMODULE)))
USEMODULE += sdcard_spi
endif
ifneq (,$(filter mtd_sdmmc_default,$(USEMODULE)))
USEMODULE += mtd_sdmmc
endif
ifneq (,$(filter mtd_sdmmc,$(USEMODULE)))
USEMODULE += sdmmc
endif

35
drivers/mtd_sdmmc/Kconfig Normal file
View File

@ -0,0 +1,35 @@
# Copyright (c) 2020 Freie Universitaet Berlin
# 2020 HAW Hamburg
# 2023 Gunar Schorcht
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menuconfig KCONFIG_USEMODULE_MTD_SDMMC
bool "Configure MTD_SDMMC driver"
depends on USEMODULE_MTD_SDMMC
help
Configure the MTD_SDMMC driver using Kconfig.
if KCONFIG_USEMODULE_MTD_SDMMC
config SDMMC_GENERIC_MTD_OFFSET
depends on MODULE_MTD_SDMMC_DEFAULT
int "Index of first auto-configured MTD SD/MMC device"
default 0
help
If you have other MTD devices defined, set this number so that
the auto-configured SD Memory Card(s) or MMCs/eMMCs from
mtd_sdmmc_default will come after them.
config MTD_SDMMC_ERASE
bool "Enable SD Memory Card erase"
help
Enable this to erase sector before a data write operation (SD Memory
Cards only).
SD Memory Cards and MMCs/eMMCs handle sector erase internally
so it's possible to directly write to the card without erasing
the sector first hence this feature is disabled by default.
endif # KCONFIG_USEMODULE_MTD_SDMMC

View File

@ -0,0 +1,3 @@
MODULE = mtd_sdmmc
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1 @@
PSEUDOMODULES += mtd_sdmmc_default

View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2017 HAW-Hamburg
* 2023 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
*/
/**
* @ingroup drivers_mtd_sdmmc
* @{
*
* @file
* @brief Driver for using sdmmc via mtd interface
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#define ENABLE_DEBUG 0
#include "debug.h"
#include "kernel_defines.h"
#include "macros/utils.h"
#include "mtd.h"
#include "mtd_sdmmc.h"
#include "sdmmc/sdmmc.h"
static int mtd_sdmmc_init(mtd_dev_t *dev)
{
DEBUG("mtd_sdmmc_init\n");
mtd_sdmmc_t *mtd_sd = (mtd_sdmmc_t*)dev;
/* get the SDMMC device descriptor from SDMMC peripheral index */
mtd_sd->sdmmc = sdmmc_get_dev(mtd_sd->sdmmc_idx);
if ((mtd_sd->sdmmc->init_done == true) ||
(sdmmc_card_init(mtd_sd->sdmmc) == 0)) {
/* erasing whole sectors is handled internally by the card so you can
delete single blocks (i.e. pages) */
dev->pages_per_sector = 1;
dev->sector_count = (uint32_t)(sdmmc_get_capacity(mtd_sd->sdmmc) /
SDMMC_SDHC_BLOCK_SIZE);
/* sdcard uses the fixed block size of SD-HC cards */
dev->page_size = SDMMC_SDHC_BLOCK_SIZE;
dev->write_size = SDMMC_SDHC_BLOCK_SIZE;
return 0;
}
return -EIO;
}
static int mtd_sdmmc_read_page(mtd_dev_t *dev, void *buff, uint32_t page,
uint32_t offset, uint32_t size)
{
mtd_sdmmc_t *mtd_sd = (mtd_sdmmc_t*)dev;
DEBUG("mtd_sdmmc_read_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n",
page, offset, size);
if (offset || size % SDMMC_SDHC_BLOCK_SIZE) {
#if IS_USED(MODULE_MTD_WRITE_PAGE)
if (dev->work_area == NULL) {
DEBUG("mtd_sdmmc_read_page: no work area\n");
return -ENOTSUP;
}
if (sdmmc_read_blocks(mtd_sd->sdmmc, page, SDMMC_SDHC_BLOCK_SIZE,
1, dev->work_area, NULL)) {
return -EIO;
}
size = MIN(size, SDMMC_SDHC_BLOCK_SIZE - offset);
DEBUG("mtd_sdmmc_read_page: read %" PRIu32 " bytes at offset %" PRIu32 "\n",
size, offset);
memcpy(buff, (uint8_t *)dev->work_area + offset, size);
return size;
#else
return -ENOTSUP;
#endif
}
int err = sdmmc_read_blocks(mtd_sd->sdmmc, page, SDMMC_SDHC_BLOCK_SIZE,
size / SDMMC_SDHC_BLOCK_SIZE, buff, NULL);
if (err) {
DEBUG("mtd_sdmmc_read_page: error %d\n", err);
return -EIO;
}
return size;
}
static int mtd_sdmmc_write_page(mtd_dev_t *dev, const void *buff, uint32_t page,
uint32_t offset, uint32_t size)
{
mtd_sdmmc_t *mtd_sd = (mtd_sdmmc_t*)dev;
DEBUG("mtd_sdmmc_write_page: page:%" PRIu32 " offset:%" PRIu32 " size:%" PRIu32 "\n",
page, offset, size);
if (offset || size % SDMMC_SDHC_BLOCK_SIZE) {
#if IS_USED(MODULE_MTD_WRITE_PAGE)
if (dev->work_area == NULL) {
DEBUG("mtd_sdmmc_write_page: no work area\n");
return -ENOTSUP;
}
if (sdmmc_read_blocks(mtd_sd->sdmmc, page, SDMMC_SDHC_BLOCK_SIZE,
1, dev->work_area, NULL)) {
return -EIO;
}
size = MIN(size, SDMMC_SDHC_BLOCK_SIZE - offset);
DEBUG("mtd_sdmmc_write_page: write %" PRIu32 " bytes at offset %" PRIu32 "\n",
size, offset);
memcpy((uint8_t *)dev->work_area + offset, buff, size);
if (sdmmc_write_blocks(mtd_sd->sdmmc, page, SDMMC_SDHC_BLOCK_SIZE,
1, dev->work_area, NULL)) {
return -EIO;
}
#else
return -ENOTSUP;
#endif
}
else {
int err = sdmmc_write_blocks(mtd_sd->sdmmc, page, SDMMC_SDHC_BLOCK_SIZE,
size / SDMMC_SDHC_BLOCK_SIZE, buff, NULL);
if (err) {
DEBUG("mtd_sdmmc_write_page: error %d\n", err);
return -EIO;
}
}
return size;
}
static int mtd_sdmmc_erase_sector(mtd_dev_t *dev, uint32_t sector, uint32_t count)
{
#if IS_ACTIVE(CONFIG_MTD_SDMMC_ERASE) && IS_USED(MODULE_MTD_WRITE_PAGE)
mtd_sdmmc_t *mtd_sd = (mtd_sdmmc_t*)dev;
DEBUG("mtd_sdmmc_erase_sector: sector: %" PRIu32 " count: %" PRIu32 "\n",
sector, count);
if (dev->work_area == NULL) {
DEBUG("mtd_sdmmc_erase_sector: no work area\n");
return -ENOTSUP;
}
memset(dev->work_area, 0, SDMMC_SDHC_BLOCK_SIZE);
while (count) {
if (sdmmc_write_blocks(mtd_sd->sdmmc, sector, SDMMC_SDHC_BLOCK_SIZE,
1, dev->work_area, NULL)) {
return -EIO;
}
--count;
++sector;
}
#else
(void)dev;
(void)sector;
(void)count;
mtd_sdmmc_t *mtd_sd = (mtd_sdmmc_t*)dev;
if (IS_ACTIVE(CONFIG_MTD_SDMMC_ERASE)) {
return sdmmc_erase_blocks(mtd_sd->sdmmc, sector, count);
}
#endif
return 0;
}
static int mtd_sdmmc_power(mtd_dev_t *dev, enum mtd_power_state power)
{
(void)dev;
(void)power;
/* TODO: implement power down of sdcard in sdcard_spi
(make use of sdcard_spi_params_t.power pin) */
return -ENOTSUP; /* currently not supported */
}
static int mtd_sdmmc_read(mtd_dev_t *dev, void *buff, uint32_t addr,
uint32_t size)
{
int res = mtd_sdmmc_read_page(dev, buff, addr / SDMMC_SDHC_BLOCK_SIZE,
addr % SDMMC_SDHC_BLOCK_SIZE, size);
if (res < 0) {
return res;
}
if (res == (int)size) {
return 0;
}
return -EOVERFLOW;
}
static int mtd_sdmmc_write(mtd_dev_t *dev, const void *buff, uint32_t addr,
uint32_t size)
{
int res = mtd_sdmmc_write_page(dev, buff, addr / SDMMC_SDHC_BLOCK_SIZE,
addr % SDMMC_SDHC_BLOCK_SIZE, size);
if (res < 0) {
return res;
}
if (res == (int)size) {
return 0;
}
return -EOVERFLOW;
}
const mtd_desc_t mtd_sdmmc_driver = {
.init = mtd_sdmmc_init,
.read = mtd_sdmmc_read,
.read_page = mtd_sdmmc_read_page,
.write = mtd_sdmmc_write,
.write_page = mtd_sdmmc_write_page,
.erase_sector = mtd_sdmmc_erase_sector,
.power = mtd_sdmmc_power,
};
#if IS_USED(MODULE_MTD_SDMMC_DEFAULT)
#include "vfs_default.h"
#ifndef CONFIG_SDMMC_GENERIC_MTD_OFFSET
#define CONFIG_SDMMC_GENERIC_MTD_OFFSET 0
#endif
#define MTD_SDMMC_DEV(n, m) \
mtd_sdmmc_t mtd_sdmmc_dev ##n = { \
.base = { \
.driver = &mtd_sdmmc_driver, \
}, \
.sdmmc_idx = n, \
}; \
\
mtd_dev_t CONCAT(*mtd, m) = (mtd_dev_t *)&mtd_sdmmc_dev ##n
#if IS_USED(MODULE_MTD_SDCARD_DEFAULT)
/* we use /sd1 as default mount point for coexistence with mtd_sdcard */
#define MTD_SDMMC_DEV_FS(n, m, filesystem) \
VFS_AUTO_MOUNT(filesystem, VFS_MTD(mtd_sdmmc_dev ##n), VFS_DEFAULT_SD(1), m)
#else
#define MTD_SDMMC_DEV_FS(n, m, filesystem) \
VFS_AUTO_MOUNT(filesystem, VFS_MTD(mtd_sdmmc_dev ##n), VFS_DEFAULT_SD(n), m)
#endif
MTD_SDMMC_DEV(0, CONFIG_SDMMC_GENERIC_MTD_OFFSET);
#ifdef MODULE_FATFS_VFS
MTD_SDMMC_DEV_FS(0, CONFIG_SDMMC_GENERIC_MTD_OFFSET, fatfs);
#endif
#endif

View File

@ -153,6 +153,7 @@ config MODULE_PERIPH_INIT_RTT
default y if MODULE_PERIPH_INIT
depends on MODULE_PERIPH_RTT
rsource "Kconfig.sdmmc"
rsource "Kconfig.spi"
config MODULE_PERIPH_TEMPERATURE

View File

@ -0,0 +1,62 @@
# Copyright (c) 2020 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
menuconfig MODULE_PERIPH_SDMMC
bool "SDIO/SD/MMC peripheral driver"
depends on HAS_PERIPH_SDMMC
select MODULE_PERIPH_COMMON
if MODULE_PERIPH_SDMMC
config MODULE_PERIPH_INIT_SDMMC
bool "Auto initialize SDIO/SD/MMC peripheral"
default y if MODULE_PERIPH_INIT
config MODULE_PERIPH_SDMMC_8BIT
bool "8 Bit data bus support"
depends on HAS_PERIPH_SDMMC_8BIT
default y
help
If the SDIO/SD/MMC peripheral supports the 8-bit bus width, it can be
used by enabling this option. If the option is disabled, the driver
requires less RAM and ROM.
config MODULE_PERIPH_INIT_SDMMC_8BIT
bool
depends on MODULE_PERIPH_SDMMC_8BIT
default y if MODULE_PERIPH_INIT
config MODULE_PERIPH_SDMMC_HS
bool "High speed access"
depends on HAS_PERIPH_SDMMC_HS
default y
help
If the SDIO/SD/MMC peripheral supports the high speed access, i.e. 50
MHz for SD and 52 MHz for MMC, it can be used by enabling this option.
If the option is disabled, the driver requires less RAM and ROM.
config MODULE_PERIPH_INIT_SDMMC_HS
bool
depends on MODULE_PERIPH_SDMMC_HS
default y if MODULE_PERIPH_INIT
config MODULE_PERIPH_SDMMC_AUTO_CLK
bool
depends on HAS_PERIPH_SDMMC_AUTO_CLK
default y
help
If the SDIO/SD/MMC peripheral supports the Auto-CLK feature, i.e.
the automatic activation and deactivation of the SD CLK signal,
it is enabled automatically by this option. Otherwise, the activation
and deactivation is controlled by SDIO/SD/MMC high-level functions.
config MODULE_PERIPH_INIT_SDMMC_AUTO_CLK
bool
depends on MODULE_PERIPH_SDMMC_AUTO_CLK
default y if MODULE_PERIPH_INIT
endif # MODULE_PERIPH_SDMMC

View File

@ -57,6 +57,9 @@
#ifdef MODULE_PERIPH_INIT_PIO
#include "periph/pio.h"
#endif
#ifdef MODULE_PERIPH_INIT_SDMMC
#include "sdmmc/sdmmc.h"
#endif
#endif /* MODULE_PERIPH_INIT */
void periph_init(void)
@ -138,5 +141,11 @@ void periph_init(void)
pio_start_programs();
#endif
#if defined(MODULE_PERIPH_INIT_SDMMC)
for (unsigned i = 0; i < SDMMC_NUMOF; i++) {
sdmmc_init(sdmmc_get_dev(i));
}
#endif
#endif /* MODULE_PERIPH_INIT */
}

22
drivers/sdmmc/Kconfig Normal file
View File

@ -0,0 +1,22 @@
# Copyright (c) 2020 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
config MODULE_SDMMC
bool "SDIO/SD/MMC interface"
depends on HAS_PERIPH_SDMMC
select MODULE_PERIPH_SDMMC
select MODULE_ZTIMER_MSEC if !ZTIMER_USEC
if MODULE_SDMMC
config MODULE_SDMMC_MMC
bool "MMC support"
depends on HAS_PERIPH_SDMMC_MMC
help
Say y to support MMCs/eMMCs.
endif # MODULE_SDMMC

3
drivers/sdmmc/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = sdmmc
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,13 @@
FEATURES_REQUIRED += periph_sdmmc
FEATURES_OPTIONAL += periph_sdmmc_auto_clk
FEATURES_OPTIONAL += periph_sdmmc_auto_cmd12
FEATURES_OPTIONAL += periph_sdmmc_hs
ifneq (,$(filter sdmmc_mmc,$(USEMODULE)))
FEATURES_REQUIRED += periph_sdmmc_mmc
endif
ifeq (,$(filter ztimer_usec,$(USEMODULE)))
# enable ztimer_msec backend if ztimer_usec is not enabled
USEMODULE += ztimer_msec
endif

View File

@ -0,0 +1 @@
PSEUDOMODULES += sdmmc_mmc

1612
drivers/sdmmc/sdmmc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -415,6 +415,55 @@ config HAS_PERIPH_RTT_OVERFLOW
help
Indicates that the RTT provides an overflow callback.
config HAS_PERIPH_SDMMC
bool
help
Indicates that an SDIO/SD/MMC peripheral is present and used by the
board. This feature shall be provided by the board configuration,
if available.
config HAS_PERIPH_SDMMC_8BIT
bool
help
Indicates that the SDIO/SD/MMC peripheral supports the 8-bit bus width
and at least one component of the board is connected with 8 data lines.
This feature shall be provided by the board configuration, if available.
config HAS_PERIPH_SDMMC_AUTO_CLK
bool
help
Indicates that the SDIO/SD/MMC peripheral supports the Auto-CLK
feature, i.e. the automatic activation and deactivation of the SD CLK
signal when required. This feature shall be provided by the MCU
if supported.
config HAS_PERIPH_SDMMC_AUTO_CMD12
bool
help
Indicates that the SDIO/SD/MMC peripheral supports the Auto-CMD12
feature, i.e. CMD12 is sent automatically to stop the transmission in
multiple block operations. This feature shall be provided by the MCU
if supported.
config HAS_PERIPH_SDMMC_CLK
bool
help
Indicates that the SDIO/SD/MMC peripheral has special clock
functionality used by the peripheral driver.
config HAS_PERIPH_SDMMC_HS
bool
help
Indicates that the SDIO/SD/MMC peripheral supports the high speed
access, that is 50 MHz for SD and 52 MHz for MMC. This feature shall be
provided by the MCU.
config HAS_PERIPH_SDMMC_MMC
bool
help
Indicates that the SDIO/SD/MMC peripheral supports MMC/eMMCs. This
feature shall be provided by the MCU.
config HAS_PERIPH_SPI
bool
help

View File

@ -71,7 +71,8 @@ extern "C" {
* This can be written to by applications
*/
#ifndef VFS_DEFAULT_DATA
#if IS_USED(MODULE_MTD_MCI) || IS_USED(MODULE_MTD_SDCARD) || IS_USED(MODULE_SAM0_SDHC)
#if IS_USED(MODULE_MTD_MCI) || IS_USED(MODULE_MTD_SDCARD) || \
IS_USED(MODULE_SAM0_SDHC) || IS_USED(MODULE_MTD_SDMMC)
#define VFS_DEFAULT_DATA VFS_DEFAULT_SD(0)
#else
#define VFS_DEFAULT_DATA VFS_DEFAULT_NVM(0)

View File

@ -0,0 +1,12 @@
include ../Makefile.drivers_common
USEMODULE += sdmmc
USEMODULE += fmt
USEMODULE += shell
OUTPUT ?= 1
CFLAGS += -DOUTPUT=$(OUTPUT)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,2 @@
BOARD_INSUFFICIENT_MEMORY := \
#

View File

@ -0,0 +1,5 @@
# this file enables modules defined in Kconfig. Do not use this file for
# application configuration. This is only needed during migration.
CONFIG_MODULE_SDMMC=y
CONFIG_MODULE_FMT=y
CONFIG_MODULE_SHELL=y

679
tests/drivers/sdmmc/main.c Normal file
View File

@ -0,0 +1,679 @@
/*
* Copyright (C) 2016 Michel Rottleuthner
* 2023 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the SDIO/SD/MMC driver
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "byteorder.h"
#include "container.h"
#include "fmt.h"
#include "macros/units.h"
#include "shell.h"
#include "sdmmc/sdmmc.h"
/* independent of what you specify in a r/w cmd this is the maximum number of blocks read at once.
If you call read with a bigger blockcount the read is performed in chunks*/
#define MAX_BLOCKS_IN_BUFFER 4
#define BLOCK_PRINT_BYTES_PER_LINE 16
#define FIRST_PRINTABLE_ASCII_CHAR 0x20
#define ASCII_UNPRINTABLE_REPLACEMENT "."
sdmmc_dev_t *dev = NULL;
uint8_t buffer[SDMMC_SDHC_BLOCK_SIZE * MAX_BLOCKS_IN_BUFFER];
static int _card_assert(void)
{
if (dev == NULL) {
printf("[Error] SD/MMC device not initialized, use init command\n");
return -1;
}
if (!dev->present) {
printf("[Error] Card not present\n");
return -1;
}
if (!dev->init_done) {
printf("[Error] Card not initialized, use init command\n");
return -1;
}
return 0;
}
static int _init(int argc, char **argv)
{
int device = 0;
if ((argc == 2)) {
device = atoi(argv[1]);
}
dev = sdmmc_get_dev(device);
if (dev == NULL) {
printf("[Error] No device with index %i\n", device);
/* use the first SDMMC device by default */
dev = sdmmc_get_dev(0);
return -1;
}
if (!dev->present) {
printf("[Error] Card not present\n");
return -1;
}
printf("Initializing SD Card/MMC at SD/MMC device %i\n", device);
if (sdmmc_card_init(dev)) {
puts("[FAILED]");
puts("enable debugging in sdmmc.c for more information!");
return -1;
}
printf("card found [OK]\n");
return 0;
}
static int _cid(int argc, char **argv)
{
(void)argc;
(void)argv;
if (_card_assert()) {
return -1;
}
sdmmc_cid_t *cid = &dev->cid;
puts("----------------------------------------");
if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
printf("MID: %d\n", cid->mmc.MID);
printf("OID: 0x%04x\n", byteorder_ntohs(cid->mmc.OID));
printf("PNM: %c%c%c%c%c%c\n",
cid->mmc.PNM[0], cid->mmc.PNM[1], cid->mmc.PNM[2],
cid->mmc.PNM[3], cid->mmc.PNM[4], cid->mmc.PNM[5]);
printf("PRV: %d.%d\n", cid->mmc.PRV >> 4, cid->mmc.PRV & 0x0f);
printf("PSN: %" PRIu32 "\n", byteorder_ntohl(cid->mmc.PSN));
printf("MDT: %u/%u\n", 1997 + (cid->mmc.MDT >> 4), cid->mmc.MDT & 0x0f);
printf("CRC: 0x%02x\n", cid->mmc.CID_CRC >> 1);
}
else {
printf("MID: %d\n", cid->sd.MID);
printf("OID: %c%c (0x%04x)\n",
cid->sd.OID[0], cid->sd.OID[1], (cid->sd.OID[0] << 8) | cid->sd.OID[1]);
printf("PNM: %c%c%c%c%c\n",
cid->sd.PNM[0], cid->sd.PNM[1], cid->sd.PNM[2],
cid->sd.PNM[3], cid->sd.PNM[4]);
printf("PRV: %d.%d\n", cid->sd.PRV >> 4, cid->sd.PRV & 0x0f);
printf("PSN: %" PRIu32 "\n", byteorder_ntohl(cid->sd.PSN));
printf("MDT: %u/%u\n", 2000 + (byteorder_ntohs(cid->sd.MDT) >> 4),
byteorder_ntohs(cid->sd.MDT) & 0x000f);
printf("CRC: 0x%02x\n", cid->sd.CID_CRC >> 1);
}
puts("----------------------------------------");
return 0;
}
static int _scr(int argc, char **argv)
{
(void)argc;
(void)argv;
if (_card_assert()) {
return -1;
}
sdmmc_scr_t *scr = &dev->scr;
uint8_t ver = SDMMC_SCR_SD_SPEC(dev->scr);
printf("Physical Layer Specification Version ");
switch (ver) {
case 0:
printf("1.0 or 1.01\n");
break;
case 1:
printf("1.1\n");
break;
case 2:
printf("2.00\n");
break;
case 3:
printf("3.0X\n");
break;
default:
printf("%u.XX\n", ver);
}
printf("Reserved for manufacurer: %08"PRIx32"\n", scr->reserved0);
printf("SCR_STRUCTURE: 0x%01x\n", scr->SCR_STRUCTURE);
printf("SD_SPEC: 0x%01x\n", scr->SD_SPEC);
printf("DATA_STAT_AFTER_ERASE: %d\n", scr->DATA_STAT_AFTER_ERASE);
printf("SD_SECURITY: 0x%01x\n", scr->SD_SECURITY);
printf("SD_BUS_WIDTHS: 0x%01x\n", scr->SD_BUS_WIDTHS);
printf("SD_SPEC3: %d\n", scr->SD_SPEC3);
printf("EX_SECURITY: 0x%02x\n", scr->EX_SECURITY);
printf("SD_SPEC4: %d\n", scr->SD_SPEC4);
printf("SD_SPECX: 0x%01x\n", scr->SD_SPECX);
printf("CMD_SUPPORT 0x%02x\n", scr->CMD_SUPPORT);
return 0;
}
static int _csd(int argc, char **argv)
{
(void)argc;
(void)argv;
if (_card_assert()) {
return -1;
}
sdmmc_csd_t *csd = &dev->csd;
if (IS_USED(MODULE_SDMMC_MMC) && (dev->type == SDMMC_CARD_TYPE_MMC)) {
puts("CSD MMC\n---------------------------------------");
printf("CSD_STRUCTURE: 0x%x\n", csd->mmc.CSD_STRUCTURE);
printf("SPEC_VERS: 0x%x\n", csd->mmc.SPEC_VERS);
printf("TAAC: 0x%x\n", csd->mmc.TAAC);
printf("NSAC: 0x%x\n", csd->mmc.NSAC);
printf("TRAN_SPEED: 0x%x\n", csd->mmc.TRAN_SPEED);
printf("CCC: 0x%x\n", csd->mmc.CCC);
printf("READ_BL_LEN: 0x%x\n", csd->mmc.READ_BL_LEN);
printf("READ_BL_PARTIAL: 0x%x\n", csd->mmc.READ_BL_PARTIAL);
printf("WRITE_BLK_MISALIGN: 0x%x\n", csd->mmc.WRITE_BLK_MISALIGN);
printf("READ_BLK_MISALIGN: 0x%x\n", csd->mmc.READ_BLK_MISALIGN);
printf("DSR_IMP: 0x%x\n", csd->mmc.DSR_IMP);
printf("C_SIZE: 0x%x\n", csd->mmc.C_SIZE);
printf("VDD_R_CURR_MIN: 0x%x\n", csd->mmc.VDD_R_CURR_MIN);
printf("VDD_R_CURR_MAX: 0x%x\n", csd->mmc.VDD_R_CURR_MAX);
printf("VDD_W_CURR_MIN: 0x%x\n", csd->mmc.VDD_W_CURR_MIN);
printf("VDD_W_CURR_MAX: 0x%x\n", csd->mmc.VDD_W_CURR_MAX);
printf("C_SIZE_MULT: 0x%x\n", csd->mmc.C_SIZE_MULT);
printf("ERASE_GRP_SIZE: 0x%x\n", csd->mmc.ERASE_GRP_SIZE);
printf("ERASE_GRP_MULT: 0x%x\n", csd->mmc.ERASE_GRP_MULT);
printf("WP_GRP_SIZE: 0x%x\n", csd->mmc.WP_GRP_SIZE);
printf("WP_GRP_ENABLE: 0x%x\n", csd->mmc.WP_GRP_ENABLE);
printf("R2W_FACTOR: 0x%x\n", csd->mmc.R2W_FACTOR);
printf("WRITE_BL_LEN: 0x%x\n", csd->mmc.WRITE_BL_LEN);
printf("WRITE_BL_PARTIAL: 0x%x\n", csd->mmc.WRITE_BL_PARTIAL);
printf("CONTENT_PROT_APP: 0x%x\n", csd->mmc.CONTENT_PROT_APP);
printf("FILE_FORMAT_GRP: 0x%x\n", csd->mmc.FILE_FORMAT_GRP);
printf("COPY: 0x%x\n", csd->mmc.COPY);
printf("PERM_WRITE_PROTECT: 0x%x\n", csd->mmc.PERM_WRITE_PROTECT);
printf("TMP_WRITE_PROTECT: 0x%x\n", csd->mmc.TMP_WRITE_PROTECT);
printf("FILE_FORMAT: 0x%x\n", csd->mmc.FILE_FORMAT);
printf("ECC: 0x%x\n", csd->mmc.ECC);
printf("CRC: 0x%x\n", csd->mmc.CSD_CRC);
#if IS_USED(MODULE_SDMMC_MMC)
sdmmc_ext_csd_t *ext_csd = &dev->ext_csd;
puts("\nEXT_CSD MMC\n---------------------------------------");
printf("CSD_STRUCTURE: 0x%x\n", ext_csd->CSD_STRUCTURE);
printf("CARD_TYPE: 0x%02x\n", ext_csd->CARD_TYPE);
printf("BUS_WIDTH: %u\n", ext_csd->BUS_WIDTH);
printf("SEC_COUNT: %"PRIu32"\n", ext_csd->SEC_COUNT);
#endif
}
else if (dev->csd.v1.CSD_STRUCTURE == SDMMC_CSD_V1) {
puts("CSD V1\n----------------------------------------");
printf("CSD_STRUCTURE: 0x%x\n", csd->v1.CSD_STRUCTURE);
printf("TAAC: 0x%x\n", csd->v1.TAAC);
printf("NSAC: 0x%x\n", csd->v1.NSAC);
printf("TRAN_SPEED: 0x%x\n", csd->v1.TRAN_SPEED);
printf("CCC: 0x%x\n", csd->v1.CCC);
printf("READ_BL_LEN: 0x%x\n", csd->v1.READ_BL_LEN);
printf("READ_BL_PARTIAL: 0x%x\n", csd->v1.READ_BL_PARTIAL);
printf("WRITE_BLK_MISALIGN: 0x%x\n", csd->v1.WRITE_BLK_MISALIGN);
printf("READ_BLK_MISALIGN: 0x%x\n", csd->v1.READ_BLK_MISALIGN);
printf("DSR_IMP: 0x%x\n", csd->v1.DSR_IMP);
printf("C_SIZE: 0x%x\n", csd->v1.C_SIZE);
printf("VDD_R_CURR_MIN: 0x%x\n", csd->v1.VDD_R_CURR_MIN);
printf("VDD_R_CURR_MAX: 0x%x\n", csd->v1.VDD_R_CURR_MAX);
printf("VDD_W_CURR_MIN: 0x%x\n", csd->v1.VDD_W_CURR_MIN);
printf("VDD_W_CURR_MAX: 0x%x\n", csd->v1.VDD_W_CURR_MAX);
printf("C_SIZE_MULT: 0x%x\n", csd->v1.C_SIZE_MULT);
printf("ERASE_BLK_EN: 0x%x\n", csd->v1.ERASE_BLK_EN);
printf("SECTOR_SIZE: 0x%x\n", csd->v1.SECTOR_SIZE);
printf("WP_GRP_SIZE: 0x%x\n", csd->v1.WP_GRP_SIZE);
printf("WP_GRP_ENABLE: 0x%x\n", csd->v1.WP_GRP_ENABLE);
printf("R2W_FACTOR: 0x%x\n", csd->v1.R2W_FACTOR);
printf("WRITE_BL_LEN: 0x%x\n", csd->v1.WRITE_BL_LEN);
printf("WRITE_BL_PARTIAL: 0x%x\n", csd->v1.WRITE_BL_PARTIAL);
printf("FILE_FORMAT_GRP: 0x%x\n", csd->v1.FILE_FORMAT_GRP);
printf("COPY: 0x%x\n", csd->v1.COPY);
printf("PERM_WRITE_PROTECT: 0x%x\n", csd->v1.PERM_WRITE_PROTECT);
printf("TMP_WRITE_PROTECT: 0x%x\n", csd->v1.TMP_WRITE_PROTECT);
printf("FILE_FORMAT: 0x%x\n", csd->v1.FILE_FORMAT);
printf("CRC: 0x%x\n", csd->v1.CSD_CRC);
}
else if (dev->csd.v2.CSD_STRUCTURE == SDMMC_CSD_V2) {
puts("CSD V2:\n----------------------------------------");
printf("CSD_STRUCTURE: 0x%x\n", csd->v2.CSD_STRUCTURE);
printf("TAAC: 0x%x\n", csd->v2.TAAC);
printf("NSAC: 0x%x\n", csd->v2.NSAC);
printf("TRAN_SPEED: 0x%x\n", csd->v2.TRAN_SPEED);
printf("CCC: 0x%x\n", csd->v2.CCC);
printf("READ_BL_LEN: 0x%x\n", csd->v2.READ_BL_LEN);
printf("READ_BL_PARTIAL: 0x%x\n", csd->v2.READ_BL_PARTIAL);
printf("WRITE_BLK_MISALIGN: 0x%x\n", csd->v2.WRITE_BLK_MISALIGN);
printf("READ_BLK_MISALIGN: 0x%x\n", csd->v2.READ_BLK_MISALIGN);
printf("DSR_IMP: 0x%x\n", csd->v2.DSR_IMP);
printf("C_SIZE: 0x%"PRIu32"\n", (uint32_t)csd->v2.C_SIZE);
printf("ERASE_BLK_EN: 0x%x\n", csd->v2.ERASE_BLK_EN);
printf("SECTOR_SIZE: 0x%x\n", csd->v2.SECTOR_SIZE);
printf("WP_GRP_SIZE: 0x%x\n", csd->v2.WP_GRP_SIZE);
printf("WP_GRP_ENABLE: 0x%x\n", csd->v2.WP_GRP_ENABLE);
printf("R2W_FACTOR: 0x%x\n", csd->v2.R2W_FACTOR);
printf("WRITE_BL_LEN: 0x%x\n", csd->v2.WRITE_BL_LEN);
printf("WRITE_BL_PARTIAL: 0x%x\n", csd->v2.WRITE_BL_PARTIAL);
printf("FILE_FORMAT_GRP: 0x%x\n", csd->v2.FILE_FORMAT_GRP);
printf("COPY: 0x%x\n", csd->v2.COPY);
printf("PERM_WRITE_PROTECT: 0x%x\n", csd->v2.PERM_WRITE_PROTECT);
printf("TMP_WRITE_PROTECT: 0x%x\n", csd->v2.TMP_WRITE_PROTECT);
printf("FILE_FORMAT: 0x%x\n", csd->v2.FILE_FORMAT);
printf("CRC: 0x%x\n", csd->v2.CSD_CRC);
}
else {
printf("[FAILED] wrong CSD structure version %u\n",
dev->csd.v1.CSD_STRUCTURE);
return -1;
}
puts("----------------------------------------");
return 0;
}
static int _sds(int argc, char **argv)
{
(void)argc;
(void)argv;
sdmmc_sd_status_t sds;
if (sdmmc_read_sds(dev, &sds) == 0) {
puts("SD Status:\n----------------------------------------");
printf("SIZE_OF_PROTECTED_AREA: 0x%0lx\n", (unsigned long)sds.SIZE_OF_PROTECTED_AREA);
printf("SUS_ADDR: 0x%0lx\n", (unsigned long)sds.SUS_ADDR);
printf("VSC_AU_SIZE: 0x%0lx\n", (unsigned long)sds.VSC_AU_SIZE);
printf("SD_CARD_TYPE: 0x%0lx\n", (unsigned long)sds.SD_CARD_TYPE);
printf("ERASE_SIZE: 0x%0lx\n", (unsigned long)sds.ERASE_SIZE);
printf("SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.SPEED_CLASS);
printf("PERFORMANCE_MOVE: 0x%0lx\n", (unsigned long)sds.PERFORMANCE_MOVE);
printf("VIDEO_SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.VIDEO_SPEED_CLASS);
printf("ERASE_TIMEOUT: 0x%0lx\n", (unsigned long)sds.ERASE_TIMEOUT);
printf("ERASE_OFFSET: 0x%0lx\n", (unsigned long)sds.ERASE_OFFSET);
printf("UHS_SPEED_GRADE: 0x%0lx\n", (unsigned long)sds.UHS_SPEED_GRADE);
printf("UHS_AU_SIZE: 0x%0lx\n", (unsigned long)sds.UHS_AU_SIZE);
printf("AU_SIZE: 0x%0lx\n", (unsigned long)sds.AU_SIZE);
printf("DAT_BUS_WIDTH: 0x%0lx\n", (unsigned long)sds.DAT_BUS_WIDTH);
printf("SECURED_MODE: 0x%0lx\n", (unsigned long)sds.SECURED_MODE);
puts("----------------------------------------");
return 0;
}
return -1;
}
#define KILO (1000UL)
#define MEGA (1000000UL)
#define GIGA (1000000000UL)
static void _print_size(uint64_t bytes)
{
/* gib_frac = (bytes - gib_int * GiB) / MiB * KILO / KiB; */
uint32_t gib_int = bytes / GiB(1);
uint32_t gib_frac = (((bytes / MiB(1)) - (gib_int * KiB(1))) * KILO) / KiB(1);
/* gb_frac = (bytes - gb_int * GIGA) / MEGA */
uint32_t gb_int = bytes / GIGA;
uint32_t gb_frac = (bytes / MEGA) - (gb_int * KILO);
print_u64_dec( bytes );
printf(" bytes (%" PRIu32 ",%03" PRIu32 " GiB | %" PRIu32 ",%03" PRIu32 " GB)\n", gib_int,
gib_frac, gb_int, gb_frac);
}
static int _size(int argc, char **argv)
{
(void)argc;
(void)argv;
puts("\nCard size: ");
_print_size(sdmmc_get_capacity(dev));
return 0;
}
static int _read(int argc, char **argv)
{
int blockaddr;
int cnt;
bool print_as_char = false;
if (_card_assert()) {
return -1;
}
if ((argc == 3) || (argc == 4)) {
blockaddr = atoi(argv[1]);
cnt = atoi(argv[2]);
if (argc == 4 && (strcmp("-c", argv[3]) == 0)) {
print_as_char = true;
}
}
else {
printf("usage: %s blockaddr cnt [-c]\n", argv[0]);
return -1;
}
int total_read = 0;
while (total_read < cnt) {
int chunk_blocks = cnt - total_read;
if (chunk_blocks > MAX_BLOCKS_IN_BUFFER) {
chunk_blocks = MAX_BLOCKS_IN_BUFFER;
}
uint16_t chunks_read;
int res = sdmmc_read_blocks(dev, blockaddr + total_read,
SDMMC_SDHC_BLOCK_SIZE,
chunk_blocks, buffer, &chunks_read);
if (res) {
printf("read error %d (block %d/%d)\n",
res, total_read + chunks_read, cnt);
return -1;
}
if (IS_USED(OUTPUT)) {
for (int i = 0; i < chunk_blocks * SDMMC_SDHC_BLOCK_SIZE; i++) {
if ((i % SDMMC_SDHC_BLOCK_SIZE) == 0) {
printf("BLOCK %d:\n",
blockaddr + total_read + i / SDMMC_SDHC_BLOCK_SIZE);
}
if (print_as_char) {
if (buffer[i] >= FIRST_PRINTABLE_ASCII_CHAR) {
printf("%c", buffer[i]);
}
else {
printf(ASCII_UNPRINTABLE_REPLACEMENT);
}
}
else {
printf("%02x ", buffer[i]);
}
if ((i % BLOCK_PRINT_BYTES_PER_LINE) == (BLOCK_PRINT_BYTES_PER_LINE - 1)) {
puts(""); /* line break after BLOCK_PRINT_BYTES_PER_LINE bytes */
}
if ((i % SDMMC_SDHC_BLOCK_SIZE) == (SDMMC_SDHC_BLOCK_SIZE - 1)) {
puts(""); /* empty line after each printed block */
}
}
}
total_read += chunks_read;
}
printf("read %d block(s) from %d [OK]\n", cnt, blockaddr);
return 0;
}
static int _write(int argc, char **argv)
{
int bladdr;
char *data;
int size;
bool repeat_data = false;
if (_card_assert()) {
return -1;
}
if (argc == 3 || argc == 4) {
bladdr = atoi(argv[1]);
data = argv[2];
size = strlen(argv[2]);
printf("will write '%s' (%d chars) at start of block %d\n",
data, size, bladdr);
if (argc == 4 && (strcmp("-r", argv[3]) == 0)) {
repeat_data = true;
puts("the rest of the block will be filled with copies of that string");
}
else {
puts("the rest of the block will be filled with zeros");
}
}
else {
printf("usage: %s blockaddr string [-r]\n", argv[0]);
return -1;
}
if (size > SDMMC_SDHC_BLOCK_SIZE) {
printf("maximum stringsize to write at once is %d ...aborting\n",
SDMMC_SDHC_BLOCK_SIZE);
return -1;
}
/* copy data to a full-block-sized buffer an fill remaining block space
* according to -r param*/
uint8_t write_buffer[SDMMC_SDHC_BLOCK_SIZE];
for (unsigned i = 0; i < sizeof(write_buffer); i++) {
if (repeat_data || ((int)i < size)) {
write_buffer[i] = data[i % size];
}
else {
write_buffer[i] = 0;
}
}
int res = sdmmc_write_blocks(dev, bladdr, SDMMC_SDHC_BLOCK_SIZE, 1,
write_buffer, NULL);
if (res) {
printf("write error %d (wrote 1/1 blocks)\n", res);
return -1;
}
printf("write block %d [OK]\n", bladdr);
return 0;
}
static int _writem(int argc, char **argv)
{
int bladdr;
int cnt;
uint16_t done;
if (_card_assert()) {
return -1;
}
if (argc == 3) {
bladdr = atoi(argv[1]);
cnt = atoi(argv[2]);
}
else {
printf("usage: %s blockaddr num\n", argv[0]);
return -1;
}
/* writing cnt blocks with data from stack */
int res = sdmmc_write_blocks(dev, bladdr, SDMMC_SDHC_BLOCK_SIZE, cnt,
(void *)&bladdr, &done);
if (res) {
printf("write error %d (wrote %u/%d blocks)\n", res, done, cnt);
return -1;
}
printf("write %d block(s) to %d [OK]\n", cnt, bladdr);
return 0;
}
static int _erase(int argc, char **argv)
{
int blockaddr;
int cnt;
if (_card_assert()) {
return -1;
}
if (argc == 3) {
blockaddr = atoi(argv[1]);
cnt = atoi(argv[2]);
}
else {
printf("usage: %s blockaddr cnt\n", argv[0]);
return -1;
}
int res = sdmmc_erase_blocks(dev, blockaddr, cnt);
if (res) {
printf("erase error %d\n", res);
return -1;
}
printf("erase %d block(s) from %d [OK]\n", cnt, blockaddr);
return 0;
}
static int _copy(int argc, char **argv)
{
int src_block;
int dst_block;
int num_block = 1;
uint8_t tmp_copy[SDMMC_SDHC_BLOCK_SIZE];
if (_card_assert()) {
return -1;
}
if (argc < 3) {
printf("usage: %s src dst [num]\n", argv[0]);
return -1;
}
src_block = atoi(argv[1]);
dst_block = atoi(argv[2]);
if (argc == 4) {
num_block = atoi(argv[3]);
}
for (int i = 0; i < num_block; i++) {
int res = sdmmc_read_blocks(dev, src_block + i, SDMMC_SDHC_BLOCK_SIZE, 1,
tmp_copy, NULL);
if (res) {
printf("read error %d (block %d)\n", res, src_block + i);
return -1;
}
res = sdmmc_write_blocks(dev, dst_block + i, SDMMC_SDHC_BLOCK_SIZE, 1,
tmp_copy, NULL);
if (res) {
printf("write error %d (block %d)\n", res, dst_block + i);
return -1;
}
if (IS_USED(OUTPUT) && (num_block > 1)) {
extern ssize_t stdio_write(const void *buffer, size_t len);
stdio_write(".", 1); //printf(".");
if ((num_block % 79) == 79) {
printf("\n");
}
}
}
if (IS_USED(OUTPUT)) {
printf("\n");
}
printf("copy %d block(s) from %d to %d [OK]\n",
num_block, src_block, dst_block);
return 0;
}
static int _sector_count(int argc, char **argv)
{
(void)argc;
(void)argv;
printf("available sectors on card: %" PRIu32 "\n",
(uint32_t)(sdmmc_get_capacity(dev) / SDMMC_SDHC_BLOCK_SIZE));
return 0;
}
void _card_event_cb(sdmmc_dev_t *dev, sdmmc_event_t event)
{
(void)dev;
switch (event) {
case SDMMC_EVENT_CARD_INSERTED:
puts("Event: Card inserted");
break;
case SDMMC_EVENT_CARD_REMOVED:
puts("Event: Card removed");
break;
default:
puts("Event: unknown");
}
}
static const shell_command_t shell_commands[] = {
{ "init", "initializes default card", _init },
{ "cid", "print content of CID (Card IDentification) register", _cid },
{ "csd", "print content of CSD (Card-Specific Data) register", _csd },
{ "sds", "print SD Status", _sds },
{ "scr", "print content of SCR (SD Card Configuration Register)", _scr },
{ "size", "print card size", _size },
{ "sectors", "print sector count of card", _sector_count },
{ "read", "'read n m' reads m blocks beginning at block address n and prints the result. "
"Append -c option to print data readable chars", _read },
{ "write", "'write n data' writes data to block n. Append -r option to "
"repeatedly write data to complete block", _write },
{ "copy", "'copy src dst' copies block src to block dst", _copy },
{ "erase", "'erase n m' erases m blocks beginning at block address n", _erase },
{ "writem", "'write n m' writes m data blocks beginning at block address n.",
_writem },
{ NULL, NULL, NULL }
};
int main(void)
{
/* use the first SDMMC device by default */
dev = sdmmc_get_dev(0);
dev->event_cb = _card_event_cb;
puts("SDMMC driver test application");
puts("insert a SD Card/MMC and use 'init' command to set card to spi mode");
puts("WARNING: using 'write' or 'copy' commands WILL overwrite data on your card and");
puts("almost for sure corrupt existing filesystems, partitions and contained data!");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}