|
@@ -24,14 +24,16 @@ DECLARE_GLOBAL_DATA_PTR;
|
|
#define TIMEOUT_DRAIN_FIFO 5 /* in ms */
|
|
#define TIMEOUT_DRAIN_FIFO 5 /* in ms */
|
|
#define CHIP_DELAY_TIMEOUT 200
|
|
#define CHIP_DELAY_TIMEOUT 200
|
|
#define NAND_STOP_DELAY 40
|
|
#define NAND_STOP_DELAY 40
|
|
-#define PAGE_CHUNK_SIZE (2048)
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
* Define a buffer size for the initial command that detects the flash device:
|
|
* Define a buffer size for the initial command that detects the flash device:
|
|
- * STATUS, READID and PARAM. The largest of these is the PARAM command,
|
|
|
|
- * needing 256 bytes.
|
|
|
|
|
|
+ * STATUS, READID and PARAM.
|
|
|
|
+ * ONFI param page is 256 bytes, and there are three redundant copies
|
|
|
|
+ * to be read. JEDEC param page is 512 bytes, and there are also three
|
|
|
|
+ * redundant copies to be read.
|
|
|
|
+ * Hence this buffer should be at least 512 x 3. Let's pick 2048.
|
|
*/
|
|
*/
|
|
-#define INIT_BUFFER_SIZE 256
|
|
|
|
|
|
+#define INIT_BUFFER_SIZE 2048
|
|
|
|
|
|
/* registers and bit definitions */
|
|
/* registers and bit definitions */
|
|
#define NDCR (0x00) /* Control register */
|
|
#define NDCR (0x00) /* Control register */
|
|
@@ -58,7 +60,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
|
#define NDCR_ND_MODE (0x3 << 21)
|
|
#define NDCR_ND_MODE (0x3 << 21)
|
|
#define NDCR_NAND_MODE (0x0)
|
|
#define NDCR_NAND_MODE (0x0)
|
|
#define NDCR_CLR_PG_CNT (0x1 << 20)
|
|
#define NDCR_CLR_PG_CNT (0x1 << 20)
|
|
-#define NDCR_STOP_ON_UNCOR (0x1 << 19)
|
|
|
|
|
|
+#define NFCV1_NDCR_ARB_CNTL (0x1 << 19)
|
|
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
|
|
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
|
|
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
|
|
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
|
|
|
|
|
|
@@ -109,6 +111,13 @@ DECLARE_GLOBAL_DATA_PTR;
|
|
#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
|
|
#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
|
|
#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
|
|
#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * This should be large enough to read 'ONFI' and 'JEDEC'.
|
|
|
|
+ * Let's use 7 bytes, which is the maximum ID count supported
|
|
|
|
+ * by the controller (see NDCR_RD_ID_CNT_MASK).
|
|
|
|
+ */
|
|
|
|
+#define READ_ID_BYTES 7
|
|
|
|
+
|
|
/* macros for registers read/write */
|
|
/* macros for registers read/write */
|
|
#define nand_writel(info, off, val) \
|
|
#define nand_writel(info, off, val) \
|
|
writel((val), (info)->mmio_base + (off))
|
|
writel((val), (info)->mmio_base + (off))
|
|
@@ -146,7 +155,6 @@ enum pxa3xx_nand_variant {
|
|
|
|
|
|
struct pxa3xx_nand_host {
|
|
struct pxa3xx_nand_host {
|
|
struct nand_chip chip;
|
|
struct nand_chip chip;
|
|
- struct mtd_info *mtd;
|
|
|
|
void *info_data;
|
|
void *info_data;
|
|
|
|
|
|
/* page size of attached chip */
|
|
/* page size of attached chip */
|
|
@@ -156,8 +164,6 @@ struct pxa3xx_nand_host {
|
|
/* calculated from pxa3xx_nand_flash data */
|
|
/* calculated from pxa3xx_nand_flash data */
|
|
unsigned int col_addr_cycles;
|
|
unsigned int col_addr_cycles;
|
|
unsigned int row_addr_cycles;
|
|
unsigned int row_addr_cycles;
|
|
- size_t read_id_bytes;
|
|
|
|
-
|
|
|
|
};
|
|
};
|
|
|
|
|
|
struct pxa3xx_nand_info {
|
|
struct pxa3xx_nand_info {
|
|
@@ -193,15 +199,44 @@ struct pxa3xx_nand_info {
|
|
int use_spare; /* use spare ? */
|
|
int use_spare; /* use spare ? */
|
|
int need_wait;
|
|
int need_wait;
|
|
|
|
|
|
- unsigned int data_size; /* data to be read from FIFO */
|
|
|
|
- unsigned int chunk_size; /* split commands chunk size */
|
|
|
|
- unsigned int oob_size;
|
|
|
|
|
|
+ /* Amount of real data per full chunk */
|
|
|
|
+ unsigned int chunk_size;
|
|
|
|
+
|
|
|
|
+ /* Amount of spare data per full chunk */
|
|
unsigned int spare_size;
|
|
unsigned int spare_size;
|
|
|
|
+
|
|
|
|
+ /* Number of full chunks (i.e chunk_size + spare_size) */
|
|
|
|
+ unsigned int nfullchunks;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Total number of chunks. If equal to nfullchunks, then there
|
|
|
|
+ * are only full chunks. Otherwise, there is one last chunk of
|
|
|
|
+ * size (last_chunk_size + last_spare_size)
|
|
|
|
+ */
|
|
|
|
+ unsigned int ntotalchunks;
|
|
|
|
+
|
|
|
|
+ /* Amount of real data in the last chunk */
|
|
|
|
+ unsigned int last_chunk_size;
|
|
|
|
+
|
|
|
|
+ /* Amount of spare data in the last chunk */
|
|
|
|
+ unsigned int last_spare_size;
|
|
|
|
+
|
|
unsigned int ecc_size;
|
|
unsigned int ecc_size;
|
|
unsigned int ecc_err_cnt;
|
|
unsigned int ecc_err_cnt;
|
|
unsigned int max_bitflips;
|
|
unsigned int max_bitflips;
|
|
int retcode;
|
|
int retcode;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Variables only valid during command
|
|
|
|
+ * execution. step_chunk_size and step_spare_size is the
|
|
|
|
+ * amount of real data and spare data in the current
|
|
|
|
+ * chunk. cur_chunk is the current chunk being
|
|
|
|
+ * read/programmed.
|
|
|
|
+ */
|
|
|
|
+ unsigned int step_chunk_size;
|
|
|
|
+ unsigned int step_spare_size;
|
|
|
|
+ unsigned int cur_chunk;
|
|
|
|
+
|
|
/* cached register value */
|
|
/* cached register value */
|
|
uint32_t reg_ndcr;
|
|
uint32_t reg_ndcr;
|
|
uint32_t ndtr0cs0;
|
|
uint32_t ndtr0cs0;
|
|
@@ -215,13 +250,33 @@ struct pxa3xx_nand_info {
|
|
};
|
|
};
|
|
|
|
|
|
static struct pxa3xx_nand_timing timing[] = {
|
|
static struct pxa3xx_nand_timing timing[] = {
|
|
|
|
+ /*
|
|
|
|
+ * tCH Enable signal hold time
|
|
|
|
+ * tCS Enable signal setup time
|
|
|
|
+ * tWH ND_nWE high duration
|
|
|
|
+ * tWP ND_nWE pulse time
|
|
|
|
+ * tRH ND_nRE high duration
|
|
|
|
+ * tRP ND_nRE pulse width
|
|
|
|
+ * tR ND_nWE high to ND_nRE low for read
|
|
|
|
+ * tWHR ND_nWE high to ND_nRE low for status read
|
|
|
|
+ * tAR ND_ALE low to ND_nRE low delay
|
|
|
|
+ */
|
|
|
|
+ /*ch cs wh wp rh rp r whr ar */
|
|
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
|
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
|
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
|
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
|
{ 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
|
|
{ 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
|
|
{ 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
|
|
{ 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
|
|
|
|
+ { 5, 20, 10, 12, 10, 12, 25000, 60, 10, },
|
|
};
|
|
};
|
|
|
|
|
|
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
|
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
|
|
|
+ /*
|
|
|
|
+ * chip_id
|
|
|
|
+ * flash_width Width of Flash memory (DWIDTH_M)
|
|
|
|
+ * dfc_width Width of flash controller(DWIDTH_C)
|
|
|
|
+ * *timing
|
|
|
|
+ * http://www.linux-mtd.infradead.org/nand-data/nanddata.html
|
|
|
|
+ */
|
|
{ 0x46ec, 16, 16, &timing[1] },
|
|
{ 0x46ec, 16, 16, &timing[1] },
|
|
{ 0xdaec, 8, 8, &timing[1] },
|
|
{ 0xdaec, 8, 8, &timing[1] },
|
|
{ 0xd7ec, 8, 8, &timing[1] },
|
|
{ 0xd7ec, 8, 8, &timing[1] },
|
|
@@ -230,6 +285,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
|
{ 0xdc2c, 8, 8, &timing[2] },
|
|
{ 0xdc2c, 8, 8, &timing[2] },
|
|
{ 0xcc2c, 16, 16, &timing[2] },
|
|
{ 0xcc2c, 16, 16, &timing[2] },
|
|
{ 0xba20, 16, 16, &timing[3] },
|
|
{ 0xba20, 16, 16, &timing[3] },
|
|
|
|
+ { 0xda98, 8, 8, &timing[4] },
|
|
};
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
|
|
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
|
|
@@ -267,6 +323,20 @@ static struct nand_ecclayout ecc_layout_2KB_bch4bit = {
|
|
.oobfree = { {2, 30} }
|
|
.oobfree = { {2, 30} }
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct nand_ecclayout ecc_layout_2KB_bch8bit = {
|
|
|
|
+ .eccbytes = 64,
|
|
|
|
+ .eccpos = {
|
|
|
|
+ 64, 65, 66, 67, 68, 69, 70, 71,
|
|
|
|
+ 72, 73, 74, 75, 76, 77, 78, 79,
|
|
|
|
+ 80, 81, 82, 83, 84, 85, 86, 87,
|
|
|
|
+ 88, 89, 90, 91, 92, 93, 94, 95,
|
|
|
|
+ 96, 97, 98, 99, 100, 101, 102, 103,
|
|
|
|
+ 104, 105, 106, 107, 108, 109, 110, 111,
|
|
|
|
+ 112, 113, 114, 115, 116, 117, 118, 119,
|
|
|
|
+ 120, 121, 122, 123, 124, 125, 126, 127},
|
|
|
|
+ .oobfree = { {1, 4}, {6, 26} }
|
|
|
|
+};
|
|
|
|
+
|
|
static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
|
|
static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
|
|
.eccbytes = 64,
|
|
.eccbytes = 64,
|
|
.eccpos = {
|
|
.eccpos = {
|
|
@@ -282,6 +352,33 @@ static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
|
|
.oobfree = { {6, 26}, { 64, 32} }
|
|
.oobfree = { {6, 26}, { 64, 32} }
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct nand_ecclayout ecc_layout_8KB_bch4bit = {
|
|
|
|
+ .eccbytes = 128,
|
|
|
|
+ .eccpos = {
|
|
|
|
+ 32, 33, 34, 35, 36, 37, 38, 39,
|
|
|
|
+ 40, 41, 42, 43, 44, 45, 46, 47,
|
|
|
|
+ 48, 49, 50, 51, 52, 53, 54, 55,
|
|
|
|
+ 56, 57, 58, 59, 60, 61, 62, 63,
|
|
|
|
+
|
|
|
|
+ 96, 97, 98, 99, 100, 101, 102, 103,
|
|
|
|
+ 104, 105, 106, 107, 108, 109, 110, 111,
|
|
|
|
+ 112, 113, 114, 115, 116, 117, 118, 119,
|
|
|
|
+ 120, 121, 122, 123, 124, 125, 126, 127,
|
|
|
|
+
|
|
|
|
+ 160, 161, 162, 163, 164, 165, 166, 167,
|
|
|
|
+ 168, 169, 170, 171, 172, 173, 174, 175,
|
|
|
|
+ 176, 177, 178, 179, 180, 181, 182, 183,
|
|
|
|
+ 184, 185, 186, 187, 188, 189, 190, 191,
|
|
|
|
+
|
|
|
|
+ 224, 225, 226, 227, 228, 229, 230, 231,
|
|
|
|
+ 232, 233, 234, 235, 236, 237, 238, 239,
|
|
|
|
+ 240, 241, 242, 243, 244, 245, 246, 247,
|
|
|
|
+ 248, 249, 250, 251, 252, 253, 254, 255},
|
|
|
|
+
|
|
|
|
+ /* Bootrom looks in bytes 0 & 5 for bad blocks */
|
|
|
|
+ .oobfree = { {1, 4}, {6, 26}, { 64, 32}, {128, 32}, {192, 32} }
|
|
|
|
+};
|
|
|
|
+
|
|
static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
|
static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
|
.eccbytes = 128,
|
|
.eccbytes = 128,
|
|
.eccpos = {
|
|
.eccpos = {
|
|
@@ -292,6 +389,13 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
|
.oobfree = { }
|
|
.oobfree = { }
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct nand_ecclayout ecc_layout_8KB_bch8bit = {
|
|
|
|
+ .eccbytes = 256,
|
|
|
|
+ .eccpos = {},
|
|
|
|
+ /* HW ECC handles all ECC data and all spare area is free for OOB */
|
|
|
|
+ .oobfree = {{0, 160} }
|
|
|
|
+};
|
|
|
|
+
|
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
|
@@ -347,9 +451,9 @@ static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
|
|
u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
|
|
u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
|
|
u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
|
|
u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
|
|
u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
|
|
u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
|
|
- u32 tWP_min = DIV_ROUND_UP(t->tWC_min - tWH_min, 1000);
|
|
|
|
|
|
+ u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
|
|
u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
|
|
u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
|
|
- u32 tRP_min = DIV_ROUND_UP(t->tRC_min - tREH_min, 1000);
|
|
|
|
|
|
+ u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
|
|
u32 tR = chip->chip_delay * 1000;
|
|
u32 tR = chip->chip_delay * 1000;
|
|
u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
|
|
u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
|
|
u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
|
|
u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
|
|
@@ -381,16 +485,17 @@ static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host)
|
|
struct nand_chip *chip = &host->chip;
|
|
struct nand_chip *chip = &host->chip;
|
|
struct pxa3xx_nand_info *info = host->info_data;
|
|
struct pxa3xx_nand_info *info = host->info_data;
|
|
const struct pxa3xx_nand_flash *f = NULL;
|
|
const struct pxa3xx_nand_flash *f = NULL;
|
|
|
|
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
|
|
int mode, id, ntypes, i;
|
|
int mode, id, ntypes, i;
|
|
|
|
|
|
mode = onfi_get_async_timing_mode(chip);
|
|
mode = onfi_get_async_timing_mode(chip);
|
|
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
|
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
|
ntypes = ARRAY_SIZE(builtin_flash_types);
|
|
ntypes = ARRAY_SIZE(builtin_flash_types);
|
|
|
|
|
|
- chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
|
|
|
- id = chip->read_byte(host->mtd);
|
|
|
|
- id |= chip->read_byte(host->mtd) << 0x8;
|
|
|
|
|
|
+ id = chip->read_byte(mtd);
|
|
|
|
+ id |= chip->read_byte(mtd) << 0x8;
|
|
|
|
|
|
for (i = 0; i < ntypes; i++) {
|
|
for (i = 0; i < ntypes; i++) {
|
|
f = &builtin_flash_types[i];
|
|
f = &builtin_flash_types[i];
|
|
@@ -427,25 +532,6 @@ static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-/*
|
|
|
|
- * Set the data and OOB size, depending on the selected
|
|
|
|
- * spare and ECC configuration.
|
|
|
|
- * Only applicable to READ0, READOOB and PAGEPROG commands.
|
|
|
|
- */
|
|
|
|
-static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info,
|
|
|
|
- struct mtd_info *mtd)
|
|
|
|
-{
|
|
|
|
- int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
|
|
|
|
-
|
|
|
|
- info->data_size = mtd->writesize;
|
|
|
|
- if (!oob_enable)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- info->oob_size = info->spare_size;
|
|
|
|
- if (!info->use_ecc)
|
|
|
|
- info->oob_size += info->ecc_size;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* NOTE: it is a must to set ND_RUN first, then write
|
|
* NOTE: it is a must to set ND_RUN first, then write
|
|
* command buffer, otherwise, it does not work.
|
|
* command buffer, otherwise, it does not work.
|
|
@@ -478,8 +564,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
|
|
ndcr |= NDCR_ND_RUN;
|
|
ndcr |= NDCR_ND_RUN;
|
|
|
|
|
|
/* clear status bits and run */
|
|
/* clear status bits and run */
|
|
- nand_writel(info, NDCR, 0);
|
|
|
|
nand_writel(info, NDSR, NDSR_MASK);
|
|
nand_writel(info, NDSR, NDSR_MASK);
|
|
|
|
+ nand_writel(info, NDCR, 0);
|
|
nand_writel(info, NDCR, ndcr);
|
|
nand_writel(info, NDCR, ndcr);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -526,39 +612,38 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
|
|
|
|
|
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
|
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
|
{
|
|
{
|
|
- unsigned int do_bytes = min(info->data_size, info->chunk_size);
|
|
|
|
-
|
|
|
|
switch (info->state) {
|
|
switch (info->state) {
|
|
case STATE_PIO_WRITING:
|
|
case STATE_PIO_WRITING:
|
|
- writesl(info->mmio_base + NDDB,
|
|
|
|
- info->data_buff + info->data_buff_pos,
|
|
|
|
- DIV_ROUND_UP(do_bytes, 4));
|
|
|
|
|
|
+ if (info->step_chunk_size)
|
|
|
|
+ writesl(info->mmio_base + NDDB,
|
|
|
|
+ info->data_buff + info->data_buff_pos,
|
|
|
|
+ DIV_ROUND_UP(info->step_chunk_size, 4));
|
|
|
|
|
|
- if (info->oob_size > 0)
|
|
|
|
|
|
+ if (info->step_spare_size)
|
|
writesl(info->mmio_base + NDDB,
|
|
writesl(info->mmio_base + NDDB,
|
|
info->oob_buff + info->oob_buff_pos,
|
|
info->oob_buff + info->oob_buff_pos,
|
|
- DIV_ROUND_UP(info->oob_size, 4));
|
|
|
|
|
|
+ DIV_ROUND_UP(info->step_spare_size, 4));
|
|
break;
|
|
break;
|
|
case STATE_PIO_READING:
|
|
case STATE_PIO_READING:
|
|
- drain_fifo(info,
|
|
|
|
- info->data_buff + info->data_buff_pos,
|
|
|
|
- DIV_ROUND_UP(do_bytes, 4));
|
|
|
|
|
|
+ if (info->step_chunk_size)
|
|
|
|
+ drain_fifo(info,
|
|
|
|
+ info->data_buff + info->data_buff_pos,
|
|
|
|
+ DIV_ROUND_UP(info->step_chunk_size, 4));
|
|
|
|
|
|
- if (info->oob_size > 0)
|
|
|
|
|
|
+ if (info->step_spare_size)
|
|
drain_fifo(info,
|
|
drain_fifo(info,
|
|
info->oob_buff + info->oob_buff_pos,
|
|
info->oob_buff + info->oob_buff_pos,
|
|
- DIV_ROUND_UP(info->oob_size, 4));
|
|
|
|
|
|
+ DIV_ROUND_UP(info->step_spare_size, 4));
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
|
|
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
|
|
- info->state);
|
|
|
|
|
|
+ info->state);
|
|
BUG();
|
|
BUG();
|
|
}
|
|
}
|
|
|
|
|
|
/* Update buffer pointers for multi-page read/write */
|
|
/* Update buffer pointers for multi-page read/write */
|
|
- info->data_buff_pos += do_bytes;
|
|
|
|
- info->oob_buff_pos += info->oob_size;
|
|
|
|
- info->data_size -= do_bytes;
|
|
|
|
|
|
+ info->data_buff_pos += info->step_chunk_size;
|
|
|
|
+ info->oob_buff_pos += info->step_spare_size;
|
|
}
|
|
}
|
|
|
|
|
|
static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info)
|
|
static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info)
|
|
@@ -583,6 +668,9 @@ static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info)
|
|
cmd_done = NDSR_CS1_CMDD;
|
|
cmd_done = NDSR_CS1_CMDD;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* TODO - find out why we need the delay during write operation. */
|
|
|
|
+ ndelay(1);
|
|
|
|
+
|
|
status = nand_readl(info, NDSR);
|
|
status = nand_readl(info, NDSR);
|
|
|
|
|
|
if (status & NDSR_UNCORERR)
|
|
if (status & NDSR_UNCORERR)
|
|
@@ -620,8 +708,14 @@ static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info)
|
|
is_ready = 1;
|
|
is_ready = 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Clear all status bit before issuing the next command, which
|
|
|
|
+ * can and will alter the status bits and will deserve a new
|
|
|
|
+ * interrupt on its own. This lets the controller exit the IRQ
|
|
|
|
+ */
|
|
|
|
+ nand_writel(info, NDSR, status);
|
|
|
|
+
|
|
if (status & NDSR_WRCMDREQ) {
|
|
if (status & NDSR_WRCMDREQ) {
|
|
- nand_writel(info, NDSR, NDSR_WRCMDREQ);
|
|
|
|
status &= ~NDSR_WRCMDREQ;
|
|
status &= ~NDSR_WRCMDREQ;
|
|
info->state = STATE_CMD_HANDLE;
|
|
info->state = STATE_CMD_HANDLE;
|
|
|
|
|
|
@@ -642,8 +736,6 @@ static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info)
|
|
nand_writel(info, NDCB0, info->ndcb3);
|
|
nand_writel(info, NDCB0, info->ndcb3);
|
|
}
|
|
}
|
|
|
|
|
|
- /* clear NDSR to let the controller exit the IRQ */
|
|
|
|
- nand_writel(info, NDSR, status);
|
|
|
|
if (is_completed)
|
|
if (is_completed)
|
|
info->cmd_complete = 1;
|
|
info->cmd_complete = 1;
|
|
if (is_ready)
|
|
if (is_ready)
|
|
@@ -664,7 +756,7 @@ static void set_command_address(struct pxa3xx_nand_info *info,
|
|
unsigned int page_size, uint16_t column, int page_addr)
|
|
unsigned int page_size, uint16_t column, int page_addr)
|
|
{
|
|
{
|
|
/* small page addr setting */
|
|
/* small page addr setting */
|
|
- if (page_size < PAGE_CHUNK_SIZE) {
|
|
|
|
|
|
+ if (page_size < info->chunk_size) {
|
|
info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
|
|
info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
|
|
| (column & 0xFF);
|
|
| (column & 0xFF);
|
|
|
|
|
|
@@ -683,14 +775,16 @@ static void set_command_address(struct pxa3xx_nand_info *info,
|
|
static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
|
|
static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
|
|
{
|
|
{
|
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
- struct mtd_info *mtd = host->mtd;
|
|
|
|
|
|
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
|
|
|
|
|
|
/* reset data and oob column point to handle data */
|
|
/* reset data and oob column point to handle data */
|
|
info->buf_start = 0;
|
|
info->buf_start = 0;
|
|
info->buf_count = 0;
|
|
info->buf_count = 0;
|
|
- info->oob_size = 0;
|
|
|
|
info->data_buff_pos = 0;
|
|
info->data_buff_pos = 0;
|
|
info->oob_buff_pos = 0;
|
|
info->oob_buff_pos = 0;
|
|
|
|
+ info->step_chunk_size = 0;
|
|
|
|
+ info->step_spare_size = 0;
|
|
|
|
+ info->cur_chunk = 0;
|
|
info->use_ecc = 0;
|
|
info->use_ecc = 0;
|
|
info->use_spare = 1;
|
|
info->use_spare = 1;
|
|
info->retcode = ERR_NONE;
|
|
info->retcode = ERR_NONE;
|
|
@@ -700,10 +794,9 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
|
|
|
|
|
|
switch (command) {
|
|
switch (command) {
|
|
case NAND_CMD_READ0:
|
|
case NAND_CMD_READ0:
|
|
|
|
+ case NAND_CMD_READOOB:
|
|
case NAND_CMD_PAGEPROG:
|
|
case NAND_CMD_PAGEPROG:
|
|
info->use_ecc = 1;
|
|
info->use_ecc = 1;
|
|
- case NAND_CMD_READOOB:
|
|
|
|
- pxa3xx_set_datasize(info, mtd);
|
|
|
|
break;
|
|
break;
|
|
case NAND_CMD_PARAM:
|
|
case NAND_CMD_PARAM:
|
|
info->use_spare = 0;
|
|
info->use_spare = 0;
|
|
@@ -734,7 +827,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
struct mtd_info *mtd;
|
|
struct mtd_info *mtd;
|
|
|
|
|
|
host = info->host[info->cs];
|
|
host = info->host[info->cs];
|
|
- mtd = host->mtd;
|
|
|
|
|
|
+ mtd = nand_to_mtd(&host->chip);
|
|
addr_cycle = 0;
|
|
addr_cycle = 0;
|
|
exec_cmd = 1;
|
|
exec_cmd = 1;
|
|
|
|
|
|
@@ -760,19 +853,27 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
if (command == NAND_CMD_READOOB)
|
|
if (command == NAND_CMD_READOOB)
|
|
info->buf_start += mtd->writesize;
|
|
info->buf_start += mtd->writesize;
|
|
|
|
|
|
|
|
+ if (info->cur_chunk < info->nfullchunks) {
|
|
|
|
+ info->step_chunk_size = info->chunk_size;
|
|
|
|
+ info->step_spare_size = info->spare_size;
|
|
|
|
+ } else {
|
|
|
|
+ info->step_chunk_size = info->last_chunk_size;
|
|
|
|
+ info->step_spare_size = info->last_spare_size;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Multiple page read needs an 'extended command type' field,
|
|
* Multiple page read needs an 'extended command type' field,
|
|
* which is either naked-read or last-read according to the
|
|
* which is either naked-read or last-read according to the
|
|
* state.
|
|
* state.
|
|
*/
|
|
*/
|
|
- if (mtd->writesize == PAGE_CHUNK_SIZE) {
|
|
|
|
|
|
+ if (mtd->writesize == info->chunk_size) {
|
|
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
|
|
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
|
|
- } else if (mtd->writesize > PAGE_CHUNK_SIZE) {
|
|
|
|
|
|
+ } else if (mtd->writesize > info->chunk_size) {
|
|
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
|
|
info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
|
|
| NDCB0_LEN_OVRD
|
|
| NDCB0_LEN_OVRD
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
|
- info->ndcb3 = info->chunk_size +
|
|
|
|
- info->oob_size;
|
|
|
|
|
|
+ info->ndcb3 = info->step_chunk_size +
|
|
|
|
+ info->step_spare_size;
|
|
}
|
|
}
|
|
|
|
|
|
set_command_address(info, mtd->writesize, column, page_addr);
|
|
set_command_address(info, mtd->writesize, column, page_addr);
|
|
@@ -787,13 +888,11 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
* Multiple page programming needs to execute the initial
|
|
* Multiple page programming needs to execute the initial
|
|
* SEQIN command that sets the page address.
|
|
* SEQIN command that sets the page address.
|
|
*/
|
|
*/
|
|
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
|
|
|
|
|
|
+ if (mtd->writesize > info->chunk_size) {
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type)
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type)
|
|
| addr_cycle
|
|
| addr_cycle
|
|
| command;
|
|
| command;
|
|
- /* No data transfer in this case */
|
|
|
|
- info->data_size = 0;
|
|
|
|
exec_cmd = 1;
|
|
exec_cmd = 1;
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
@@ -805,8 +904,16 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (info->cur_chunk < info->nfullchunks) {
|
|
|
|
+ info->step_chunk_size = info->chunk_size;
|
|
|
|
+ info->step_spare_size = info->spare_size;
|
|
|
|
+ } else {
|
|
|
|
+ info->step_chunk_size = info->last_chunk_size;
|
|
|
|
+ info->step_spare_size = info->last_spare_size;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Second command setting for large pages */
|
|
/* Second command setting for large pages */
|
|
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
|
|
|
|
|
|
+ if (mtd->writesize > info->chunk_size) {
|
|
/*
|
|
/*
|
|
* Multiple page write uses the 'extended command'
|
|
* Multiple page write uses the 'extended command'
|
|
* field. This can be used to issue a command dispatch
|
|
* field. This can be used to issue a command dispatch
|
|
@@ -815,14 +922,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
|
| NDCB0_LEN_OVRD
|
|
| NDCB0_LEN_OVRD
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type);
|
|
- info->ndcb3 = info->chunk_size +
|
|
|
|
- info->oob_size;
|
|
|
|
|
|
+ info->ndcb3 = info->step_chunk_size +
|
|
|
|
+ info->step_spare_size;
|
|
|
|
|
|
/*
|
|
/*
|
|
* This is the command dispatch that completes a chunked
|
|
* This is the command dispatch that completes a chunked
|
|
* page program operation.
|
|
* page program operation.
|
|
*/
|
|
*/
|
|
- if (info->data_size == 0) {
|
|
|
|
|
|
+ if (info->cur_chunk == info->ntotalchunks) {
|
|
info->ndcb0 = NDCB0_CMD_TYPE(0x1)
|
|
info->ndcb0 = NDCB0_CMD_TYPE(0x1)
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type)
|
|
| NDCB0_EXT_CMD_TYPE(ext_cmd_type)
|
|
| command;
|
|
| command;
|
|
@@ -842,24 +949,24 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
break;
|
|
break;
|
|
|
|
|
|
case NAND_CMD_PARAM:
|
|
case NAND_CMD_PARAM:
|
|
- info->buf_count = 256;
|
|
|
|
|
|
+ info->buf_count = INIT_BUFFER_SIZE;
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
|
| NDCB0_ADDR_CYC(1)
|
|
| NDCB0_ADDR_CYC(1)
|
|
| NDCB0_LEN_OVRD
|
|
| NDCB0_LEN_OVRD
|
|
| command;
|
|
| command;
|
|
info->ndcb1 = (column & 0xFF);
|
|
info->ndcb1 = (column & 0xFF);
|
|
- info->ndcb3 = 256;
|
|
|
|
- info->data_size = 256;
|
|
|
|
|
|
+ info->ndcb3 = INIT_BUFFER_SIZE;
|
|
|
|
+ info->step_chunk_size = INIT_BUFFER_SIZE;
|
|
break;
|
|
break;
|
|
|
|
|
|
case NAND_CMD_READID:
|
|
case NAND_CMD_READID:
|
|
- info->buf_count = host->read_id_bytes;
|
|
|
|
|
|
+ info->buf_count = READ_ID_BYTES;
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(3)
|
|
info->ndcb0 |= NDCB0_CMD_TYPE(3)
|
|
| NDCB0_ADDR_CYC(1)
|
|
| NDCB0_ADDR_CYC(1)
|
|
| command;
|
|
| command;
|
|
info->ndcb1 = (column & 0xFF);
|
|
info->ndcb1 = (column & 0xFF);
|
|
|
|
|
|
- info->data_size = 8;
|
|
|
|
|
|
+ info->step_chunk_size = 8;
|
|
break;
|
|
break;
|
|
case NAND_CMD_STATUS:
|
|
case NAND_CMD_STATUS:
|
|
info->buf_count = 1;
|
|
info->buf_count = 1;
|
|
@@ -867,7 +974,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
|
|
| NDCB0_ADDR_CYC(1)
|
|
| NDCB0_ADDR_CYC(1)
|
|
| command;
|
|
| command;
|
|
|
|
|
|
- info->data_size = 8;
|
|
|
|
|
|
+ info->step_chunk_size = 8;
|
|
break;
|
|
break;
|
|
|
|
|
|
case NAND_CMD_ERASE1:
|
|
case NAND_CMD_ERASE1:
|
|
@@ -1051,22 +1158,31 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Only a few commands need several steps */
|
|
|
|
+ if (command != NAND_CMD_PAGEPROG &&
|
|
|
|
+ command != NAND_CMD_READ0 &&
|
|
|
|
+ command != NAND_CMD_READOOB)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ info->cur_chunk++;
|
|
|
|
+
|
|
/* Check if the sequence is complete */
|
|
/* Check if the sequence is complete */
|
|
- if (info->data_size == 0 && command != NAND_CMD_PAGEPROG)
|
|
|
|
|
|
+ if (info->cur_chunk == info->ntotalchunks &&
|
|
|
|
+ command != NAND_CMD_PAGEPROG)
|
|
break;
|
|
break;
|
|
|
|
|
|
/*
|
|
/*
|
|
* After a splitted program command sequence has issued
|
|
* After a splitted program command sequence has issued
|
|
* the command dispatch, the command sequence is complete.
|
|
* the command dispatch, the command sequence is complete.
|
|
*/
|
|
*/
|
|
- if (info->data_size == 0 &&
|
|
|
|
|
|
+ if (info->cur_chunk == (info->ntotalchunks + 1) &&
|
|
command == NAND_CMD_PAGEPROG &&
|
|
command == NAND_CMD_PAGEPROG &&
|
|
ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
|
|
ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
|
|
break;
|
|
break;
|
|
|
|
|
|
if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
|
|
if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
|
|
/* Last read: issue a 'last naked read' */
|
|
/* Last read: issue a 'last naked read' */
|
|
- if (info->data_size == info->chunk_size)
|
|
|
|
|
|
+ if (info->cur_chunk == info->ntotalchunks - 1)
|
|
ext_cmd_type = EXT_CMD_TYPE_LAST_RW;
|
|
ext_cmd_type = EXT_CMD_TYPE_LAST_RW;
|
|
else
|
|
else
|
|
ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
|
|
ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
|
|
@@ -1076,7 +1192,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
|
* the command dispatch must be issued to complete.
|
|
* the command dispatch must be issued to complete.
|
|
*/
|
|
*/
|
|
} else if (command == NAND_CMD_PAGEPROG &&
|
|
} else if (command == NAND_CMD_PAGEPROG &&
|
|
- info->data_size == 0) {
|
|
|
|
|
|
+ info->cur_chunk == info->ntotalchunks) {
|
|
ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
|
|
ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
|
|
}
|
|
}
|
|
} while (1);
|
|
} while (1);
|
|
@@ -1218,42 +1334,42 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
|
return NAND_STATUS_READY;
|
|
return NAND_STATUS_READY;
|
|
}
|
|
}
|
|
|
|
|
|
-static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info)
|
|
|
|
|
|
+static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info)
|
|
|
|
+{
|
|
|
|
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
|
|
|
|
+
|
|
|
|
+ /* Configure default flash values */
|
|
|
|
+ info->reg_ndcr = 0x0; /* enable all interrupts */
|
|
|
|
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
|
|
+ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
|
|
|
+ info->reg_ndcr |= NDCR_SPARE_EN;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info)
|
|
{
|
|
{
|
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
- struct mtd_info *mtd = host->mtd;
|
|
|
|
|
|
+ struct mtd_info *mtd = nand_to_mtd(&info->host[info->cs]->chip);
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
|
|
|
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
|
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
|
info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
|
|
info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
|
|
info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
|
|
info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
|
|
-
|
|
|
|
- return 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
|
|
|
|
|
+static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
|
{
|
|
{
|
|
- /*
|
|
|
|
- * We set 0 by hard coding here, for we don't support keep_config
|
|
|
|
- * when there is more than one chip attached to the controller
|
|
|
|
- */
|
|
|
|
- struct pxa3xx_nand_host *host = info->host[0];
|
|
|
|
|
|
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
|
|
uint32_t ndcr = nand_readl(info, NDCR);
|
|
uint32_t ndcr = nand_readl(info, NDCR);
|
|
|
|
|
|
- if (ndcr & NDCR_PAGE_SZ) {
|
|
|
|
- /* Controller's FIFO size */
|
|
|
|
- info->chunk_size = 2048;
|
|
|
|
- host->read_id_bytes = 4;
|
|
|
|
- } else {
|
|
|
|
- info->chunk_size = 512;
|
|
|
|
- host->read_id_bytes = 2;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/* Set an initial chunk size */
|
|
/* Set an initial chunk size */
|
|
- info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
|
|
|
|
|
|
+ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
|
|
|
+ info->reg_ndcr = ndcr &
|
|
|
|
+ ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
|
|
|
|
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
|
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
|
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
|
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
|
- return 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
@@ -1273,13 +1389,13 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host)
|
|
const struct nand_sdr_timings *timings;
|
|
const struct nand_sdr_timings *timings;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- mtd = info->host[info->cs]->mtd;
|
|
|
|
|
|
+ mtd = nand_to_mtd(&info->host[info->cs]->chip);
|
|
chip = mtd_to_nand(mtd);
|
|
chip = mtd_to_nand(mtd);
|
|
|
|
|
|
/* configure default flash values */
|
|
/* configure default flash values */
|
|
info->reg_ndcr = 0x0; /* enable all interrupts */
|
|
info->reg_ndcr = 0x0; /* enable all interrupts */
|
|
info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
- info->reg_ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);
|
|
|
|
|
|
+ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
|
info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
|
info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
|
|
|
|
|
/* use the common timing to make a try */
|
|
/* use the common timing to make a try */
|
|
@@ -1302,6 +1418,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|
int strength, int ecc_stepsize, int page_size)
|
|
int strength, int ecc_stepsize, int page_size)
|
|
{
|
|
{
|
|
if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
|
|
if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
|
|
|
|
+ info->nfullchunks = 1;
|
|
|
|
+ info->ntotalchunks = 1;
|
|
info->chunk_size = 2048;
|
|
info->chunk_size = 2048;
|
|
info->spare_size = 40;
|
|
info->spare_size = 40;
|
|
info->ecc_size = 24;
|
|
info->ecc_size = 24;
|
|
@@ -1310,6 +1428,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|
ecc->strength = 1;
|
|
ecc->strength = 1;
|
|
|
|
|
|
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
|
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
|
|
|
|
+ info->nfullchunks = 1;
|
|
|
|
+ info->ntotalchunks = 1;
|
|
info->chunk_size = 512;
|
|
info->chunk_size = 512;
|
|
info->spare_size = 8;
|
|
info->spare_size = 8;
|
|
info->ecc_size = 8;
|
|
info->ecc_size = 8;
|
|
@@ -1323,6 +1443,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|
*/
|
|
*/
|
|
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) {
|
|
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) {
|
|
info->ecc_bch = 1;
|
|
info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 1;
|
|
|
|
+ info->ntotalchunks = 1;
|
|
info->chunk_size = 2048;
|
|
info->chunk_size = 2048;
|
|
info->spare_size = 32;
|
|
info->spare_size = 32;
|
|
info->ecc_size = 32;
|
|
info->ecc_size = 32;
|
|
@@ -1333,6 +1455,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|
|
|
|
|
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
|
|
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
|
|
info->ecc_bch = 1;
|
|
info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 2;
|
|
|
|
+ info->ntotalchunks = 2;
|
|
info->chunk_size = 2048;
|
|
info->chunk_size = 2048;
|
|
info->spare_size = 32;
|
|
info->spare_size = 32;
|
|
info->ecc_size = 32;
|
|
info->ecc_size = 32;
|
|
@@ -1341,19 +1465,64 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
|
|
ecc->layout = &ecc_layout_4KB_bch4bit;
|
|
ecc->layout = &ecc_layout_4KB_bch4bit;
|
|
ecc->strength = 16;
|
|
ecc->strength = 16;
|
|
|
|
|
|
|
|
+ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 8192) {
|
|
|
|
+ info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 4;
|
|
|
|
+ info->ntotalchunks = 4;
|
|
|
|
+ info->chunk_size = 2048;
|
|
|
|
+ info->spare_size = 32;
|
|
|
|
+ info->ecc_size = 32;
|
|
|
|
+ ecc->mode = NAND_ECC_HW;
|
|
|
|
+ ecc->size = info->chunk_size;
|
|
|
|
+ ecc->layout = &ecc_layout_8KB_bch4bit;
|
|
|
|
+ ecc->strength = 16;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Required ECC: 8-bit correction per 512 bytes
|
|
* Required ECC: 8-bit correction per 512 bytes
|
|
* Select: 16-bit correction per 1024 bytes
|
|
* Select: 16-bit correction per 1024 bytes
|
|
*/
|
|
*/
|
|
|
|
+ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 2048) {
|
|
|
|
+ info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 1;
|
|
|
|
+ info->ntotalchunks = 2;
|
|
|
|
+ info->chunk_size = 1024;
|
|
|
|
+ info->spare_size = 0;
|
|
|
|
+ info->last_chunk_size = 1024;
|
|
|
|
+ info->last_spare_size = 64;
|
|
|
|
+ info->ecc_size = 32;
|
|
|
|
+ ecc->mode = NAND_ECC_HW;
|
|
|
|
+ ecc->size = info->chunk_size;
|
|
|
|
+ ecc->layout = &ecc_layout_2KB_bch8bit;
|
|
|
|
+ ecc->strength = 16;
|
|
|
|
+
|
|
} else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) {
|
|
} else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) {
|
|
info->ecc_bch = 1;
|
|
info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 4;
|
|
|
|
+ info->ntotalchunks = 5;
|
|
info->chunk_size = 1024;
|
|
info->chunk_size = 1024;
|
|
info->spare_size = 0;
|
|
info->spare_size = 0;
|
|
|
|
+ info->last_chunk_size = 0;
|
|
|
|
+ info->last_spare_size = 64;
|
|
info->ecc_size = 32;
|
|
info->ecc_size = 32;
|
|
ecc->mode = NAND_ECC_HW;
|
|
ecc->mode = NAND_ECC_HW;
|
|
ecc->size = info->chunk_size;
|
|
ecc->size = info->chunk_size;
|
|
ecc->layout = &ecc_layout_4KB_bch8bit;
|
|
ecc->layout = &ecc_layout_4KB_bch8bit;
|
|
ecc->strength = 16;
|
|
ecc->strength = 16;
|
|
|
|
+
|
|
|
|
+ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 8192) {
|
|
|
|
+ info->ecc_bch = 1;
|
|
|
|
+ info->nfullchunks = 8;
|
|
|
|
+ info->ntotalchunks = 9;
|
|
|
|
+ info->chunk_size = 1024;
|
|
|
|
+ info->spare_size = 0;
|
|
|
|
+ info->last_chunk_size = 0;
|
|
|
|
+ info->last_spare_size = 160;
|
|
|
|
+ info->ecc_size = 32;
|
|
|
|
+ ecc->mode = NAND_ECC_HW;
|
|
|
|
+ ecc->size = info->chunk_size;
|
|
|
|
+ ecc->layout = &ecc_layout_8KB_bch8bit;
|
|
|
|
+ ecc->strength = 16;
|
|
|
|
+
|
|
} else {
|
|
} else {
|
|
dev_err(&info->pdev->dev,
|
|
dev_err(&info->pdev->dev,
|
|
"ECC strength %d at page size %d is not supported\n",
|
|
"ECC strength %d at page size %d is not supported\n",
|
|
@@ -1373,21 +1542,21 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|
int ret;
|
|
int ret;
|
|
uint16_t ecc_strength, ecc_step;
|
|
uint16_t ecc_strength, ecc_step;
|
|
|
|
|
|
- if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
|
|
|
- goto KEEP_CONFIG;
|
|
|
|
-
|
|
|
|
- /* Set a default chunk size */
|
|
|
|
- info->chunk_size = 512;
|
|
|
|
-
|
|
|
|
- ret = pxa3xx_nand_sensing(host);
|
|
|
|
- if (ret) {
|
|
|
|
- dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
|
|
|
|
- info->cs);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
|
|
+ if (pdata->keep_config) {
|
|
|
|
+ pxa3xx_nand_detect_config(info);
|
|
|
|
+ } else {
|
|
|
|
+ ret = pxa3xx_nand_config_ident(info);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ ret = pxa3xx_nand_sensing(host);
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_info(&info->pdev->dev,
|
|
|
|
+ "There is no chip on cs %d!\n",
|
|
|
|
+ info->cs);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-KEEP_CONFIG:
|
|
|
|
/* Device detection must be done with ECC disabled */
|
|
/* Device detection must be done with ECC disabled */
|
|
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
|
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
|
nand_writel(info, NDECCCTRL, 0x0);
|
|
nand_writel(info, NDECCCTRL, 0x0);
|
|
@@ -1404,10 +1573,6 @@ KEEP_CONFIG:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- ret = pxa3xx_nand_config_flash(info);
|
|
|
|
- if (ret)
|
|
|
|
- return ret;
|
|
|
|
-
|
|
|
|
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
|
|
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
|
|
/*
|
|
/*
|
|
* We'll use a bad block table stored in-flash and don't
|
|
* We'll use a bad block table stored in-flash and don't
|
|
@@ -1418,21 +1583,6 @@ KEEP_CONFIG:
|
|
chip->bbt_md = &bbt_mirror_descr;
|
|
chip->bbt_md = &bbt_mirror_descr;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- /*
|
|
|
|
- * If the page size is bigger than the FIFO size, let's check
|
|
|
|
- * we are given the right variant and then switch to the extended
|
|
|
|
- * (aka splitted) command handling,
|
|
|
|
- */
|
|
|
|
- if (mtd->writesize > PAGE_CHUNK_SIZE) {
|
|
|
|
- if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) {
|
|
|
|
- chip->cmdfunc = nand_cmdfunc_extended;
|
|
|
|
- } else {
|
|
|
|
- dev_err(&info->pdev->dev,
|
|
|
|
- "unsupported page size on this variant\n");
|
|
|
|
- return -ENODEV;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (pdata->ecc_strength && pdata->ecc_step_size) {
|
|
if (pdata->ecc_strength && pdata->ecc_step_size) {
|
|
ecc_strength = pdata->ecc_strength;
|
|
ecc_strength = pdata->ecc_strength;
|
|
ecc_step = pdata->ecc_step_size;
|
|
ecc_step = pdata->ecc_step_size;
|
|
@@ -1452,6 +1602,21 @@ KEEP_CONFIG:
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If the page size is bigger than the FIFO size, let's check
|
|
|
|
+ * we are given the right variant and then switch to the extended
|
|
|
|
+ * (aka split) command handling,
|
|
|
|
+ */
|
|
|
|
+ if (mtd->writesize > info->chunk_size) {
|
|
|
|
+ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) {
|
|
|
|
+ chip->cmdfunc = nand_cmdfunc_extended;
|
|
|
|
+ } else {
|
|
|
|
+ dev_err(&info->pdev->dev,
|
|
|
|
+ "unsupported page size on this variant\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/* calculate addressing information */
|
|
/* calculate addressing information */
|
|
if (mtd->writesize >= 2048)
|
|
if (mtd->writesize >= 2048)
|
|
host->col_addr_cycles = 2;
|
|
host->col_addr_cycles = 2;
|
|
@@ -1472,6 +1637,10 @@ KEEP_CONFIG:
|
|
host->row_addr_cycles = 3;
|
|
host->row_addr_cycles = 3;
|
|
else
|
|
else
|
|
host->row_addr_cycles = 2;
|
|
host->row_addr_cycles = 2;
|
|
|
|
+
|
|
|
|
+ if (!pdata->keep_config)
|
|
|
|
+ pxa3xx_nand_config_tail(info);
|
|
|
|
+
|
|
return nand_scan_tail(mtd);
|
|
return nand_scan_tail(mtd);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1494,10 +1663,8 @@ static int alloc_nand_resource(struct pxa3xx_nand_info *info)
|
|
mtd = nand_to_mtd(chip);
|
|
mtd = nand_to_mtd(chip);
|
|
host = (struct pxa3xx_nand_host *)chip;
|
|
host = (struct pxa3xx_nand_host *)chip;
|
|
info->host[cs] = host;
|
|
info->host[cs] = host;
|
|
- host->mtd = mtd;
|
|
|
|
host->cs = cs;
|
|
host->cs = cs;
|
|
host->info_data = info;
|
|
host->info_data = info;
|
|
- host->read_id_bytes = 4;
|
|
|
|
mtd->owner = THIS_MODULE;
|
|
mtd->owner = THIS_MODULE;
|
|
|
|
|
|
nand_set_controller_data(chip, host);
|
|
nand_set_controller_data(chip, host);
|
|
@@ -1612,7 +1779,7 @@ static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info)
|
|
|
|
|
|
probe_success = 0;
|
|
probe_success = 0;
|
|
for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
- struct mtd_info *mtd = info->host[cs]->mtd;
|
|
|
|
|
|
+ struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip);
|
|
|
|
|
|
/*
|
|
/*
|
|
* The mtd name matches the one used in 'mtdparts' kernel
|
|
* The mtd name matches the one used in 'mtdparts' kernel
|