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