123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- /*
- * Copyright (c) 2022-2023, Intel Corporation. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <errno.h>
- #include <stdbool.h>
- #include <string.h>
- #include <arch_helpers.h>
- #include <common/debug.h>
- #include <drivers/cadence/cdns_nand.h>
- #include <drivers/delay_timer.h>
- #include <lib/mmio.h>
- #include <lib/utils.h>
- #include <platform_def.h>
- /* NAND flash device information struct */
- static cnf_dev_info_t dev_info;
- /* Scratch buffers for read and write operations */
- static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE];
- /* Wait for controller to be in idle state */
- static inline void cdns_nand_wait_idle(void)
- {
- uint32_t reg = 0U;
- do {
- udelay(CNF_DEF_DELAY_US);
- reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS));
- } while (CNF_GET_CTRL_BUSY(reg) != 0U);
- }
- /* Wait for given thread to be in ready state */
- static inline void cdns_nand_wait_thread_ready(uint8_t thread_id)
- {
- uint32_t reg = 0U;
- do {
- udelay(CNF_DEF_DELAY_US);
- reg = mmio_read_32(CNF_CMDREG(TRD_STATUS));
- reg &= (1U << (uint32_t)thread_id);
- } while (reg != 0U);
- }
- /* Check if the last operation/command in selected thread is completed */
- static int cdns_nand_last_opr_status(uint8_t thread_id)
- {
- uint8_t nthreads = 0U;
- uint32_t reg = 0U;
- /* Get number of threads */
- reg = mmio_read_32(CNF_CTRLPARAM(FEATURE));
- nthreads = CNF_GET_NTHREADS(reg);
- if (thread_id > nthreads) {
- ERROR("%s: Invalid thread ID\n", __func__);
- return -EINVAL;
- }
- /* Select thread */
- mmio_write_32(CNF_CMDREG(CMD_STAT_PTR), (uint32_t)thread_id);
- uint32_t err_mask = CNF_ECMD | CNF_EECC | CNF_EDEV | CNF_EDQS | CNF_EFAIL |
- CNF_EBUS | CNF_EDI | CNF_EPAR | CNF_ECTX | CNF_EPRO;
- do {
- udelay(CNF_DEF_DELAY_US * 2);
- reg = mmio_read_32(CNF_CMDREG(CMD_STAT));
- } while ((reg & CNF_CMPLT) == 0U);
- /* last operation is completed, make sure no other error bits are set */
- if ((reg & err_mask) == 1U) {
- ERROR("%s, CMD_STATUS:0x%x\n", __func__, reg);
- return -EIO;
- }
- return 0;
- }
- /* Set feature command */
- int cdns_nand_set_feature(uint8_t feat_addr, uint8_t feat_val, uint8_t thread_id)
- {
- /* Wait for thread to be ready */
- cdns_nand_wait_thread_ready(thread_id);
- /* Set feature address */
- mmio_write_32(CNF_CMDREG(CMD_REG1), (uint32_t)feat_addr);
- /* Set feature volume */
- mmio_write_32(CNF_CMDREG(CMD_REG2), (uint32_t)feat_val);
- /* Set feature command */
- uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
- reg |= (thread_id << CNF_CMDREG0_TRD);
- reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
- reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
- reg |= (CNF_CT_SET_FEATURE << CNF_CMDREG0_CMD);
- mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
- return cdns_nand_last_opr_status(thread_id);
- }
- /* Reset command to the selected device */
- int cdns_nand_reset(uint8_t thread_id)
- {
- /* Operation is executed in selected thread */
- cdns_nand_wait_thread_ready(thread_id);
- /* Select memory */
- mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
- /* Issue reset command */
- uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
- reg |= (thread_id << CNF_CMDREG0_TRD);
- reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
- reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
- reg |= (CNF_CT_RESET_ASYNC << CNF_CMDREG0_CMD);
- mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
- return cdns_nand_last_opr_status(thread_id);
- }
- /* Set operation work mode */
- static void cdns_nand_set_opr_mode(uint8_t opr_mode)
- {
- /* Wait for controller to be in idle state */
- cdns_nand_wait_idle();
- /* Reset DLL PHY */
- uint32_t reg = mmio_read_32(CNF_MINICTRL(DLL_PHY_CTRL));
- reg &= ~(1 << CNF_DLL_PHY_RST_N);
- mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg);
- if (opr_mode == CNF_OPR_WORK_MODE_SDR) {
- /* Combo PHY Control Timing Block register settings */
- mmio_write_32(CP_CTB(CTRL_REG), CP_CTRL_REG_SDR);
- mmio_write_32(CP_CTB(TSEL_REG), CP_TSEL_REG_SDR);
- /* Combo PHY DLL register settings */
- mmio_write_32(CP_DLL(DQ_TIMING_REG), CP_DQ_TIMING_REG_SDR);
- mmio_write_32(CP_DLL(DQS_TIMING_REG), CP_DQS_TIMING_REG_SDR);
- mmio_write_32(CP_DLL(GATE_LPBK_CTRL_REG), CP_GATE_LPBK_CTRL_REG_SDR);
- mmio_write_32(CP_DLL(MASTER_CTRL_REG), CP_DLL_MASTER_CTRL_REG_SDR);
- /* Async mode timing settings */
- mmio_write_32(CNF_MINICTRL(ASYNC_TOGGLE_TIMINGS),
- (2 << CNF_ASYNC_TIMINGS_TRH) |
- (4 << CNF_ASYNC_TIMINGS_TRP) |
- (2 << CNF_ASYNC_TIMINGS_TWH) |
- (4 << CNF_ASYNC_TIMINGS_TWP));
- /* Set extended read and write mode */
- reg |= (1 << CNF_DLL_PHY_EXT_RD_MODE);
- reg |= (1 << CNF_DLL_PHY_EXT_WR_MODE);
- /* Set operation work mode in common settings */
- uint32_t data = mmio_read_32(CNF_MINICTRL(CMN_SETTINGS));
- data |= (CNF_OPR_WORK_MODE_SDR << CNF_CMN_SETTINGS_OPR);
- mmio_write_32(CNF_MINICTRL(CMN_SETTINGS), data);
- } else if (opr_mode == CNF_OPR_WORK_MODE_NVDDR) {
- ; /* ToDo: add DDR mode settings also once available on SIMICS */
- } else {
- ;
- }
- reg |= (1 << CNF_DLL_PHY_RST_N);
- mmio_write_32(CNF_MINICTRL(DLL_PHY_CTRL), reg);
- }
- /* Data transfer configuration */
- static void cdns_nand_transfer_config(void)
- {
- /* Wait for controller to be in idle state */
- cdns_nand_wait_idle();
- /* Configure data transfer parameters */
- mmio_write_32(CNF_CTRLCFG(TRANS_CFG0), 1);
- /* ECC is disabled */
- mmio_write_32(CNF_CTRLCFG(ECC_CFG0), 0);
- /* DMA burst select */
- mmio_write_32(CNF_CTRLCFG(DMA_SETTINGS),
- (CNF_DMA_BURST_SIZE_MAX << CNF_DMA_SETTINGS_BURST) |
- (1 << CNF_DMA_SETTINGS_OTE));
- /* Enable pre-fetching for 1K */
- mmio_write_32(CNF_CTRLCFG(FIFO_TLEVEL),
- (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_POS) |
- (CNF_DMA_PREFETCH_SIZE << CNF_FIFO_TLEVEL_DMA_SIZE));
- /* Select access type */
- mmio_write_32(CNF_CTRLCFG(MULTIPLANE_CFG), 0);
- mmio_write_32(CNF_CTRLCFG(CACHE_CFG), 0);
- }
- /* Update the nand flash device info */
- static int cdns_nand_update_dev_info(void)
- {
- uint32_t reg = 0U;
- /* Read the device type and number of LUNs */
- reg = mmio_read_32(CNF_CTRLPARAM(DEV_PARAMS0));
- dev_info.type = CNF_GET_DEV_TYPE(reg);
- if (dev_info.type == CNF_DT_UNKNOWN) {
- ERROR("%s: device type unknown\n", __func__);
- return -ENXIO;
- }
- dev_info.nluns = CNF_GET_NLUNS(reg);
- /* Pages per block */
- reg = mmio_read_32(CNF_CTRLCFG(DEV_LAYOUT));
- dev_info.npages_per_block = CNF_GET_NPAGES_PER_BLOCK(reg);
- /* Sector size and last sector size */
- reg = mmio_read_32(CNF_CTRLCFG(TRANS_CFG1));
- dev_info.sector_size = CNF_GET_SCTR_SIZE(reg);
- dev_info.last_sector_size = CNF_GET_LAST_SCTR_SIZE(reg);
- /* Page size and spare size */
- reg = mmio_read_32(CNF_CTRLPARAM(DEV_AREA));
- dev_info.page_size = CNF_GET_PAGE_SIZE(reg);
- dev_info.spare_size = CNF_GET_SPARE_SIZE(reg);
- /* Device blocks per LUN */
- dev_info.nblocks_per_lun = mmio_read_32(CNF_CTRLPARAM(DEV_BLOCKS_PLUN));
- /* Calculate block size and total device size */
- dev_info.block_size = (dev_info.npages_per_block * dev_info.page_size);
- dev_info.total_size = (dev_info.block_size * dev_info.nblocks_per_lun *
- dev_info.nluns);
- VERBOSE("CNF params: page %d, spare %d, block %d, total %lld\n",
- dev_info.page_size, dev_info.spare_size,
- dev_info.block_size, dev_info.total_size);
- return 0;
- }
- /* NAND Flash Controller/Host initialization */
- int cdns_nand_host_init(void)
- {
- uint32_t reg = 0U;
- int ret = 0;
- do {
- /* Read controller status register for init complete */
- reg = mmio_read_32(CNF_CMDREG(CTRL_STATUS));
- } while (CNF_GET_INIT_COMP(reg) == 0);
- ret = cdns_nand_update_dev_info();
- if (ret != 0) {
- return ret;
- }
- INFO("CNF: device discovery process completed and device type %d\n",
- dev_info.type);
- /* Enable data integrity, enable CRC and parity */
- reg = mmio_read_32(CNF_DI(CONTROL));
- reg |= (1 << CNF_DI_PAR_EN);
- reg |= (1 << CNF_DI_CRC_EN);
- mmio_write_32(CNF_DI(CONTROL), reg);
- /* Status polling mode, device control and status register */
- cdns_nand_wait_idle();
- reg = mmio_read_32(CNF_CTRLCFG(DEV_STAT));
- reg = reg & ~1;
- mmio_write_32(CNF_CTRLCFG(DEV_STAT), reg);
- /* Set operation work mode */
- cdns_nand_set_opr_mode(CNF_OPR_WORK_MODE_SDR);
- /* Set data transfer configuration parameters */
- cdns_nand_transfer_config();
- return 0;
- }
- /* erase: Block erase command */
- int cdns_nand_erase(uint32_t offset, uint32_t size)
- {
- /* Determine the starting block offset i.e row address */
- uint32_t row_address = dev_info.npages_per_block * offset;
- /* Wait for thread to be in ready state */
- cdns_nand_wait_thread_ready(CNF_DEF_TRD);
- /*Set row address */
- mmio_write_32(CNF_CMDREG(CMD_REG1), row_address);
- /* Operation bank number */
- mmio_write_32(CNF_CMDREG(CMD_REG4), (CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
- /* Block erase command */
- uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
- reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD);
- reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
- reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
- reg |= (CNF_CT_ERASE << CNF_CMDREG0_CMD);
- reg |= (((size-1) & 0xFF) << CNF_CMDREG0_CMD);
- mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
- /* Wait for erase operation to complete */
- return cdns_nand_last_opr_status(CNF_DEF_TRD);
- }
- /* io mtd functions */
- int cdns_nand_init_mtd(unsigned long long *size, unsigned int *erase_size)
- {
- *size = dev_info.total_size;
- *erase_size = dev_info.block_size;
- return 0;
- }
- /* NAND Flash page read */
- static int cdns_nand_read_page(uint32_t block, uint32_t page, uintptr_t buffer)
- {
- /* Wait for thread to be ready */
- cdns_nand_wait_thread_ready(CNF_DEF_TRD);
- /* Select device */
- mmio_write_32(CNF_CMDREG(CMD_REG4),
- (CNF_DEF_DEVICE << CNF_CMDREG4_MEM));
- /* Set host memory address for DMA transfers */
- mmio_write_32(CNF_CMDREG(CMD_REG2), (buffer & 0xFFFF));
- mmio_write_32(CNF_CMDREG(CMD_REG3), ((buffer >> 32) & 0xFFFF));
- /* Set row address */
- uint32_t row_address = 0U;
- row_address |= ((page & 0x3F) | (block << 6));
- mmio_write_32(CNF_CMDREG(CMD_REG1), row_address);
- /* Page read command */
- uint32_t reg = (CNF_WORK_MODE_PIO << CNF_CMDREG0_CT);
- reg |= (CNF_DEF_TRD << CNF_CMDREG0_TRD);
- reg |= (CNF_DEF_VOL_ID << CNF_CMDREG0_VOL);
- reg |= (CNF_INT_DIS << CNF_CMDREG0_INTR);
- reg |= (CNF_DMA_MASTER_SEL << CNF_CMDREG0_DMA);
- reg |= (CNF_CT_PAGE_READ << CNF_CMDREG0_CMD);
- reg |= (((CNF_READ_SINGLE_PAGE-1) & 0xFF) << CNF_CMDREG0_CMD);
- mmio_write_32(CNF_CMDREG(CMD_REG0), reg);
- /* Wait for read operation to complete */
- if (cdns_nand_last_opr_status(CNF_DEF_TRD)) {
- ERROR("%s: Page read failed\n", __func__);
- return -EIO;
- }
- return 0;
- }
- int cdns_nand_read(unsigned int offset, uintptr_t buffer, size_t length,
- size_t *out_length)
- {
- uint32_t block = offset / dev_info.block_size;
- uint32_t end_block = (offset + length - 1U) / dev_info.block_size;
- uint32_t page_start = (offset % dev_info.block_size) / dev_info.page_size;
- uint32_t start_offset = offset % dev_info.page_size;
- uint32_t nb_pages = dev_info.block_size / dev_info.page_size;
- uint32_t bytes_read = 0U;
- uint32_t page = 0U;
- int result = 0;
- VERBOSE("CNF: block %u-%u, page_start %u, len %zu, offset %u\n",
- block, end_block, page_start, length, offset);
- if ((offset >= dev_info.total_size) ||
- (offset + length-1 >= dev_info.total_size) ||
- (length == 0U)) {
- ERROR("CNF: Invalid read parameters\n");
- return -EINVAL;
- }
- *out_length = 0UL;
- while (block <= end_block) {
- for (page = page_start; page < nb_pages; page++) {
- if ((start_offset != 0U) || (length < dev_info.page_size)) {
- /* Partial page read */
- result = cdns_nand_read_page(block, page,
- (uintptr_t)scratch_buff);
- if (result != 0) {
- return result;
- }
- bytes_read = MIN((size_t)(dev_info.page_size - start_offset),
- length);
- memcpy((uint8_t *)buffer, scratch_buff + start_offset,
- bytes_read);
- start_offset = 0U;
- } else {
- /* Full page read */
- result = cdns_nand_read_page(block, page,
- (uintptr_t)scratch_buff);
- if (result != 0) {
- return result;
- }
- bytes_read = dev_info.page_size;
- memcpy((uint8_t *)buffer, scratch_buff, bytes_read);
- }
- length -= bytes_read;
- buffer += bytes_read;
- *out_length += bytes_read;
- /* All the bytes have read */
- if (length == 0U) {
- break;
- }
- udelay(CNF_READ_INT_DELAY_US);
- } /* for */
- page_start = 0U;
- block++;
- } /* while */
- return 0;
- }
|