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