mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 06:23:53 +01:00
drivers/mtd: add SDMMC support
This commit is contained in:
parent
556ef5235c
commit
00275326a8
80
drivers/include/mtd_sdmmc.h
Normal file
80
drivers/include/mtd_sdmmc.h
Normal 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 */
|
||||
/** @} */
|
||||
@ -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
35
drivers/mtd_sdmmc/Kconfig
Normal 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
|
||||
3
drivers/mtd_sdmmc/Makefile
Normal file
3
drivers/mtd_sdmmc/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = mtd_sdmmc
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
1
drivers/mtd_sdmmc/Makefile.include
Normal file
1
drivers/mtd_sdmmc/Makefile.include
Normal file
@ -0,0 +1 @@
|
||||
PSEUDOMODULES += mtd_sdmmc_default
|
||||
254
drivers/mtd_sdmmc/mtd_sdmmc.c
Normal file
254
drivers/mtd_sdmmc/mtd_sdmmc.c
Normal 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
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user