Compare commits

...

3 Commits

132 changed files with 8066 additions and 239 deletions

View File

@ -6,7 +6,8 @@
"${workspaceFolder}/build/generated/pico_base",
"${env:PICO_SDK_PATH}/src/**/include",
"${env:PICO_SDK_PATH}/lib/**/src",
"${workspaceFolder}/lib/source"
"${workspaceFolder}/lib/FatFs/source",
"${workspaceFolder}/lib/sd_driver"
],
"myCompilerPath": "/usr/bin/arm-none-eabi-gcc"

12
.vscode/settings.json vendored
View File

@ -4,6 +4,16 @@
"tusb.h": "c",
"inttypes.h": "c",
"stdlib.h": "c",
"cdefs.h": "c"
"cdefs.h": "c",
"tusb_fifo.h": "c",
"tusb_common.h": "c",
"diskio.h": "c",
"ff.h": "c",
"time.h": "c",
"hw_config.h": "c",
"sd_card.h": "c",
"rp2040_sdio.h": "c",
"util.h": "c",
"stddef.h": "c"
}
}

View File

@ -12,24 +12,61 @@ pico_sdk_init()
add_executable(host_cdc_msc_hid)
pico_generate_pio_header(host_cdc_msc_hid ${CMAKE_CURRENT_LIST_DIR}/lib/sd_driver/SDIO/rp2040_sdio.pio)
# Example source
target_sources(host_cdc_msc_hid PUBLIC
hid_app.c
main.c
msc_app.c
cdc_app.c
lib/source/ff.c
lib/source/ffsystem.c
diskio_USB.c
diskio_SDIO.c
lib/FatFs/source/ff.c
lib/FatFs/source/ffsystem.c
lib/FatFs/source/diskio.c
lib/sd_driver/my_debug.c
lib/sd_driver/dma_interrupts.c
lib/sd_driver/sd_card.c
lib/sd_driver/crc.c
lib/sd_driver/crash.c
lib/sd_driver/f_util.c
lib/sd_driver/my_rtc.c
lib/sd_driver/sd_timeouts.c
lib/sd_driver/SDIO/rp2040_sdio.c
lib/sd_driver/SDIO/sd_card_sdio.c
lib/sd_driver/SPI/my_spi.c
lib/sd_driver/SPI/sd_card_spi.c
lib/sd_driver/SPI/sd_spi.c
)
# Make sure TinyUSB can find tusb_config.h
target_include_directories(host_cdc_msc_hid PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/lib/source/)
${CMAKE_CURRENT_LIST_DIR}/
${CMAKE_CURRENT_LIST_DIR}/lib/FatFs/source/
${CMAKE_CURRENT_LIST_DIR}/lib/sd_driver/)
if(PICO_RISCV)
set(HWDEP_LIBS hardware_watchdog)
else()
set(HWDEP_LIBS cmsis_core)
endif()
# In addition to pico_stdlib required for common PicoSDK functionality, add dependency on tinyusb_host
# for TinyUSB device support and tinyusb_board for the additional board support library used by the example
target_link_libraries(host_cdc_msc_hid PUBLIC pico_stdlib tinyusb_host tinyusb_board)
target_link_libraries( host_cdc_msc_hid PUBLIC
pico_stdlib
tinyusb_host
tinyusb_board
hardware_dma
hardware_pio
hardware_spi
hardware_sync
pico_aon_timer
pico_stdlib
${HWDEP_LIBS}
)
pico_add_extra_outputs(host_cdc_msc_hid)

6
README.md Normal file
View File

@ -0,0 +1,6 @@
USB Hôte sur le RP2040 - Ajout FsFAT
======================
Code pour écrire un fichier sur une clé USB formatée en exFAT ou sur une carte SD, les fonctions cohabitent !
Compatible avec le PICO SDK v2.1.1.

173
diskio_SDIO.c Normal file
View File

@ -0,0 +1,173 @@
/* glue.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
//
//
#include "lib/sd_driver/hw_config.h"
#include "lib/sd_driver/my_debug.h"
#include "lib/sd_driver/sd_card.h"
//
#include "diskio.h" /* Declarations of disk functions */
//#define TRACE_PRINTF(fmt, args...)
#define TRACE_PRINTF printf
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS SDIO_disk_status(BYTE pdrv /* Physical drive number to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
sd_card_detect(sd_card_p); // Fast: just a GPIO read
return sd_card_p->state.m_Status; // See http://elm-chan.org/fsw/ff/doc/dstat.html
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS SDIO_disk_initialize(
BYTE pdrv /* Physical drive number to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
bool ok = sd_init_driver();
if (!ok) return RES_NOTRDY;
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
DSTATUS ds = disk_status(pdrv);
if (STA_NODISK & ds)
return ds;
// See http://elm-chan.org/fsw/ff/doc/dstat.html
return sd_card_p->init(sd_card_p);
}
static int sdrc2dresult(int sd_rc) {
switch (sd_rc) {
case SD_BLOCK_DEVICE_ERROR_NONE:
return RES_OK;
case SD_BLOCK_DEVICE_ERROR_UNUSABLE:
case SD_BLOCK_DEVICE_ERROR_NO_RESPONSE:
case SD_BLOCK_DEVICE_ERROR_NO_INIT:
case SD_BLOCK_DEVICE_ERROR_NO_DEVICE:
return RES_NOTRDY;
case SD_BLOCK_DEVICE_ERROR_PARAMETER:
case SD_BLOCK_DEVICE_ERROR_UNSUPPORTED:
return RES_PARERR;
case SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED:
return RES_WRPRT;
case SD_BLOCK_DEVICE_ERROR_CRC:
case SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK:
case SD_BLOCK_DEVICE_ERROR_ERASE:
case SD_BLOCK_DEVICE_ERROR_WRITE:
default:
return RES_ERROR;
}
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT SDIO_disk_read(BYTE pdrv, /* Physical drive number to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) {
TRACE_PRINTF("Error: !sd_card_p, pdrv: %d\n", pdrv);
return RES_PARERR;
}
int rc = sd_card_p->read_blocks(sd_card_p, buff, sector, count);
return sdrc2dresult(rc);
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT SDIO_disk_write(BYTE pdrv, /* Physical drive number to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
int rc = sd_card_p->write_blocks(sd_card_p, buff, sector, count);
return sdrc2dresult(rc);
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT SDIO_disk_ioctl(BYTE pdrv, /* Physical drive number (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
switch (cmd) {
case GET_SECTOR_COUNT: { // Retrieves number of available sectors, the
// largest allowable LBA + 1, on the drive
// into the LBA_t variable pointed by buff.
// This command is used by f_mkfs and f_fdisk
// function to determine the size of
// volume/partition to be created. It is
// required when FF_USE_MKFS == 1.
static LBA_t n;
n = sd_card_p->get_num_sectors(sd_card_p);
*(LBA_t *)buff = n;
if (!n) return RES_ERROR;
return RES_OK;
}
case GET_BLOCK_SIZE: { // Retrieves erase block size of the flash
// memory media in unit of sector into the DWORD
// variable pointed by buff. The allowable value
// is 1 to 32768 in power of 2. Return 1 if the
// erase block size is unknown or non flash
// memory media. This command is used by only
// f_mkfs function and it attempts to align data
// area on the erase block boundary. It is
// required when FF_USE_MKFS == 1.
static DWORD bs = 1;
*(DWORD *)buff = bs;
return RES_OK;
}
case CTRL_SYNC:
sd_card_p->sync(sd_card_p);
return RES_OK;
default:
return RES_PARERR;
}
}

123
diskio_USB.c Normal file
View File

@ -0,0 +1,123 @@
#include "ff.h" /* Obtains integer types */
#include "tusb.h"
#include "diskio.h"
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
//--------------------------------------------------------------------+
// DiskIO
//--------------------------------------------------------------------+
static void wait_for_disk_io(BYTE pdrv)
{
while(_disk_busy[pdrv])
{
tuh_task();
}
}
static bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
{
(void) dev_addr; (void) cb_data;
_disk_busy[dev_addr-1] = false;
return true;
}
DSTATUS USB_disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
uint8_t dev_addr = pdrv + 1;
return tuh_msc_mounted(dev_addr) ? 0 : STA_NODISK;
}
DSTATUS USB_disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
(void) pdrv;
return 0; // nothing to do
}
DRESULT USB_disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
uint8_t const dev_addr = pdrv + 1;
uint8_t const lun = 0;
_disk_busy[pdrv] = true;
tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
wait_for_disk_io(pdrv);
return RES_OK;
}
#if FF_FS_READONLY == 0
DRESULT USB_disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
uint8_t const dev_addr = pdrv + 1;
uint8_t const lun = 0;
_disk_busy[pdrv] = true;
tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
wait_for_disk_io(pdrv);
return RES_OK;
}
#endif
DRESULT USB_disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
uint8_t const dev_addr = pdrv + 1;
uint8_t const lun = 0;
switch ( cmd )
{
case CTRL_SYNC:
// nothing to do since we do blocking
return RES_OK;
case GET_SECTOR_COUNT:
*((DWORD*) buff) = (WORD) tuh_msc_get_block_count(dev_addr, lun);
return RES_OK;
case GET_SECTOR_SIZE:
*((WORD*) buff) = (WORD) tuh_msc_get_block_size(dev_addr, lun);
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD*) buff) = 1; // erase block size in units of sector size
return RES_OK;
default:
return RES_PARERR;
}
return RES_OK;
}
/*
DWORD get_fattime (void){
return
(2025-1970) << 25 | // Année
6 << 21 | // Mois
7 << 16 | // jour du mois
17 << 11 | // Heures
29 << 5 | // Minutes
30 << 0 // Secondes
;
}*/

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -11,9 +11,7 @@
#include "diskio.h" /* Declarations of disk functions */
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
/*-----------------------------------------------------------------------*/
@ -27,28 +25,16 @@ DSTATUS disk_status (
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM :
result = RAM_disk_status();
// translate the reslut code here
return stat;
case DEV_MMC :
result = MMC_disk_status();
// translate the reslut code here
return stat;
case DEV_USB :
result = USB_disk_status();
// translate the reslut code here
return stat;
if(pdrv >= DEV_SDIO_MIN && pdrv <= DEV_SDIO_MAX){
SDIO_disk_status(pdrv - DEV_USB_MIN);
return 0; // OK
}
if(pdrv >= DEV_USB_MIN && pdrv <= DEV_USB_MAX){
USB_disk_status(pdrv - DEV_USB_MIN);
return 0; // OK
}
return STA_NOINIT;
}
@ -64,28 +50,15 @@ DSTATUS disk_initialize (
{
DSTATUS stat;
int result;
printf("disk_initialize : %d\n", pdrv);
if(pdrv >= DEV_SDIO_MIN && pdrv <= DEV_SDIO_MAX){
SDIO_disk_initialize(pdrv - DEV_SDIO_MIN);
return 0; // OK
}
switch (pdrv) {
case DEV_RAM :
result = RAM_disk_initialize();
// translate the reslut code here
return stat;
case DEV_MMC :
result = MMC_disk_initialize();
// translate the reslut code here
return stat;
case DEV_USB :
result = USB_disk_initialize();
// translate the reslut code here
return stat;
if(pdrv >= DEV_USB_MIN && pdrv <= DEV_USB_MAX){
USB_disk_initialize(pdrv - DEV_USB_MIN);
return 0; // OK
}
return STA_NOINIT;
}
@ -106,33 +79,14 @@ DRESULT disk_read (
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
if(pdrv >= DEV_SDIO_MIN && pdrv <= DEV_SDIO_MAX){
SDIO_disk_read(pdrv - DEV_SDIO_MIN, buff, sector, count);
return 0; // OK
}
result = RAM_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC :
// translate the arguments here
result = MMC_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case DEV_USB :
// translate the arguments here
result = USB_disk_read(buff, sector, count);
// translate the reslut code here
return res;
if(pdrv >= DEV_USB_MIN && pdrv <= DEV_USB_MAX){
USB_disk_read(pdrv - DEV_USB_MIN, buff, sector, count);
return 0; // OK
}
return RES_PARERR;
@ -156,35 +110,16 @@ DRESULT disk_write (
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
result = RAM_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC :
// translate the arguments here
result = MMC_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_USB :
// translate the arguments here
result = USB_disk_write(buff, sector, count);
// translate the reslut code here
return res;
if(pdrv >= DEV_SDIO_MIN && pdrv <= DEV_SDIO_MAX){
result = SDIO_disk_write(pdrv - DEV_SDIO_MIN, buff, sector, count);
return 0; // OK
}
if(pdrv >= DEV_USB_MIN && pdrv <= DEV_USB_MAX){
result = USB_disk_write(pdrv - DEV_USB_MIN, buff, sector, count);
return 0; // OK
}
return RES_PARERR;
}
@ -204,24 +139,15 @@ DRESULT disk_ioctl (
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// Process of the command for the RAM drive
if(pdrv >= DEV_SDIO_MIN && pdrv <= DEV_SDIO_MAX){
SDIO_disk_ioctl(pdrv - DEV_SDIO_MIN, cmd, buff);
return 0; // OK
}
return res;
case DEV_MMC :
// Process of the command for the MMC/SD card
return res;
case DEV_USB :
// Process of the command the USB drive
return res;
if(pdrv >= DEV_USB_MIN && pdrv <= DEV_USB_MAX){
USB_disk_ioctl(pdrv - DEV_USB_MIN, cmd, buff);
return 0; // OK
}
return RES_PARERR;

View File

@ -2,6 +2,8 @@
/ Low level disk interface modlue include file (C)ChaN, 2019 /
/-----------------------------------------------------------------------*/
#include "ff.h"
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
@ -32,6 +34,18 @@ DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
DSTATUS USB_disk_initialize (BYTE pdrv);
DSTATUS USB_disk_status (BYTE pdrv);
DRESULT USB_disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT USB_disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT USB_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
DSTATUS SDIO_disk_initialize (BYTE pdrv);
DSTATUS SDIO_disk_status (BYTE pdrv);
DRESULT SDIO_disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT SDIO_disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT SDIO_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
/* Disk Status Bits (DSTATUS) */

View File

@ -22,6 +22,11 @@
#ifndef FF_DEFINED
#define FF_DEFINED 5380 /* Revision ID */
#define DEV_SDIO_MIN 0
#define DEV_SDIO_MAX 1
#define DEV_USB_MIN 2
#define DEV_USB_MAX 9
#ifdef __cplusplus
extern "C" {
#endif

214
lib/sd_driver/SDIO/SdioCard.h Executable file
View File

@ -0,0 +1,214 @@
/**
* Copyright (c) 2011-2022 Bill Greiman
* This file is part of the SdFat library for SD memory cards.
*
* MIT License
*
* 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 sd_sdio_h
#define sd_sdio_h
#include "sd_card.h"
/** Initialize the SD card.
* \return true for success or false for failure.
*/
bool sd_sdio_begin(sd_card_t *sd_card_p);
/** CMD6 Switch mode: Check Function Set Function.
* \param[in] arg CMD6 argument.
* \param[out] status return status data.
*
* \return true for success or false for failure.
*/
bool sd_sdio_cardCMD6(sd_card_t *sd_card_p, uint32_t arg, uint8_t *status);
/** Disable an SDIO card.
* not implemented.
*/
// void sd_sdio_end() {}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
uint32_t __attribute__((error("use sectorCount()"))) cardSize();
#endif // DOXYGEN_SHOULD_SKIP_THIS
/** Erase a range of sectors.
*
* \param[in] firstSector The address of the first sector in the range.
* \param[in] lastSector The address of the last sector in the range.
*
* \note This function requests the SD card to do a flash erase for a
* range of sectors. The data on the card after an erase operation is
* either 0 or 1, depends on the card vendor. The card must support
* single sector erase.
*
* \return true for success or false for failure.
*/
bool sd_sdio_erase(sd_card_t *sd_card_p, uint32_t firstSector, uint32_t lastSector);
/**
* \return code for the last error. See SdCardInfo.h for a list of error codes.
*/
uint8_t sd_sdio_errorCode(sd_card_t *sd_card_p) /* const */;
/** \return error data for last error. */
uint32_t sd_sdio_errorData() /* const */;
/** \return error line for last error. Tmp function for debug. */
uint32_t sd_sdio_errorLine(sd_card_t *sd_card_p) /* const */;
/**
* Check for busy with CMD13.
*
* \return true if busy else false.
*/
bool sd_sdio_isBusy(sd_card_t *sd_card_p);
/** \return the SD clock frequency in kHz. */
//uint32_t sd_sdio_kHzSdClk();
/**
* Read a 512 byte sector from an SD card.
*
* \param[in] sector Logical sector to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return true for success or false for failure.
*/
bool sd_sdio_readSector(sd_card_t *sd_card_p, uint32_t sector, uint8_t *dst);
/**
* Read multiple 512 byte sectors from an SD card.
*
* \param[in] sector Logical sector to be read.
* \param[in] ns Number of sectors to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return true for success or false for failure.
*/
bool sd_sdio_readSectors(sd_card_t *sd_card_p, uint32_t sector, uint8_t *dst, size_t ns);
// Read 512-bit SD status
bool rp2040_sdio_get_sd_status(sd_card_t *sd_card_p, uint8_t response[64]);
/** Read one data sector in a multiple sector read sequence
*
* \param[out] dst Pointer to the location for the data to be read.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readData(sd_card_t *sd_card_p, uint8_t *dst);
/** Read OCR register.
*
* \param[out] ocr Value of OCR register.
* \return true for success or false for failure.
*/
bool sd_sdio_readOCR(sd_card_t *sd_card_p, uint32_t *ocr);
/** Read SCR register.
*
* \param[out] scr Value of SCR register.
* \return true for success or false for failure.
*/
// bool sd_sdio_readSCR(sd_card_t *sd_card_p, scr_t *scr);
/** Start a read multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
*
* \note This function is used with readData() and readStop() for optimized
* multiple sector reads. SPI chipSelect must be low for the entire sequence.
*
* \return true for success or false for failure.
*/
// bool sd_sdio_readStart(uint32_t sector);
/** Start a read multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
* \param[in] count Maximum sector count.
* \note This function is used with readData() and readStop() for optimized
* multiple sector reads. SPI chipSelect must be low for the entire sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readStart(sd_card_t *sd_card_p, uint32_t sector, uint32_t count);
/** End a read multiple sectors sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readStop(sd_card_t *sd_card_p);
/** \return SDIO card status. */
uint32_t sd_sdio_status(sd_card_t *sd_card_p);
/**
* Determine the size of an SD flash memory card.
*
* \return The number of 512 byte data sectors in the card
* or zero if an error occurs.
*/
uint32_t sd_sdio_sectorCount(sd_card_t *sd_card_p);
/**
* Send CMD12 to stop read or write.
*
* \param[in] blocking If true, wait for command complete.
*
* \return true for success or false for failure.
*/
bool sd_sdio_stopTransmission(sd_card_t *sd_card_p, bool blocking);
/** \return success if sync successful. Not for user apps. */
//bool sd_sdio_syncDevice(sd_card_t *sd_card_p);
/** Return the card type: SD V1, SD V2 or SDHC
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
*/
uint8_t sd_sdio_type(sd_card_t *sd_card_p) /* const */;
/**
* Writes a 512 byte sector to an SD card.
*
* \param[in] sector Logical sector to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeSector(sd_card_t *sd_card_p, uint32_t sector, const uint8_t *src);
/**
* Write multiple 512 byte sectors to an SD card.
*
* \param[in] sector Logical sector to be written.
* \param[in] ns Number of sectors to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeSectors(sd_card_t *sd_card_p, uint32_t sector, const uint8_t *src, size_t ns);
/** Write one data sector in a multiple sector write sequence.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeData(sd_card_t *sd_card_p, const uint8_t *src);
/** Start a write multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
*
* \note This function is used with writeData() and writeStop()
* for optimized multiple sector writes.
*
* \return true for success or false for failure.
*/
// bool sd_sdio_writeStart(sd_card_t *sd_card_p, uint32_t sector);
/** Start a write multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
* \param[in] count Maximum sector count.
* \note This function is used with writeData() and writeStop()
* for optimized multiple sector writes.
*
* \return true for success or false for failure.
*/
bool sd_sdio_writeStart(sd_card_t *sd_card_p, uint32_t sector, uint32_t count);
/** End a write multiple sectors sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_writeStop(sd_card_t *sd_card_p);
void sd_sdio_ctor(sd_card_t *sd_card_p);
#endif // sd_sdio_h

View File

@ -0,0 +1,181 @@
// Platform-specific definitions for ZuluSCSI RP2040 hardware.
#pragma once
#include <stdint.h>
// #include <Arduino.h>
// #include "ZuluSCSI_platform_gpio.h"
// #include "scsiHostPhy.h"
#include "SdioCard.h"
#include "pico/stdlib.h"
#ifdef __cplusplus
extern "C" {
#endif
#define delayMicroseconds sleep_us
/* These are used in debug output and default SCSI strings */
extern const char *g_azplatform_name;
#define PLATFORM_NAME "ZuluSCSI RP2040"
#define PLATFORM_REVISION "2.0"
#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
#define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768
#define PLATFORM_OPTIMAL_MAX_SD_WRITE_SIZE 65536
#define PLATFORM_OPTIMAL_LAST_SD_WRITE_SIZE 8192
#define SD_USE_SDIO 1
#define PLATFORM_HAS_INITIATOR_MODE 1
// NOTE: The driver supports synchronous speeds higher than 10MB/s, but this
// has not been tested due to lack of fast enough SCSI adapter.
// #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_TURBO
// Debug logging function, can be used to print to e.g. serial port.
// May get called from interrupt handlers.
void azplatform_log(const char *s);
void azplatform_emergency_log_save();
#if 0
// Timing and delay functions.
// Arduino platform already provides these
// unsigned long millis(void);
void delay(unsigned long ms);
// Short delays, can be called from interrupt mode
static inline void delay_ns(unsigned long ns)
{
delayMicroseconds((ns + 999) / 1000);
}
#endif
// Approximate fast delay
static inline void delay_100ns()
{
asm volatile ("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop");
}
// Initialize SD card and GPIO configuration
void azplatform_init();
// Initialization for main application, not used for bootloader
void azplatform_late_init();
// Disable the status LED
void azplatform_disable_led(void);
// Query whether initiator mode is enabled on targets with PLATFORM_HAS_INITIATOR_MODE
bool azplatform_is_initiator_mode_enabled();
// Setup soft watchdog if supported
void azplatform_reset_watchdog();
// Set callback that will be called during data transfer to/from SD card.
// This can be used to implement simultaneous transfer to SCSI bus.
typedef void (*sd_callback_t)(uint32_t bytes_complete);
void azplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer);
// Reprogram firmware in main program area.
#ifndef RP2040_DISABLE_BOOTLOADER
#define AZPLATFORM_BOOTLOADER_SIZE (128 * 1024)
#define AZPLATFORM_FLASH_TOTAL_SIZE (1024 * 1024)
#define AZPLATFORM_FLASH_PAGE_SIZE 4096
bool azplatform_rewrite_flash_page(uint32_t offset, uint8_t buffer[AZPLATFORM_FLASH_PAGE_SIZE]);
void azplatform_boot_to_main_firmware();
#endif
// ROM drive in the unused external flash area
#ifndef RP2040_DISABLE_ROMDRIVE
#define PLATFORM_HAS_ROM_DRIVE 1
// Check maximum available space for ROM drive in bytes
uint32_t azplatform_get_romdrive_maxsize();
// Read ROM drive area
bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count);
// Reprogram ROM drive area
#define AZPLATFORM_ROMDRIVE_PAGE_SIZE 4096
bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count);
#endif
// Parity lookup tables for write and read from SCSI bus.
// These are used by macros below and the code in scsi_accel_rp2040.cpp
extern const uint16_t g_scsi_parity_lookup[256];
extern const uint16_t g_scsi_parity_check_lookup[512];
// Below are GPIO access definitions that are used from scsiPhy.cpp.
// Write a single SCSI pin.
// Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
#define SCSI_OUT(pin, state) \
*(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
// Read a single SCSI pin.
// Example use: SCSI_IN(ATN), returns 1 for active low state.
#define SCSI_IN(pin) \
((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
// Set pin directions for initiator vs. target mode
#define SCSI_ENABLE_INITIATOR() \
(sio_hw->gpio_oe_set = (1 << SCSI_OUT_ACK) | \
(1 << SCSI_OUT_ATN)), \
(sio_hw->gpio_oe_clr = (1 << SCSI_IN_IO) | \
(1 << SCSI_IN_CD) | \
(1 << SCSI_IN_MSG) | \
(1 << SCSI_IN_REQ))
// Enable driving of shared control pins
#define SCSI_ENABLE_CONTROL_OUT() \
(sio_hw->gpio_oe_set = (1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG))
// Set SCSI data bus to output
#define SCSI_ENABLE_DATA_OUT() \
(sio_hw->gpio_clr = (1 << SCSI_DATA_DIR), \
sio_hw->gpio_oe_set = SCSI_IO_DATA_MASK)
// Write SCSI data bus, also sets REQ to inactive.
#define SCSI_OUT_DATA(data) \
gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
SCSI_ENABLE_DATA_OUT()
// Release SCSI data bus and REQ signal
#define SCSI_RELEASE_DATA_REQ() \
(sio_hw->gpio_oe_clr = SCSI_IO_DATA_MASK, \
sio_hw->gpio_set = (1 << SCSI_DATA_DIR) | (1 << SCSI_OUT_REQ))
// Release all SCSI outputs
#define SCSI_RELEASE_OUTPUTS() \
SCSI_RELEASE_DATA_REQ(), \
sio_hw->gpio_oe_clr = (1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG), \
sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
(1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG) | \
(1 << SCSI_OUT_RST) | \
(1 << SCSI_OUT_BSY) | \
(1 << SCSI_OUT_REQ) | \
(1 << SCSI_OUT_SEL)
// Read SCSI data bus
#define SCSI_IN_DATA() \
(~sio_hw->gpio_in & SCSI_IO_DATA_MASK) >> SCSI_IO_SHIFT
// SD card driver for SdFat
#ifdef SD_USE_SDIO
struct SdioConfig;
// extern SdioConfig g_sd_sdio_config;
// #define SD_CONFIG g_sd_sdio_config
#define SD_CONFIG_CRASH g_sd_sdio_config
#else
struct SdSpiConfig;
extern SdSpiConfig g_sd_spi_config;
#define SD_CONFIG g_sd_spi_config
#define SD_CONFIG_CRASH g_sd_spi_config
#endif
#ifdef __cplusplus
}
#endif

860
lib/sd_driver/SDIO/rp2040_sdio.c Executable file
View File

@ -0,0 +1,860 @@
// Implementation of SDIO communication for RP2040
//
// The RP2040 official work-in-progress code at
// https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
// may be useful reference, but this is independent implementation.
//
// For official SDIO specifications, refer to:
// https://www.sdcard.org/downloads/pls/
// "SDIO Physical Layer Simplified Specification Version 8.00"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
//
#include "hardware/dma.h"
#include "hardware/gpio.h"
#include "hardware/pio.h"
#if !PICO_RISCV
# if PICO_RP2040
# include "RP2040.h"
# endif
# if PICO_RP2350
# include "RP2350.h"
# endif
#endif
//
#include "dma_interrupts.h"
#include "hw_config.h"
#include "rp2040_sdio.h"
#include "rp2040_sdio.pio.h"
#include "delays.h"
#include "sd_card.h"
#include "sd_timeouts.h"
#include "my_debug.h"
#include "util.h"
//
#include "rp2040_sdio.h"
#define azdbg(arg1, ...) {\
DBG_PRINTF("%s,%s:%d %s\n", __func__, __FILE__, __LINE__, arg1); \
}
#define STATE sd_card_p->sdio_if_p->state
#define SDIO_PIO sd_card_p->sdio_if_p->SDIO_PIO
#define SDIO_CMD_SM STATE.SDIO_CMD_SM
#define SDIO_DATA_SM STATE.SDIO_DATA_SM
#define SDIO_DMA_CH STATE.SDIO_DMA_CH
#define SDIO_DMA_CHB STATE.SDIO_DMA_CHB
#define SDIO_CMD sd_card_p->sdio_if_p->CMD_gpio
#define SDIO_CLK sd_card_p->sdio_if_p->CLK_gpio
#define SDIO_D0 sd_card_p->sdio_if_p->D0_gpio
#define SDIO_D1 sd_card_p->sdio_if_p->D1_gpio
#define SDIO_D2 sd_card_p->sdio_if_p->D2_gpio
#define SDIO_D3 sd_card_p->sdio_if_p->D3_gpio
// Force everything to idle state
static sdio_status_t rp2040_sdio_stop(sd_card_t *sd_card_p);
/*******************************************************
* Checksum algorithms
*******************************************************/
// Table lookup for calculating CRC-7 checksum that is used in SDIO command packets.
// Usage:
// uint8_t crc = 0;
// crc = crc7_table[crc ^ byte];
// .. repeat for every byte ..
static const uint8_t crc7_table[256] = {
0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2
};
// Calculate the CRC16 checksum for parallel 4 bit lines separately.
// When the SDIO bus operates in 4-bit mode, the CRC16 algorithm
// is applied to each line separately and generates total of
// 4 x 16 = 64 bits of checksum.
__attribute__((optimize("Ofast")))
uint64_t sdio_crc16_4bit_checksum(uint32_t *data, uint32_t num_words)
{
uint64_t crc = 0;
uint32_t *end = data + num_words;
while (data < end)
{
for (int unroll = 0; unroll < 4; unroll++)
{
// Each 32-bit word contains 8 bits per line.
// Reverse the bytes because SDIO protocol is big-endian.
uint32_t data_in = __builtin_bswap32(*data++);
// Shift out 8 bits for each line
uint32_t data_out = crc >> 32;
crc <<= 32;
// XOR outgoing data to itself with 4 bit delay
data_out ^= (data_out >> 16);
// XOR incoming data to outgoing data with 4 bit delay
data_out ^= (data_in >> 16);
// XOR outgoing and incoming data to accumulator at each tap
uint64_t xorred = data_out ^ data_in;
crc ^= xorred;
crc ^= xorred << (5 * 4);
crc ^= xorred << (12 * 4);
}
}
return crc;
}
/*******************************************************
* Basic SDIO command execution
*******************************************************/
static void sdio_send_command(const sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint8_t response_bits)
{
// azdbg("SDIO Command: ", (int)command, " arg ", arg);
// Format the arguments in the way expected by the PIO code.
uint32_t word0 =
(47 << 24) | // Number of bits in command minus one
( 1 << 22) | // Transfer direction from host to card
(command << 16) | // Command byte
(((arg >> 24) & 0xFF) << 8) | // MSB byte of argument
(((arg >> 16) & 0xFF) << 0);
uint32_t word1 =
(((arg >> 8) & 0xFF) << 24) |
(((arg >> 0) & 0xFF) << 16) | // LSB byte of argument
( 1 << 8); // End bit
// Set number of bits in response minus one, or leave at 0 if no response expected
if (response_bits)
{
word1 |= ((response_bits - 1) << 0);
}
// Calculate checksum in the order that the bytes will be transmitted (big-endian)
uint8_t crc = 0;
crc = crc7_table[crc ^ ((word0 >> 16) & 0xFF)];
crc = crc7_table[crc ^ ((word0 >> 8) & 0xFF)];
crc = crc7_table[crc ^ ((word0 >> 0) & 0xFF)];
crc = crc7_table[crc ^ ((word1 >> 24) & 0xFF)];
crc = crc7_table[crc ^ ((word1 >> 16) & 0xFF)];
word1 |= crc << 8;
// Transmit command
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word0);
pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word1);
}
sdio_status_t rp2040_sdio_command_R1(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response)
{
sdio_send_command(sd_card_p, command, arg, response ? 48 : 0);
// Wait for response
uint32_t start = millis();
uint32_t wait_words = response ? 2 : 1;
while (pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM) < wait_words)
{
if ((uint32_t)(millis() - start) > sd_timeouts.rp2040_sdio_command_R1)
{
if (command != 8) // Don't log for missing SD card
{
azdbg("Timeout waiting for response in rp2040_sdio_command_R1(", (int)command, "), ",
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)STATE.pio_cmd_clk_offset,
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
EMSG_PRINTF("%s: Timeout waiting for response in rp2040_sdio_command_R1(0x%hx)\n", __func__, command);
}
// Reset the state machine program
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(STATE.pio_cmd_clk_offset));
return SDIO_ERR_RESPONSE_TIMEOUT;
}
}
if (response)
{
// Read out response packet
uint32_t resp0 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
uint32_t resp1 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
// azdbg("SDIO R1 response: ", resp0, " ", resp1);
// Calculate response checksum
uint8_t crc = 0;
crc = crc7_table[crc ^ ((resp0 >> 24) & 0xFF)];
crc = crc7_table[crc ^ ((resp0 >> 16) & 0xFF)];
crc = crc7_table[crc ^ ((resp0 >> 8) & 0xFF)];
crc = crc7_table[crc ^ ((resp0 >> 0) & 0xFF)];
crc = crc7_table[crc ^ ((resp1 >> 8) & 0xFF)];
uint8_t actual_crc = ((resp1 >> 0) & 0xFE);
if (crc != actual_crc)
{
// azdbg("rp2040_sdio_command_R1(", (int)command, "): CRC error, calculated ", crc, " packet has ", actual_crc);
EMSG_PRINTF("rp2040_sdio_command_R1(%d): CRC error, calculated 0x%hx, packet has 0x%hx\n", command, crc, actual_crc);
return SDIO_ERR_RESPONSE_CRC;
}
uint8_t response_cmd = ((resp0 >> 24) & 0xFF);
if (response_cmd != command && command != 41)
{
// azdbg("rp2040_sdio_command_R1(", (int)command, "): received reply for ", (int)response_cmd);
EMSG_PRINTF("%d rp2040_sdio_command_R1(%d): received reply for %d\n", __LINE__, command, response_cmd);
return SDIO_ERR_RESPONSE_CODE;
}
*response = ((resp0 & 0xFFFFFF) << 8) | ((resp1 >> 8) & 0xFF);
}
else
{
// Read out dummy marker
pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
}
return SDIO_OK;
}
sdio_status_t rp2040_sdio_command_R2(const sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint8_t *response)
{
// The response is too long to fit in the PIO FIFO, so use DMA to receive it.
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
uint32_t response_buf[5];
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
channel_config_set_read_increment(&dmacfg, false);
channel_config_set_write_increment(&dmacfg, true);
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_CMD_SM, false));
dma_channel_configure(SDIO_DMA_CH, &dmacfg, &response_buf, &SDIO_PIO->rxf[SDIO_CMD_SM], 5, true);
sdio_send_command(sd_card_p, command, arg, 136);
uint32_t start = millis();
while (dma_channel_is_busy(SDIO_DMA_CH))
{
if ((uint32_t)(millis() - start) > sd_timeouts.rp2040_sdio_command_R2)
{
azdbg("Timeout waiting for response in rp2040_sdio_command_R2(", (int)command, "), ",
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)STATE.pio_cmd_clk_offset,
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
// Reset the state machine program
dma_channel_abort(SDIO_DMA_CH);
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(STATE.pio_cmd_clk_offset));
return SDIO_ERR_RESPONSE_TIMEOUT;
}
}
dma_channel_abort(SDIO_DMA_CH);
// Copy the response payload to output buffer
response[0] = ((response_buf[0] >> 16) & 0xFF);
response[1] = ((response_buf[0] >> 8) & 0xFF);
response[2] = ((response_buf[0] >> 0) & 0xFF);
response[3] = ((response_buf[1] >> 24) & 0xFF);
response[4] = ((response_buf[1] >> 16) & 0xFF);
response[5] = ((response_buf[1] >> 8) & 0xFF);
response[6] = ((response_buf[1] >> 0) & 0xFF);
response[7] = ((response_buf[2] >> 24) & 0xFF);
response[8] = ((response_buf[2] >> 16) & 0xFF);
response[9] = ((response_buf[2] >> 8) & 0xFF);
response[10] = ((response_buf[2] >> 0) & 0xFF);
response[11] = ((response_buf[3] >> 24) & 0xFF);
response[12] = ((response_buf[3] >> 16) & 0xFF);
response[13] = ((response_buf[3] >> 8) & 0xFF);
response[14] = ((response_buf[3] >> 0) & 0xFF);
response[15] = ((response_buf[4] >> 0) & 0xFF);
// Calculate checksum of the payload
uint8_t crc = 0;
for (int i = 0; i < 15; i++)
{
crc = crc7_table[crc ^ response[i]];
}
uint8_t actual_crc = response[15] & 0xFE;
if (crc != actual_crc)
{
azdbg("rp2040_sdio_command_R2(", (int)command, "): CRC error, calculated ", crc, " packet has ", actual_crc);
return SDIO_ERR_RESPONSE_CRC;
}
uint8_t response_cmd = ((response_buf[0] >> 24) & 0xFF);
if (response_cmd != 0x3F)
{
azdbg("rp2040_sdio_command_R2(", (int)command, "): Expected reply code 0x3F");
return SDIO_ERR_RESPONSE_CODE;
}
return SDIO_OK;
}
sdio_status_t rp2040_sdio_command_R3(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response)
{
sdio_send_command(sd_card_p, command, arg, 48);
// Wait for response
uint32_t start = millis();
while (pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM) < 2)
{
if ((uint32_t)(millis() - start) > sd_timeouts.rp2040_sdio_command_R3)
{
azdbg("Timeout waiting for response in rp2040_sdio_command_R3(", (int)command, "), ",
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)STATE.pio_cmd_clk_offset,
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
// Reset the state machine program
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(STATE.pio_cmd_clk_offset));
return SDIO_ERR_RESPONSE_TIMEOUT;
}
}
// Read out response packet
uint32_t resp0 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
uint32_t resp1 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
*response = ((resp0 & 0xFFFFFF) << 8) | ((resp1 >> 8) & 0xFF);
// azdbg("SDIO R3 response: ", resp0, " ", resp1);
return SDIO_OK;
}
/*******************************************************
* Data reception from SD card
*******************************************************/
sdio_status_t rp2040_sdio_rx_start(sd_card_t *sd_card_p, uint8_t *buffer, uint32_t num_blocks, size_t block_size)
{
// Buffer must be aligned
assert(((uint32_t)buffer & 3) == 0 && num_blocks <= SDIO_MAX_BLOCKS);
STATE.transfer_state = SDIO_RX;
STATE.transfer_start_time = millis();
STATE.data_buf = (uint32_t*)buffer;
STATE.blocks_done = 0;
STATE.total_blocks = num_blocks;
STATE.blocks_checksumed = 0;
STATE.checksum_errors = 0;
// Create DMA block descriptors to store each block of 512 bytes of data to buffer
// and then 8 bytes to STATE.received_checksums.
for (uint32_t i = 0; i < num_blocks; i++)
{
STATE.dma_blocks[i * 2].write_addr = buffer + i * block_size;
STATE.dma_blocks[i * 2].transfer_count = block_size / sizeof(uint32_t);
STATE.dma_blocks[i * 2 + 1].write_addr = &STATE.received_checksums[i];
STATE.dma_blocks[i * 2 + 1].transfer_count = 2;
}
STATE.dma_blocks[num_blocks * 2].write_addr = 0;
STATE.dma_blocks[num_blocks * 2].transfer_count = 0;
// Configure first DMA channel for reading from the PIO RX fifo
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
channel_config_set_read_increment(&dmacfg, false);
channel_config_set_write_increment(&dmacfg, true);
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, false));
channel_config_set_bswap(&dmacfg, true);
channel_config_set_chain_to(&dmacfg, SDIO_DMA_CHB);
dma_channel_configure(SDIO_DMA_CH, &dmacfg, 0, &SDIO_PIO->rxf[SDIO_DATA_SM], 0, false);
// Configure second DMA channel for reconfiguring the first one
dmacfg = dma_channel_get_default_config(SDIO_DMA_CHB);
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
channel_config_set_read_increment(&dmacfg, true);
channel_config_set_write_increment(&dmacfg, true);
channel_config_set_ring(&dmacfg, true, 3);
dma_channel_configure(SDIO_DMA_CHB, &dmacfg, &dma_hw->ch[SDIO_DMA_CH].al1_write_addr,
STATE.dma_blocks, 2, false);
// Initialize PIO state machine
pio_sm_init(SDIO_PIO, SDIO_DATA_SM, STATE.pio_data_rx_offset, &STATE.pio_cfg_data_rx);
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_DATA_SM, SDIO_D0, 4, false);
// Write number of nibbles to receive to Y register
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, block_size * 2 + 16 - 1);
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_out(pio_y, 32));
// Enable RX FIFO join because we don't need the TX FIFO during transfer.
// This gives more leeway for the DMA block switching
SDIO_PIO->sm[SDIO_DATA_SM].shiftctrl |= PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS;
// Start PIO and DMA
dma_channel_start(SDIO_DMA_CHB);
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, true);
return SDIO_OK;
}
// Check checksums for received blocks
static void sdio_verify_rx_checksums(sd_card_t *sd_card_p, uint32_t maxcount, size_t block_size_words)
{
while (STATE.blocks_checksumed < STATE.blocks_done && maxcount-- > 0)
{
// Calculate checksum from received data
int blockidx = STATE.blocks_checksumed++;
uint64_t checksum = sdio_crc16_4bit_checksum(STATE.data_buf + blockidx * block_size_words,
block_size_words);
// Convert received checksum to little-endian format
uint32_t top = __builtin_bswap32(STATE.received_checksums[blockidx].top);
uint32_t bottom = __builtin_bswap32(STATE.received_checksums[blockidx].bottom);
uint64_t expected = ((uint64_t)top << 32) | bottom;
if (checksum != expected)
{
STATE.checksum_errors++;
if (STATE.checksum_errors == 1)
{
EMSG_PRINTF("SDIO checksum error in reception: block %d calculated 0x%llx expected 0x%llx\n",
blockidx, checksum, expected);
dump_bytes(block_size_words, (uint8_t *)STATE.data_buf + blockidx * block_size_words);
}
}
}
}
sdio_status_t rp2040_sdio_rx_poll(sd_card_t *sd_card_p, size_t block_size_words)
{
// Was everything done when the previous rx_poll() finished?
if (STATE.blocks_done >= STATE.total_blocks)
{
STATE.transfer_state = SDIO_IDLE;
}
else
{
// Use the idle time to calculate checksums
sdio_verify_rx_checksums(sd_card_p, 4, block_size_words);
// Check how many DMA control blocks have been consumed
uint32_t dma_ctrl_block_count = (dma_hw->ch[SDIO_DMA_CHB].read_addr - (uint32_t)&STATE.dma_blocks);
dma_ctrl_block_count /= sizeof(STATE.dma_blocks[0]);
// Compute how many complete SDIO blocks have been transferred
// When transfer ends, dma_ctrl_block_count == STATE.total_blocks * 2 + 1
STATE.blocks_done = (dma_ctrl_block_count - 1) / 2;
// NOTE: When all blocks are done, rx_poll() still returns SDIO_BUSY once.
// This provides a chance to start the SCSI transfer before the last checksums
// are computed. Any checksum failures can be indicated in SCSI status after
// the data transfer has finished.
}
if (STATE.transfer_state == SDIO_IDLE)
{
// Verify all remaining checksums.
sdio_verify_rx_checksums(sd_card_p, STATE.total_blocks, block_size_words);
if (STATE.checksum_errors == 0)
return SDIO_OK;
else
return SDIO_ERR_DATA_CRC;
}
else if (millis() - STATE.transfer_start_time >= sd_timeouts.rp2040_sdio_rx_poll)
{
azdbg("rp2040_sdio_rx_poll() timeout, "
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_DATA_SM) - (int)STATE.pio_data_rx_offset,
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
" DMA CNT: ", dma_hw->ch[SDIO_DMA_CH].al2_transfer_count);
rp2040_sdio_stop(sd_card_p);
return SDIO_ERR_DATA_TIMEOUT;
}
return SDIO_BUSY;
}
/*******************************************************
* Data transmission to SD card
*******************************************************/
static void sdio_start_next_block_tx(sd_card_t *sd_card_p)
{
// Initialize PIO
pio_sm_init(SDIO_PIO, SDIO_DATA_SM, STATE.pio_data_tx_offset, &STATE.pio_cfg_data_tx);
// Configure DMA to send the data block payload (512 bytes)
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
channel_config_set_read_increment(&dmacfg, true);
channel_config_set_write_increment(&dmacfg, false);
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, true));
channel_config_set_bswap(&dmacfg, true);
channel_config_set_chain_to(&dmacfg, SDIO_DMA_CHB);
dma_channel_configure(SDIO_DMA_CH, &dmacfg,
&SDIO_PIO->txf[SDIO_DATA_SM], STATE.data_buf + STATE.blocks_done * SDIO_WORDS_PER_BLOCK,
SDIO_WORDS_PER_BLOCK, false);
// Prepare second DMA channel to send the CRC and block end marker
uint64_t crc = STATE.next_wr_block_checksum;
STATE.end_token_buf[0] = (uint32_t)(crc >> 32);
STATE.end_token_buf[1] = (uint32_t)(crc >> 0);
STATE.end_token_buf[2] = 0xFFFFFFFF;
channel_config_set_bswap(&dmacfg, false);
dma_channel_configure(SDIO_DMA_CHB, &dmacfg,
&SDIO_PIO->txf[SDIO_DATA_SM], STATE.end_token_buf, 3, false);
// Enable IRQ to trigger when block is done
switch (sd_card_p->sdio_if_p->DMA_IRQ_num) {
case DMA_IRQ_0:
// Clear any pending interrupt service request:
dma_hw->ints0 = 1 << SDIO_DMA_CHB;
dma_channel_set_irq0_enabled(SDIO_DMA_CHB, true);
break;
case DMA_IRQ_1:
// Clear any pending interrupt service request:
dma_hw->ints1 = 1 << SDIO_DMA_CHB;
dma_channel_set_irq1_enabled(SDIO_DMA_CHB, true);
break;
default:
assert(false);
}
// Initialize register X with nibble count and register Y with response bit count
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 1048);
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_out(pio_x, 32));
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 31);
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_out(pio_y, 32));
// Initialize pins to output and high
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_set(pio_pins, 15));
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_set(pio_pindirs, 15));
// Write start token and start the DMA transfer.
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 0xFFFFFFF0);
dma_channel_start(SDIO_DMA_CH);
// Start state machine
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, true);
}
static void sdio_compute_next_tx_checksum(sd_card_t *sd_card_p)
{
assert (STATE.blocks_done < STATE.total_blocks && STATE.blocks_checksumed < STATE.total_blocks);
int blockidx = STATE.blocks_checksumed++;
STATE.next_wr_block_checksum = sdio_crc16_4bit_checksum(STATE.data_buf + blockidx * SDIO_WORDS_PER_BLOCK,
SDIO_WORDS_PER_BLOCK);
}
// Start transferring data from memory to SD card
sdio_status_t rp2040_sdio_tx_start(sd_card_t *sd_card_p, const uint8_t *buffer, uint32_t num_blocks)
{
// Buffer must be aligned
assert(((uint32_t)buffer & 3) == 0 && num_blocks <= SDIO_MAX_BLOCKS);
STATE.transfer_state = SDIO_TX;
STATE.transfer_start_time = millis();
STATE.data_buf = (uint32_t*)buffer;
STATE.blocks_done = 0;
STATE.total_blocks = num_blocks;
STATE.blocks_checksumed = 0;
STATE.checksum_errors = 0;
// Compute first block checksum
sdio_compute_next_tx_checksum(sd_card_p);
// Start first DMA transfer and PIO
sdio_start_next_block_tx(sd_card_p);
if (STATE.blocks_checksumed < STATE.total_blocks)
{
// Precompute second block checksum
sdio_compute_next_tx_checksum(sd_card_p);
}
return SDIO_OK;
}
static sdio_status_t check_sdio_write_response(uint32_t card_response)
{
// Shift card response until top bit is 0 (the start bit)
// The format of response is poorly documented in SDIO spec but refer to e.g.
// http://my-cool-projects.blogspot.com/2013/02/the-mysterious-sd-card-crc-status.html
uint32_t resp = card_response;
if (!(~resp & 0xFFFF0000)) resp <<= 16;
if (!(~resp & 0xFF000000)) resp <<= 8;
if (!(~resp & 0xF0000000)) resp <<= 4;
if (!(~resp & 0xC0000000)) resp <<= 2;
if (!(~resp & 0x80000000)) resp <<= 1;
uint32_t wr_status = (resp >> 28) & 7;
if (wr_status == 2)
{
return SDIO_OK;
}
else if (wr_status == 5)
{
EMSG_PRINTF("SDIO card reports write CRC error, status %lx\n", card_response);
return SDIO_ERR_WRITE_CRC;
}
else if (wr_status == 6)
{
EMSG_PRINTF("SDIO card reports write failure, status %lx\n", card_response);
return SDIO_ERR_WRITE_FAIL;
}
else
{
EMSG_PRINTF("SDIO card reports unknown write status %lx\n", card_response);
return SDIO_ERR_WRITE_FAIL;
}
}
// When a block finishes, this IRQ handler starts the next one
void sdio_irq_handler(sd_card_t *sd_card_p) {
if (STATE.transfer_state == SDIO_TX)
{
if (!dma_channel_is_busy(SDIO_DMA_CH) && !dma_channel_is_busy(SDIO_DMA_CHB))
{
// Main data transfer is finished now.
// When card is ready, PIO will put card response on RX fifo
STATE.transfer_state = SDIO_TX_WAIT_IDLE;
if (!pio_sm_is_rx_fifo_empty(SDIO_PIO, SDIO_DATA_SM))
{
// Card is already idle
STATE.card_response = pio_sm_get(SDIO_PIO, SDIO_DATA_SM);
}
else
{
// Use DMA to wait for the response
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CHB);
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
channel_config_set_read_increment(&dmacfg, false);
channel_config_set_write_increment(&dmacfg, false);
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, false));
dma_channel_configure(SDIO_DMA_CHB, &dmacfg,
&STATE.card_response, &SDIO_PIO->rxf[SDIO_DATA_SM], 1, true);
}
}
}
if (STATE.transfer_state == SDIO_TX_WAIT_IDLE)
{
if (!dma_channel_is_busy(SDIO_DMA_CHB))
{
STATE.wr_status = check_sdio_write_response(STATE.card_response);
if (STATE.wr_status != SDIO_OK)
{
rp2040_sdio_stop(sd_card_p);
return;
}
STATE.blocks_done++;
if (STATE.blocks_done < STATE.total_blocks)
{
sdio_start_next_block_tx(sd_card_p);
STATE.transfer_state = SDIO_TX;
if (STATE.blocks_checksumed < STATE.total_blocks)
{
// Precompute the CRC for next block so that it is ready when
// we want to send it.
sdio_compute_next_tx_checksum(sd_card_p);
}
}
else
{
rp2040_sdio_stop(sd_card_p);
}
}
}
}
// Check if transmission is complete
sdio_status_t rp2040_sdio_tx_poll(sd_card_t *sd_card_p, uint32_t *bytes_complete)
{
#if !PICO_RISCV
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
{
// Verify that IRQ handler gets called even if we are in hardfault handler
sdio_irq_handler(sd_card_p);
}
#endif
if (bytes_complete)
{
*bytes_complete = STATE.blocks_done * SDIO_BLOCK_SIZE;
}
if (STATE.transfer_state == SDIO_IDLE)
{
rp2040_sdio_stop(sd_card_p);
return STATE.wr_status;
}
else if (millis() - STATE.transfer_start_time >= sd_timeouts.rp2040_sdio_tx_poll)
{
EMSG_PRINTF("rp2040_sdio_tx_poll() timeout\n");
DBG_PRINTF("rp2040_sdio_tx_poll() timeout, "
"PIO PC: %d"
" RXF: %d"
" TXF: %d"
" DMA CNT: %lu\n",
(int)pio_sm_get_pc(SDIO_PIO, SDIO_DATA_SM) - (int)STATE.pio_data_tx_offset,
(int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
(int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
dma_hw->ch[SDIO_DMA_CH].al2_transfer_count
);
rp2040_sdio_stop(sd_card_p);
return SDIO_ERR_DATA_TIMEOUT;
}
return SDIO_BUSY;
}
// Force everything to idle state
static sdio_status_t rp2040_sdio_stop(sd_card_t *sd_card_p)
{
dma_channel_abort(SDIO_DMA_CH);
dma_channel_abort(SDIO_DMA_CHB);
switch (sd_card_p->sdio_if_p->DMA_IRQ_num) {
case DMA_IRQ_0:
dma_channel_set_irq0_enabled(SDIO_DMA_CHB, false);
break;
case DMA_IRQ_1:
dma_channel_set_irq1_enabled(SDIO_DMA_CHB, false);
break;
default:
myASSERT(false);
}
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, false);
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_DATA_SM, SDIO_D0, 4, false);
STATE.transfer_state = SDIO_IDLE;
return SDIO_OK;
}
bool rp2040_sdio_init(sd_card_t *sd_card_p, float clk_div) {
// Mark resources as being in use, unless it has been done already.
if (!STATE.resources_claimed) {
if (!SDIO_PIO)
SDIO_PIO = pio0; // Default
if (!sd_card_p->sdio_if_p->DMA_IRQ_num)
sd_card_p->sdio_if_p->DMA_IRQ_num = DMA_IRQ_0; // Default
// pio_sm_claim(SDIO_PIO, SDIO_CMD_SM);
// int pio_claim_unused_sm(PIO pio, bool required);
SDIO_CMD_SM = pio_claim_unused_sm(SDIO_PIO, true);
// pio_sm_claim(SDIO_PIO, SDIO_DATA_SM);
SDIO_DATA_SM = pio_claim_unused_sm(SDIO_PIO, true);
// dma_channel_claim(SDIO_DMA_CH);
SDIO_DMA_CH = dma_claim_unused_channel(true);
// dma_channel_claim(SDIO_DMA_CHB);
SDIO_DMA_CHB = dma_claim_unused_channel(true);
/* Set up IRQ handler for when DMA completes. */
dma_irq_add_handler(sd_card_p->sdio_if_p->DMA_IRQ_num,
sd_card_p->sdio_if_p->use_exclusive_DMA_IRQ_handler);
STATE.resources_claimed = true;
}
dma_channel_abort(SDIO_DMA_CH);
dma_channel_abort(SDIO_DMA_CHB);
pio_sm_set_enabled(SDIO_PIO, SDIO_CMD_SM, false);
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, false);
// Load PIO programs
pio_clear_instruction_memory(SDIO_PIO);
// Command & clock state machine
STATE.pio_cmd_clk_offset = pio_add_program(SDIO_PIO, &sdio_cmd_clk_program);
pio_sm_config cfg = sdio_cmd_clk_program_get_default_config(STATE.pio_cmd_clk_offset);
sm_config_set_out_pins(&cfg, SDIO_CMD, 1);
sm_config_set_in_pins(&cfg, SDIO_CMD);
sm_config_set_set_pins(&cfg, SDIO_CMD, 1);
sm_config_set_jmp_pin(&cfg, SDIO_CMD);
sm_config_set_sideset_pins(&cfg, SDIO_CLK);
sm_config_set_out_shift(&cfg, false, true, 32);
sm_config_set_in_shift(&cfg, false, true, 32);
sm_config_set_clkdiv(&cfg, clk_div);
sm_config_set_mov_status(&cfg, STATUS_TX_LESSTHAN, 2);
pio_sm_init(SDIO_PIO, SDIO_CMD_SM, STATE.pio_cmd_clk_offset, &cfg);
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_CMD_SM, SDIO_CLK, 1, true);
pio_sm_set_enabled(SDIO_PIO, SDIO_CMD_SM, true);
// Data reception program
STATE.pio_data_rx_offset = pio_add_program(SDIO_PIO, &sdio_data_rx_program);
STATE.pio_cfg_data_rx = sdio_data_rx_program_get_default_config(STATE.pio_data_rx_offset);
sm_config_set_in_pins(&STATE.pio_cfg_data_rx, SDIO_D0);
sm_config_set_in_shift(&STATE.pio_cfg_data_rx, false, true, 32);
sm_config_set_out_shift(&STATE.pio_cfg_data_rx, false, true, 32);
sm_config_set_clkdiv(&STATE.pio_cfg_data_rx, clk_div);
// Data transmission program
STATE.pio_data_tx_offset = pio_add_program(SDIO_PIO, &sdio_data_tx_program);
STATE.pio_cfg_data_tx = sdio_data_tx_program_get_default_config(STATE.pio_data_tx_offset);
sm_config_set_in_pins(&STATE.pio_cfg_data_tx, SDIO_D0);
sm_config_set_set_pins(&STATE.pio_cfg_data_tx, SDIO_D0, 4);
sm_config_set_out_pins(&STATE.pio_cfg_data_tx, SDIO_D0, 4);
sm_config_set_in_shift(&STATE.pio_cfg_data_tx, false, false, 32);
sm_config_set_out_shift(&STATE.pio_cfg_data_tx, false, true, 32);
sm_config_set_clkdiv(&STATE.pio_cfg_data_tx, clk_div);
// Disable SDIO pins input synchronizer.
// This reduces input delay.
// Because the CLK is driven synchronously to CPU clock,
// there should be no metastability problems.
SDIO_PIO->input_sync_bypass |= (1 << SDIO_CLK) | (1 << SDIO_CMD) | (1 << SDIO_D0) | (1 << SDIO_D1) | (1 << SDIO_D2) | (1 << SDIO_D3);
// Redirect GPIOs to PIO
#if PICO_SDK_VERSION_MAJOR < 2
typedef enum gpio_function gpio_function_t;
#endif
gpio_function_t fn;
if (pio1 == SDIO_PIO)
fn = GPIO_FUNC_PIO1;
else
fn = GPIO_FUNC_PIO0;
gpio_set_function(SDIO_CMD, fn);
gpio_set_function(SDIO_CLK, fn);
gpio_set_function(SDIO_D0, fn);
gpio_set_function(SDIO_D1, fn);
gpio_set_function(SDIO_D2, fn);
gpio_set_function(SDIO_D3, fn);
gpio_set_slew_rate(SDIO_CMD, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(SDIO_CLK, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(SDIO_D0, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(SDIO_D1, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(SDIO_D2, GPIO_SLEW_RATE_FAST);
gpio_set_slew_rate(SDIO_D3, GPIO_SLEW_RATE_FAST);
if (sd_card_p->sdio_if_p->set_drive_strength) {
gpio_set_drive_strength(SDIO_CMD, sd_card_p->sdio_if_p->CMD_gpio_drive_strength);
gpio_set_drive_strength(SDIO_CLK, sd_card_p->sdio_if_p->CLK_gpio_drive_strength);
gpio_set_drive_strength(SDIO_D0, sd_card_p->sdio_if_p->D0_gpio_drive_strength);
gpio_set_drive_strength(SDIO_D1, sd_card_p->sdio_if_p->D1_gpio_drive_strength);
gpio_set_drive_strength(SDIO_D2, sd_card_p->sdio_if_p->D2_gpio_drive_strength);
gpio_set_drive_strength(SDIO_D3, sd_card_p->sdio_if_p->D3_gpio_drive_strength);
}
return true;
}

120
lib/sd_driver/SDIO/rp2040_sdio.h Executable file
View File

@ -0,0 +1,120 @@
// SD card access using SDIO for RP2040 platform.
// This module contains the low-level SDIO bus implementation using
// the PIO peripheral. The high-level commands are in sd_card_sdio.cpp.
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "sd_card.h"
//FIXME: why?
typedef struct sd_card_t sd_card_t;
typedef
enum sdio_status_t {
SDIO_OK = 0,
SDIO_BUSY = 1,
SDIO_ERR_RESPONSE_TIMEOUT = 2, // Timed out waiting for response from card
SDIO_ERR_RESPONSE_CRC = 3, // Response CRC is wrong
SDIO_ERR_RESPONSE_CODE = 4, // Response command code does not match what was sent
SDIO_ERR_DATA_TIMEOUT = 5, // Timed out waiting for data block
SDIO_ERR_DATA_CRC = 6, // CRC for data packet is wrong
SDIO_ERR_WRITE_CRC = 7, // Card reports bad CRC for write
SDIO_ERR_WRITE_FAIL = 8, // Card reports write failure
} sdio_status_t;
#define SDIO_BLOCK_SIZE 512
#define SDIO_WORDS_PER_BLOCK (SDIO_BLOCK_SIZE / 4) // 128
// Maximum number of 512 byte blocks to transfer in one request
#define SDIO_MAX_BLOCKS 256
typedef enum sdio_transfer_state_t { SDIO_IDLE, SDIO_RX, SDIO_TX, SDIO_TX_WAIT_IDLE} sdio_transfer_state_t;
typedef struct sd_sdio_if_state_t {
bool resources_claimed;
uint32_t ocr; // Operating condition register from card
uint32_t rca; // Relative card address
int error_line;
sdio_status_t error;
uint32_t dma_buf[128];
int SDIO_DMA_CH;
int SDIO_DMA_CHB;
int SDIO_CMD_SM;
int SDIO_DATA_SM;
uint32_t pio_cmd_clk_offset;
uint32_t pio_data_rx_offset;
pio_sm_config pio_cfg_data_rx;
uint32_t pio_data_tx_offset;
pio_sm_config pio_cfg_data_tx;
sdio_transfer_state_t transfer_state;
uint32_t transfer_start_time;
uint32_t *data_buf;
uint32_t blocks_done; // Number of blocks transferred so far
uint32_t total_blocks; // Total number of blocks to transfer
uint32_t blocks_checksumed; // Number of blocks that have had CRC calculated
uint32_t checksum_errors; // Number of checksum errors detected
// Variables for block writes
uint64_t next_wr_block_checksum;
uint32_t end_token_buf[3]; // CRC and end token for write block
sdio_status_t wr_status;
uint32_t card_response;
// Variables for extended block writes
bool ongoing_wr_mlt_blk;
uint32_t wr_mlt_blk_cnt_sector;
// Variables for block reads
// This is used to perform DMA into data buffers and checksum buffers separately.
struct {
void * write_addr;
uint32_t transfer_count;
} dma_blocks[SDIO_MAX_BLOCKS * 2];
struct {
uint32_t top;
uint32_t bottom;
} received_checksums[SDIO_MAX_BLOCKS];
} sd_sdio_if_state_t;
// Execute a command that has 48-bit reply (response types R1, R6, R7)
// If response is NULL, does not wait for reply.
sdio_status_t rp2040_sdio_command_R1(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response);
// Execute a command that has 136-bit reply (response type R2)
// Response buffer should have space for 16 bytes (the 128 bit payload)
sdio_status_t rp2040_sdio_command_R2(const sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint8_t *response);
// Execute a command that has 48-bit reply but without CRC (response R3)
sdio_status_t rp2040_sdio_command_R3(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response);
// Start transferring data from SD card to memory buffer
sdio_status_t rp2040_sdio_rx_start(sd_card_t *sd_card_p, uint8_t *buffer, uint32_t num_blocks, size_t block_size);
// Check if reception is complete
// Returns SDIO_BUSY while transferring, SDIO_OK when done and error on failure.
sdio_status_t rp2040_sdio_rx_poll(sd_card_t *sd_card_p, size_t block_size_words);
// Start transferring data from memory to SD card
sdio_status_t rp2040_sdio_tx_start(sd_card_t *sd_card_p, const uint8_t *buffer, uint32_t num_blocks);
// Check if transmission is complete
sdio_status_t rp2040_sdio_tx_poll(sd_card_t *sd_card_p, uint32_t *bytes_complete /* = nullptr */);
// (Re)initialize the SDIO interface
bool rp2040_sdio_init(sd_card_t *sd_card_p, float clk_div);
void __not_in_flash_func(sdio_irq_handler)(sd_card_t *sd_card_p);
#ifdef __cplusplus
}
#endif

Some files were not shown because too many files have changed in this diff Show More