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