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