crashtest-r0ket/firmware/filesystem/at45db041d.c

249 lines
6.7 KiB
C

#include "projectconfig.h"
#include "diskio.h"
#include "iobase.h"
#include "core/ssp/ssp.h"
#include "basic/basic.h"
/* Opcodes */
#define OP_POWERDOWN (0xB9)
#define OP_RESUME (0xAB)
#define OP_PAGEREAD (0xD2)
#define OP_BUFFER1READ (0xD1) /* Low Frequency (<=33MHz) */
#define OP_BUFFER2READ (0xD3) /* Low Frequency (<=33MHz) */
#define OP_BUFFER1WRITE (0x84)
#define OP_BUFFER2WRITE (0x87)
#define OP_BUFFER1PROG (0x83) /* with builtin erase */
#define OP_BUFFER2PROG (0x86) /* with builtin erase */
#define OP_STATUSREAD (0xD7)
#define OP_DEVICEID (0x9F)
#define OP_PAGE2BUFFER1 (0x53)
#define OP_PAGE2BUFFER2 (0x55)
#define OP_BUFFER1PAGECMP (0x60)
#define OP_BUFFER2PAGECMP (0x61)
#define OP_AUTOREWRITE1 (0x58) /* Auto Page Rewrite throught Buffer 1 */
#define OP_AUTOREWRITE2 (0x59) /* Auto Page Rewrite throught Buffer 2 */
#define SB_READY (1 << 7)
#define SB_COMP (1 << 6)
#define SB_PROTECT (1 << 1)
#define SB_PAGESIZE (1 << 0)
#define MAX_PAGE (2048)
#define CS_LOW() gpioSetValue(RB_SPI_CS_DF, 0)
#define CS_HIGH() gpioSetValue(RB_SPI_CS_DF, 1)
static volatile DSTATUS status = STA_NOINIT;
static void wait_for_ready() {
BYTE reg_status = 0xFF;
CS_LOW();
xmit_spi(OP_STATUSREAD);
do {
rcvr_spi_m((uint8_t *) &reg_status);
} while (!(reg_status & SB_READY));
CS_HIGH();
}
static void dataflash_powerdown() {
CS_LOW();
xmit_spi(OP_POWERDOWN);
CS_HIGH();
}
static void dataflash_resume() {
CS_LOW();
xmit_spi(OP_RESUME);
CS_HIGH();
}
DSTATUS dataflash_initialize() {
sspInit(0, sspClockPolarity_Low, sspClockPhase_RisingEdge);
gpioSetDir(RB_SPI_CS_DF, gpioDirection_Output);
dataflash_resume();
status &= ~STA_NOINIT;
return status;
}
DSTATUS dataflash_status() {
return status;
}
DRESULT dataflash_random_read(BYTE *buff, DWORD offset, DWORD length) {
if (!length) return RES_PARERR;
if (status & STA_NOINIT) return RES_NOTRDY;
if (offset+length > MAX_PAGE*256) return RES_PARERR;
do {
wait_for_ready();
DWORD pageaddr = ((offset/256) << 9) | (offset%256);
DWORD remaining = 256 - offset%256;
if (remaining > length) {
remaining = length;
}
length -= remaining;
offset += remaining;
CS_LOW();
xmit_spi(OP_PAGEREAD);
xmit_spi((BYTE)(pageaddr >> 16));
xmit_spi((BYTE)(pageaddr >> 8));
xmit_spi((BYTE)pageaddr);
xmit_spi(0x00); // follow up with 4 don't care bytes
xmit_spi(0x00);
xmit_spi(0x00);
xmit_spi(0x00);
do {
rcvr_spi_m(buff++);
} while (--remaining);
CS_HIGH();
} while (length);
return length ? RES_ERROR : RES_OK;
}
DRESULT dataflash_read(BYTE *buff, DWORD sector, BYTE count) {
return dataflash_random_read(buff, sector*512, count*512);
}
#if _READONLY == 0
DRESULT dataflash_random_write(const BYTE *buff, DWORD offset, DWORD length) {
if (!length) return RES_PARERR;
if (status & STA_NOINIT) return RES_NOTRDY;
if (offset+length > MAX_PAGE*256) return RES_PARERR;
do {
wait_for_ready();
DWORD pageaddr = (offset/256) << 9;
DWORD buffaddr = (offset%256);
DWORD remaining = 256 - offset%256;
if (remaining > length) {
remaining = length;
}
length -= remaining;
offset += remaining;
// read page into the internal buffer
CS_LOW();
xmit_spi(OP_PAGE2BUFFER1);
xmit_spi((BYTE)(pageaddr >> 16));
xmit_spi((BYTE)(pageaddr >> 8));
xmit_spi((BYTE)pageaddr);
CS_HIGH();
wait_for_ready();
// write bytes into the dataflash buffer
CS_LOW();
xmit_spi(OP_BUFFER1WRITE);
xmit_spi((BYTE)(buffaddr >> 16));
xmit_spi((BYTE)(buffaddr >> 8));
xmit_spi((BYTE)buffaddr);
do {
xmit_spi(*buff++);
} while (--remaining);
CS_HIGH();
wait_for_ready();
// compare buffer with target memory page
CS_LOW();
xmit_spi(OP_BUFFER1PAGECMP);
xmit_spi((BYTE)(pageaddr >> 16));
xmit_spi((BYTE)(pageaddr >> 8));
xmit_spi((BYTE)pageaddr);
CS_HIGH();
wait_for_ready();
CS_LOW();
BYTE reg_status = 0xFF;
xmit_spi(OP_STATUSREAD);
rcvr_spi_m((uint8_t *) &reg_status);
CS_HIGH();
// trigger program only if data changed
if (reg_status & SB_COMP) {
CS_LOW();
xmit_spi(OP_BUFFER1PROG);
xmit_spi((BYTE)(pageaddr >> 16));
xmit_spi((BYTE)(pageaddr >> 8));
xmit_spi((BYTE)pageaddr);
CS_HIGH();
}
} while (length);
return length ? RES_ERROR : RES_OK;
}
DRESULT dataflash_write(const BYTE *buff, DWORD sector, BYTE count) {
return dataflash_random_write(buff, sector*512, count*512);
}
#endif /* _READONLY */
#if _USE_IOCTL != 0
DRESULT dataflash_ioctl(BYTE ctrl, void *buff) {
DRESULT res;
BYTE *ptr = buff;
res = RES_ERROR;
if (ctrl == CTRL_POWER) {
switch (*ptr) {
case 0: /* Sub control code == 0 (POWER_OFF) */
dataflash_powerdown();
res = RES_OK;
break;
case 1: /* Sub control code == 1 (POWER_ON) */
dataflash_resume();
res = RES_OK;
break;
case 2: /* Sub control code == 2 (POWER_GET) */
// TODO: figure out a way to retrieve the powerstate
*(ptr+1) = (BYTE)1;
res = RES_OK;
break;
default :
res = RES_PARERR;
}
} else {
if (status & STA_NOINIT) return RES_NOTRDY;
switch (ctrl) {
case CTRL_SYNC:
wait_for_ready();
res = RES_OK;
break;
case GET_SECTOR_COUNT:
// TODO: read from device ID register
*(WORD*)buff = MAX_PAGE/2;
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 1;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
}
return res;
}
#endif /* _USE_IOCTL != 0 */
DWORD get_fattime () {
struct tm* tm=mygmtime(getSeconds());
DWORD t= (((tm->tm_year+YEAR0-1980)<<9)|
((tm->tm_mon+1)<<5)|
(tm->tm_mday))<<16 |
((tm->tm_hour<<11)|
(tm->tm_min<<5)|
(tm->tm_sec>>1));
return t;
}