X-Git-Url: http://git.rohieb.name/hackover2013-badge-firmware.git/blobdiff_plain/bedee2e6174d9f54d07c1b6a11dfd4df57474037..521f943b75fbb221bfb4749124c66e799662eb45:/dataflash/at25df041a.c diff --git a/dataflash/at25df041a.c b/dataflash/at25df041a.c new file mode 100644 index 0000000..04ef300 --- /dev/null +++ b/dataflash/at25df041a.c @@ -0,0 +1,239 @@ +#include "projectconfig.h" +#include "diskio.h" +#include "iobase.h" +#include "core/ssp/ssp.h" + +#include + +/* Opcodes */ +enum { + OP_READARRAY = 0x0b, + OP_READARRAY_SLOW = 0x03, + + OP_ERASE_BLOCK_4K = 0x20, + OP_ERASE_BLOCK_32K = 0x52, + OP_ERASE_BLOCK_64k = 0xd8, + OP_ERASE_CHIP = 0x60, + OP_ERASE_CHIP2 = 0xc7, + + OP_PROGRAM_PAGE = 0x02, + OP_PROGRAM_SEQ = 0xad, + OP_PROGRAM_SEQ2 = 0xaf, + + OP_WRITE_ENABLE = 0x06, + OP_WRITE_DISABLE = 0x04, + + OP_SECTOR_PROTEXT = 0x36, + OP_SECTOR_UNPROTECT = 0x39, + OP_SECTOR_STATUS = 0x3c, + + OP_STATUS_READ = 0x05, + OP_STATUS_WRITE = 0x01, + + OP_DEVICEID_READ = 0x9f, + + OP_POWERDOWN = 0xb9, + OP_RESUME = 0xab, +}; + +enum { + STATUS_SPRL = 1 << 7, // sector protection registers locked + STATUS_SPM = 1 << 6, // sequential program mode + STATUS_EPE = 1 << 5, // erase/program error + STATUS_WPP = 1 << 4, // write protect pin + STATUS_SWP = 3 << 2, // software protection + STATUS_WEL = 1 << 1, // write enable latch + STATUS_BSY = 1 << 0, // ready/busy + + STATUS_SWP_NONE = 0, // no sectors are software-protected + STATUS_SWP_SOME = 1 << 2, // some sectors are software-protected + STATUS_SWP_ALL = 3 << 2 // all sectors are software-protected +}; + +enum { + PAGE_MAX = 2048, + PAGE_SIZE = 256, + + SECTOR_SIZE_FATFS = 512 +}; + +#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_STATUS_READ); + do { + rcvr_spi_m((uint8_t *) ®_status); + } while (reg_status & STATUS_BSY); + 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 > PAGE_MAX * PAGE_SIZE) return RES_PARERR; + + wait_for_ready(); + CS_LOW(); + xmit_spi(OP_READARRAY); + xmit_spi((BYTE)(offset >> 16)); + xmit_spi((BYTE)(offset >> 8)); + xmit_spi((BYTE)offset); + xmit_spi(0x00); // follow up with don't care byte + + do { + rcvr_spi_m(buff++); + } while (--length); + CS_HIGH(); + + return RES_OK; +} + +DRESULT dataflash_read(BYTE *buff, DWORD sector, BYTE count) { + return dataflash_random_read(buff, sector * SECTOR_SIZE_FATFS, count * SECTOR_SIZE_FATFS); +} + +#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 > PAGE_MAX * PAGE_SIZE) return RES_PARERR; + + do { + wait_for_ready(); + + DWORD pageaddr = (offset / PAGE_SIZE) * PAGE_SIZE; + DWORD remaining = PAGE_SIZE - offset % PAGE_SIZE; + + if (remaining > length) { + remaining = length; + } + + length -= remaining; + offset += remaining; + + CS_LOW(); + xmit_spi(OP_WRITE_ENABLE); + CS_HIGH(); + + // read page into the internal buffer + CS_LOW(); + xmit_spi(OP_PROGRAM_PAGE); + xmit_spi((BYTE)(pageaddr >> 16)); + xmit_spi((BYTE)(pageaddr >> 8)); + xmit_spi((BYTE) pageaddr ); + + do { + xmit_spi(*buff++); + } while (--remaining); + + CS_HIGH(); + } while (length); + + return RES_OK; +} + +DRESULT dataflash_write(const BYTE *buff, DWORD sector, BYTE count) { + return dataflash_random_write(buff, sector * SECTOR_SIZE_FATFS, count * SECTOR_SIZE_FATFS); +} +#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 = PAGE_MAX * PAGE_SIZE / SECTOR_SIZE_FATFS; + res = RES_OK; + break; + case GET_SECTOR_SIZE: + *(WORD*)buff = SECTOR_SIZE_FATFS; + 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 () { + return 0; + /* + struct tm* tm=mygmtime(getSeconds()); + DWORD t= (((tm->tm_year-80)<<9)| + ((tm->tm_mon+1)<<5)| + (tm->tm_mday))<<16 | + ((tm->tm_hour<<11)| + (tm->tm_min<<5)| + (tm->tm_sec>>1)); + return t; + */ +}