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