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 */
cdns_nand_wait_idle(void)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 */
cdns_nand_wait_thread_ready(uint8_t thread_id)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
cdns_nand_get_thread_status(uint8_t thread_id)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 */
cdns_nand_last_opr_status(uint8_t thread_id)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 */
cdns_nand_set_feature(uint8_t feat_addr,uint8_t feat_val,uint8_t thread_id)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 */
cdns_nand_reset(uint8_t thread_id)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 */
cdns_nand_set_opr_mode(void)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 */
cdns_nand_transfer_config(void)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 */
cdns_nand_update_dev_info(void)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 */
cdns_nand_host_init(void)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 */
cdns_nand_erase(uint32_t offset,uint32_t size)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 */
cdns_nand_init_mtd(unsigned long long * size,unsigned int * erase_size)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
cdns_nand_get_row_address(uint32_t page,uint32_t block)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 */
cdns_nand_read_page(uint32_t block,uint32_t page,uintptr_t buffer)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
cdns_nand_read(unsigned int offset,uintptr_t buffer,size_t length,size_t * out_length)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 }