1*ddaf02d1SJit Loon Lim /* 2*ddaf02d1SJit Loon Lim * Copyright (c) 2022-2023, Intel Corporation. All rights reserved. 3*ddaf02d1SJit Loon Lim * 4*ddaf02d1SJit Loon Lim * SPDX-License-Identifier: BSD-3-Clause 5*ddaf02d1SJit Loon Lim */ 6*ddaf02d1SJit Loon Lim 7*ddaf02d1SJit Loon Lim #include <assert.h> 8*ddaf02d1SJit Loon Lim #include <errno.h> 9*ddaf02d1SJit Loon Lim #include <stdbool.h> 10*ddaf02d1SJit Loon Lim #include <string.h> 11*ddaf02d1SJit Loon Lim 12*ddaf02d1SJit Loon Lim #include <arch_helpers.h> 13*ddaf02d1SJit Loon Lim #include <common/debug.h> 14*ddaf02d1SJit Loon Lim #include <drivers/cadence/cdns_nand.h> 15*ddaf02d1SJit Loon Lim #include <drivers/delay_timer.h> 16*ddaf02d1SJit Loon Lim #include <lib/mmio.h> 17*ddaf02d1SJit Loon Lim #include <lib/utils.h> 18*ddaf02d1SJit Loon Lim #include <platform_def.h> 19*ddaf02d1SJit Loon Lim 20*ddaf02d1SJit Loon Lim /* NAND flash device information struct */ 21*ddaf02d1SJit Loon Lim static cnf_dev_info_t dev_info; 22*ddaf02d1SJit Loon Lim 23*ddaf02d1SJit Loon Lim /* Scratch buffers for read and write operations */ 24*ddaf02d1SJit Loon Lim static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE]; 25*ddaf02d1SJit Loon Lim 26*ddaf02d1SJit Loon Lim /* Wait for controller to be in idle state */ 27*ddaf02d1SJit Loon Lim static inline void cdns_nand_wait_idle(void) 28*ddaf02d1SJit Loon Lim { 29*ddaf02d1SJit Loon Lim uint32_t reg = 0U; 30*ddaf02d1SJit Loon Lim 31*ddaf02d1SJit Loon Lim do { 32*ddaf02d1SJit Loon Lim udelay(CNF_DEF_DELAY_US); 33*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS)); 34*ddaf02d1SJit Loon Lim } while (CNF_GET_CTRL_BUSY(reg) != 0U); 35*ddaf02d1SJit Loon Lim } 36*ddaf02d1SJit Loon Lim 37*ddaf02d1SJit Loon Lim /* Wait for given thread to be in ready state */ 38*ddaf02d1SJit Loon Lim static inline void cdns_nand_wait_thread_ready(uint8_t thread_id) 39*ddaf02d1SJit Loon Lim { 40*ddaf02d1SJit Loon Lim uint32_t reg = 0U; 41*ddaf02d1SJit Loon Lim 42*ddaf02d1SJit Loon Lim do { 43*ddaf02d1SJit Loon Lim udelay(CNF_DEF_DELAY_US); 44*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CMDREG(TRD_STATUS)); 45*ddaf02d1SJit Loon Lim reg &= (1U << (uint32_t)thread_id); 46*ddaf02d1SJit Loon Lim } while (reg != 0U); 47*ddaf02d1SJit Loon Lim } 48*ddaf02d1SJit Loon Lim 49*ddaf02d1SJit Loon Lim /* Check if the last operation/command in selected thread is completed */ 50*ddaf02d1SJit Loon Lim static int cdns_nand_last_opr_status(uint8_t thread_id) 51*ddaf02d1SJit Loon Lim { 52*ddaf02d1SJit Loon Lim uint8_t nthreads = 0U; 53*ddaf02d1SJit Loon Lim uint32_t reg = 0U; 54*ddaf02d1SJit Loon Lim 55*ddaf02d1SJit Loon Lim /* Get number of threads */ 56*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLPARAM(FEATURE)); 57*ddaf02d1SJit Loon Lim nthreads = CNF_GET_NTHREADS(reg); 58*ddaf02d1SJit Loon Lim 59*ddaf02d1SJit Loon Lim if (thread_id > nthreads) { 60*ddaf02d1SJit Loon Lim ERROR("%s: Invalid thread ID\n", __func__); 61*ddaf02d1SJit Loon Lim return -EINVAL; 62*ddaf02d1SJit Loon Lim } 63*ddaf02d1SJit Loon Lim 64*ddaf02d1SJit Loon Lim /* Select thread */ 65*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_STAT_PTR), (uint32_t)thread_id); 66*ddaf02d1SJit Loon Lim 67*ddaf02d1SJit Loon Lim uint32_t err_mask = CNF_ECMD | CNF_EECC | CNF_EDEV | CNF_EDQS | CNF_EFAIL | 68*ddaf02d1SJit Loon Lim CNF_EBUS | CNF_EDI | CNF_EPAR | CNF_ECTX | CNF_EPRO; 69*ddaf02d1SJit Loon Lim 70*ddaf02d1SJit Loon Lim do { 71*ddaf02d1SJit Loon Lim udelay(CNF_DEF_DELAY_US * 2); 72*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CMDREG(CMD_STAT)); 73*ddaf02d1SJit Loon Lim } while ((reg & CNF_CMPLT) == 0U); 74*ddaf02d1SJit Loon Lim 75*ddaf02d1SJit Loon Lim /* last operation is completed, make sure no other error bits are set */ 76*ddaf02d1SJit Loon Lim if ((reg & err_mask) == 1U) { 77*ddaf02d1SJit Loon Lim ERROR("%s, CMD_STATUS:0x%x\n", __func__, reg); 78*ddaf02d1SJit Loon Lim return -EIO; 79*ddaf02d1SJit Loon Lim } 80*ddaf02d1SJit Loon Lim 81*ddaf02d1SJit Loon Lim return 0; 82*ddaf02d1SJit Loon Lim } 83*ddaf02d1SJit Loon Lim 84*ddaf02d1SJit Loon Lim /* Set feature command */ 85*ddaf02d1SJit Loon Lim int cdns_nand_set_feature(uint8_t feat_addr, uint8_t feat_val, uint8_t thread_id) 86*ddaf02d1SJit Loon Lim { 87*ddaf02d1SJit Loon Lim /* Wait for thread to be ready */ 88*ddaf02d1SJit Loon Lim cdns_nand_wait_thread_ready(thread_id); 89*ddaf02d1SJit Loon Lim 90*ddaf02d1SJit Loon Lim /* Set feature address */ 91*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG1), (uint32_t)feat_addr); 92*ddaf02d1SJit Loon Lim /* Set feature volume */ 93*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG2), (uint32_t)feat_val); 94*ddaf02d1SJit Loon Lim 95*ddaf02d1SJit Loon Lim /* Set feature command */ 96*ddaf02d1SJit Loon Lim uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); 97*ddaf02d1SJit Loon Lim 98*ddaf02d1SJit Loon Lim reg |= (thread_id << CNF_CMDREG0_TRD); 99*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL); 100*ddaf02d1SJit Loon Lim reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR); 101*ddaf02d1SJit Loon Lim reg |= (CNF_CT_SET_FEATURE << CNF_CMDREG0_CMD); 102*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG0), reg); 103*ddaf02d1SJit Loon Lim 104*ddaf02d1SJit Loon Lim return cdns_nand_last_opr_status(thread_id); 105*ddaf02d1SJit Loon Lim } 106*ddaf02d1SJit Loon Lim 107*ddaf02d1SJit Loon Lim /* Reset command to the selected device */ 108*ddaf02d1SJit Loon Lim int cdns_nand_reset(uint8_t thread_id) 109*ddaf02d1SJit Loon Lim { 110*ddaf02d1SJit Loon Lim /* Operation is executed in selected thread */ 111*ddaf02d1SJit Loon Lim cdns_nand_wait_thread_ready(thread_id); 112*ddaf02d1SJit Loon Lim 113*ddaf02d1SJit Loon Lim /* Select memory */ 114*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); 115*ddaf02d1SJit Loon Lim 116*ddaf02d1SJit Loon Lim /* Issue reset command */ 117*ddaf02d1SJit Loon Lim uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); 118*ddaf02d1SJit Loon Lim 119*ddaf02d1SJit Loon Lim reg |= (thread_id << CNF_CMDREG0_TRD); 120*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL); 121*ddaf02d1SJit Loon Lim reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR); 122*ddaf02d1SJit Loon Lim reg |= (CNF_CT_RESET_ASYNC << CNF_CMDREG0_CMD); 123*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG0), reg); 124*ddaf02d1SJit Loon Lim 125*ddaf02d1SJit Loon Lim return cdns_nand_last_opr_status(thread_id); 126*ddaf02d1SJit Loon Lim } 127*ddaf02d1SJit Loon Lim 128*ddaf02d1SJit Loon Lim /* Set operation work mode */ 129*ddaf02d1SJit Loon Lim static void cdns_nand_set_opr_mode(uint8_t opr_mode) 130*ddaf02d1SJit Loon Lim { 131*ddaf02d1SJit Loon Lim /* Wait for controller to be in idle state */ 132*ddaf02d1SJit Loon Lim cdns_nand_wait_idle(); 133*ddaf02d1SJit Loon Lim 134*ddaf02d1SJit Loon Lim /* Reset DLL PHY */ 135*ddaf02d1SJit Loon Lim uint32_t reg = mmio_read_32(CNF_MINICTRL(DLL_PHY_CTRL)); 136*ddaf02d1SJit Loon Lim 137*ddaf02d1SJit Loon Lim reg &= ~(1 << CNF_DLL_PHY_RST_N); 138*ddaf02d1SJit Loon Lim mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg); 139*ddaf02d1SJit Loon Lim 140*ddaf02d1SJit Loon Lim if (opr_mode == CNF_OPR_WORK_MODE_SDR) { 141*ddaf02d1SJit Loon Lim /* Combo PHY Control Timing Block register settings */ 142*ddaf02d1SJit Loon Lim mmio_write_32(CP_CTB(CTRL_REG), CP_CTRL_REG_SDR); 143*ddaf02d1SJit Loon Lim mmio_write_32(CP_CTB(TSEL_REG), CP_TSEL_REG_SDR); 144*ddaf02d1SJit Loon Lim 145*ddaf02d1SJit Loon Lim /* Combo PHY DLL register settings */ 146*ddaf02d1SJit Loon Lim mmio_write_32(CP_DLL(DQ_TIMING_REG), CP_DQ_TIMING_REG_SDR); 147*ddaf02d1SJit Loon Lim mmio_write_32(CP_DLL(DQS_TIMING_REG), CP_DQS_TIMING_REG_SDR); 148*ddaf02d1SJit Loon Lim mmio_write_32(CP_DLL(GATE_LPBK_CTRL_REG), CP_GATE_LPBK_CTRL_REG_SDR); 149*ddaf02d1SJit Loon Lim mmio_write_32(CP_DLL(MASTER_CTRL_REG), CP_DLL_MASTER_CTRL_REG_SDR); 150*ddaf02d1SJit Loon Lim 151*ddaf02d1SJit Loon Lim /* Async mode timing settings */ 152*ddaf02d1SJit Loon Lim mmio_write_32(CNF_MINICTRL(ASYNC_TOGGLE_TIMINGS), 153*ddaf02d1SJit Loon Lim (2 << CNF_ASYNC_TIMINGS_TRH) | 154*ddaf02d1SJit Loon Lim (4 << CNF_ASYNC_TIMINGS_TRP) | 155*ddaf02d1SJit Loon Lim (2 << CNF_ASYNC_TIMINGS_TWH) | 156*ddaf02d1SJit Loon Lim (4 << CNF_ASYNC_TIMINGS_TWP)); 157*ddaf02d1SJit Loon Lim 158*ddaf02d1SJit Loon Lim /* Set extended read and write mode */ 159*ddaf02d1SJit Loon Lim reg |= (1 << CNF_DLL_PHY_EXT_RD_MODE); 160*ddaf02d1SJit Loon Lim reg |= (1 << CNF_DLL_PHY_EXT_WR_MODE); 161*ddaf02d1SJit Loon Lim 162*ddaf02d1SJit Loon Lim /* Set operation work mode in common settings */ 163*ddaf02d1SJit Loon Lim uint32_t data = mmio_read_32(CNF_MINICTRL(CMN_SETTINGS)); 164*ddaf02d1SJit Loon Lim 165*ddaf02d1SJit Loon Lim data |= (CNF_OPR_WORK_MODE_SDR << CNF_CMN_SETTINGS_OPR); 166*ddaf02d1SJit Loon Lim mmio_write_32(CNF_MINICTRL(CMN_SETTINGS), data); 167*ddaf02d1SJit Loon Lim 168*ddaf02d1SJit Loon Lim } else if (opr_mode == CNF_OPR_WORK_MODE_NVDDR) { 169*ddaf02d1SJit Loon Lim ; /* ToDo: add DDR mode settings also once available on SIMICS */ 170*ddaf02d1SJit Loon Lim } else { 171*ddaf02d1SJit Loon Lim ; 172*ddaf02d1SJit Loon Lim } 173*ddaf02d1SJit Loon Lim 174*ddaf02d1SJit Loon Lim reg |= (1 << CNF_DLL_PHY_RST_N); 175*ddaf02d1SJit Loon Lim mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg); 176*ddaf02d1SJit Loon Lim } 177*ddaf02d1SJit Loon Lim 178*ddaf02d1SJit Loon Lim /* Data transfer configuration */ 179*ddaf02d1SJit Loon Lim static void cdns_nand_transfer_config(void) 180*ddaf02d1SJit Loon Lim { 181*ddaf02d1SJit Loon Lim /* Wait for controller to be in idle state */ 182*ddaf02d1SJit Loon Lim cdns_nand_wait_idle(); 183*ddaf02d1SJit Loon Lim 184*ddaf02d1SJit Loon Lim /* Configure data transfer parameters */ 185*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(TRANS_CFG0), 1); 186*ddaf02d1SJit Loon Lim 187*ddaf02d1SJit Loon Lim /* ECC is disabled */ 188*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(ECC_CFG0), 0); 189*ddaf02d1SJit Loon Lim 190*ddaf02d1SJit Loon Lim /* DMA burst select */ 191*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(DMA_SETTINGS), 192*ddaf02d1SJit Loon Lim (CNF_DMA_BURST_SIZE_MAX << CNF_DMA_SETTINGS_BURST) | 193*ddaf02d1SJit Loon Lim (1 << CNF_DMA_SETTINGS_OTE)); 194*ddaf02d1SJit Loon Lim 195*ddaf02d1SJit Loon Lim /* Enable pre-fetching for 1K */ 196*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(FIFO_TLEVEL), 197*ddaf02d1SJit Loon Lim (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_POS) | 198*ddaf02d1SJit Loon Lim (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_DMA_SIZE)); 199*ddaf02d1SJit Loon Lim 200*ddaf02d1SJit Loon Lim /* Select access type */ 201*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(MULTIPLANE_CFG), 0); 202*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(CACHE_CFG), 0); 203*ddaf02d1SJit Loon Lim } 204*ddaf02d1SJit Loon Lim 205*ddaf02d1SJit Loon Lim /* Update the nand flash device info */ 206*ddaf02d1SJit Loon Lim static int cdns_nand_update_dev_info(void) 207*ddaf02d1SJit Loon Lim { 208*ddaf02d1SJit Loon Lim uint32_t reg = 0U; 209*ddaf02d1SJit Loon Lim 210*ddaf02d1SJit Loon Lim /* Read the device type and number of LUNs */ 211*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLPARAM(DEV_PARAMS0)); 212*ddaf02d1SJit Loon Lim dev_info.type = CNF_GET_DEV_TYPE(reg); 213*ddaf02d1SJit Loon Lim if (dev_info.type == CNF_DT_UNKNOWN) { 214*ddaf02d1SJit Loon Lim ERROR("%s: device type unknown\n", __func__); 215*ddaf02d1SJit Loon Lim return -ENXIO; 216*ddaf02d1SJit Loon Lim } 217*ddaf02d1SJit Loon Lim dev_info.nluns = CNF_GET_NLUNS(reg); 218*ddaf02d1SJit Loon Lim 219*ddaf02d1SJit Loon Lim /* Pages per block */ 220*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLCFG(DEV_LAYOUT)); 221*ddaf02d1SJit Loon Lim dev_info.npages_per_block = CNF_GET_NPAGES_PER_BLOCK(reg); 222*ddaf02d1SJit Loon Lim 223*ddaf02d1SJit Loon Lim /* Sector size and last sector size */ 224*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLCFG(TRANS_CFG1)); 225*ddaf02d1SJit Loon Lim dev_info.sector_size = CNF_GET_SCTR_SIZE(reg); 226*ddaf02d1SJit Loon Lim dev_info.last_sector_size = CNF_GET_LAST_SCTR_SIZE(reg); 227*ddaf02d1SJit Loon Lim 228*ddaf02d1SJit Loon Lim /* Page size and spare size */ 229*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLPARAM(DEV_AREA)); 230*ddaf02d1SJit Loon Lim dev_info.page_size = CNF_GET_PAGE_SIZE(reg); 231*ddaf02d1SJit Loon Lim dev_info.spare_size = CNF_GET_SPARE_SIZE(reg); 232*ddaf02d1SJit Loon Lim 233*ddaf02d1SJit Loon Lim /* Device blocks per LUN */ 234*ddaf02d1SJit Loon Lim dev_info.nblocks_per_lun = mmio_read_32(CNF_CTRLPARAM(DEV_BLOCKS_PLUN)); 235*ddaf02d1SJit Loon Lim 236*ddaf02d1SJit Loon Lim /* Calculate block size and total device size */ 237*ddaf02d1SJit Loon Lim dev_info.block_size = (dev_info.npages_per_block * dev_info.page_size); 238*ddaf02d1SJit Loon Lim dev_info.total_size = (dev_info.block_size * dev_info.nblocks_per_lun * 239*ddaf02d1SJit Loon Lim dev_info.nluns); 240*ddaf02d1SJit Loon Lim 241*ddaf02d1SJit Loon Lim VERBOSE("CNF params: page %d, spare %d, block %d, total %lld\n", 242*ddaf02d1SJit Loon Lim dev_info.page_size, dev_info.spare_size, 243*ddaf02d1SJit Loon Lim dev_info.block_size, dev_info.total_size); 244*ddaf02d1SJit Loon Lim 245*ddaf02d1SJit Loon Lim return 0; 246*ddaf02d1SJit Loon Lim } 247*ddaf02d1SJit Loon Lim 248*ddaf02d1SJit Loon Lim /* NAND Flash Controller/Host initialization */ 249*ddaf02d1SJit Loon Lim int cdns_nand_host_init(void) 250*ddaf02d1SJit Loon Lim { 251*ddaf02d1SJit Loon Lim uint32_t reg = 0U; 252*ddaf02d1SJit Loon Lim int ret = 0; 253*ddaf02d1SJit Loon Lim 254*ddaf02d1SJit Loon Lim do { 255*ddaf02d1SJit Loon Lim /* Read controller status register for init complete */ 256*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS)); 257*ddaf02d1SJit Loon Lim } while (CNF_GET_INIT_COMP(reg) == 0); 258*ddaf02d1SJit Loon Lim 259*ddaf02d1SJit Loon Lim ret = cdns_nand_update_dev_info(); 260*ddaf02d1SJit Loon Lim if (ret != 0) { 261*ddaf02d1SJit Loon Lim return ret; 262*ddaf02d1SJit Loon Lim } 263*ddaf02d1SJit Loon Lim 264*ddaf02d1SJit Loon Lim INFO("CNF: device discovery process completed and device type %d\n", 265*ddaf02d1SJit Loon Lim dev_info.type); 266*ddaf02d1SJit Loon Lim 267*ddaf02d1SJit Loon Lim /* Enable data integrity, enable CRC and parity */ 268*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_DI(CONTROL)); 269*ddaf02d1SJit Loon Lim reg |= (1 << CNF_DI_PAR_EN); 270*ddaf02d1SJit Loon Lim reg |= (1 << CNF_DI_CRC_EN); 271*ddaf02d1SJit Loon Lim mmio_write_32(CNF_DI(CONTROL), reg); 272*ddaf02d1SJit Loon Lim 273*ddaf02d1SJit Loon Lim /* Status polling mode, device control and status register */ 274*ddaf02d1SJit Loon Lim cdns_nand_wait_idle(); 275*ddaf02d1SJit Loon Lim reg = mmio_read_32(CNF_CTRLCFG(DEV_STAT)); 276*ddaf02d1SJit Loon Lim reg = reg & ~1; 277*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CTRLCFG(DEV_STAT), reg); 278*ddaf02d1SJit Loon Lim 279*ddaf02d1SJit Loon Lim /* Set operation work mode */ 280*ddaf02d1SJit Loon Lim cdns_nand_set_opr_mode(CNF_OPR_WORK_MODE_SDR); 281*ddaf02d1SJit Loon Lim 282*ddaf02d1SJit Loon Lim /* Set data transfer configuration parameters */ 283*ddaf02d1SJit Loon Lim cdns_nand_transfer_config(); 284*ddaf02d1SJit Loon Lim 285*ddaf02d1SJit Loon Lim return 0; 286*ddaf02d1SJit Loon Lim } 287*ddaf02d1SJit Loon Lim 288*ddaf02d1SJit Loon Lim /* erase: Block erase command */ 289*ddaf02d1SJit Loon Lim int cdns_nand_erase(uint32_t offset, uint32_t size) 290*ddaf02d1SJit Loon Lim { 291*ddaf02d1SJit Loon Lim /* Determine the starting block offset i.e row address */ 292*ddaf02d1SJit Loon Lim uint32_t row_address = dev_info.npages_per_block * offset; 293*ddaf02d1SJit Loon Lim 294*ddaf02d1SJit Loon Lim /* Wait for thread to be in ready state */ 295*ddaf02d1SJit Loon Lim cdns_nand_wait_thread_ready(CNF_DEF_TRD); 296*ddaf02d1SJit Loon Lim 297*ddaf02d1SJit Loon Lim /*Set row address */ 298*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG1), row_address); 299*ddaf02d1SJit Loon Lim 300*ddaf02d1SJit Loon Lim /* Operation bank number */ 301*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); 302*ddaf02d1SJit Loon Lim 303*ddaf02d1SJit Loon Lim /* Block erase command */ 304*ddaf02d1SJit Loon Lim uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); 305*ddaf02d1SJit Loon Lim 306*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD); 307*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL); 308*ddaf02d1SJit Loon Lim reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR); 309*ddaf02d1SJit Loon Lim reg |= (CNF_CT_ERASE << CNF_CMDREG0_CMD); 310*ddaf02d1SJit Loon Lim reg |= (((size-1) & 0xFF) << CNF_CMDREG0_CMD); 311*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG0), reg); 312*ddaf02d1SJit Loon Lim 313*ddaf02d1SJit Loon Lim /* Wait for erase operation to complete */ 314*ddaf02d1SJit Loon Lim return cdns_nand_last_opr_status(CNF_DEF_TRD); 315*ddaf02d1SJit Loon Lim } 316*ddaf02d1SJit Loon Lim 317*ddaf02d1SJit Loon Lim /* io mtd functions */ 318*ddaf02d1SJit Loon Lim int cdns_nand_init_mtd(unsigned long long *size, unsigned int *erase_size) 319*ddaf02d1SJit Loon Lim { 320*ddaf02d1SJit Loon Lim *size = dev_info.total_size; 321*ddaf02d1SJit Loon Lim *erase_size = dev_info.block_size; 322*ddaf02d1SJit Loon Lim 323*ddaf02d1SJit Loon Lim return 0; 324*ddaf02d1SJit Loon Lim } 325*ddaf02d1SJit Loon Lim 326*ddaf02d1SJit Loon Lim /* NAND Flash page read */ 327*ddaf02d1SJit Loon Lim static int cdns_nand_read_page(uint32_t block, uint32_t page, uintptr_t buffer) 328*ddaf02d1SJit Loon Lim { 329*ddaf02d1SJit Loon Lim /* Wait for thread to be ready */ 330*ddaf02d1SJit Loon Lim cdns_nand_wait_thread_ready(CNF_DEF_TRD); 331*ddaf02d1SJit Loon Lim 332*ddaf02d1SJit Loon Lim /* Select device */ 333*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG4), 334*ddaf02d1SJit Loon Lim (CNF_DEF_DEVICE << CNF_CMDREG4_MEM)); 335*ddaf02d1SJit Loon Lim 336*ddaf02d1SJit Loon Lim /* Set host memory address for DMA transfers */ 337*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG2), (buffer & 0xFFFF)); 338*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG3), ((buffer >> 32) & 0xFFFF)); 339*ddaf02d1SJit Loon Lim 340*ddaf02d1SJit Loon Lim /* Set row address */ 341*ddaf02d1SJit Loon Lim uint32_t row_address = 0U; 342*ddaf02d1SJit Loon Lim 343*ddaf02d1SJit Loon Lim row_address |= ((page & 0x3F) | (block << 6)); 344*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG1), row_address); 345*ddaf02d1SJit Loon Lim 346*ddaf02d1SJit Loon Lim /* Page read command */ 347*ddaf02d1SJit Loon Lim uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT); 348*ddaf02d1SJit Loon Lim 349*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD); 350*ddaf02d1SJit Loon Lim reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL); 351*ddaf02d1SJit Loon Lim reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR); 352*ddaf02d1SJit Loon Lim reg |= (CNF_DMA_MASTER_SEL << CNF_CMDREG0_DMA); 353*ddaf02d1SJit Loon Lim reg |= (CNF_CT_PAGE_READ << CNF_CMDREG0_CMD); 354*ddaf02d1SJit Loon Lim reg |= (((CNF_READ_SINGLE_PAGE-1) & 0xFF) << CNF_CMDREG0_CMD); 355*ddaf02d1SJit Loon Lim mmio_write_32(CNF_CMDREG(CMD_REG0), reg); 356*ddaf02d1SJit Loon Lim 357*ddaf02d1SJit Loon Lim /* Wait for read operation to complete */ 358*ddaf02d1SJit Loon Lim if (cdns_nand_last_opr_status(CNF_DEF_TRD)) { 359*ddaf02d1SJit Loon Lim ERROR("%s: Page read failed\n", __func__); 360*ddaf02d1SJit Loon Lim return -EIO; 361*ddaf02d1SJit Loon Lim } 362*ddaf02d1SJit Loon Lim 363*ddaf02d1SJit Loon Lim return 0; 364*ddaf02d1SJit Loon Lim } 365*ddaf02d1SJit Loon Lim 366*ddaf02d1SJit Loon Lim int cdns_nand_read(unsigned int offset, uintptr_t buffer, size_t length, 367*ddaf02d1SJit Loon Lim size_t *out_length) 368*ddaf02d1SJit Loon Lim { 369*ddaf02d1SJit Loon Lim uint32_t block = offset / dev_info.block_size; 370*ddaf02d1SJit Loon Lim uint32_t end_block = (offset + length - 1U) / dev_info.block_size; 371*ddaf02d1SJit Loon Lim uint32_t page_start = (offset % dev_info.block_size) / dev_info.page_size; 372*ddaf02d1SJit Loon Lim uint32_t start_offset = offset % dev_info.page_size; 373*ddaf02d1SJit Loon Lim uint32_t nb_pages = dev_info.block_size / dev_info.page_size; 374*ddaf02d1SJit Loon Lim uint32_t bytes_read = 0U; 375*ddaf02d1SJit Loon Lim uint32_t page = 0U; 376*ddaf02d1SJit Loon Lim int result = 0; 377*ddaf02d1SJit Loon Lim 378*ddaf02d1SJit Loon Lim VERBOSE("CNF: block %u-%u, page_start %u, len %zu, offset %u\n", 379*ddaf02d1SJit Loon Lim block, end_block, page_start, length, offset); 380*ddaf02d1SJit Loon Lim 381*ddaf02d1SJit Loon Lim if ((offset >= dev_info.total_size) || 382*ddaf02d1SJit Loon Lim (offset + length-1 >= dev_info.total_size) || 383*ddaf02d1SJit Loon Lim (length == 0U)) { 384*ddaf02d1SJit Loon Lim ERROR("CNF: Invalid read parameters\n"); 385*ddaf02d1SJit Loon Lim return -EINVAL; 386*ddaf02d1SJit Loon Lim } 387*ddaf02d1SJit Loon Lim 388*ddaf02d1SJit Loon Lim *out_length = 0UL; 389*ddaf02d1SJit Loon Lim 390*ddaf02d1SJit Loon Lim while (block <= end_block) { 391*ddaf02d1SJit Loon Lim for (page = page_start; page < nb_pages; page++) { 392*ddaf02d1SJit Loon Lim if ((start_offset != 0U) || (length < dev_info.page_size)) { 393*ddaf02d1SJit Loon Lim /* Partial page read */ 394*ddaf02d1SJit Loon Lim result = cdns_nand_read_page(block, page, 395*ddaf02d1SJit Loon Lim (uintptr_t)scratch_buff); 396*ddaf02d1SJit Loon Lim if (result != 0) { 397*ddaf02d1SJit Loon Lim return result; 398*ddaf02d1SJit Loon Lim } 399*ddaf02d1SJit Loon Lim 400*ddaf02d1SJit Loon Lim bytes_read = MIN((size_t)(dev_info.page_size - start_offset), 401*ddaf02d1SJit Loon Lim length); 402*ddaf02d1SJit Loon Lim 403*ddaf02d1SJit Loon Lim memcpy((uint8_t *)buffer, scratch_buff + start_offset, 404*ddaf02d1SJit Loon Lim bytes_read); 405*ddaf02d1SJit Loon Lim start_offset = 0U; 406*ddaf02d1SJit Loon Lim } else { 407*ddaf02d1SJit Loon Lim /* Full page read */ 408*ddaf02d1SJit Loon Lim result = cdns_nand_read_page(block, page, 409*ddaf02d1SJit Loon Lim (uintptr_t)scratch_buff); 410*ddaf02d1SJit Loon Lim if (result != 0) { 411*ddaf02d1SJit Loon Lim return result; 412*ddaf02d1SJit Loon Lim } 413*ddaf02d1SJit Loon Lim 414*ddaf02d1SJit Loon Lim bytes_read = dev_info.page_size; 415*ddaf02d1SJit Loon Lim memcpy((uint8_t *)buffer, scratch_buff, bytes_read); 416*ddaf02d1SJit Loon Lim } 417*ddaf02d1SJit Loon Lim 418*ddaf02d1SJit Loon Lim length -= bytes_read; 419*ddaf02d1SJit Loon Lim buffer += bytes_read; 420*ddaf02d1SJit Loon Lim *out_length += bytes_read; 421*ddaf02d1SJit Loon Lim 422*ddaf02d1SJit Loon Lim /* All the bytes have read */ 423*ddaf02d1SJit Loon Lim if (length == 0U) { 424*ddaf02d1SJit Loon Lim break; 425*ddaf02d1SJit Loon Lim } 426*ddaf02d1SJit Loon Lim 427*ddaf02d1SJit Loon Lim udelay(CNF_READ_INT_DELAY_US); 428*ddaf02d1SJit Loon Lim } /* for */ 429*ddaf02d1SJit Loon Lim 430*ddaf02d1SJit Loon Lim page_start = 0U; 431*ddaf02d1SJit Loon Lim block++; 432*ddaf02d1SJit Loon Lim } /* while */ 433*ddaf02d1SJit Loon Lim 434*ddaf02d1SJit Loon Lim return 0; 435*ddaf02d1SJit Loon Lim } 436