diff options
Diffstat (limited to 'target/linux/mvebu/patches-3.10/0151-mtd-nand-pxa3xx-Introduce-multiple-page-I-O-support.patch')
-rw-r--r-- | target/linux/mvebu/patches-3.10/0151-mtd-nand-pxa3xx-Introduce-multiple-page-I-O-support.patch | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-3.10/0151-mtd-nand-pxa3xx-Introduce-multiple-page-I-O-support.patch b/target/linux/mvebu/patches-3.10/0151-mtd-nand-pxa3xx-Introduce-multiple-page-I-O-support.patch new file mode 100644 index 0000000000..f8e3c8796d --- /dev/null +++ b/target/linux/mvebu/patches-3.10/0151-mtd-nand-pxa3xx-Introduce-multiple-page-I-O-support.patch @@ -0,0 +1,325 @@ +From cfd1799f9ec5c9820f371e1fcf2f3c458bd24ebb Mon Sep 17 00:00:00 2001 +From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> +Date: Thu, 14 Nov 2013 18:25:37 -0300 +Subject: [PATCH 151/203] mtd: nand: pxa3xx: Introduce multiple page I/O + support + +As preparation work to fully support large pages, this commit adds +the initial infrastructure to support splitted (aka chunked) I/O +operation. This commit adds support for read, and follow-up patches +will add write support. + +When a read (aka READ0) command is issued, the driver loops issuing +the same command until all the requested data is transfered, changing +the 'extended' command field as needed. + +For instance, if the driver is required to read a 4 KiB page, using a +chunk size of 2 KiB, the transaction is splitted in: +1. Monolithic read, first 2 KiB page chunk is read +2. Last naked read, second and last 2KiB page chunk is read + +If ECC is enabled it is calculated on each chunk transfered and added +at a controller-fixed location after the data chunk that must be +spare area. + +Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> +Tested-by: Daniel Mack <zonque@gmail.com> +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/nand/pxa3xx_nand.c | 182 ++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 172 insertions(+), 10 deletions(-) + +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -103,6 +103,8 @@ + #define NDCB0_ST_ROW_EN (0x1 << 26) + #define NDCB0_AUTO_RS (0x1 << 25) + #define NDCB0_CSEL (0x1 << 24) ++#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) ++#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) + #define NDCB0_CMD_TYPE_MASK (0x7 << 21) + #define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) + #define NDCB0_NC (0x1 << 20) +@@ -113,6 +115,14 @@ + #define NDCB0_CMD1_MASK (0xff) + #define NDCB0_ADDR_CYC_SHIFT (16) + ++#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ ++#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ ++#define EXT_CMD_TYPE_READ 4 /* Read */ ++#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ ++#define EXT_CMD_TYPE_FINAL 3 /* Final command */ ++#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ ++#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ ++ + /* macros for registers read/write */ + #define nand_writel(info, off, val) \ + __raw_writel((val), (info)->mmio_base + (off)) +@@ -206,8 +216,8 @@ struct pxa3xx_nand_info { + int use_spare; /* use spare ? */ + int need_wait; + +- unsigned int fifo_size; /* max. data size in the FIFO */ + unsigned int data_size; /* data to be read from FIFO */ ++ unsigned int chunk_size; /* split commands chunk size */ + unsigned int oob_size; + unsigned int spare_size; + unsigned int ecc_size; +@@ -271,6 +281,31 @@ static struct nand_bbt_descr bbt_mirror_ + .pattern = bbt_mirror_pattern + }; + ++static struct nand_ecclayout ecc_layout_4KB_bch4bit = { ++ .eccbytes = 64, ++ .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}, ++ /* Bootrom looks in bytes 0 & 5 for bad blocks */ ++ .oobfree = { {6, 26}, { 64, 32} } ++}; ++ ++static struct nand_ecclayout ecc_layout_4KB_bch8bit = { ++ .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}, ++ .oobfree = { } ++}; ++ + /* Define a default flash type setting serve as flash detecting only */ + #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0]) + +@@ -433,7 +468,7 @@ static void disable_int(struct pxa3xx_na + + static void handle_data_pio(struct pxa3xx_nand_info *info) + { +- unsigned int do_bytes = min(info->data_size, info->fifo_size); ++ unsigned int do_bytes = min(info->data_size, info->chunk_size); + + switch (info->state) { + case STATE_PIO_WRITING: +@@ -670,7 +705,7 @@ static void prepare_start_command(struct + } + + static int prepare_set_command(struct pxa3xx_nand_info *info, int command, +- uint16_t column, int page_addr) ++ int ext_cmd_type, uint16_t column, int page_addr) + { + int addr_cycle, exec_cmd; + struct pxa3xx_nand_host *host; +@@ -703,9 +738,20 @@ static int prepare_set_command(struct px + if (command == NAND_CMD_READOOB) + info->buf_start += mtd->writesize; + +- /* Second command setting for large pages */ +- if (mtd->writesize >= PAGE_CHUNK_SIZE) ++ /* ++ * Multiple page read needs an 'extended command type' field, ++ * which is either naked-read or last-read according to the ++ * state. ++ */ ++ if (mtd->writesize == PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); ++ } else if (mtd->writesize > PAGE_CHUNK_SIZE) { ++ info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) ++ | NDCB0_LEN_OVRD ++ | NDCB0_EXT_CMD_TYPE(ext_cmd_type); ++ info->ndcb3 = info->chunk_size + ++ info->oob_size; ++ } + + set_command_address(info, mtd->writesize, column, page_addr); + break; +@@ -821,7 +867,8 @@ static void pxa3xx_nand_cmdfunc(struct m + prepare_start_command(info, command); + + info->state = STATE_PREPARED; +- exec_cmd = prepare_set_command(info, command, column, page_addr); ++ exec_cmd = prepare_set_command(info, command, 0, column, page_addr); ++ + if (exec_cmd) { + init_completion(&info->cmd_complete); + init_completion(&info->dev_ready); +@@ -839,6 +886,93 @@ static void pxa3xx_nand_cmdfunc(struct m + info->state = STATE_IDLE; + } + ++static void armada370_nand_cmdfunc(struct mtd_info *mtd, ++ const unsigned command, ++ int column, int page_addr) ++{ ++ struct pxa3xx_nand_host *host = mtd->priv; ++ struct pxa3xx_nand_info *info = host->info_data; ++ int ret, exec_cmd, ext_cmd_type; ++ ++ /* ++ * if this is a x16 device then convert the input ++ * "byte" address into a "word" address appropriate ++ * for indexing a word-oriented device ++ */ ++ if (info->reg_ndcr & NDCR_DWIDTH_M) ++ column /= 2; ++ ++ /* ++ * There may be different NAND chip hooked to ++ * different chip select, so check whether ++ * chip select has been changed, if yes, reset the timing ++ */ ++ if (info->cs != host->cs) { ++ info->cs = host->cs; ++ nand_writel(info, NDTR0CS0, info->ndtr0cs0); ++ nand_writel(info, NDTR1CS0, info->ndtr1cs0); ++ } ++ ++ /* Select the extended command for the first command */ ++ switch (command) { ++ case NAND_CMD_READ0: ++ case NAND_CMD_READOOB: ++ ext_cmd_type = EXT_CMD_TYPE_MONO; ++ break; ++ default: ++ ext_cmd_type = 0; ++ } ++ ++ prepare_start_command(info, command); ++ ++ /* ++ * Prepare the "is ready" completion before starting a command ++ * transaction sequence. If the command is not executed the ++ * completion will be completed, see below. ++ * ++ * We can do that inside the loop because the command variable ++ * is invariant and thus so is the exec_cmd. ++ */ ++ info->need_wait = 1; ++ init_completion(&info->dev_ready); ++ do { ++ info->state = STATE_PREPARED; ++ exec_cmd = prepare_set_command(info, command, ext_cmd_type, ++ column, page_addr); ++ if (!exec_cmd) { ++ info->need_wait = 0; ++ complete(&info->dev_ready); ++ break; ++ } ++ ++ init_completion(&info->cmd_complete); ++ pxa3xx_nand_start(info); ++ ++ ret = wait_for_completion_timeout(&info->cmd_complete, ++ CHIP_DELAY_TIMEOUT); ++ if (!ret) { ++ dev_err(&info->pdev->dev, "Wait time out!!!\n"); ++ /* Stop State Machine for next command cycle */ ++ pxa3xx_nand_stop(info); ++ break; ++ } ++ ++ /* Check if the sequence is complete */ ++ if (info->data_size == 0) ++ break; ++ ++ if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { ++ /* Last read: issue a 'last naked read' */ ++ if (info->data_size == info->chunk_size) ++ ext_cmd_type = EXT_CMD_TYPE_LAST_RW; ++ else ++ ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; ++ } ++ } while (1); ++ ++ info->state = STATE_IDLE; ++} ++ + static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) + { +@@ -1019,13 +1153,14 @@ static int pxa3xx_nand_detect_config(str + + if (ndcr & NDCR_PAGE_SZ) { + /* Controller's FIFO size */ +- info->fifo_size = 2048; ++ info->chunk_size = 2048; + host->read_id_bytes = 4; + } else { +- info->fifo_size = 512; ++ info->chunk_size = 512; + host->read_id_bytes = 2; + } + ++ /* Set an initial chunk size */ + info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); +@@ -1129,6 +1264,7 @@ static int pxa_ecc_init(struct pxa3xx_na + * is used with non-ONFI compliant devices. + */ + if (page_size == 2048) { ++ info->chunk_size = 2048; + info->spare_size = 40; + info->ecc_size = 24; + ecc->mode = NAND_ECC_HW; +@@ -1137,6 +1273,7 @@ static int pxa_ecc_init(struct pxa3xx_na + return 1; + + } else if (page_size == 512) { ++ info->chunk_size = 512; + info->spare_size = 8; + info->ecc_size = 8; + ecc->mode = NAND_ECC_HW; +@@ -1151,7 +1288,28 @@ static int armada370_ecc_init(struct pxa + struct nand_ecc_ctrl *ecc, + int strength, int page_size) + { +- /* Unimplemented yet */ ++ if (strength == 4 && page_size == 4096) { ++ info->ecc_bch = 1; ++ 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_4KB_bch4bit; ++ ecc->strength = 16; ++ return 1; ++ ++ } else if (strength == 8 && page_size == 4096) { ++ info->ecc_bch = 1; ++ info->chunk_size = 1024; ++ info->spare_size = 0; ++ info->ecc_size = 32; ++ ecc->mode = NAND_ECC_HW; ++ ecc->size = info->chunk_size; ++ ecc->layout = &ecc_layout_4KB_bch8bit; ++ ecc->strength = 16; ++ return 1; ++ } + return 0; + } + +@@ -1319,12 +1477,16 @@ static int alloc_nand_resource(struct pl + chip->controller = &info->controller; + chip->waitfunc = pxa3xx_nand_waitfunc; + chip->select_chip = pxa3xx_nand_select_chip; +- chip->cmdfunc = pxa3xx_nand_cmdfunc; + chip->read_word = pxa3xx_nand_read_word; + chip->read_byte = pxa3xx_nand_read_byte; + chip->read_buf = pxa3xx_nand_read_buf; + chip->write_buf = pxa3xx_nand_write_buf; + chip->options |= NAND_NO_SUBPAGE_WRITE; ++ ++ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) ++ chip->cmdfunc = armada370_nand_cmdfunc; ++ else ++ chip->cmdfunc = pxa3xx_nand_cmdfunc; + } + + spin_lock_init(&chip->controller->lock); |