* Read page data and ECC, checking for errors.
* TODO: fix errors with ECC when possible.
*/
-bool mt29f_read(Mt29f *chip, uint32_t page, void *buf, uint16_t size)
+static bool mt29f_read(Mt29f *chip, uint32_t page, void *buf, uint16_t size)
{
ASSERT(size <= MT29F_DATA_SIZE);
memcpy(buf, (void *)NFC_SRAM_BASE_ADDR, size);
- return checkEcc();
+ return checkEcc(chip);
}
}
-bool mt29f_write(Mt29f *chip, uint32_t page, const void *buf, uint16_t size)
+static bool mt29f_write(Mt29f *chip, uint32_t page, const void *buf, uint16_t size)
{
return
mt29f_writePageData(chip, page, buf, size) &&
}
-int mt29f_error(Mt29f *chip)
-{
- return chip->status;
-}
-
-
-void mt29f_clearError(Mt29f *chip)
-{
- chip->status = 0;
-}
-
-
/*
* Check if the given block is marked bad: ONFI standard mandates
* that bad block are marked with "00" bytes on the spare area of the
}
-bool mt29f_init(Mt29f *chip, struct Heap *heap, uint8_t chip_select)
+static bool commonInit(Mt29f *chip, struct Heap *heap, unsigned chip_select)
{
memset(chip, 0, sizeof(Mt29f));
+ DB(chip->kblock.priv.type = KBT_NAND);
+ chip->kblock.blk_size = MT29F_BLOCK_SIZE;
+ chip->kblock.blk_cnt = MT29F_NUM_USER_BLOCKS;
+
chip->chip_select = chip_select;
chip->block_map = heap_allocmem(heap, MT29F_NUM_USER_BLOCKS * sizeof(*chip->block_map));
if (!chip->block_map)
return true;
}
+
+/**************** Kblock interface ****************/
+
+
+static size_t mt29f_writeDirect(struct KBlock *kblk, block_idx_t idx, const void *buf, size_t offset, size_t size)
+{
+ ASSERT(offset <= MT29F_BLOCK_SIZE);
+ ASSERT(offset % MT29F_DATA_SIZE == 0);
+ ASSERT(size <= MT29F_BLOCK_SIZE);
+ ASSERT(size % MT29F_DATA_SIZE == 0);
+
+ while (offset < size)
+ {
+ uint32_t page = (idx * MT29F_PAGES_PER_BLOCK) + (offset / MT29F_DATA_SIZE);
+
+ if (!mt29f_write(MT29F_CAST(kblk), page, buf, MT29F_DATA_SIZE))
+ break;
+
+ offset += MT29F_DATA_SIZE;
+ buf = (const char *)buf + MT29F_DATA_SIZE;
+ }
+
+ return offset;
+}
+
+
+static size_t mt29f_readDirect(struct KBlock *kblk, block_idx_t idx, void *buf, size_t offset, size_t size)
+{
+ ASSERT(offset <= MT29F_BLOCK_SIZE);
+ ASSERT(offset % MT29F_DATA_SIZE == 0);
+ ASSERT(size <= MT29F_BLOCK_SIZE);
+ ASSERT(size % MT29F_DATA_SIZE == 0);
+
+ while (offset < size)
+ {
+ uint32_t page = (idx * MT29F_PAGES_PER_BLOCK) + (offset / MT29F_DATA_SIZE);
+
+ if (!mt29f_read(MT29F_CAST(kblk), page, buf, MT29F_DATA_SIZE))
+ break;
+
+ offset += MT29F_DATA_SIZE;
+ buf = (char *)buf + MT29F_DATA_SIZE;
+ }
+
+ return offset;
+}
+
+
+static int mt29f_error(struct KBlock *kblk)
+{
+ Mt29f *chip = MT29F_CAST(kblk);
+ return chip->status;
+}
+
+
+static void mt29f_clearError(struct KBlock *kblk)
+{
+ Mt29f *chip = MT29F_CAST(kblk);
+ chip->status = 0;
+}
+
+
+static const KBlockVTable mt29f_buffered_vt =
+{
+ .readDirect = mt29f_readDirect,
+ .writeDirect = mt29f_writeDirect,
+
+ .readBuf = kblock_swReadBuf,
+ .writeBuf = kblock_swWriteBuf,
+ .load = kblock_swLoad,
+ .store = kblock_swStore,
+
+ .error = mt29f_error,
+ .clearerr = mt29f_clearError,
+};
+
+static const KBlockVTable mt29f_unbuffered_vt =
+{
+ .readDirect = mt29f_readDirect,
+ .writeDirect = mt29f_writeDirect,
+
+ .error = mt29f_error,
+ .clearerr = mt29f_clearError,
+};
+
+
+bool mt29f_init(Mt29f *chip, struct Heap *heap, unsigned chip_select)
+{
+ if (!commonInit(chip, heap, chip_select))
+ return false;
+
+ chip->kblock.priv.vt = &mt29f_buffered_vt;
+ chip->kblock.priv.flags |= KB_BUFFERED;
+
+ chip->kblock.priv.buf = heap_allocmem(heap, MT29F_BLOCK_SIZE);
+ if (!chip->kblock.priv.buf)
+ {
+ LOG_ERR("mt29f: error allocating block buffer\n");
+ return false;
+ }
+
+ // Load the first block in the cache
+ return mt29f_readDirect(&chip->kblock, 0, chip->kblock.priv.buf, 0, MT29F_DATA_SIZE);
+}
+
+
+bool mt29f_initUnbuffered(Mt29f *chip, struct Heap *heap, unsigned chip_select)
+{
+ chip->kblock.priv.vt = &mt29f_unbuffered_vt;
+ return commonInit(chip, heap, chip_select);
+}
+
+
* \author Stefano Fedrigo <aleph@develer.com>
*
* $WIZ$ module_name = "mt29f"
-* $WIZ$ module_depends = "kfile", "kfile_block", "kblock", "heap"
+* $WIZ$ module_depends = "timer", "kblock", "heap"
* $WIZ$ module_configuration = "bertos/cfg/cfg_mt29f.h"
*/
#include "cfg/cfg_mt29f.h"
#include <cfg/macros.h>
+#include <io/kblock.h>
/**
*/
#define MT29F_ERR_ERASE BV(1) ///< Error erasing a block
#define MT29F_ERR_WRITE BV(2) ///< Error writing a page
-#define MT29F_ERR_RD_TMOUT BV(2) ///< Read timeout
-#define MT29F_ERR_WR_TMOUT BV(2) ///< Write timeout
+#define MT29F_ERR_RD_TMOUT BV(3) ///< Read timeout
+#define MT29F_ERR_WR_TMOUT BV(4) ///< Write timeout
+#define MT29F_ERR_ECC BV(5) ///< Unrecoverable ECC error
/** \} */
*/
typedef struct Mt29f
{
+ KBlock kblock;
+
uint8_t chip_select;
uint8_t status;
uint16_t remap_start; // First unused remap block
} Mt29f;
+/*
+ * Kblock id.
+ */
+#define KBT_NAND MAKE_ID('N', 'A', 'N', 'D')
+
+/**
+* Convert + ASSERT from generic KBlock to NAND context.
+*/
+INLINE Mt29f *MT29F_CAST(KBlock *kb)
+{
+ ASSERT(kb->priv.type == KBT_NAND);
+ return (Mt29f *)kb;
+}
+
struct Heap;
-bool mt29f_init(Mt29f *chip, struct Heap *heap, uint8_t chip_select);
+// Kblock interface
+bool mt29f_init(Mt29f *chip, struct Heap *heap, unsigned chip_select);
+bool mt29f_initUnbuffered(Mt29f *chip, struct Heap *heap, unsigned chip_select);
+
+// Private functions: use at your own risk, could change in any moment
bool mt29f_getDevId(Mt29f *chip, uint8_t dev_id[5]);
int mt29f_blockErase(Mt29f *chip, uint16_t block);
-bool mt29f_read(Mt29f *chip, uint32_t page, void *buf, uint16_t size);
-bool mt29f_write(Mt29f *chip, uint32_t page, const void *buf, uint16_t size);
-int mt29f_error(Mt29f *chip);
-void mt29f_clearError(Mt29f *chip);
#endif /* DRV_MT29F_H */