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