123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2014 Panasonic Corporation
- * Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
- */
- #include <common.h>
- #include <asm/io.h>
- #include <asm/unaligned.h>
- #include <linux/mtd/rawnand.h>
- #include "denali.h"
- #define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
- #define DENALI_MAP10 (2 << 26) /* high-level control plane */
- #define INDEX_CTRL_REG 0x0
- #define INDEX_DATA_REG 0x10
- #define SPARE_ACCESS 0x41
- #define MAIN_ACCESS 0x42
- #define PIPELINE_ACCESS 0x2000
- #define BANK(x) ((x) << 24)
- static void __iomem *denali_flash_mem =
- (void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
- static void __iomem *denali_flash_reg =
- (void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
- static const int flash_bank;
- static int page_size, oob_size, pages_per_block;
- static void index_addr(uint32_t address, uint32_t data)
- {
- writel(address, denali_flash_mem + INDEX_CTRL_REG);
- writel(data, denali_flash_mem + INDEX_DATA_REG);
- }
- static int wait_for_irq(uint32_t irq_mask)
- {
- unsigned long timeout = 1000000;
- uint32_t intr_status;
- do {
- intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
- if (intr_status & INTR__ECC_UNCOR_ERR) {
- debug("Uncorrected ECC detected\n");
- return -EBADMSG;
- }
- if (intr_status & irq_mask)
- break;
- udelay(1);
- timeout--;
- } while (timeout);
- if (!timeout) {
- debug("Timeout with interrupt status %08x\n", intr_status);
- return -EIO;
- }
- return 0;
- }
- static void read_data_from_flash_mem(uint8_t *buf, int len)
- {
- int i;
- uint32_t *buf32;
- /* transfer the data from the flash */
- buf32 = (uint32_t *)buf;
- /*
- * Let's take care of unaligned access although it rarely happens.
- * Avoid put_unaligned() for the normal use cases since it leads to
- * a bit performance regression.
- */
- if ((unsigned long)buf32 % 4) {
- for (i = 0; i < len / 4; i++)
- put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG),
- buf32++);
- } else {
- for (i = 0; i < len / 4; i++)
- *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
- }
- if (len % 4) {
- u32 tmp;
- tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG));
- buf = (uint8_t *)buf32;
- for (i = 0; i < len % 4; i++) {
- *buf++ = tmp;
- tmp >>= 8;
- }
- }
- }
- int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
- {
- uint32_t addr, cmd;
- static uint32_t page_count = 1;
- writel(ecc_en, denali_flash_reg + ECC_ENABLE);
- /* clear all bits of intr_status. */
- writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
- addr = BANK(flash_bank) | page;
- /* setup the acccess type */
- cmd = DENALI_MAP10 | addr;
- index_addr(cmd, access_type);
- /* setup the pipeline command */
- index_addr(cmd, PIPELINE_ACCESS | page_count);
- cmd = DENALI_MAP01 | addr;
- writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
- return wait_for_irq(INTR__LOAD_COMP);
- }
- static int nand_read_oob(void *buf, int page)
- {
- int ret;
- ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
- if (ret < 0)
- return ret;
- read_data_from_flash_mem(buf, oob_size);
- return 0;
- }
- static int nand_read_page(void *buf, int page)
- {
- int ret;
- ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
- if (ret < 0)
- return ret;
- read_data_from_flash_mem(buf, page_size);
- return 0;
- }
- static int nand_block_isbad(void *buf, int block)
- {
- int ret;
- ret = nand_read_oob(buf, block * pages_per_block);
- if (ret < 0)
- return ret;
- return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff;
- }
- /* nand_init() - initialize data to make nand usable by SPL */
- void nand_init(void)
- {
- /* access to main area */
- writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
- /*
- * These registers are expected to be already set by the hardware
- * or earlier boot code. So we read these values out.
- */
- page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
- oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
- pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
- }
- int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
- {
- int block, page, column, readlen;
- int ret;
- int force_bad_block_check = 1;
- page = offs / page_size;
- column = offs % page_size;
- block = page / pages_per_block;
- page = page % pages_per_block;
- while (size) {
- if (force_bad_block_check || page == 0) {
- ret = nand_block_isbad(dst, block);
- if (ret < 0)
- return ret;
- if (ret) {
- block++;
- continue;
- }
- }
- force_bad_block_check = 0;
- ret = nand_read_page(dst, block * pages_per_block + page);
- if (ret < 0)
- return ret;
- readlen = min(page_size - column, (int)size);
- if (unlikely(column)) {
- /* Partial page read */
- memmove(dst, dst + column, readlen);
- column = 0;
- }
- size -= readlen;
- dst += readlen;
- page++;
- if (page == pages_per_block) {
- block++;
- page = 0;
- }
- }
- return 0;
- }
- void nand_deselect(void) {}
|