123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- /*
- * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <stdint.h>
- #include <platform_def.h>
- #include <arch_helpers.h>
- #include <common/debug.h>
- #include <drivers/io/io_block.h>
- #include <lib/mmio.h>
- #include <lib/utils_def.h>
- #include "uniphier.h"
- #define NAND_CMD_READ0 0
- #define NAND_CMD_READSTART 0x30
- #define DENALI_ECC_ENABLE 0x0e0
- #define DENALI_PAGES_PER_BLOCK 0x150
- #define DENALI_DEVICE_MAIN_AREA_SIZE 0x170
- #define DENALI_DEVICE_SPARE_AREA_SIZE 0x180
- #define DENALI_TWO_ROW_ADDR_CYCLES 0x190
- #define DENALI_INTR_STATUS0 0x410
- #define DENALI_INTR_ECC_UNCOR_ERR BIT(1)
- #define DENALI_INTR_DMA_CMD_COMP BIT(2)
- #define DENALI_INTR_INT_ACT BIT(12)
- #define DENALI_DMA_ENABLE 0x700
- #define DENALI_HOST_ADDR 0x00
- #define DENALI_HOST_DATA 0x10
- #define DENALI_MAP01 (1 << 26)
- #define DENALI_MAP10 (2 << 26)
- #define DENALI_MAP11 (3 << 26)
- #define DENALI_MAP11_CMD ((DENALI_MAP11) | 0)
- #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1)
- #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2)
- #define DENALI_ACCESS_DEFAULT_AREA 0x42
- #define UNIPHIER_NAND_BBT_UNKNOWN 0xff
- struct uniphier_nand {
- uintptr_t host_base;
- uintptr_t reg_base;
- int pages_per_block;
- int page_size;
- int two_row_addr_cycles;
- uint8_t bbt[16];
- };
- struct uniphier_nand uniphier_nand;
- static void uniphier_nand_host_write(struct uniphier_nand *nand,
- uint32_t addr, uint32_t data)
- {
- mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
- mmio_write_32(nand->host_base + DENALI_HOST_DATA, data);
- }
- static uint32_t uniphier_nand_host_read(struct uniphier_nand *nand,
- uint32_t addr)
- {
- mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
- return mmio_read_32(nand->host_base + DENALI_HOST_DATA);
- }
- static int uniphier_nand_block_isbad(struct uniphier_nand *nand, int block)
- {
- int page = nand->pages_per_block * block;
- int column = nand->page_size;
- uint8_t bbm;
- uint32_t status;
- int is_bad;
- /* use cache if available */
- if (block < ARRAY_SIZE(nand->bbt) &&
- nand->bbt[block] != UNIPHIER_NAND_BBT_UNKNOWN)
- return nand->bbt[block];
- mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 0);
- mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
- uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READ0);
- uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, column & 0xff);
- uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (column >> 8) & 0xff);
- uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, page & 0xff);
- uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (page >> 8) & 0xff);
- if (!nand->two_row_addr_cycles)
- uniphier_nand_host_write(nand, DENALI_MAP11_ADDR,
- (page >> 16) & 0xff);
- uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READSTART);
- do {
- status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
- } while (!(status & DENALI_INTR_INT_ACT));
- bbm = uniphier_nand_host_read(nand, DENALI_MAP11_DATA);
- is_bad = bbm != 0xff;
- /* if possible, save the result for future re-use */
- if (block < ARRAY_SIZE(nand->bbt))
- nand->bbt[block] = is_bad;
- if (is_bad)
- WARN("found bad block at %d. skip.\n", block);
- return is_bad;
- }
- static int uniphier_nand_read_pages(struct uniphier_nand *nand, uintptr_t buf,
- int page_start, int page_count)
- {
- uint32_t status;
- mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 1);
- mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 1);
- mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
- /* use Data DMA (64bit) */
- mmio_write_32(nand->host_base + DENALI_HOST_ADDR,
- DENALI_MAP10 | page_start);
- /*
- * 1. setup transfer type, interrupt when complete,
- * burst len = 64 bytes, the number of pages
- */
- mmio_write_32(nand->host_base + DENALI_HOST_DATA,
- 0x01002000 | (64 << 16) | page_count);
- /* 2. set memory low address */
- mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf);
- /* 3. set memory high address */
- mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf >> 32);
- do {
- status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
- } while (!(status & DENALI_INTR_DMA_CMD_COMP));
- mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 0);
- if (status & DENALI_INTR_ECC_UNCOR_ERR) {
- ERROR("uncorrectable error in page range %d-%d",
- page_start, page_start + page_count - 1);
- return -EBADMSG;
- }
- return 0;
- }
- static size_t __uniphier_nand_read(struct uniphier_nand *nand, int lba,
- uintptr_t buf, size_t size)
- {
- int pages_per_block = nand->pages_per_block;
- int page_size = nand->page_size;
- int blocks_to_skip = lba / pages_per_block;
- int pages_to_read = div_round_up(size, page_size);
- int page = lba % pages_per_block;
- int block = 0;
- uintptr_t p = buf;
- int page_count, ret;
- while (blocks_to_skip) {
- ret = uniphier_nand_block_isbad(nand, block);
- if (ret < 0)
- goto out;
- if (!ret)
- blocks_to_skip--;
- block++;
- }
- while (pages_to_read) {
- ret = uniphier_nand_block_isbad(nand, block);
- if (ret < 0)
- goto out;
- if (ret) {
- block++;
- continue;
- }
- page_count = MIN(pages_per_block - page, pages_to_read);
- ret = uniphier_nand_read_pages(nand, p,
- block * pages_per_block + page,
- page_count);
- if (ret)
- goto out;
- block++;
- page = 0;
- p += page_size * page_count;
- pages_to_read -= page_count;
- }
- out:
- /* number of read bytes */
- return MIN(size, p - buf);
- }
- static size_t uniphier_nand_read(int lba, uintptr_t buf, size_t size)
- {
- size_t count;
- inv_dcache_range(buf, size);
- count = __uniphier_nand_read(&uniphier_nand, lba, buf, size);
- inv_dcache_range(buf, size);
- return count;
- }
- static struct io_block_dev_spec uniphier_nand_dev_spec = {
- .ops = {
- .read = uniphier_nand_read,
- },
- /* fill .block_size at run-time */
- };
- static int uniphier_nand_hw_init(struct uniphier_nand *nand)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(nand->bbt); i++)
- nand->bbt[i] = UNIPHIER_NAND_BBT_UNKNOWN;
- nand->reg_base = nand->host_base + 0x100000;
- nand->pages_per_block =
- mmio_read_32(nand->reg_base + DENALI_PAGES_PER_BLOCK);
- nand->page_size =
- mmio_read_32(nand->reg_base + DENALI_DEVICE_MAIN_AREA_SIZE);
- if (mmio_read_32(nand->reg_base + DENALI_TWO_ROW_ADDR_CYCLES) & BIT(0))
- nand->two_row_addr_cycles = 1;
- uniphier_nand_host_write(nand, DENALI_MAP10,
- DENALI_ACCESS_DEFAULT_AREA);
- return 0;
- }
- static const uintptr_t uniphier_nand_base[] = {
- [UNIPHIER_SOC_LD11] = 0x68000000,
- [UNIPHIER_SOC_LD20] = 0x68000000,
- [UNIPHIER_SOC_PXS3] = 0x68000000,
- };
- int uniphier_nand_init(unsigned int soc,
- struct io_block_dev_spec **block_dev_spec)
- {
- int ret;
- assert(soc < ARRAY_SIZE(uniphier_nand_base));
- uniphier_nand.host_base = uniphier_nand_base[soc];
- if (!uniphier_nand.host_base)
- return -ENOTSUP;
- ret = uniphier_nand_hw_init(&uniphier_nand);
- if (ret)
- return ret;
- uniphier_nand_dev_spec.block_size = uniphier_nand.page_size;
- *block_dev_spec = &uniphier_nand_dev_spec;
- return 0;
- }
|