BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
2342395
Branches
Tags
master
flaneth
firmware
cf.c
change email address in header to blinkenarea address
Stefan Schuermans
commited
2342395
at 2012-05-22 19:18:57
cf.c
Blame
History
Raw
/* flaneth - flash and ethernet Copyright (C) 2007-2012 Stefan Schuermans <stefan@blinkenarea.org> Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html a BlinkenArea project - http://www.blinkenarea.org/ */ #include <avr/io.h> #include "bus.h" #include "cf.h" #include "config.h" #include "debug.h" #include "macros.h" #include "status.h" #include "tasks.h" #include "timing.h" // IO pins of compact flash #define CF_DDR_nCD (DDRB) #define CF_PORT_nCD (PORTB) #define CF_PIN_nCD (PINB) #define CF_BIT_nCD (4) #define CF_DDR_nRST (DDRB) #define CF_PORT_nRST (PORTB) #define CF_BIT_nRST (5) #define CF_DDR_RDY (DDRB) #define CF_PORT_RDY (PORTB) #define CF_PIN_RDY (PINB) #define CF_BIT_RDY (7) #define CF_DDR_nCE (DDRB) #define CF_PORT_nCE (PORTB) #define CF_BIT_nCE (6) // special pin commands #define CF_IS_DETECT() (is_bit_clear(CF_PIN_nCD, CF_BIT_nCD)) #define CF_RESET_ACT() (bit_clear(CF_PORT_nRST, CF_BIT_nRST)) #define CF_RESET_IDLE() (bit_set(CF_PORT_nRST, CF_BIT_nRST)) #define CF_IS_READY() (is_bit_set(CF_PIN_RDY, CF_BIT_RDY)) #define CF_CE_ACT() (bit_clear(CF_PORT_nCE, CF_BIT_nCE)) #define CF_CE_IDLE() (bit_set(CF_PORT_nCE, CF_BIT_nCE)) // compact flash registers #define CF_REG_DATA (0x00) #define CF_REG_ERR (0x01) #define CF_REG_SEC_CNT (0x02) #define CF_REG_SEC_NO (0x03) #define CF_REG_CYL_L (0x04) #define CF_REG_CYL_H (0x05) #define CF_REG_HEAD (0x06) #define CF_REG_STATUS (0x07) #define CF_REG_CMD (0x07) // compact flash status bits #define CF_SB_BUSY (7) #define CF_SB_RDY (6) #define CF_SB_DWF (5) #define CF_SB_DSC (4) #define CF_SB_DRQ (3) #define CF_SB_CORR (2) #define CF_SB_IDX (1) #define CF_SB_ERR (0) // time to give CF to power up (in 20ms steps) #define CF_POWERUP_TIME (5) // 100ms // time to give CF to set outputs after reset (in 20ms steps) #define CF_POST_RESET_TIME (2) // 40ms // timeout value for wait for ready after reset counter (in 20ms steps) #define CF_RESET_READY_TIMEOUT (50) // 1s // timeout value for wait for ready counter (in 20ms steps) #define CF_READY_TIMEOUT (4) // 80ms // compact flash commands #define CF_CMD_IDENTIFY (0xEC) #define CF_CMD_READ_SEC (0x20) #define CF_CMD_WRITE_SEC (0x30) // some number of bytes #define CF_BYTES_IDENTIFY (124) // number of bytes to read for identify // interesting locations in identify data #define CF_ID_OFS_ID_16 (0) // CF identifier, 16 bit #define CF_ID_OFS_CAPA_16 (98) // CF capabilities, 16 bit #define CF_ID_OFS_SEC_CNT_32 (120) // number of sectors on CF (in LBA // mode), 32 bit // various CF constants #define CF_ID (0x848A) // identifier of compact flash cards #define CF_CAPA_BIT_LBA (9) // bit number of the LBA bit in the CF // capabilities // compact flash error state (0 for no error, 1 for error) char CfErrorState = 1; // tick/timeout counter of compact flash // - counts down in 20ms steps, stops at zero unsigned char CfTickCnt = 0; // number of sectors on compact flash card // - zero if no CF card is present, CF card has not yet been identified or CF // card error occured unsigned long CfSectorCnt = 0; // write a compact flash register (returns 1 if successful or 0 on error) // - returns 0 on success and -1 on error extern inline char CfWriteReg(unsigned char reg, unsigned char val) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_DATA = val; // output value BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable BUS_WRITE_ACT(); // activate write signal nop(); nop(); nop(); nop(); nop(); nop(); BUS_WRITE_IDLE(); // take back write signal CF_CE_IDLE(); // take back card enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups return 0; // success } // write buffer to compact flash register // (returns 1 if successful or 0 on error) // - returns 0 on success and -1 on error extern inline char CfWriteRegBuf(unsigned char reg, unsigned char *p_buf, unsigned short len) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_DATA = *p_buf; // output first value to initialize port status // before switching to output BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable for (; len > 0; p_buf++, len--) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready break; BUS_DATA = *p_buf; // output value BUS_WRITE_ACT(); // activate write signal nop(); nop(); nop(); nop(); nop(); nop(); BUS_WRITE_IDLE(); // take back write signal } CF_CE_IDLE(); // take back card enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups return len <= 0 ? 0 : -1; // success if everything has been written } // write constant value to compact flash register multiple times (returns 1 // if successful or 0 on error) // - returns 0 on success and -1 on error extern inline char CfWriteRegConst(unsigned char reg, unsigned char val, unsigned short cnt) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_DATA = val; // output value BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable for (; cnt > 0; cnt--) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready break; BUS_WRITE_ACT(); // activate write signal nop(); nop(); nop(); nop(); nop(); nop(); BUS_WRITE_IDLE(); // take back write signal } CF_CE_IDLE(); // take back card enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups return cnt <= 0 ? 0 : -1; // success if everything has been written } // read a compact flash register (returns 1 if successful or 0 on error) // - returns 0 on success and -1 on error extern inline char CfReadReg(unsigned char reg, unsigned char *p_var) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable BUS_READ_ACT(); // activate read signal nop(); nop(); nop(); nop(); nop(); nop(); *p_var = BUS_DATA_IN; // read data BUS_READ_IDLE(); // take back read signal CF_CE_IDLE(); // take back card enable return 0; // success } // read buffer from a compact flash register (returns 1 if successful or 0 on // error) // - returns 0 on success and -1 on error extern inline char CfReadRegBuf(unsigned char reg, unsigned char *p_buf, unsigned short len) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable for (; len > 0; p_buf++, len--) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready break; BUS_READ_ACT(); // activate read signal nop(); nop(); nop(); nop(); nop(); nop(); *p_buf = BUS_DATA_IN; // read data BUS_READ_IDLE(); // take back read signal } CF_CE_IDLE(); // take back card enable return len <= 0 ? 0 : -1; // success if everything has been read } // read a compact flash register multiple times and throw away data (returns // 1 if successful or 0 on error) // - returns 0 on success and -1 on error extern inline char CfReadRegMulti(unsigned char reg, unsigned short cnt) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready return -1; // error BUS_ADDR = reg; // output address CF_CE_ACT(); // set card enable for (; cnt > 0; cnt--) { if (!CF_IS_DETECT() || !CF_IS_READY()) // check that card is present // and ready break; BUS_READ_ACT(); // activate read signal nop(); nop(); nop(); nop(); nop(); nop(); BUS_READ_IDLE(); // take back read signal } CF_CE_IDLE(); // take back card enable return cnt <= 0 ? 0 : -1; // success if everything has been read } // compact flash error occured static void CfError(void) { debug_cf_printf("CF error"); CfErrorState = 1; CfTickCnt = 0; CfSectorCnt = 0; // report no working CF present StatusInfoCfPresent = 0; } // compact flash is gone static void CfGone(void) { debug_cf_printf("CF gone"); CfError(); } // compact flash timeout static void CfTimeout(void) { debug_cf_printf("CF timeout"); CfError(); } // initialize void CfInit(void) // (extern) { // setup ports bit_clear(CF_PORT_nCD, CF_BIT_nCD); // pull-up of card detect pin off // (external pull-up is present) bit_clear(CF_DDR_nCD, CF_BIT_nCD); // card detect pin to input bit_set(CF_PORT_nRST, CF_BIT_nRST); // reset not active bit_set(CF_DDR_nRST, CF_BIT_nRST); // reset pin to output bit_clear(CF_PORT_RDY, CF_BIT_RDY); // pull-up of ready pin pin off // (external pull-up is present) bit_clear(CF_DDR_RDY, CF_BIT_RDY); // ready pin to input bit_set(CF_PORT_nCE, CF_BIT_nCE); // card not enabled bit_set(CF_DDR_nCE, CF_BIT_nCE); // card enable pin to output } // tick procedure - call every 20ms void CfTick20(void) // (extern) { // count down ticks if (CfTickCnt > 0) --CfTickCnt; } /** * @brief wait for CF to become ready * @param[in] timeout timeout in 20ms steps * @return 0 if CF ready, -1 if CF gone or timeout */ static int CfWaitReady(unsigned char timeout) { CfTickCnt = timeout; while (!CF_IS_READY()) { // card gone -> error if (!CF_IS_DETECT()) { CfGone(); return -1; } // timeout -> error if (CfTickCnt <= 0) { CfTimeout(); return -1; } // execute other tasks while waiting Tasks(); } // while (!CF_IS_READY()) return 0; } /** * @brief read bytes from CF card * @param[in] ptr pointer to data buffer * @param[in] sz size of data to read * @return 0 if reading was successful, -1 if CF gon, timeout or error */ static int CfReadBytes(unsigned char *ptr, unsigned int sz) { // read bytes if (CfReadRegBuf(CF_REG_DATA, ptr, sz) != 0) { debug_cf_printf("reading from CF failed (offset length 0x%03X)", sz); CfError(); return -1; } return 0; } /** * @brief check if CF card is present * @return 1 if CF card is present, 0 if not */ char CfIsPresent(void) // (extern) { if (CF_IS_DETECT()) return 1; else return 0; } /** * @brief reset CF card * @return 0 if CF card is present and could be reset, -1 if not */ char CfReset(void) // (extern) { // wait a little bit if (!CF_IS_DETECT()) { CfGone(); return -1; } CfTickCnt = CF_POWERUP_TIME; while (CfTickCnt > 0) Tasks(); // set reset, wait, take back reset if (!CF_IS_DETECT()) { CfGone(); return -1; } CF_RESET_ACT(); nop(); nop(); nop(); CF_RESET_IDLE(); debug_cf_printf("CF reset"); // wait a little bit if (!CF_IS_DETECT()) { CfGone(); return -1; } CfTickCnt = CF_POST_RESET_TIME; while (CfTickCnt > 0) Tasks(); // wait for CF to become ready if (CfWaitReady(CF_RESET_READY_TIMEOUT) != 0) return -1; // report working CF present CfErrorState = 0; StatusInfoCfPresent = 1; debug_cf_printf("CF ready"); return 0; } /** * @brief identify CF card * @param[out] sectors number of sectors on CF card * @return 0 if CF card could be identified, -1 if not */ char CfIdentify(unsigned long *sectors) // (extern) { unsigned char status, identifyBuf[CF_BYTES_IDENTIFY]; unsigned short val16; debug_cf_printf("CF identify"); if (CfErrorState) { debug_cf_printf("CF in error state"); return -1; } // issue identify drive command if (CfWriteReg(CF_REG_CMD, CF_CMD_IDENTIFY) != 0) { CfError(); return -1; } if (CfWaitReady(CF_READY_TIMEOUT) != 0) return -1; // read status register CfReadReg(CF_REG_STATUS, &status); // check that BUSY=0, RDY=1, DWF=0, DSC=1, IDX=0, ERR=0 if ((status & (1 << CF_SB_BUSY | 1 << CF_SB_RDY | 1 << CF_SB_DWF | 1 << CF_SB_DSC | 1 << CF_SB_IDX | 1 << CF_SB_ERR)) != (1 << CF_SB_RDY | 1 << CF_SB_DSC)) { debug_cf_printf("unexpected status 0x%02X (identify)", status); CfError(); return -1; } // read identify bytes if (CfReadBytes(identifyBuf, sizeof(identifyBuf)) != 0) return -1; // check identifier val16 = (unsigned short)identifyBuf[CF_ID_OFS_ID_16 + 0] | (unsigned short)identifyBuf[CF_ID_OFS_ID_16 + 1] << 8; if (val16 != CF_ID) { debug_cf_printf("invalid CF identifier: 0x%04X", val16); CfError(); return -1; } // check if LBA mode is supported val16 = (unsigned short)identifyBuf[CF_ID_OFS_CAPA_16 + 0] | (unsigned short)identifyBuf[CF_ID_OFS_CAPA_16 + 1] << 8; if ((val16 & 1 << CF_CAPA_BIT_LBA) == 0) { debug_cf_printf("CF does not support LBA mode"); CfError(); return -1; } // get number of sectors on CF CfSectorCnt = (unsigned long)identifyBuf[CF_ID_OFS_SEC_CNT_32 + 0] | (unsigned long)identifyBuf[CF_ID_OFS_SEC_CNT_32 + 1] << 8 | (unsigned long)identifyBuf[CF_ID_OFS_SEC_CNT_32 + 2] << 16 | (unsigned long)identifyBuf[CF_ID_OFS_SEC_CNT_32 + 3] << 24; if (CfSectorCnt <= 0) { debug_cf_printf("CF does not contain any sectors"); CfError(); return -1; } debug_cf_printf("CF done (%lu sectors)", CfSectorCnt); *sectors = CfSectorCnt; return 0; } /** * @brief read from CF card * @param[in] sector number of sector to read * @param[out] data data read from sector * @return 0 if sector could be read, -1 if not */ char CfRead(unsigned long sector, unsigned char data[CF_SECTOR_SIZE]) // (extern) { unsigned char status; debug_cf_printf("CF read (sector %lu)", sector); if (CfErrorState) { debug_cf_printf("CF in error state"); return -1; } if (sector >= CfSectorCnt) { debug_cf_printf("sector does not exist"); return -1; } // set sector number (LBA mode) and sector count (1) if (CfWriteReg(CF_REG_HEAD, 0xE0 | (sector >> 24 & 0x0F)) != 0 || CfWriteReg(CF_REG_CYL_H, sector >> 16) != 0 || CfWriteReg(CF_REG_CYL_L, sector >> 8) != 0 || CfWriteReg(CF_REG_SEC_NO, sector) != 0 || CfWriteReg(CF_REG_SEC_CNT, 1) != 0) { CfError(); return -1; } // issue read sector command if (CfWriteReg(CF_REG_CMD, CF_CMD_READ_SEC) != 0) { CfError(); return -1; } if (CfWaitReady(CF_READY_TIMEOUT) != 0) return -1; // read status register CfReadReg(CF_REG_STATUS, &status); // check that BUSY=0, RDY=1, DWF=0, DSC=1, IDX=0, ERR=0 if ((status & (1 << CF_SB_BUSY | 1 << CF_SB_RDY | 1 << CF_SB_DWF | 1 << CF_SB_DSC | 1 << CF_SB_IDX | 1 << CF_SB_ERR)) != (1 << CF_SB_RDY | 1 << CF_SB_DSC)) { debug_cf_printf("unexpected status 0x%02X (identify)", status); CfError(); return -1; } // read sector bytes if (CfReadBytes(data, CF_SECTOR_SIZE) != 0) return -1; return 0; }