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