Compare commits
3 Commits
2f051541cc
...
ea82637e99
Author | SHA1 | Date | |
---|---|---|---|
ea82637e99 | |||
e2d49bac4f | |||
c877fd05f6 |
3
.vscode/c_cpp_properties.json
vendored
@ -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
@ -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"
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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
@ -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
|
||||
;
|
||||
}*/
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@ -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;
|
@ -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) */
|
||||
|
@ -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
@ -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
|
181
lib/sd_driver/SDIO/ZuluSCSI_platform.h
Executable 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
@ -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
@ -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
|