1d8e919c7SMasahiro Yamada /* 2*93c78ed2SAntonio Nino Diaz * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. 3d8e919c7SMasahiro Yamada * 4d8e919c7SMasahiro Yamada * SPDX-License-Identifier: BSD-3-Clause 5d8e919c7SMasahiro Yamada */ 6d8e919c7SMasahiro Yamada 7d8e919c7SMasahiro Yamada #include <arch_helpers.h> 8d8e919c7SMasahiro Yamada #include <debug.h> 9d8e919c7SMasahiro Yamada #include <io/io_block.h> 10d8e919c7SMasahiro Yamada #include <mmio.h> 11d8e919c7SMasahiro Yamada #include <platform_def.h> 12*93c78ed2SAntonio Nino Diaz #include <stdint.h> 13d8e919c7SMasahiro Yamada #include <utils_def.h> 14d8e919c7SMasahiro Yamada 15d8e919c7SMasahiro Yamada #include "uniphier.h" 16d8e919c7SMasahiro Yamada 17d8e919c7SMasahiro Yamada #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 18d8e919c7SMasahiro Yamada 19d8e919c7SMasahiro Yamada #define NAND_CMD_READ0 0 20d8e919c7SMasahiro Yamada #define NAND_CMD_READSTART 0x30 21d8e919c7SMasahiro Yamada 22d8e919c7SMasahiro Yamada #define DENALI_ECC_ENABLE 0x0e0 23d8e919c7SMasahiro Yamada #define DENALI_PAGES_PER_BLOCK 0x150 24d8e919c7SMasahiro Yamada #define DENALI_DEVICE_MAIN_AREA_SIZE 0x170 25d8e919c7SMasahiro Yamada #define DENALI_DEVICE_SPARE_AREA_SIZE 0x180 26d8e919c7SMasahiro Yamada #define DENALI_TWO_ROW_ADDR_CYCLES 0x190 27d8e919c7SMasahiro Yamada #define DENALI_INTR_STATUS0 0x410 28d8e919c7SMasahiro Yamada #define DENALI_INTR_ECC_UNCOR_ERR BIT(1) 29d8e919c7SMasahiro Yamada #define DENALI_INTR_DMA_CMD_COMP BIT(2) 30d8e919c7SMasahiro Yamada #define DENALI_INTR_INT_ACT BIT(12) 31d8e919c7SMasahiro Yamada 32d8e919c7SMasahiro Yamada #define DENALI_DMA_ENABLE 0x700 33d8e919c7SMasahiro Yamada 34d8e919c7SMasahiro Yamada #define DENALI_HOST_ADDR 0x00 35d8e919c7SMasahiro Yamada #define DENALI_HOST_DATA 0x10 36d8e919c7SMasahiro Yamada 37d8e919c7SMasahiro Yamada #define DENALI_MAP01 (1 << 26) 38d8e919c7SMasahiro Yamada #define DENALI_MAP10 (2 << 26) 39d8e919c7SMasahiro Yamada #define DENALI_MAP11 (3 << 26) 40d8e919c7SMasahiro Yamada 41d8e919c7SMasahiro Yamada #define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) 42d8e919c7SMasahiro Yamada #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) 43d8e919c7SMasahiro Yamada #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) 44d8e919c7SMasahiro Yamada 45d8e919c7SMasahiro Yamada #define DENALI_ACCESS_DEFAULT_AREA 0x42 46d8e919c7SMasahiro Yamada 47d8e919c7SMasahiro Yamada #define UNIPHIER_NAND_BBT_UNKNOWN 0xff 48d8e919c7SMasahiro Yamada 49d8e919c7SMasahiro Yamada struct uniphier_nand { 50d8e919c7SMasahiro Yamada uintptr_t host_base; 51d8e919c7SMasahiro Yamada uintptr_t reg_base; 52d8e919c7SMasahiro Yamada int pages_per_block; 53d8e919c7SMasahiro Yamada int page_size; 54d8e919c7SMasahiro Yamada int two_row_addr_cycles; 55d8e919c7SMasahiro Yamada uint8_t bbt[16]; 56d8e919c7SMasahiro Yamada }; 57d8e919c7SMasahiro Yamada 58d8e919c7SMasahiro Yamada struct uniphier_nand uniphier_nand; 59d8e919c7SMasahiro Yamada 60d8e919c7SMasahiro Yamada static void uniphier_nand_host_write(struct uniphier_nand *nand, 61d8e919c7SMasahiro Yamada uint32_t addr, uint32_t data) 62d8e919c7SMasahiro Yamada { 63d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr); 64d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_DATA, data); 65d8e919c7SMasahiro Yamada } 66d8e919c7SMasahiro Yamada 67d8e919c7SMasahiro Yamada static uint32_t uniphier_nand_host_read(struct uniphier_nand *nand, 68d8e919c7SMasahiro Yamada uint32_t addr) 69d8e919c7SMasahiro Yamada { 70d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr); 71d8e919c7SMasahiro Yamada return mmio_read_32(nand->host_base + DENALI_HOST_DATA); 72d8e919c7SMasahiro Yamada } 73d8e919c7SMasahiro Yamada 74d8e919c7SMasahiro Yamada static int uniphier_nand_block_isbad(struct uniphier_nand *nand, int block) 75d8e919c7SMasahiro Yamada { 76d8e919c7SMasahiro Yamada int page = nand->pages_per_block * block; 77d8e919c7SMasahiro Yamada int column = nand->page_size; 78d8e919c7SMasahiro Yamada uint8_t bbm; 79d8e919c7SMasahiro Yamada uint32_t status; 80d8e919c7SMasahiro Yamada int is_bad; 81d8e919c7SMasahiro Yamada 82d8e919c7SMasahiro Yamada /* use cache if available */ 83d8e919c7SMasahiro Yamada if (block < ARRAY_SIZE(nand->bbt) && 84d8e919c7SMasahiro Yamada nand->bbt[block] != UNIPHIER_NAND_BBT_UNKNOWN) 85d8e919c7SMasahiro Yamada return nand->bbt[block]; 86d8e919c7SMasahiro Yamada 87d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 0); 88d8e919c7SMasahiro Yamada 89d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1); 90d8e919c7SMasahiro Yamada 91d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READ0); 92d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, column & 0xff); 93d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (column >> 8) & 0xff); 94d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, page & 0xff); 95d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (page >> 8) & 0xff); 96d8e919c7SMasahiro Yamada if (!nand->two_row_addr_cycles) 97d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, 98d8e919c7SMasahiro Yamada (page >> 16) & 0xff); 99d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READSTART); 100d8e919c7SMasahiro Yamada 101d8e919c7SMasahiro Yamada do { 102d8e919c7SMasahiro Yamada status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0); 103d8e919c7SMasahiro Yamada } while (!(status & DENALI_INTR_INT_ACT)); 104d8e919c7SMasahiro Yamada 105d8e919c7SMasahiro Yamada bbm = uniphier_nand_host_read(nand, DENALI_MAP11_DATA); 106d8e919c7SMasahiro Yamada 107d8e919c7SMasahiro Yamada is_bad = bbm != 0xff; 108d8e919c7SMasahiro Yamada 1093eba78d3SMasahiro Yamada /* if possible, save the result for future re-use */ 1103eba78d3SMasahiro Yamada if (block < ARRAY_SIZE(nand->bbt)) 111d8e919c7SMasahiro Yamada nand->bbt[block] = is_bad; 112d8e919c7SMasahiro Yamada 113d8e919c7SMasahiro Yamada if (is_bad) 114d8e919c7SMasahiro Yamada WARN("found bad block at %d. skip.\n", block); 115d8e919c7SMasahiro Yamada 116d8e919c7SMasahiro Yamada return is_bad; 117d8e919c7SMasahiro Yamada } 118d8e919c7SMasahiro Yamada 119d8e919c7SMasahiro Yamada static int uniphier_nand_read_pages(struct uniphier_nand *nand, uintptr_t buf, 120d8e919c7SMasahiro Yamada int page_start, int page_count) 121d8e919c7SMasahiro Yamada { 122d8e919c7SMasahiro Yamada uint32_t status; 123d8e919c7SMasahiro Yamada 124d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 1); 125d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 1); 126d8e919c7SMasahiro Yamada 127d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1); 128d8e919c7SMasahiro Yamada 129d8e919c7SMasahiro Yamada /* use Data DMA (64bit) */ 130d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_ADDR, 131d8e919c7SMasahiro Yamada DENALI_MAP10 | page_start); 132d8e919c7SMasahiro Yamada 133d8e919c7SMasahiro Yamada /* 134d8e919c7SMasahiro Yamada * 1. setup transfer type, interrupt when complete, 135d8e919c7SMasahiro Yamada * burst len = 64 bytes, the number of pages 136d8e919c7SMasahiro Yamada */ 137d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_DATA, 138d8e919c7SMasahiro Yamada 0x01002000 | (64 << 16) | page_count); 139d8e919c7SMasahiro Yamada 140d8e919c7SMasahiro Yamada /* 2. set memory low address */ 141d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf); 142d8e919c7SMasahiro Yamada 143d8e919c7SMasahiro Yamada /* 3. set memory high address */ 144d8e919c7SMasahiro Yamada mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf >> 32); 145d8e919c7SMasahiro Yamada 146d8e919c7SMasahiro Yamada do { 147d8e919c7SMasahiro Yamada status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0); 148d8e919c7SMasahiro Yamada } while (!(status & DENALI_INTR_DMA_CMD_COMP)); 149d8e919c7SMasahiro Yamada 150d8e919c7SMasahiro Yamada mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 0); 151d8e919c7SMasahiro Yamada 152d8e919c7SMasahiro Yamada if (status & DENALI_INTR_ECC_UNCOR_ERR) { 153d8e919c7SMasahiro Yamada ERROR("uncorrectable error in page range %d-%d", 154d8e919c7SMasahiro Yamada page_start, page_start + page_count - 1); 155d8e919c7SMasahiro Yamada return -EBADMSG; 156d8e919c7SMasahiro Yamada } 157d8e919c7SMasahiro Yamada 158d8e919c7SMasahiro Yamada return 0; 159d8e919c7SMasahiro Yamada } 160d8e919c7SMasahiro Yamada 161d8e919c7SMasahiro Yamada static size_t __uniphier_nand_read(struct uniphier_nand *nand, int lba, 162d8e919c7SMasahiro Yamada uintptr_t buf, size_t size) 163d8e919c7SMasahiro Yamada { 164d8e919c7SMasahiro Yamada int pages_per_block = nand->pages_per_block; 165d8e919c7SMasahiro Yamada int page_size = nand->page_size; 166d8e919c7SMasahiro Yamada int blocks_to_skip = lba / pages_per_block; 167d8e919c7SMasahiro Yamada int pages_to_read = DIV_ROUND_UP(size, page_size); 168d8e919c7SMasahiro Yamada int page = lba % pages_per_block; 169d8e919c7SMasahiro Yamada int block = 0; 170d8e919c7SMasahiro Yamada uintptr_t p = buf; 171d8e919c7SMasahiro Yamada int page_count, ret; 172d8e919c7SMasahiro Yamada 173d8e919c7SMasahiro Yamada while (blocks_to_skip) { 174d8e919c7SMasahiro Yamada ret = uniphier_nand_block_isbad(nand, block); 175d8e919c7SMasahiro Yamada if (ret < 0) 176d8e919c7SMasahiro Yamada goto out; 177d8e919c7SMasahiro Yamada 178d8e919c7SMasahiro Yamada if (!ret) 179d8e919c7SMasahiro Yamada blocks_to_skip--; 180d8e919c7SMasahiro Yamada 181d8e919c7SMasahiro Yamada block++; 182d8e919c7SMasahiro Yamada } 183d8e919c7SMasahiro Yamada 184d8e919c7SMasahiro Yamada while (pages_to_read) { 185d8e919c7SMasahiro Yamada ret = uniphier_nand_block_isbad(nand, block); 186d8e919c7SMasahiro Yamada if (ret < 0) 187d8e919c7SMasahiro Yamada goto out; 188d8e919c7SMasahiro Yamada 189d8e919c7SMasahiro Yamada if (ret) { 190d8e919c7SMasahiro Yamada block++; 191d8e919c7SMasahiro Yamada continue; 192d8e919c7SMasahiro Yamada } 193d8e919c7SMasahiro Yamada 194d8e919c7SMasahiro Yamada page_count = MIN(pages_per_block - page, pages_to_read); 195d8e919c7SMasahiro Yamada 196d8e919c7SMasahiro Yamada ret = uniphier_nand_read_pages(nand, p, 197d8e919c7SMasahiro Yamada block * pages_per_block + page, 198d8e919c7SMasahiro Yamada page_count); 199d8e919c7SMasahiro Yamada if (ret) 200d8e919c7SMasahiro Yamada goto out; 201d8e919c7SMasahiro Yamada 202d8e919c7SMasahiro Yamada block++; 203d8e919c7SMasahiro Yamada page = 0; 204d8e919c7SMasahiro Yamada p += page_size * page_count; 205d8e919c7SMasahiro Yamada pages_to_read -= page_count; 206d8e919c7SMasahiro Yamada } 207d8e919c7SMasahiro Yamada 208d8e919c7SMasahiro Yamada out: 209d8e919c7SMasahiro Yamada /* number of read bytes */ 210d8e919c7SMasahiro Yamada return MIN(size, p - buf); 211d8e919c7SMasahiro Yamada } 212d8e919c7SMasahiro Yamada 213d8e919c7SMasahiro Yamada static size_t uniphier_nand_read(int lba, uintptr_t buf, size_t size) 214d8e919c7SMasahiro Yamada { 215d8e919c7SMasahiro Yamada size_t count; 216d8e919c7SMasahiro Yamada 217d8e919c7SMasahiro Yamada inv_dcache_range(buf, size); 218d8e919c7SMasahiro Yamada 219d8e919c7SMasahiro Yamada count = __uniphier_nand_read(&uniphier_nand, lba, buf, size); 220d8e919c7SMasahiro Yamada 221d8e919c7SMasahiro Yamada inv_dcache_range(buf, size); 222d8e919c7SMasahiro Yamada 223d8e919c7SMasahiro Yamada return count; 224d8e919c7SMasahiro Yamada } 225d8e919c7SMasahiro Yamada 226d8e919c7SMasahiro Yamada static struct io_block_dev_spec uniphier_nand_dev_spec = { 227d8e919c7SMasahiro Yamada .buffer = { 228d8e919c7SMasahiro Yamada .offset = UNIPHIER_BLOCK_BUF_BASE, 229d8e919c7SMasahiro Yamada .length = UNIPHIER_BLOCK_BUF_SIZE, 230d8e919c7SMasahiro Yamada }, 231d8e919c7SMasahiro Yamada .ops = { 232d8e919c7SMasahiro Yamada .read = uniphier_nand_read, 233d8e919c7SMasahiro Yamada }, 234d8e919c7SMasahiro Yamada /* fill .block_size at run-time */ 235d8e919c7SMasahiro Yamada }; 236d8e919c7SMasahiro Yamada 237d8e919c7SMasahiro Yamada static int uniphier_nand_hw_init(struct uniphier_nand *nand) 238d8e919c7SMasahiro Yamada { 239d8e919c7SMasahiro Yamada int i; 240d8e919c7SMasahiro Yamada 241d8e919c7SMasahiro Yamada for (i = 0; i < ARRAY_SIZE(nand->bbt); i++) 242d8e919c7SMasahiro Yamada nand->bbt[i] = UNIPHIER_NAND_BBT_UNKNOWN; 243d8e919c7SMasahiro Yamada 244d8e919c7SMasahiro Yamada nand->host_base = 0x68000000; 245d8e919c7SMasahiro Yamada nand->reg_base = 0x68100000; 246d8e919c7SMasahiro Yamada 247d8e919c7SMasahiro Yamada nand->pages_per_block = 248d8e919c7SMasahiro Yamada mmio_read_32(nand->reg_base + DENALI_PAGES_PER_BLOCK); 249d8e919c7SMasahiro Yamada 250d8e919c7SMasahiro Yamada nand->page_size = 251d8e919c7SMasahiro Yamada mmio_read_32(nand->reg_base + DENALI_DEVICE_MAIN_AREA_SIZE); 252d8e919c7SMasahiro Yamada 253d8e919c7SMasahiro Yamada if (mmio_read_32(nand->reg_base + DENALI_TWO_ROW_ADDR_CYCLES) & BIT(0)) 254d8e919c7SMasahiro Yamada nand->two_row_addr_cycles = 1; 255d8e919c7SMasahiro Yamada 256d8e919c7SMasahiro Yamada uniphier_nand_host_write(nand, DENALI_MAP10, 257d8e919c7SMasahiro Yamada DENALI_ACCESS_DEFAULT_AREA); 258d8e919c7SMasahiro Yamada 259d8e919c7SMasahiro Yamada return 0; 260d8e919c7SMasahiro Yamada } 261d8e919c7SMasahiro Yamada 262d8e919c7SMasahiro Yamada int uniphier_nand_init(uintptr_t *block_dev_spec) 263d8e919c7SMasahiro Yamada { 264d8e919c7SMasahiro Yamada int ret; 265d8e919c7SMasahiro Yamada 266d8e919c7SMasahiro Yamada ret = uniphier_nand_hw_init(&uniphier_nand); 267d8e919c7SMasahiro Yamada if (ret) 268d8e919c7SMasahiro Yamada return ret; 269d8e919c7SMasahiro Yamada 270d8e919c7SMasahiro Yamada uniphier_nand_dev_spec.block_size = uniphier_nand.page_size; 271d8e919c7SMasahiro Yamada 272d8e919c7SMasahiro Yamada *block_dev_spec = (uintptr_t)&uniphier_nand_dev_spec; 273d8e919c7SMasahiro Yamada 274d8e919c7SMasahiro Yamada return 0; 275d8e919c7SMasahiro Yamada } 276