diff options
Diffstat (limited to 'arch_msm7k/nand.c')
-rw-r--r-- | arch_msm7k/nand.c | 630 |
1 files changed, 0 insertions, 630 deletions
diff --git a/arch_msm7k/nand.c b/arch_msm7k/nand.c deleted file mode 100644 index ea123f0..0000000 --- a/arch_msm7k/nand.c +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright (c) 2008, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <boot/boot.h> -#include <boot/flash.h> - -#include <msm7k/dmov.h> -#include <msm7k/nand.h> -#include <msm7k/shared.h> - -#define VERBOSE 0 - -typedef struct dmov_ch dmov_ch; -struct dmov_ch -{ - volatile unsigned cmd; - volatile unsigned result; - volatile unsigned status; - volatile unsigned config; -}; - -static void dmov_prep_ch(dmov_ch *ch, unsigned id) -{ - ch->cmd = DMOV_CMD_PTR(id); - ch->result = DMOV_RSLT(id); - ch->status = DMOV_STATUS(id); - ch->config = DMOV_CONFIG(id); -} - -#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) -#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD) -#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA) -#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA) - -static unsigned CFG0, CFG1; - -#define CFG1_WIDE_FLASH (1U << 1) - -#define paddr(n) ((unsigned) (n)) - -static int dmov_exec_cmdptr(unsigned id, unsigned *ptr) -{ - dmov_ch ch; - unsigned n; - - dmov_prep_ch(&ch, id); - - writel(DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(paddr(ptr)), ch.cmd); - - while(!(readl(ch.status) & DMOV_STATUS_RSLT_VALID)) ; - - n = readl(ch.status); - while(DMOV_STATUS_RSLT_COUNT(n)) { - n = readl(ch.result); - if(n != 0x80000002) { - dprintf("ERROR: result: %x\n", n); - dprintf("ERROR: flush: %x %x %x %x\n", - readl(DMOV_FLUSH0(DMOV_NAND_CHAN)), - readl(DMOV_FLUSH1(DMOV_NAND_CHAN)), - readl(DMOV_FLUSH2(DMOV_NAND_CHAN)), - readl(DMOV_FLUSH3(DMOV_NAND_CHAN))); - } - n = readl(ch.status); - } - - return 0; -} - -static unsigned flash_maker = 0; -static unsigned flash_device = 0; - -static void flash_read_id(dmov_s *cmdlist, unsigned *ptrlist) -{ - dmov_s *cmd = cmdlist; - unsigned *ptr = ptrlist; - unsigned *data = ptrlist + 4; - - data[0] = 0 | 4; - data[1] = NAND_CMD_FETCH_ID; - data[2] = 1; - data[3] = 0; - data[4] = 0; - - cmd[0].cmd = 0 | CMD_OCB; - cmd[0].src = paddr(&data[0]); - cmd[0].dst = NAND_FLASH_CHIP_SELECT; - cmd[0].len = 4; - - cmd[1].cmd = DST_CRCI_NAND_CMD; - cmd[1].src = paddr(&data[1]); - cmd[1].dst = NAND_FLASH_CMD; - cmd[1].len = 4; - - cmd[2].cmd = 0; - cmd[2].src = paddr(&data[2]); - cmd[2].dst = NAND_EXEC_CMD; - cmd[2].len = 4; - - cmd[3].cmd = SRC_CRCI_NAND_DATA; - cmd[3].src = NAND_FLASH_STATUS; - cmd[3].dst = paddr(&data[3]); - cmd[3].len = 4; - - cmd[4].cmd = CMD_OCU | CMD_LC; - cmd[4].src = NAND_READ_ID; - cmd[4].dst = paddr(&data[4]); - cmd[4].len = 4; - - ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP; - - dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr); - -#if VERBOSE - dprintf("status: %x\n", data[3]); -#endif - dprintf("nandid: %x maker %b device %b\n", - data[4], data[4] & 0xff, (data[4] >> 8) & 0xff); - - flash_maker = data[4] & 0xff; - flash_device = (data[4] >> 8) & 0xff; -} - -static int flash_erase_block(dmov_s *cmdlist, unsigned *ptrlist, unsigned page) -{ - dmov_s *cmd = cmdlist; - unsigned *ptr = ptrlist; - unsigned *data = ptrlist + 4; - - /* only allow erasing on block boundaries */ - if(page & 63) return -1; - - data[0] = NAND_CMD_BLOCK_ERASE; - data[1] = page; - data[2] = 0; - data[3] = 0 | 4; - data[4] = 1; - data[5] = 0xeeeeeeee; - data[6] = CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ - data[7] = CFG1; - - cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB; - cmd[0].src = paddr(&data[0]); - cmd[0].dst = NAND_FLASH_CMD; - cmd[0].len = 16; - - cmd[1].cmd = 0; - cmd[1].src = paddr(&data[6]); - cmd[1].dst = NAND_DEV0_CFG0; - cmd[1].len = 8; - - cmd[2].cmd = 0; - cmd[2].src = paddr(&data[4]); - cmd[2].dst = NAND_EXEC_CMD; - cmd[2].len = 4; - - cmd[3].cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC; - cmd[3].src = NAND_FLASH_STATUS; - cmd[3].dst = paddr(&data[5]); - cmd[3].len = 4; - - ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP; - - dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr); - -#if VERBOSE - dprintf("status: %x\n", data[5]); -#endif - - /* we fail if there was an operation error, a mpu error, or the - ** erase success bit was not set. - */ - if(data[5] & 0x110) return -1; - if(!(data[5] & 0x80)) return -1; - - return 0; -} - -struct data_flash_io { - unsigned cmd; - unsigned addr0; - unsigned addr1; - unsigned chipsel; - unsigned cfg0; - unsigned cfg1; - unsigned exec; - unsigned ecc_cfg; - unsigned ecc_cfg_save; - struct { - unsigned flash_status; - unsigned buffer_status; - } result[4]; -}; - -static int _flash_read_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page, void *_addr, void *_spareaddr) -{ - dmov_s *cmd = cmdlist; - unsigned *ptr = ptrlist; - struct data_flash_io *data = (void*) (ptrlist + 4); - unsigned addr = (unsigned) _addr; - unsigned spareaddr = (unsigned) _spareaddr; - unsigned n; - - data->cmd = NAND_CMD_PAGE_READ_ECC; - data->addr0 = page << 16; - data->addr1 = (page >> 16) & 0xff; - data->chipsel = 0 | 4; /* flash0 + undoc bit */ - - /* GO bit for the EXEC register */ - data->exec = 1; - - data->cfg0 = CFG0; - data->cfg1 = CFG1; - - data->ecc_cfg = 0x203; - - /* save existing ecc config */ - cmd->cmd = CMD_OCB; - cmd->src = NAND_EBI2_ECC_BUF_CFG; - cmd->dst = paddr(&data->ecc_cfg_save); - cmd->len = 4; - cmd++; - - for(n = 0; n < 4; n++) { - /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */ - cmd->cmd = DST_CRCI_NAND_CMD; - cmd->src = paddr(&data->cmd); - cmd->dst = NAND_FLASH_CMD; - cmd->len = ((n == 0) ? 16 : 4); - cmd++; - - if (n == 0) { - /* block on cmd ready, set configuration */ - cmd->cmd = 0; - cmd->src = paddr(&data->cfg0); - cmd->dst = NAND_DEV0_CFG0; - cmd->len = 8; - cmd++; - - /* set our ecc config */ - cmd->cmd = 0; - cmd->src = paddr(&data->ecc_cfg); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; - cmd->len = 4; - cmd++; - } - /* kick the execute register */ - cmd->cmd = 0; - cmd->src = paddr(&data->exec); - cmd->dst = NAND_EXEC_CMD; - cmd->len = 4; - cmd++; - - /* block on data ready, then read the status register */ - cmd->cmd = SRC_CRCI_NAND_DATA; - cmd->src = NAND_FLASH_STATUS; - cmd->dst = paddr(&data->result[n]); - cmd->len = 8; - cmd++; - - /* read data block */ - cmd->cmd = 0; - cmd->src = NAND_FLASH_BUFFER; - cmd->dst = addr + n * 516; - cmd->len = ((n < 3) ? 516 : 500); - cmd++; - } - - /* read extra data */ - cmd->cmd = 0; - cmd->src = NAND_FLASH_BUFFER + 500; - cmd->dst = spareaddr; - cmd->len = 16; - cmd++; - - /* restore saved ecc config */ - cmd->cmd = CMD_OCU | CMD_LC; - cmd->src = paddr(&data->ecc_cfg_save); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; - cmd->len = 4; - - ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP; - - dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr); - -#if VERBOSE - dprintf("read page %d: status: %x %x %x %x\n", - page, data[5], data[6], data[7], data[8]); - for(n = 0; n < 4; n++) { - ptr = (unsigned*)(addr + 512 * n); - dprintf("data%d: %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]); - ptr = (unsigned*)(spareaddr + 16 * n); - dprintf("spare data%d %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]); - } -#endif - - /* if any of the writes failed (0x10), or there was a - ** protection violation (0x100), we lose - */ - for(n = 0; n < 4; n++) { - if (data->result[n].flash_status & 0x110) { - return -1; - } - } - - return 0; -} - -static int _flash_write_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page, - const void *_addr, const void *_spareaddr) -{ - dmov_s *cmd = cmdlist; - unsigned *ptr = ptrlist; - struct data_flash_io *data = (void*) (ptrlist + 4); - unsigned addr = (unsigned) _addr; - unsigned spareaddr = (unsigned) _spareaddr; - unsigned n; - - data->cmd = NAND_CMD_PRG_PAGE; - data->addr0 = page << 16; - data->addr1 = (page >> 16) & 0xff; - data->chipsel = 0 | 4; /* flash0 + undoc bit */ - - data->cfg0 = CFG0; - data->cfg1 = CFG1; - - /* GO bit for the EXEC register */ - data->exec = 1; - - data->ecc_cfg = 0x203; - - /* save existing ecc config */ - cmd->cmd = CMD_OCB; - cmd->src = NAND_EBI2_ECC_BUF_CFG; - cmd->dst = paddr(&data->ecc_cfg_save); - cmd->len = 4; - cmd++; - - for(n = 0; n < 4; n++) { - /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */ - cmd->cmd = DST_CRCI_NAND_CMD; - cmd->src = paddr(&data->cmd); - cmd->dst = NAND_FLASH_CMD; - cmd->len = ((n == 0) ? 16 : 4); - cmd++; - - if (n == 0) { - /* set configuration */ - cmd->cmd = 0; - cmd->src = paddr(&data->cfg0); - cmd->dst = NAND_DEV0_CFG0; - cmd->len = 8; - cmd++; - - /* set our ecc config */ - cmd->cmd = 0; - cmd->src = paddr(&data->ecc_cfg); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; - cmd->len = 4; - cmd++; - } - - /* write data block */ - cmd->cmd = 0; - cmd->src = addr + n * 516; - cmd->dst = NAND_FLASH_BUFFER; - cmd->len = ((n < 3) ? 516 : 510); - cmd++; - - if (n == 3) { - /* write extra data */ - cmd->cmd = 0; - cmd->src = spareaddr; - cmd->dst = NAND_FLASH_BUFFER + 500; - cmd->len = 16; - cmd++; - } - - /* kick the execute register */ - cmd->cmd = 0; - cmd->src = paddr(&data->exec); - cmd->dst = NAND_EXEC_CMD; - cmd->len = 4; - cmd++; - - /* block on data ready, then read the status register */ - cmd->cmd = SRC_CRCI_NAND_DATA; - cmd->src = NAND_FLASH_STATUS; - cmd->dst = paddr(&data->result[n]); - cmd->len = 8; - cmd++; - } - - /* restore saved ecc config */ - cmd->cmd = CMD_OCU | CMD_LC; - cmd->src = paddr(&data->ecc_cfg_save); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; - cmd->len = 4; - - ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP; - - dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr); - -#if VERBOSE - dprintf("write page %d: status: %x %x %x %x\n", - page, data[5], data[6], data[7], data[8]); -#endif - - /* if any of the writes failed (0x10), or there was a - ** protection violation (0x100), or the program success - ** bit (0x80) is unset, we lose - */ - for(n = 0; n < 4; n++) { - if(data->result[n].flash_status & 0x110) return -1; - if(!(data->result[n].flash_status & 0x80)) return -1; - } - - return 0; -} - -unsigned nand_cfg0; -unsigned nand_cfg1; - -static int flash_read_config(dmov_s *cmdlist, unsigned *ptrlist) -{ - cmdlist[0].cmd = CMD_OCB; - cmdlist[0].src = NAND_DEV0_CFG0; - cmdlist[0].dst = paddr(&CFG0); - cmdlist[0].len = 4; - - cmdlist[1].cmd = CMD_OCU | CMD_LC; - cmdlist[1].src = NAND_DEV0_CFG1; - cmdlist[1].dst = paddr(&CFG1); - cmdlist[1].len = 4; - - *ptrlist = (paddr(cmdlist) >> 3) | CMD_PTR_LP; - - dmov_exec_cmdptr(DMOV_NAND_CHAN, ptrlist); - - if((CFG0 == 0) || (CFG1 == 0)) { - return -1; - } - - dprintf("nandcfg: %x %x (initial)\n", CFG0, CFG1); - - CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */ - | (516 << 9) /* 516 user data bytes */ - | (10 << 19) /* 10 parity bytes */ - | (5 << 27) /* 5 address cycles */ - | (1 << 30) /* Read status before data */ - | (1 << 31) /* Send read cmd */ - /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ - | ((nand_cfg1 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); - CFG1 = (0 << 0) /* Enable ecc */ - | (7 << 2) /* 8 recovery cycles */ - | (0 << 5) /* Allow CS deassertion */ - | (465 << 6) /* Bad block marker location */ - | (0 << 16) /* Bad block in user data area */ - | (2 << 17) /* 6 cycle tWB/tRB */ - | (nand_cfg1 & CFG1_WIDE_FLASH); /* preserve wide flash flag */ - - dprintf("nandcfg: %x %x (used)\n", CFG0, CFG1); - - return 0; -} - -static unsigned *flash_ptrlist; -static dmov_s *flash_cmdlist; -static void *flash_spare; -static void *flash_data; - -int flash_init(void) -{ - flash_ptrlist = alloc(1024); - flash_cmdlist = alloc(1024); - flash_data = alloc(2048); - flash_spare = alloc(64); - - if(flash_read_config(flash_cmdlist, flash_ptrlist)) { - dprintf("ERROR: could not read CFG0/CFG1 state\n"); - for(;;); - } - - flash_read_id(flash_cmdlist, flash_ptrlist); - - return 0; -} - -int flash_erase(ptentry *ptn) -{ - unsigned block = ptn->start; - unsigned count = ptn->length; - - while(count-- > 0) { - if(flash_erase_block(flash_cmdlist, flash_ptrlist, block * 64)) { - dprintf("cannot erase @ %d (bad block?)\n", block); - } - block++; - } - return 0; -} - -int flash_read_ext(ptentry *ptn, unsigned extra_per_page, unsigned offset, void *data, unsigned bytes) -{ - unsigned page = (ptn->start * 64) + (offset / 2048); - unsigned lastpage = (ptn->start + ptn->length) * 64; - unsigned count = (bytes + 2047 + extra_per_page) / (2048 + extra_per_page); - unsigned *spare = (unsigned*) flash_spare; - unsigned errors = 0; - unsigned char *image = data; - - if(offset & 2047) - return -1; - - while(page < lastpage) { - if(count == 0) { - dprintf("flash_read_image: success (%d errors)\n", errors); - return 0; - } - - if(_flash_read_page(flash_cmdlist, flash_ptrlist, page++, image, spare)) { - errors++; - continue; - } - image += 2048; - memcpy(image, spare, extra_per_page); - image += extra_per_page; - count -= 1; - } - - /* could not find enough valid pages before we hit the end */ - dprintf("flash_read_image: failed (%d errors)\n", errors); - return 0xffffffff; -} - -int flash_write(ptentry *ptn, unsigned extra_per_page, const void *data, unsigned bytes) -{ - unsigned page = ptn->start * 64; - unsigned lastpage = (ptn->start + ptn->length) * 64; - unsigned *spare = (unsigned*) flash_spare; - const unsigned char *image = data; - unsigned wsize = 2048 + extra_per_page; - unsigned n; - int r; - - for(n = 0; n < 16; n++) spare[n] = 0xffffffff; - - while(bytes > 0) { - if(bytes < wsize) { - dprintf("flash_write_image: image undersized (%d < %d)\n", bytes, wsize); - return -1; - } - if(page >= lastpage) { - dprintf("flash_write_image: out of space\n"); - return -1; - } - - if((page & 63) == 0) { - if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) { - dprintf("flash_write_image: bad block @ %d\n", page >> 6); - page += 64; - continue; - } - } - - if(extra_per_page) { - r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, image + 2048); - } else { - r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, spare); - } - if(r) { - dprintf("flash_write_image: write failure @ page %d (src %d)\n", page, image - (const unsigned char *)data); - image -= (page & 63) * wsize; - bytes += (page & 63) * wsize; - page &= ~63; - if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) { - dprintf("flash_write_image: erase failure @ page %d\n", page); - } - dprintf("flash_write_image: restart write @ page %d (src %d)\n", page, image - (const unsigned char *)data); - page += 64; - continue; - } - - image += wsize; - bytes -= wsize; - } - - /* erase any remaining pages in the partition */ - page = (page + 63) & (~63); - while(page < lastpage){ - if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) { - dprintf("flash_write_image: bad block @ %d\n", page >> 6); - } - page += 64; - } - - dprintf("flash_write_image: success\n"); - return 0; -} - -static int flash_read_page(unsigned page, void *data, void *extra) -{ - return _flash_read_page(flash_cmdlist, flash_ptrlist, - page, data, extra); -} - |