Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
|
a62c3a954b | ||
|
113e100708 | ||
|
68f86aa4f7 |
15 changed files with 1405 additions and 36 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
*.o
|
||||
*.bin
|
||||
test/test
|
||||
test/test.bin
|
||||
tags
|
||||
|
|
1
Makefile
1
Makefile
|
@ -2,4 +2,5 @@ test/test:
|
|||
make -C test
|
||||
|
||||
clean:
|
||||
rm -f src/*.o
|
||||
make -C test clean
|
||||
|
|
11
src/spi_drv.h
Normal file
11
src/spi_drv.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef SPI_DRV_H_INCLUDED
|
||||
#define SPI_DRV_H_INCLUDED
|
||||
#include <stdint.h>
|
||||
|
||||
void spi_init(int polarity, int phase);
|
||||
void spi_write(const char byte);
|
||||
uint8_t spi_read(void);
|
||||
void spi_cs_on(void);
|
||||
void spi_cs_off(void);
|
||||
|
||||
#endif
|
98
src/spi_drv_stm32f4.c
Normal file
98
src/spi_drv_stm32f4.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <stdint.h>
|
||||
#include "spi_drv.h"
|
||||
#include "system.h"
|
||||
|
||||
void spi_cs_off(void)
|
||||
{
|
||||
GPIOE_BSRR |= (1 << SPI_FLASH_PIN);
|
||||
DMB();
|
||||
while(!(GPIOE_ODR & (1 << SPI_FLASH_PIN)))
|
||||
;
|
||||
}
|
||||
|
||||
void spi_cs_on(void)
|
||||
{
|
||||
volatile int i;
|
||||
GPIOE_BSRR |= (1 << (SPI_FLASH_PIN + 16));
|
||||
DMB();
|
||||
while(GPIOE_ODR & (1 << SPI_FLASH_PIN))
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
static void spi_flash_pin_setup(void)
|
||||
{
|
||||
uint32_t reg;
|
||||
AHB1_CLOCK_ER |= GPIOE_AHB1_CLOCK_ER;
|
||||
reg = GPIOE_MODE & ~ (0x03 << (SPI_FLASH_PIN * 2));
|
||||
GPIOE_MODE = reg | (1 << (SPI_FLASH_PIN * 2));
|
||||
|
||||
reg = GPIOE_PUPD & (0x03 << (SPI_FLASH_PIN * 2));
|
||||
GPIOE_PUPD = reg | (0x01 << (SPI_FLASH_PIN * 2));
|
||||
|
||||
reg = GPIOE_OSPD & ~(0x03 << (SPI_FLASH_PIN * 2));
|
||||
GPIOE_OSPD |= (0x03 << (SPI_FLASH_PIN * 2));
|
||||
|
||||
}
|
||||
|
||||
static void spi1_pins_setup(void)
|
||||
{
|
||||
uint32_t reg;
|
||||
AHB1_CLOCK_ER |= GPIOB_AHB1_CLOCK_ER;
|
||||
/* Set mode = AF */
|
||||
reg = GPIOB_MODE & ~ (0x03 << (SPI1_CLOCK_PIN * 2));
|
||||
GPIOB_MODE = reg | (2 << (SPI1_CLOCK_PIN * 2));
|
||||
reg = GPIOB_MODE & ~ (0x03 << (SPI1_MOSI_PIN * 2));
|
||||
GPIOB_MODE = reg | (2 << (SPI1_MOSI_PIN * 2));
|
||||
reg = GPIOB_MODE & ~ (0x03 << (SPI1_MISO_PIN * 2));
|
||||
GPIOB_MODE = reg | (2 << (SPI1_MISO_PIN * 2));
|
||||
|
||||
/* Alternate function: use low pins (5,6,7) */
|
||||
reg = GPIOB_AFL & ~(0xf << ((SPI1_CLOCK_PIN) * 4));
|
||||
GPIOB_AFL = reg | (SPI1_PIN_AF << ((SPI1_CLOCK_PIN) * 4));
|
||||
reg = GPIOB_AFL & ~(0xf << ((SPI1_MOSI_PIN) * 4));
|
||||
GPIOB_AFL = reg | (SPI1_PIN_AF << ((SPI1_MOSI_PIN) * 4));
|
||||
reg = GPIOB_AFL & ~(0xf << ((SPI1_MISO_PIN) * 4));
|
||||
GPIOB_AFL = reg | (SPI1_PIN_AF << ((SPI1_MISO_PIN) * 4));
|
||||
|
||||
}
|
||||
|
||||
static void spi1_reset(void)
|
||||
{
|
||||
APB2_CLOCK_RST |= SPI1_APB2_CLOCK_ER_VAL;
|
||||
APB2_CLOCK_RST &= ~SPI1_APB2_CLOCK_ER_VAL;
|
||||
}
|
||||
|
||||
uint8_t spi_read(void)
|
||||
{
|
||||
volatile uint32_t reg;
|
||||
do {
|
||||
reg = SPI1_SR;
|
||||
} while(!(reg & SPI_SR_RX_NOTEMPTY));
|
||||
return (uint8_t)SPI1_DR;
|
||||
}
|
||||
|
||||
void spi_write(const char byte)
|
||||
{
|
||||
int i;
|
||||
volatile uint32_t reg;
|
||||
do {
|
||||
reg = SPI1_SR;
|
||||
} while ((reg & SPI_SR_TX_EMPTY) == 0);
|
||||
SPI1_DR = byte;
|
||||
do {
|
||||
reg = SPI1_SR;
|
||||
} while ((reg & SPI_SR_TX_EMPTY) == 0);
|
||||
}
|
||||
|
||||
|
||||
void spi_init(int polarity, int phase)
|
||||
{
|
||||
spi1_pins_setup();
|
||||
spi_flash_pin_setup();
|
||||
APB2_CLOCK_ER |= SPI1_APB2_CLOCK_ER_VAL;
|
||||
spi1_reset();
|
||||
SPI1_CR1 = SPI_CR1_MASTER | (5 << 3) | (polarity << 1) | (phase << 0);
|
||||
SPI1_CR2 |= SPI_CR2_SSOE;
|
||||
SPI1_CR1 |= SPI_CR1_SPI_EN;
|
||||
}
|
247
src/spi_flash.c
Normal file
247
src/spi_flash.c
Normal file
|
@ -0,0 +1,247 @@
|
|||
#include "system.h"
|
||||
#include "spi_drv.h"
|
||||
|
||||
#define SPI_FLASH_SECTOR_SIZE (4096)
|
||||
#define SPI_FLASH_PAGE_SIZE (256)
|
||||
|
||||
#define MDID 0x90
|
||||
#define RDSR 0x05
|
||||
#define WRSR 0x01
|
||||
# define ST_BUSY (1 << 0)
|
||||
# define ST_WEL (1 << 1)
|
||||
# define ST_BP0 (1 << 2)
|
||||
# define ST_BP1 (1 << 3)
|
||||
# define ST_BP2 (1 << 4)
|
||||
# define ST_BP3 (1 << 5)
|
||||
# define ST_AAI (1 << 6)
|
||||
# define ST_BRO (1 << 7)
|
||||
#define WREN 0x06
|
||||
#define WRDI 0x04
|
||||
#define SECTOR_ERASE 0x20
|
||||
#define BYTE_READ 0x03
|
||||
#define BYTE_WRITE 0x02
|
||||
#define AUTOINC 0xAD
|
||||
#define EWSR 0x50
|
||||
#define EBSY 0x70
|
||||
#define DBSY 0x80
|
||||
|
||||
|
||||
static enum write_mode {
|
||||
WB_WRITEPAGE = 0x00,
|
||||
SST_AAI = 0x01
|
||||
} chip_write_mode = WB_WRITEPAGE;
|
||||
|
||||
static void write_address(uint32_t address)
|
||||
{
|
||||
spi_write((address & 0xFF00) >> 8);
|
||||
spi_read();
|
||||
spi_write((address & 0xFF0000) >> 16);
|
||||
spi_read();
|
||||
spi_write((address & 0xFF000000) >> 24);
|
||||
spi_read();
|
||||
}
|
||||
|
||||
static uint8_t read_status(void)
|
||||
{
|
||||
uint8_t status;
|
||||
int i;
|
||||
spi_cs_on();
|
||||
spi_write(RDSR);
|
||||
spi_read();
|
||||
spi_write(0xFF);
|
||||
status = spi_read();
|
||||
spi_cs_off();
|
||||
return status;
|
||||
}
|
||||
|
||||
static void spi_cmd(uint8_t cmd)
|
||||
{
|
||||
spi_cs_on();
|
||||
spi_write(cmd);
|
||||
spi_read();
|
||||
spi_cs_off();
|
||||
}
|
||||
|
||||
static inline void flash_aai_enable(void)
|
||||
{
|
||||
spi_cmd(EBSY);
|
||||
}
|
||||
|
||||
static inline void flash_aai_disable(void)
|
||||
{
|
||||
spi_cmd(DBSY);
|
||||
}
|
||||
|
||||
static void flash_write_enable(void)
|
||||
{
|
||||
uint8_t status;
|
||||
do {
|
||||
spi_cmd(WREN);
|
||||
status = read_status();
|
||||
} while ((status & ST_WEL) == 0);
|
||||
}
|
||||
|
||||
static void flash_write_disable(void)
|
||||
{
|
||||
uint8_t status;
|
||||
spi_cmd(WRDI);
|
||||
}
|
||||
static void wait_busy(void)
|
||||
{
|
||||
uint8_t status;
|
||||
do {
|
||||
status = read_status();
|
||||
} while(status & ST_BUSY);
|
||||
}
|
||||
|
||||
static int spi_flash_write_page(uint32_t address, const void *data, int len)
|
||||
{
|
||||
const uint8_t *buf = data;
|
||||
int j = 0;
|
||||
while (len > 0) {
|
||||
wait_busy();
|
||||
flash_write_enable();
|
||||
spi_cs_on();
|
||||
spi_write(BYTE_WRITE);
|
||||
spi_read();
|
||||
write_address(address);
|
||||
do {
|
||||
spi_write(buf[j++]);
|
||||
address++;
|
||||
spi_read();
|
||||
len--;
|
||||
} while ((address % SPI_FLASH_PAGE_SIZE) != 0);
|
||||
spi_cs_off();
|
||||
}
|
||||
wait_busy();
|
||||
return j;
|
||||
}
|
||||
|
||||
static int spi_flash_write_aai(uint32_t address, const void *data, int len)
|
||||
{
|
||||
const uint8_t *buf = data;
|
||||
int j = 0;
|
||||
int cont = 0;
|
||||
wait_busy();
|
||||
if (len < 1)
|
||||
return -1;
|
||||
while (len > 0) {
|
||||
if ((address & 0x01) || (len < 2)) {
|
||||
flash_write_enable();
|
||||
spi_cs_on();
|
||||
spi_write(BYTE_WRITE);
|
||||
spi_read();
|
||||
write_address(address);
|
||||
spi_write(buf[j++]);
|
||||
spi_read();
|
||||
spi_cs_off();
|
||||
len--;
|
||||
address++;
|
||||
} else {
|
||||
if (!cont) {
|
||||
flash_aai_enable();
|
||||
flash_write_enable();
|
||||
}
|
||||
spi_cs_on();
|
||||
spi_write(AUTOINC);
|
||||
spi_read();
|
||||
if (!cont) {
|
||||
/* First AAI transaction, send address. */
|
||||
write_address(address);
|
||||
cont = 1;
|
||||
}
|
||||
spi_write(buf[j++]);
|
||||
spi_read();
|
||||
spi_write(buf[j++]);
|
||||
spi_read();
|
||||
spi_cs_off();
|
||||
len -= 2;
|
||||
address += 2;
|
||||
read_status();
|
||||
}
|
||||
}
|
||||
if (cont) {
|
||||
flash_write_disable();
|
||||
flash_aai_disable();
|
||||
}
|
||||
wait_busy();
|
||||
return j;
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
uint16_t spi_flash_probe(void)
|
||||
{
|
||||
uint8_t manuf, product, b0;
|
||||
int i;
|
||||
wait_busy();
|
||||
spi_cs_on();
|
||||
spi_write(MDID);
|
||||
b0 = spi_read();
|
||||
|
||||
write_address(0);
|
||||
spi_write(0xFF);
|
||||
manuf = spi_read();
|
||||
spi_write(0xFF);
|
||||
product = spi_read();
|
||||
spi_cs_off();
|
||||
if (manuf == 0xBF)
|
||||
chip_write_mode = SST_AAI;
|
||||
if (manuf == 0xEF)
|
||||
chip_write_mode = WB_WRITEPAGE;
|
||||
|
||||
#ifndef READONLY
|
||||
spi_cmd(EWSR);
|
||||
spi_cs_on();
|
||||
spi_write(WRSR);
|
||||
spi_read();
|
||||
spi_write(0x00);
|
||||
spi_read();
|
||||
spi_cs_off();
|
||||
#endif
|
||||
return (uint16_t)(manuf << 8 | product);
|
||||
}
|
||||
|
||||
|
||||
void spi_flash_sector_erase(uint32_t address)
|
||||
{
|
||||
uint8_t status;
|
||||
address &= (~(SPI_FLASH_SECTOR_SIZE - 1));
|
||||
|
||||
wait_busy();
|
||||
flash_write_enable();
|
||||
spi_cs_on();
|
||||
spi_write(SECTOR_ERASE);
|
||||
spi_read();
|
||||
write_address(address);
|
||||
spi_cs_off();
|
||||
wait_busy();
|
||||
}
|
||||
|
||||
int spi_flash_read(uint32_t address, void *data, int len)
|
||||
{
|
||||
uint8_t *buf = data;
|
||||
int i = 0;
|
||||
wait_busy();
|
||||
spi_cs_on();
|
||||
spi_write(BYTE_READ);
|
||||
spi_read();
|
||||
write_address(address);
|
||||
while (len > 0) {
|
||||
spi_write(0xFF);
|
||||
buf[i++] = spi_read();
|
||||
len--;
|
||||
}
|
||||
spi_cs_off();
|
||||
return i;
|
||||
}
|
||||
|
||||
int spi_flash_write(uint32_t address, const void *data, int len)
|
||||
{
|
||||
if (chip_write_mode == SST_AAI)
|
||||
return spi_flash_write_aai(address, data, len);
|
||||
if (chip_write_mode == WB_WRITEPAGE)
|
||||
return spi_flash_write_page(address, data, len);
|
||||
return -1;
|
||||
}
|
||||
|
9
src/spi_flash.h
Normal file
9
src/spi_flash.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef SPI_FLASH_DRI_H
|
||||
#define SPI_FLASH_DRI_H
|
||||
#include <stdint.h>
|
||||
uint16_t spi_flash_probe(void);
|
||||
|
||||
void spi_flash_sector_erase(uint32_t address);
|
||||
int spi_flash_read(uint32_t address, void *data, int len);
|
||||
int spi_flash_write(uint32_t address, const void *data, int len);
|
||||
#endif
|
80
src/usecfs.c
80
src/usecfs.c
|
@ -18,9 +18,10 @@
|
|||
#define MAX_INLINE_SIZE ((MAX_BLOCKS_0 - 1) * 4) /* Max data size for self-contained block */
|
||||
#define INLINE_PAYLOAD(x) ((uint8_t *)(&x->blk[1])) /* Macro to access INLINE paylaod */
|
||||
|
||||
#ifdef CRYPTO
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
#ifdef CRYPTO
|
||||
#define CRYPTO_BLOCK_SIZE 16
|
||||
|
||||
uint8_t crypto_tmp[CRYPTO_BLOCK_SIZE];
|
||||
|
@ -28,12 +29,14 @@ uint8_t crypto_iv[CRYPTO_BLOCK_SIZE];
|
|||
|
||||
#include <wolfssl/wolfcrypt/chacha.h>
|
||||
#include <wolfssl/wolfcrypt/pwdbased.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
|
||||
static ChaCha chacha;
|
||||
#define CRYPTO_KEY_SIZE 32
|
||||
#endif
|
||||
|
||||
#define HASH_LEN 32
|
||||
#define MAGIC 0x5AFED15C
|
||||
|
||||
static uint8_t cache[BLOCK_SIZE];
|
||||
static uint8_t inline_buffer_copy[MAX_INLINE_SIZE];
|
||||
|
||||
|
@ -51,11 +54,19 @@ struct __attribute__((packed)) inode {
|
|||
char filename[MAX_FILENAME];
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) root_block {
|
||||
uint32_t magic;
|
||||
uint32_t blk[((BLOCK_SIZE - MAX_FILENAME) / 4) - 3];
|
||||
uint32_t fs_size; /* unused */
|
||||
uint32_t nextfile;
|
||||
uint8_t uuid[UUID_LEN];
|
||||
uint8_t hash[HASH_LEN];
|
||||
};
|
||||
|
||||
|
||||
/* One-sector cache, single entry point for read/write
|
||||
* on blocks
|
||||
*/
|
||||
|
||||
|
||||
static uint32_t cached_block = NO_BLOCK;
|
||||
void *blockdev = NULL;
|
||||
|
||||
|
@ -87,7 +98,7 @@ static void cache_commit(void)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
block_write(blockdev, cache, cached_block, 0, BLOCK_SIZE);
|
||||
block_write(blockdev, cache, cached_block);
|
||||
cached_block = NO_BLOCK;
|
||||
}
|
||||
|
||||
|
@ -97,7 +108,7 @@ static void cache_load(uint32_t blk)
|
|||
return;
|
||||
if (cached_block != NO_BLOCK)
|
||||
cache_commit();
|
||||
if (block_read(blockdev, cache, blk, 0, BLOCK_SIZE) == BLOCK_SIZE) {
|
||||
if (block_read(blockdev, cache, blk) == BLOCK_SIZE) {
|
||||
cached_block = blk;
|
||||
#ifdef CRYPTO
|
||||
if (!is_block_empty()) {
|
||||
|
@ -140,6 +151,13 @@ static uint32_t get_free_block(uint32_t *fs_tail)
|
|||
uint32_t cur_inode_0 = 0;
|
||||
int i;
|
||||
cache_load(0);
|
||||
if (ci->nextfile == NO_BLOCK) {
|
||||
if (fs_tail)
|
||||
*fs_tail = 0;
|
||||
return 1;
|
||||
}
|
||||
cur_inode_0 = ci->nextfile;
|
||||
cache_load(cur_inode_0);
|
||||
while (1) {
|
||||
for (i = 0; i < MAX_BLOCKS_0; i++)
|
||||
{
|
||||
|
@ -187,6 +205,7 @@ static uint32_t new_inode(void)
|
|||
cache_load(fs_tail);
|
||||
ci->nextfile = new_block;
|
||||
cache_load(new_block);
|
||||
memset(cache, 0xFF, BLOCK_SIZE);
|
||||
ci->nextfile = NO_BLOCK;
|
||||
return new_block;
|
||||
}
|
||||
|
@ -291,6 +310,42 @@ static void file_grow(uint32_t node0, uint32_t newsize)
|
|||
ci->size = newsize;
|
||||
}
|
||||
|
||||
/* Public interface */
|
||||
|
||||
int usecfs_format(const uint8_t *uuid)
|
||||
{
|
||||
struct root_block *rb = (struct root_block *)cache;
|
||||
wc_Sha256 sha;
|
||||
cache_load(0);
|
||||
memset(cache, 0xFF, BLOCK_SIZE);
|
||||
rb->magic = MAGIC;
|
||||
rb->blk[0] = 0;
|
||||
memcpy(rb->uuid, uuid, UUID_LEN);
|
||||
wc_InitSha256(&sha);
|
||||
wc_Sha256Update(&sha, uuid, UUID_LEN);
|
||||
wc_Sha256Final(&sha, rb->hash);
|
||||
cache_commit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usecfs_mount(uint8_t *uuid)
|
||||
{
|
||||
struct root_block *rb = (struct root_block *)cache;
|
||||
wc_Sha256 sha;
|
||||
uint8_t hash[HASH_LEN];
|
||||
cache_load(0);
|
||||
if (rb->magic != MAGIC)
|
||||
return -1;
|
||||
wc_InitSha256(&sha);
|
||||
wc_Sha256Update(&sha, rb->uuid, UUID_LEN);
|
||||
wc_Sha256Final(&sha, hash);
|
||||
if (memcmp(hash, rb->hash, HASH_LEN) != 0)
|
||||
return -1;
|
||||
if (uuid)
|
||||
memcpy(uuid, rb->uuid, UUID_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usecfs_read(int fd, void *data, uint32_t len)
|
||||
{
|
||||
int r = 0;
|
||||
|
@ -505,7 +560,7 @@ int usecfs_truncate(int fd, uint32_t newsize)
|
|||
cache_load(new_idx_block);
|
||||
if (idx_block != new_idx_block)
|
||||
ci->extra = NO_BLOCK;
|
||||
if (idx_off <= MAX_BLOCKS_0) {
|
||||
if (idx_block <= MAX_BLOCKS_0) {
|
||||
idx_off = new_idx_block;
|
||||
for (i = idx_off; i < MAX_BLOCKS_N; i++)
|
||||
ce->blk[i] = NO_BLOCK;
|
||||
|
@ -551,6 +606,7 @@ int usecfs_close(int fd)
|
|||
cache_commit();
|
||||
OpenFiles[fd].blk = NO_BLOCK;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CRYPTO
|
||||
|
@ -562,7 +618,7 @@ const uint8_t password_salt[SALT_LEN] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
int usecfs_init(const char *password)
|
||||
int usecfs_init(const char *password, int format, uint8_t *uuid)
|
||||
{
|
||||
blockdev = block_open(BLOCKDEV_OPEN_ARGS);
|
||||
if (!blockdev)
|
||||
|
@ -576,5 +632,11 @@ int usecfs_init(const char *password)
|
|||
wc_Chacha_SetKey(&chacha, chacha_key, CRYPTO_KEY_SIZE);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
if (format) {
|
||||
if (!uuid)
|
||||
return -1;
|
||||
else
|
||||
return usecfs_format(uuid);
|
||||
} else
|
||||
return usecfs_mount(uuid);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
#define INC_USECFS
|
||||
#define MAX_FILENAME 256
|
||||
#define MAX_OPEN_FILES 16
|
||||
#define UUID_LEN 64
|
||||
#include <stdint.h>
|
||||
|
||||
int usecfs_init(const char *password);
|
||||
int usecfs_init(const char *password, int format, uint8_t *uuid);
|
||||
int usecfs_format(const uint8_t *uuid);
|
||||
int usecfs_mount(uint8_t *uuid);
|
||||
int usecfs_open(const char *name);
|
||||
int usecfs_creat(const char *name);
|
||||
int usecfs_read(int fd, void *data, uint32_t len);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
#endif
|
||||
|
||||
void *block_open(void *args);
|
||||
int block_read(void *dev, void *_buf, uint32_t lba, int offset, int count);
|
||||
int block_write(void *dev, const void *_buf, uint32_t lba, int offset, int count);
|
||||
int block_read(void *dev, void *_buf, uint32_t lba);
|
||||
int block_write(void *dev, const void *_buf, uint32_t lba);
|
||||
void block_close(void *dev);
|
||||
|
||||
#endif
|
||||
|
|
803
src/usecfs_dev_sdio.c
Normal file
803
src/usecfs_dev_sdio.c
Normal file
|
@ -0,0 +1,803 @@
|
|||
|
||||
/*
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original Author: Chuck M. (see https://github.com/ChuckM/stm32f4-sdio-driver/)
|
||||
* Re-adapted by: Daniele Lacamera
|
||||
*
|
||||
* Permission to release under the terms of GPLv2 are granted by the
|
||||
* copyright holders.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* SDIO Bus Driver layer. This code sends commands and drives
|
||||
* the SDIO peripheral on the STM32F4xx, there is a layer above
|
||||
* this, the SD Card driver, which uses this driver to talk to
|
||||
* SD Cards. The SPI driver can also talk to SD Cards, hence the
|
||||
* split at this layer.
|
||||
*
|
||||
* Note that the simple implementation for the SDIO driver runs
|
||||
* in a 'polled' mode. This is easier to explain and debug and
|
||||
* sufficient for the first few projects. A more sophisticated
|
||||
* version with DMA and interrupts will follow.
|
||||
*
|
||||
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "usecfs_dev_sdio.h"
|
||||
#include "system.h"
|
||||
|
||||
#if (!defined(BLOCK_SIZE)) || (BLOCK_SIZE != 512)
|
||||
# error BLOCK_SIZE undefined or invalid
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL (void *)(0x00000000)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper defines to pull out various bit fields of the CSD for
|
||||
* the size calculation.
|
||||
*/
|
||||
#define SDIO_CSD_VERSION(x) stm32_sdio_bit_slice(x->csd, 128, 127, 126)
|
||||
#define SDIO_CSD1_CSIZE_MULT(x) stm32_sdio_bit_slice(x->csd, 128, 49, 47)
|
||||
#define SDIO_CSD1_RBLKLEN(x) stm32_sdio_bit_slice(x->csd, 128, 83, 80)
|
||||
#define SDIO_CSD1_CSIZE(x) stm32_sdio_bit_slice(x->csd, 128, 73, 62)
|
||||
#define SDIO_CSD2_CSIZE(x) stm32_sdio_bit_slice(x->csd, 128, 69, 48)
|
||||
|
||||
/*
|
||||
* Conveniently swaps the bytes in a long around
|
||||
* used by the SCR code.
|
||||
*/
|
||||
#define byte_swap(val) \
|
||||
asm("rev %[swap], %[swap]" : [swap] "=r" (val) : "0" (val));
|
||||
|
||||
static const uint32_t sdio_pins[5] = { 8, 9, 10, 11, 12}; /* GPIOC */
|
||||
static const uint32_t sdio_cmd_pin = 2; /* GPIOD */
|
||||
static const uint32_t sdio_detect_pin = 8; /* GPIOA */
|
||||
static const uint32_t sdio_af = 12;
|
||||
static struct dev_sd SD;
|
||||
|
||||
/*
|
||||
* sdio_bus
|
||||
*
|
||||
* Set the bus width and the clock speed for the
|
||||
* SDIO bus.
|
||||
*
|
||||
* Returns 0 on success
|
||||
* -1 illegal bit specification
|
||||
* -2 illegal clock specification
|
||||
*/
|
||||
int
|
||||
stm32_sdio_bus(struct dev_sd *sd, int bits, enum SDIO_CLOCK_DIV freq) {
|
||||
int clkreg = 0;
|
||||
|
||||
switch (bits) {
|
||||
case 1:
|
||||
clkreg |= SDIO_CLKCR_WIDBUS_1;
|
||||
break;
|
||||
case 4:
|
||||
clkreg |= SDIO_CLKCR_WIDBUS_4;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
switch (freq) {
|
||||
case SDIO_24MHZ:
|
||||
break;
|
||||
case SDIO_16MHZ:
|
||||
clkreg |= 1;
|
||||
break;
|
||||
case SDIO_12MHZ:
|
||||
clkreg |= 2;
|
||||
break;
|
||||
case SDIO_8MHZ:
|
||||
clkreg |= 8;
|
||||
break;
|
||||
case SDIO_4MHZ:
|
||||
clkreg |= 10;
|
||||
break;
|
||||
case SDIO_1MHZ:
|
||||
clkreg |= 46;
|
||||
break;
|
||||
case SDIO_400KHZ:
|
||||
clkreg |= 118;
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
clkreg |= SDIO_CLKCR_CLKEN;
|
||||
SDIO_CLKCR = clkreg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the state of the SDIO bus and peripheral. This code tries
|
||||
* to reset the bus *AND* the card if one is plugged in. The bus
|
||||
* can be reset by software but the card is reset by powering it down.
|
||||
*
|
||||
* The SDIO_POWER_STATE tells the code which state to leave the bus in,
|
||||
* powered up or powered down.
|
||||
*
|
||||
* If the state is POWER_ON, then the bus is reset to 400Khz, 1 bit wide
|
||||
* which is what he spec requires. Once the type and capabilities of the
|
||||
* card have been determined, it can be upgraded.
|
||||
*/
|
||||
void
|
||||
stm32_sdio_reset(struct dev_sd *sd, enum SDIO_POWER_STATE state) {
|
||||
|
||||
/* Step 1 power off the interface */
|
||||
SDIO_POWER = SDIO_POWER_PWRCTRL_PWROFF;
|
||||
|
||||
/* reset the SDIO peripheral interface */
|
||||
APB2_CLOCK_RST |= SDIO_APB2_CLOCK_ER_VAL;
|
||||
APB2_CLOCK_RST &= ~SDIO_APB2_CLOCK_ER_VAL;
|
||||
|
||||
if (state == SDIO_POWER_ON) {
|
||||
SDIO_POWER = SDIO_POWER_PWRCTRL_PWRON;
|
||||
stm32_sdio_bus(sd, 1, SDIO_400KHZ); // required by the spec
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The error message catalog.
|
||||
*/
|
||||
static const char *__sdio_error_msgs[] = {
|
||||
"Success",
|
||||
"Command Timeout", // -1
|
||||
"Command CRC Failure", // -2
|
||||
"Soft Timeout (No Response)", // -3
|
||||
"Data CRC Failure", // -4
|
||||
"RX FIFO Overrun", // -5
|
||||
"TX FIFO Underrun", // -6
|
||||
"Unsupported Card" // -7
|
||||
};
|
||||
|
||||
#define SDIO_ESUCCESS 0
|
||||
#define SDIO_ECTIMEOUT -1
|
||||
#define SDIO_ECCRCFAIL -2
|
||||
#define SDIO_ENORESP -3
|
||||
#define SDIO_EDCRCFAIL -4
|
||||
#define SDIO_ERXOVERR -5
|
||||
#define SDIO_ETXUNDER -6
|
||||
#define SDIO_EBADCARD -7
|
||||
#define SDIO_EUNKNOWN -8
|
||||
|
||||
/*
|
||||
* Return a text string description of the error code.
|
||||
*/
|
||||
const char *
|
||||
stm32_sdio_errmsg(int err) {
|
||||
return (err <= SDIO_EUNKNOWN) ? (const char *) "Unknown Error" :
|
||||
__sdio_error_msgs[0-err];
|
||||
}
|
||||
|
||||
/*
|
||||
* stm32_sdio_bit_slice - helper function
|
||||
*
|
||||
* A number of the things the SDIO returns are in bit
|
||||
* fields. This code is designed to slice out a range
|
||||
* of bits and return them as a value (up to 32 bits
|
||||
* worth).
|
||||
*/
|
||||
uint32_t
|
||||
stm32_sdio_bit_slice(uint32_t a[], int bits, int msb, int lsb) {
|
||||
uint32_t t;
|
||||
int i;
|
||||
|
||||
if (((msb >= bits) || (msb < 0)) ||
|
||||
(lsb > msb) ||
|
||||
((lsb < 0) || (lsb >= bits))) {
|
||||
return 0;
|
||||
}
|
||||
t = 0;
|
||||
for (i = msb; i > lsb; i--) {
|
||||
t |= (a[((bits-1) - i)/32] >> (i % 32)) & 0x1;
|
||||
t <<= 1;
|
||||
}
|
||||
t |= (a[((bits-1) - lsb)/32] >> (lsb % 32)) & 0x1;
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* A convienence define. These are the flags we care about when
|
||||
* sending a command. During command processing SDIO_STA_CMDACT
|
||||
* will be set.
|
||||
*/
|
||||
#define COMMAND_FLAGS (SDIO_STA_CMDSENT |\
|
||||
SDIO_STA_CCRCFAIL |\
|
||||
SDIO_STA_CMDREND |\
|
||||
SDIO_STA_CTIMEOUT)
|
||||
|
||||
/*
|
||||
* Send a command over the SDIO bus.
|
||||
* Passed a command (8 bit value) and an argument (32 bit value)
|
||||
* This command figures out if the command will return a short (32 bit)
|
||||
* or long (64 bit) response. It is up to the calling program to pull
|
||||
* data from the long response commands.
|
||||
* Passed:
|
||||
* cmd - Command to execute
|
||||
* arg - Argument to pass to the command
|
||||
* buf - pointer to a long aligned buffer if data
|
||||
* len - expected length of buffer (in bytes)
|
||||
*/
|
||||
int
|
||||
stm32_sdio_command(struct dev_sd *sd, uint32_t cmd, uint32_t arg)
|
||||
{
|
||||
uint32_t tmp_val;
|
||||
int error = 0;
|
||||
|
||||
tmp_val = SDIO_CMD & ~0x7ff; // Read pre-existing state
|
||||
tmp_val |= (cmd & SDIO_CMD_CMDINDEX_MSK); // Put the Command in
|
||||
tmp_val |= SDIO_CMD_CPSMEN; // We'll be running CPSM
|
||||
|
||||
switch(cmd) {
|
||||
case 0:
|
||||
tmp_val |= SDIO_CMD_WAITRESP_NO_0;
|
||||
break;
|
||||
case 2:
|
||||
case 9:
|
||||
tmp_val |= SDIO_CMD_WAITRESP_LONG;
|
||||
break;
|
||||
default:
|
||||
tmp_val |= SDIO_CMD_WAITRESP_SHORT; // the common case
|
||||
break;
|
||||
}
|
||||
/* If a data transaction is in progress, wait for it to finish */
|
||||
|
||||
while ((cmd != 12) & (SDIO_STA & (SDIO_STA_RXACT | SDIO_STA_TXACT)));;
|
||||
|
||||
|
||||
/*
|
||||
* EXECUTE:
|
||||
* o Reset all status bits
|
||||
* o Put ARG into SDIO ARG
|
||||
* o reset the error indicator
|
||||
* o Enable all interrupts.
|
||||
* o Do the command
|
||||
*/
|
||||
SDIO_ICR = 0x7ff; // Reset everything that isn't bolted down.
|
||||
SDIO_ARG = arg;
|
||||
SDIO_CMD = tmp_val;
|
||||
/*
|
||||
* In a polled mode we should be able to just read the status bits
|
||||
* directly.
|
||||
*/
|
||||
tmp_val = 0;
|
||||
do {
|
||||
tmp_val |= (SDIO_STA & 0x7ff);
|
||||
} while ((SDIO_STA & SDIO_STA_CMDACT) || (! tmp_val));;
|
||||
SDIO_ICR = tmp_val;
|
||||
|
||||
/*
|
||||
* Compute the error here. Which can be one of
|
||||
* -- Success (either CMDSENT or CMDREND depending on response)
|
||||
* -- Timeout (based on CTIMEOUT)
|
||||
* -- No Response (based on no response in the time alloted)
|
||||
* -- CRC Error (based on CCRCFAIL)
|
||||
*/
|
||||
if (! tmp_val) {
|
||||
error = SDIO_ENORESP;
|
||||
} else if (tmp_val & SDIO_STA_CCRCFAIL) {
|
||||
error = SDIO_ECCRCFAIL;
|
||||
} else if (tmp_val & (SDIO_STA_CMDREND | SDIO_STA_CMDSENT)) {
|
||||
error = SDIO_ESUCCESS;
|
||||
} else if (tmp_val & SDIO_STA_CTIMEOUT) {
|
||||
error = SDIO_ECTIMEOUT;
|
||||
} else {
|
||||
error = SDIO_EUNKNOWN;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* our static data buffer we use for data movement commands */
|
||||
|
||||
/*
|
||||
* Helper function - sdio_select
|
||||
*
|
||||
* This function "selects" a card using CMD7, note that if
|
||||
* you select card 0 that deselects the card (RCA is not allowed
|
||||
* to be 0)
|
||||
*/
|
||||
static int
|
||||
sdio_select(struct dev_sd *sd, int rca) {
|
||||
int err;
|
||||
|
||||
err = stm32_sdio_command(sd, 7, rca << 16);
|
||||
if ((rca == 0) && (err == SDIO_ECTIMEOUT)) {
|
||||
return 0; // "cheat" a timeout selecting 0 is a successful deselect
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function - sdio_scr
|
||||
*
|
||||
* Unlike the CID and CSD functions this function transfers data
|
||||
* so it needs to use the DPSM.
|
||||
*
|
||||
* Note that data over the wire is byte swapped so we swap it back
|
||||
* to "fix" it.
|
||||
*
|
||||
* Note when this return 0 the first two longs in the data_buf are
|
||||
* the SCR register.
|
||||
*/
|
||||
|
||||
static uint32_t data_buf[129];
|
||||
static int
|
||||
sdio_scr(struct dev_sd *sd, SDIO_CARD c) {
|
||||
int err;
|
||||
uint32_t tmp_reg;
|
||||
int ndx;
|
||||
int data_len;
|
||||
|
||||
/* Select the card */
|
||||
err = sdio_select(sd, c->rca);
|
||||
if (! err) {
|
||||
/* Set the Block Size */
|
||||
err = stm32_sdio_command(sd, 16, 8);
|
||||
if (! err) {
|
||||
/* APPCMD (our RCA) */
|
||||
err = stm32_sdio_command(sd, 55, c->rca << 16);
|
||||
if (! err) {
|
||||
SDIO_ICR = 0xFFFFFFFF; /* Clear all status flags */
|
||||
SDIO_DTIMER = 0xffffffff;
|
||||
SDIO_DLEN = 8;
|
||||
SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 |
|
||||
SDIO_DCTRL_DTDIR |
|
||||
SDIO_DCTRL_DTEN;
|
||||
/* ACMD51 - Send SCR */
|
||||
err = stm32_sdio_command(sd, 51, 0);
|
||||
if (! err) {
|
||||
data_len = 0;
|
||||
do {
|
||||
tmp_reg = SDIO_STA;
|
||||
if (tmp_reg & SDIO_STA_RXDAVL) {
|
||||
data_buf[data_len++] = SDIO_FIFO;
|
||||
}
|
||||
} while (tmp_reg & SDIO_STA_RXACT);
|
||||
if ((tmp_reg & SDIO_STA_DBCKEND) == 0) {
|
||||
if (tmp_reg & SDIO_STA_DCRCFAIL) {
|
||||
err = SDIO_EDCRCFAIL;
|
||||
} else if (tmp_reg & SDIO_STA_RXOVERR) {
|
||||
err = SDIO_ERXOVERR;
|
||||
} else {
|
||||
err = SDIO_EUNKNOWN; // unknown error
|
||||
}
|
||||
}
|
||||
if (! err) {
|
||||
for (ndx = 0; ndx < 2; ndx++) {
|
||||
byte_swap(data_buf[ndx]);
|
||||
c->scr[ndx] = data_buf[ndx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(void) sdio_select(sd, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a Block from our Card
|
||||
*
|
||||
* NB: There is a possibly useless test in this code, during the read
|
||||
* phase it allows that the SDIO card might try to send more than BLOCK_SIZE
|
||||
* bytes (128 32 bit longs) and allows it to do so, constantly over
|
||||
* writing the last long in the just-in-case-over-long-by-1 data buffer.
|
||||
* To compromise the system you would need a borked or custom crafted
|
||||
* sdio card which did that.
|
||||
*/
|
||||
|
||||
int block_read(void *dev, void *_buf, uint32_t lba)
|
||||
{
|
||||
int err;
|
||||
uint32_t tmp_reg;
|
||||
uint32_t addr = lba;
|
||||
uint8_t *t;
|
||||
int ndx, bdx = 0;
|
||||
struct dev_sd *sd = &SD;
|
||||
uint32_t *buf = _buf;
|
||||
uint32_t buf_len = 0;
|
||||
|
||||
if (! SDIO_CARD_CCS(sd->card)) {
|
||||
addr = lba * BLOCK_SIZE; // non HC cards use byte address
|
||||
}
|
||||
err = sdio_select(sd, sd->card->rca);
|
||||
if (! err) {
|
||||
err = stm32_sdio_command(sd, 16, BLOCK_SIZE);
|
||||
if (!err) {
|
||||
SDIO_DTIMER = 0xffffffff;
|
||||
SDIO_DLEN = BLOCK_SIZE;
|
||||
SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 |
|
||||
SDIO_DCTRL_DTDIR |
|
||||
SDIO_DCTRL_DTEN;
|
||||
err = stm32_sdio_command(sd, 17, addr);
|
||||
if (! err) {
|
||||
buf_len = 0;
|
||||
do {
|
||||
tmp_reg = SDIO_STA;
|
||||
if (tmp_reg & SDIO_STA_RXDAVL) {
|
||||
buf[buf_len] = SDIO_FIFO;
|
||||
if (buf_len < 128) {
|
||||
++buf_len;
|
||||
}
|
||||
}
|
||||
} while (tmp_reg & SDIO_STA_RXACT);
|
||||
if ((tmp_reg & SDIO_STA_DBCKEND) == 0) {
|
||||
if (tmp_reg & SDIO_STA_DCRCFAIL) {
|
||||
err = SDIO_EDCRCFAIL;
|
||||
} else if (tmp_reg & SDIO_STA_RXOVERR) {
|
||||
err = SDIO_ERXOVERR;
|
||||
} else {
|
||||
err = SDIO_EUNKNOWN; // Unknown Error!
|
||||
}
|
||||
} else {
|
||||
err = BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// deselect the card
|
||||
(void) sdio_select(sd, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a Block from our Card
|
||||
*/
|
||||
int block_write(void *dev, void *_buf, uint32_t lba)
|
||||
{
|
||||
int err;
|
||||
uint32_t tmp_reg;
|
||||
uint32_t addr = lba;
|
||||
uint8_t *t;
|
||||
int ndx;
|
||||
struct dev_sd *sd = &SD;
|
||||
SDIO_CARD c;
|
||||
uint32_t *buf = _buf;
|
||||
uint32_t buf_len = 0;
|
||||
|
||||
c = SD.card;
|
||||
|
||||
|
||||
if (! SDIO_CARD_CCS(c)) {
|
||||
addr = lba * BLOCK_SIZE; // non HC cards use byte address
|
||||
}
|
||||
|
||||
err = sdio_select(sd, c->rca);
|
||||
if (! err) {
|
||||
/* Set Block Size to BLOCK_SIZE */
|
||||
err = stm32_sdio_command(sd, 16, BLOCK_SIZE);
|
||||
if (!err) {
|
||||
SDIO_DTIMER = 0xffffffff;
|
||||
SDIO_DLEN = BLOCK_SIZE;
|
||||
SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_9 |
|
||||
SDIO_DCTRL_DTEN;
|
||||
err = stm32_sdio_command(sd, 24, addr);
|
||||
if (! err) {
|
||||
do {
|
||||
tmp_reg = SDIO_STA;
|
||||
if (tmp_reg & SDIO_STA_TXFIFOHE) {
|
||||
SDIO_FIFO = buf[buf_len];
|
||||
if (buf_len < 128) {
|
||||
++buf_len;
|
||||
}
|
||||
}
|
||||
} while (tmp_reg & SDIO_STA_TXACT);
|
||||
if ((tmp_reg & SDIO_STA_DBCKEND) == 0) {
|
||||
if (tmp_reg & SDIO_STA_DCRCFAIL) {
|
||||
err = SDIO_EDCRCFAIL;
|
||||
} else if (tmp_reg & SDIO_STA_TXUNDERR) {
|
||||
err = SDIO_ETXUNDER;
|
||||
} else {
|
||||
err = SDIO_EUNKNOWN; // Unknown Error!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// deselect the card
|
||||
(void) sdio_select(sd, 0);
|
||||
if (err == 0)
|
||||
return BLOCK_SIZE;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* sdio-status - Get Card Status page
|
||||
*
|
||||
* This function fetches the SD Card Status page and
|
||||
* copies it into the CARD structure.
|
||||
*/
|
||||
/*
|
||||
int
|
||||
sdio_status(SDIO_CARD c) {
|
||||
uint32_t tmp_reg;
|
||||
int ndx;
|
||||
int err;
|
||||
|
||||
err = sdio_select(c->rca);
|
||||
if (! err) {
|
||||
err = stm32_sdio_command(16, 64);
|
||||
if (! err) {
|
||||
err = stm32_sdio_command(55, c->rca << 16);
|
||||
if (! err) {
|
||||
SDIO_DTIMER = 0xffffffff;
|
||||
SDIO_DLEN = 64;
|
||||
SDIO_DCTRL = SDIO_DCTRL_DBLOCKSIZE_6 |
|
||||
SDIO_DCTRL_DTDIR |
|
||||
SDIO_DCTRL_DTEN; */
|
||||
/* ACMD13 - Send Status Reg */ /*
|
||||
err = stm32_sdio_command(13, 0);
|
||||
if (! err) {
|
||||
data_len = 0;
|
||||
do {
|
||||
tmp_reg = SDIO_STA;
|
||||
if (tmp_reg & SDIO_STA_RXDAVL) {
|
||||
data_buf[data_len] = SDIO_FIFO;
|
||||
if (data_len < 128) {
|
||||
++data_len;
|
||||
}
|
||||
}
|
||||
} while (tmp_reg & SDIO_STA_RXACT);
|
||||
if ((tmp_reg & SDIO_STA_DBCKEND) == 0) {
|
||||
if (tmp_reg & SDIO_STA_DCRCFAIL) {
|
||||
err = SDIO_EDCRCFAIL;
|
||||
} else if (tmp_reg & SDIO_STA_RXOVERR) {
|
||||
err = SDIO_ERXOVERR;
|
||||
} else {
|
||||
err = SDIO_EUNKNOWN; // Unknown Error!
|
||||
}
|
||||
} else {
|
||||
for (ndx = 0; ndx < 16; ndx++) {
|
||||
byte_swap(data_buf[ndx]);
|
||||
c->status[ndx] = data_buf[ndx];
|
||||
}
|
||||
}
|
||||
(void) sdio_select(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
*/
|
||||
|
||||
static struct SDIO_CARD_DATA sdio_card_data;
|
||||
#define MAX_RETRIES 5
|
||||
|
||||
/*
|
||||
* stm32_sdio_open - Prepare to use SDIO card
|
||||
*
|
||||
* This function resets the SDIO bus and identifies the
|
||||
* card (if any) that is plugged in. If there is no card
|
||||
* present, or an error in figuring out what the card is
|
||||
* (for example its an old MMC card) the function returns
|
||||
* NULL. If it fails and you have logging enabled you can
|
||||
* look at the last few commands sent.
|
||||
*/
|
||||
SDIO_CARD
|
||||
stm32_sdio_open(struct dev_sd *sd) {
|
||||
int err;
|
||||
int i;
|
||||
uint8_t *t;
|
||||
uint32_t tmp_reg;
|
||||
SDIO_CARD res = &sdio_card_data;
|
||||
|
||||
// basically bset(0, sdio_card_data)
|
||||
t = (uint8_t *) &sdio_card_data;
|
||||
for (i = 0; i < (int) sizeof(sdio_card_data); i++) {
|
||||
*t++ = 0;
|
||||
}
|
||||
stm32_sdio_reset(sd, SDIO_POWER_ON);
|
||||
err = stm32_sdio_command(sd, 0, 0);
|
||||
if (!err) {
|
||||
err = stm32_sdio_command(sd, 8, 0x1aa);
|
||||
if (!err) {
|
||||
// Woot! We support CMD8 so we're a v2 card at least */
|
||||
tmp_reg = SDIO_RESP1;
|
||||
sdio_card_data.props = 1;
|
||||
i = 0;
|
||||
err = stm32_sdio_command(sd, 5, 0);
|
||||
if (! err) {
|
||||
// It is an SDIO card which is unsupported!
|
||||
err = SDIO_EBADCARD;
|
||||
return NULL;
|
||||
}
|
||||
do {
|
||||
err = stm32_sdio_command(sd, 55, 0); // broadcast ACMD
|
||||
if (err) {
|
||||
break; // try again
|
||||
}
|
||||
// Testing Card Busy, Voltage match, and capacity
|
||||
err = stm32_sdio_command(sd, 41, 0xc0100000);
|
||||
if (err != -2) { // Expect CCRCFAIL here
|
||||
break; // try again
|
||||
}
|
||||
tmp_reg = SDIO_RESP1; // what did the card send?
|
||||
if ((tmp_reg & 0x80000000) == 0) {
|
||||
continue; // still powering up
|
||||
}
|
||||
res->ocr = tmp_reg; // Ok OCR is valid
|
||||
break;
|
||||
} while (++i < MAX_RETRIES);
|
||||
if (res->ocr) {
|
||||
err = stm32_sdio_command(sd, 2, 0);
|
||||
if (! err) {
|
||||
res->cid[0] = SDIO_RESP1;
|
||||
res->cid[1] = SDIO_RESP2;
|
||||
res->cid[2] = SDIO_RESP3;
|
||||
res->cid[3] = SDIO_RESP4;
|
||||
err = stm32_sdio_command(sd, 3, 0); // get the RCA
|
||||
if (! err) {
|
||||
tmp_reg = SDIO_RESP1;
|
||||
res->rca = (tmp_reg >> 16) & 0xffff;
|
||||
if (! res->rca) {
|
||||
/*
|
||||
* If the card says '0' tell it to pick
|
||||
* we assume this will work because the
|
||||
* previous send RCA worked and the card
|
||||
* should be in the ident state if it is
|
||||
* functioning correctly.
|
||||
*/
|
||||
(void) stm32_sdio_command(sd, 3, 0); // try again
|
||||
tmp_reg = SDIO_RESP1;
|
||||
res->rca = (tmp_reg >> 16) & 0xffff;
|
||||
}
|
||||
err = stm32_sdio_command(sd, 9, res->rca << 16);
|
||||
if (! err) {
|
||||
res->csd[0] = SDIO_RESP1;
|
||||
res->csd[1] = SDIO_RESP2;
|
||||
res->csd[2] = SDIO_RESP3;
|
||||
res->csd[3] = SDIO_RESP4;
|
||||
err = sdio_scr(sd, res); // Capture the SCR
|
||||
if (! err) {
|
||||
/* All SD Cards support 4 bit bus and 24Mhz */
|
||||
err = sdio_select(sd, res->rca);
|
||||
if (! err) {
|
||||
err = stm32_sdio_command(sd, 55, res->rca << 16);
|
||||
if (! err) {
|
||||
err = stm32_sdio_command(sd, 6, 2);
|
||||
if (! err) {
|
||||
//XXX stm32_sdio_bus(4, SDIO_24MHZ);
|
||||
//Seems we have speed issues for now...
|
||||
stm32_sdio_bus(sd, 4, SDIO_12MHZ);
|
||||
(void) sdio_select(sd, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Compute the size of the card based on fields in the CSD
|
||||
* block. There are two kinds, V1 or V2.
|
||||
* In the V1 Case :
|
||||
* Size = 1<<BLOCK_LEN * 1<<(MULT+2) * (C_SIZE+1) bytes.
|
||||
* In the V2 Case :
|
||||
* Size = (C_SIZE + 1) * BLOCK_SIZEK bytes.
|
||||
* But for our structure we want the size in BLOCK_SIZE byte "blocks"
|
||||
* since that is the addressing unit we're going to export so
|
||||
* we compute the size / BLOCK_SIZE as the "size" for the structure.
|
||||
*/
|
||||
|
||||
if (! err) {
|
||||
res->size = 0;
|
||||
switch (SDIO_CSD_VERSION(res)) {
|
||||
case 0:
|
||||
tmp_reg = ((1 << (SDIO_CSD1_CSIZE_MULT(res) + 2)) *
|
||||
( 1 << SDIO_CSD1_RBLKLEN(res))) >> 9;
|
||||
res->size = tmp_reg * (SDIO_CSD1_CSIZE(res) + 1);
|
||||
break;
|
||||
case 1:
|
||||
res->size = (SDIO_CSD2_CSIZE(res)+1) << 10;
|
||||
break;
|
||||
default:
|
||||
res->size = 0; // Bug if its not CSD V1 or V2
|
||||
}
|
||||
}
|
||||
|
||||
return (err == 0) ? res : NULL;
|
||||
}
|
||||
|
||||
static int stm32_sdio_card_detect(void)
|
||||
{
|
||||
uint8_t block[BLOCK_SIZE];
|
||||
SDIO_CARD sdcard;
|
||||
if (SD.card != NULL)
|
||||
return 0;
|
||||
sdcard = stm32_sdio_open(&SD);
|
||||
if (sdcard != NULL) {
|
||||
SD.card = sdcard;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the GPIO pins and peripheral clocks for the SDIO
|
||||
* system. The code should probably take an option card detect
|
||||
* pin, at the moment it uses the one used by the Embest board.
|
||||
*/
|
||||
static void sdio_hw_init(void)
|
||||
{
|
||||
int i;
|
||||
uint32_t reg;
|
||||
/* Enable clocks for SDIO and DMA2 */
|
||||
APB2_CLOCK_ER |= SDIO_APB2_CLOCK_ER_VAL;
|
||||
/* Enable clocks for GPIOA, GPIOC, GPIOD */
|
||||
AHB1_CLOCK_ER |= GPIOA_AHB1_CLOCK_ER |
|
||||
GPIOC_AHB1_CLOCK_ER | GPIOD_AHB1_CLOCK_ER;
|
||||
for (i = 0 ; i < 5; i++) {
|
||||
/* mode = AF */
|
||||
reg = GPIOC_MODE & ~ (0x03 << (sdio_pins[i] * 2));
|
||||
GPIOC_MODE = reg | (2 << (sdio_pins[i] * 2));
|
||||
|
||||
#if 0
|
||||
/* Set 100MHz ospeed + pullup */
|
||||
GPIOC_OSPEED |= (0x03 << (sdio_pins[i] * 2));
|
||||
GPIOC_OTYPE &= ~(0x01 << (sdio_pins[i]));
|
||||
reg = GPIOC_PUPD & (~(0x03 << (sdio_pins[i] & 2)));
|
||||
GPIOC_PUPD = reg | (1 << (sdio_pins[i] * 2));
|
||||
#endif
|
||||
|
||||
/* Alternate function: use high pins (8..12) */
|
||||
reg = GPIOC_AFH & ~(0xf << ((sdio_pins[i] - 8) * 4));
|
||||
GPIOC_AFH = reg | (sdio_af << ((sdio_pins[i] - 8) * 4));
|
||||
}
|
||||
/* cmd pin */
|
||||
reg = GPIOD_MODE & ~ (0x03 << (sdio_cmd_pin * 2));
|
||||
GPIOD_MODE = reg | (2 << (sdio_cmd_pin * 2));
|
||||
#if 0
|
||||
/* Set 100MHz ospeed + pullup */
|
||||
GPIOD_OSPEED |= (0x03 << (sdio_cmd_pin * 2));
|
||||
GPIOD_OTYPE &= ~(0x01 << (sdio_cmd_pin));
|
||||
reg = GPIOD_PUPD & (~(0x03 << (sdio_cmd_pin & 2)));
|
||||
GPIOD_PUPD = reg | (1 << (sdio_cmd_pin * 2));
|
||||
#endif
|
||||
/* Alternate function: use low pin */
|
||||
reg = GPIOD_AFL & ~(0xf << ((sdio_cmd_pin) * 4));
|
||||
GPIOD_AFL = reg | (sdio_af << ((sdio_cmd_pin) * 4));
|
||||
|
||||
/* card detect pin configured as input */
|
||||
GPIOA_MODE &= ~ (0x03 << (sdio_detect_pin * 2));
|
||||
reg = GPIOA_PUPD & (~(0x03 << (sdio_detect_pin & 2)));
|
||||
GPIOA_PUPD = reg | (1 << (sdio_detect_pin * 2));
|
||||
}
|
||||
|
||||
void *block_open(void)
|
||||
{
|
||||
int ret;
|
||||
/* Initialize sdio RCC and pins */
|
||||
sdio_hw_init();
|
||||
do {
|
||||
ret = stm32_sdio_card_detect();
|
||||
} while (ret < 0);
|
||||
return &SD;
|
||||
}
|
||||
|
||||
|
54
src/usecfs_dev_sdio.h
Normal file
54
src/usecfs_dev_sdio.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/* this define lets the init code know that you are using a GPIO as a card
|
||||
* detect pin */
|
||||
#define SDIO_HAS_CARD_DETECT
|
||||
|
||||
enum SDIO_CLOCK_DIV {
|
||||
SDIO_24MHZ = 0,
|
||||
SDIO_16MHZ,
|
||||
SDIO_12MHZ,
|
||||
SDIO_8MHZ,
|
||||
SDIO_4MHZ,
|
||||
SDIO_1MHZ,
|
||||
SDIO_400KHZ
|
||||
};
|
||||
|
||||
enum SDIO_POWER_STATE {
|
||||
SDIO_POWER_ON,
|
||||
SDIO_POWER_OFF
|
||||
};
|
||||
|
||||
#define SDIO_CARD_CCS(c) (((c)->ocr & 0x40000000) != 0)
|
||||
#define SDIO_CARD_UHS2(c) (((c)->ocr & 0x40000000) != 0)
|
||||
#define SDIO_CARD_LVOK(c) (((c)->ocr & 0x01000000) != 0)
|
||||
|
||||
typedef struct SDIO_CARD_DATA {
|
||||
uint32_t props;
|
||||
uint32_t ocr;
|
||||
uint32_t cid[4];
|
||||
uint32_t csd[4];
|
||||
uint32_t scr[2];
|
||||
uint32_t status[16];
|
||||
uint32_t size;
|
||||
uint16_t rca;
|
||||
} * SDIO_CARD;
|
||||
|
||||
struct dev_sd {
|
||||
struct device *dev;
|
||||
uint32_t base;
|
||||
uint32_t *rcc_rst_reg;
|
||||
uint32_t rcc_rst;
|
||||
SDIO_CARD card;
|
||||
};
|
||||
|
||||
/* API for sd_bus clock setting */
|
||||
enum SD_CLOCK_DIV {
|
||||
CLOCK_24MHZ = 0,
|
||||
CLOCK_16MHZ,
|
||||
CLOCK_12MHZ,
|
||||
CLOCK_8MHZ,
|
||||
CLOCK_4MHZ,
|
||||
CLOCK_1MHZ,
|
||||
CLOCK_400KHZ
|
||||
};
|
||||
|
||||
//int stm32_sd_bus(int bits, enum SD_CLOCK_DIV freq);
|
36
src/usecfs_dev_spi.c
Normal file
36
src/usecfs_dev_spi.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <stdint.h>
|
||||
#include "usecfs_dev.h"
|
||||
#include "spi_flash.h"
|
||||
|
||||
static uint32_t part_base = 0xFFFFFFFF;
|
||||
static uint32_t part_size = 0;
|
||||
|
||||
void *block_open(void *args_ptr)
|
||||
{
|
||||
uint32_t *args = (uint32_t *)args_ptr;
|
||||
part_base = args[0];
|
||||
part_size = args[1];
|
||||
return &part_base;
|
||||
}
|
||||
|
||||
int block_read(void *dev, void *_buf, uint32_t lba)
|
||||
{
|
||||
if (part_size == 0)
|
||||
return -1;
|
||||
return spi_flash_read(part_base + (lba * BLOCK_SIZE), _buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
int block_write(void *dev, const void *_buf, uint32_t lba)
|
||||
{
|
||||
if (part_size == 0)
|
||||
return -1;
|
||||
spi_flash_sector_erase(part_base + lba * BLOCK_SIZE);
|
||||
return spi_flash_write(part_base + lba * BLOCK_SIZE, _buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
void block_close(void *dev)
|
||||
{
|
||||
(void)dev;
|
||||
part_base = 0xFFFFFFFF;
|
||||
part_size = 0;
|
||||
}
|
|
@ -12,28 +12,34 @@ void *block_open(void *args)
|
|||
char *file = (char *)args;
|
||||
int i;
|
||||
uint8_t ff = 0xFF;
|
||||
fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0660);
|
||||
fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0660);
|
||||
if (fd >= 0) {
|
||||
for (i = 0; i < FS_SIZE; i++)
|
||||
write(fd, &ff, 1);
|
||||
} else {
|
||||
fd = open(file, O_RDWR);
|
||||
}
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
for (i = 0; i < FS_SIZE; i++)
|
||||
write(fd, &ff, 1);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
return &fd;
|
||||
}
|
||||
|
||||
int block_read(void *dev, void *_buf, uint32_t lba, int offset, int count)
|
||||
int block_read(void *dev, void *_buf, uint32_t lba)
|
||||
{
|
||||
(void)dev;
|
||||
lseek(fd, lba * BLOCK_SIZE + offset, SEEK_SET);
|
||||
read(fd, _buf, count);
|
||||
lseek(fd, lba * BLOCK_SIZE, SEEK_SET);
|
||||
return read(fd, _buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
int block_write(void *dev, const void *_buf, uint32_t lba, int offset, int count)
|
||||
int block_write(void *dev, const void *_buf, uint32_t lba)
|
||||
{
|
||||
(void)dev;
|
||||
lseek(fd, lba * BLOCK_SIZE + offset, SEEK_SET);
|
||||
write(fd, _buf, count);
|
||||
lseek(fd, lba * BLOCK_SIZE, SEEK_SET);
|
||||
return write(fd, _buf, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
void block_close(void *dev)
|
||||
{
|
||||
(void)dev;
|
||||
|
|
|
@ -6,4 +6,4 @@ test: main.o ../src/usecfs.o ../src/usecfs_dev_test.o
|
|||
gcc -o $@ $^ -lwolfssl
|
||||
|
||||
clean:
|
||||
rm main.o ../src/usecfs.o ../src/usecfs_dev_test.o test
|
||||
@rm -f test main.o ../src/*.o
|
||||
|
|
66
test/main.c
66
test/main.c
|
@ -1,64 +1,102 @@
|
|||
#include "usecfs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
const uint8_t test_uuid[UUID_LEN] = {
|
||||
0xb3, 0x6b, 0x82, 0x05, 0xb6, 0xcd, 0x28, 0xaa, 0xc7, 0x81, 0x2e, 0x2d,
|
||||
0xfd, 0xdd, 0x5e, 0x70, 0x3f, 0xbf, 0x09, 0x03, 0x1b, 0x5f, 0xbe, 0xc6,
|
||||
0x09, 0x62, 0xa8, 0x23, 0xe9, 0x99, 0x5e, 0xcb, 0xb4, 0xab, 0x28, 0xdc,
|
||||
0x14, 0xca, 0x35, 0xb4, 0x7d, 0xfe, 0x26, 0x81, 0x33, 0xd0, 0x4b, 0xc2,
|
||||
0x49, 0x53, 0x05, 0xc3, 0xe7, 0xbd, 0x9a, 0x50, 0xb8, 0x01, 0x30, 0x0b,
|
||||
0x62, 0x58, 0xad, 0xbf
|
||||
};
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fd;
|
||||
int buf[40] = { };
|
||||
int uuid[UUID_LEN];
|
||||
|
||||
if (usecfs_init("sEcret") < 0)
|
||||
if (usecfs_init("sEcret", 0, NULL) < 0)
|
||||
{
|
||||
printf("error.\n");
|
||||
return 1;
|
||||
printf("Filesystem is not formatted. Formatting.\n");
|
||||
if (usecfs_init("sEcret", 1, test_uuid) != 0) {
|
||||
printf("Format failed.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Creating file 'file1' \n");
|
||||
fd = usecfs_creat("file1");
|
||||
if (fd < 0) {
|
||||
printf("error: creat.\n");
|
||||
return 1;
|
||||
printf("could not create file (this is OK unless open fails)\n");
|
||||
fd = usecfs_open("file1");
|
||||
if (fd < 0) {
|
||||
printf("error: open\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (usecfs_write(fd, "test string file 1 content", 26) < 0) {
|
||||
printf("Writing to file 'file1' \n");
|
||||
if (usecfs_write(fd, "test string file 1 content\n", 27) < 0) {
|
||||
printf("error: write.\n");
|
||||
return 1;
|
||||
}
|
||||
usecfs_close(fd);
|
||||
|
||||
printf("Re-opening\n");
|
||||
fd = usecfs_open("file1");
|
||||
if (fd < 0) {
|
||||
printf("error: open\n");
|
||||
return 1;
|
||||
}
|
||||
if (usecfs_read(fd, buf, 26) != 26) {
|
||||
printf("Reading file...\n");
|
||||
if (usecfs_read(fd, buf, 27) != 27) {
|
||||
printf("error: read.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("%s", buf);
|
||||
if (usecfs_write(fd, "test string2", 12) < 0) {
|
||||
printf("content of file 'file1': ");
|
||||
printf("%s\n", buf);
|
||||
|
||||
printf("appending 'test string2'\n");
|
||||
if (usecfs_write(fd, "test string2\n", 13) < 0) {
|
||||
printf("error: write.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("seek to 0\n");
|
||||
if (usecfs_seek(fd, 0, 0) != 0) {
|
||||
printf("error: seek.\n");
|
||||
return 1;
|
||||
}
|
||||
if (usecfs_read(fd, buf, 36) != 36) {
|
||||
printf("Reading...\n");
|
||||
if (usecfs_read(fd, buf, 40) < 0) {
|
||||
printf("error: read.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("%s", buf);
|
||||
usecfs_close(fd);
|
||||
printf("Creating file 'file2' \n");
|
||||
fd = usecfs_creat("file2");
|
||||
if (fd < 0) {
|
||||
printf("error: creat2.\n");
|
||||
return 1;
|
||||
printf("could not create file (this is OK unless open fails)\n");
|
||||
fd = usecfs_open("file2");
|
||||
if (fd < 0) {
|
||||
printf("error: open\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
usecfs_close(fd);
|
||||
printf("Creating file 'file3' \n");
|
||||
fd = usecfs_creat("file3");
|
||||
if (fd < 0) {
|
||||
printf("error: creat2.\n");
|
||||
return 1;
|
||||
if (fd < 0) {
|
||||
printf("could not create file (this is OK unless open fails)\n");
|
||||
fd = usecfs_open("file3");
|
||||
if (fd < 0) {
|
||||
printf("error: open\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
usecfs_close(fd);
|
||||
|
||||
|
|
Loading…
Reference in a new issue