123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /*
- * Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <errno.h>
- #include <stddef.h>
- #include <common/debug.h>
- #include <drivers/delay_timer.h>
- #include <drivers/raw_nand.h>
- #include <lib/utils.h>
- #include <platform_def.h>
- #define ONFI_SIGNATURE_ADDR 0x20U
- /* CRC calculation */
- #define CRC_POLYNOM 0x8005U
- #define CRC_INIT_VALUE 0x4F4EU
- /* Status register */
- #define NAND_STATUS_READY BIT(6)
- static struct rawnand_device rawnand_dev;
- #pragma weak plat_get_raw_nand_data
- int plat_get_raw_nand_data(struct rawnand_device *device)
- {
- return 0;
- }
- static int nand_send_cmd(uint8_t cmd, unsigned int tim)
- {
- struct nand_req req;
- zeromem(&req, sizeof(struct nand_req));
- req.nand = rawnand_dev.nand_dev;
- req.type = NAND_REQ_CMD | cmd;
- req.inst_delay = tim;
- return rawnand_dev.ops->exec(&req);
- }
- static int nand_send_addr(uint8_t addr, unsigned int tim)
- {
- struct nand_req req;
- zeromem(&req, sizeof(struct nand_req));
- req.nand = rawnand_dev.nand_dev;
- req.type = NAND_REQ_ADDR;
- req.addr = &addr;
- req.inst_delay = tim;
- return rawnand_dev.ops->exec(&req);
- }
- static int nand_send_wait(unsigned int delay, unsigned int tim)
- {
- struct nand_req req;
- zeromem(&req, sizeof(struct nand_req));
- req.nand = rawnand_dev.nand_dev;
- req.type = NAND_REQ_WAIT;
- req.inst_delay = tim;
- req.delay_ms = delay;
- return rawnand_dev.ops->exec(&req);
- }
- static int nand_read_data(uint8_t *data, unsigned int length, bool use_8bit)
- {
- struct nand_req req;
- zeromem(&req, sizeof(struct nand_req));
- req.nand = rawnand_dev.nand_dev;
- req.type = NAND_REQ_DATAIN | (use_8bit ? NAND_REQ_BUS_WIDTH_8 : 0U);
- req.addr = data;
- req.length = length;
- return rawnand_dev.ops->exec(&req);
- }
- int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer,
- unsigned int len)
- {
- int ret;
- uint8_t addr[2];
- unsigned int i;
- ret = nand_send_cmd(NAND_CMD_CHANGE_1ST, 0U);
- if (ret != 0) {
- return ret;
- }
- if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
- offset /= 2U;
- }
- addr[0] = offset;
- addr[1] = offset >> 8;
- for (i = 0; i < 2U; i++) {
- ret = nand_send_addr(addr[i], 0U);
- if (ret != 0) {
- return ret;
- }
- }
- ret = nand_send_cmd(NAND_CMD_CHANGE_2ND, NAND_TCCS_MIN);
- if (ret != 0) {
- return ret;
- }
- return nand_read_data((uint8_t *)buffer, len, false);
- }
- int nand_read_page_cmd(unsigned int page, unsigned int offset,
- uintptr_t buffer, unsigned int len)
- {
- uint8_t addr[5];
- uint8_t i = 0U;
- uint8_t j;
- int ret;
- VERBOSE(">%s page %u offset %u buffer 0x%lx\n", __func__, page, offset,
- buffer);
- if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
- offset /= 2U;
- }
- addr[i++] = offset;
- addr[i++] = offset >> 8;
- addr[i++] = page;
- addr[i++] = page >> 8;
- if (rawnand_dev.nand_dev->size > SZ_128M) {
- addr[i++] = page >> 16;
- }
- ret = nand_send_cmd(NAND_CMD_READ_1ST, 0U);
- if (ret != 0) {
- return ret;
- }
- for (j = 0U; j < i; j++) {
- ret = nand_send_addr(addr[j], 0U);
- if (ret != 0) {
- return ret;
- }
- }
- ret = nand_send_cmd(NAND_CMD_READ_2ND, NAND_TWB_MAX);
- if (ret != 0) {
- return ret;
- }
- ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
- if (ret != 0) {
- return ret;
- }
- if (buffer != 0U) {
- ret = nand_read_data((uint8_t *)buffer, len, false);
- }
- return ret;
- }
- static int nand_status(uint8_t *status)
- {
- int ret;
- ret = nand_send_cmd(NAND_CMD_STATUS, NAND_TWHR_MIN);
- if (ret != 0) {
- return ret;
- }
- if (status != NULL) {
- ret = nand_read_data(status, 1U, true);
- }
- return ret;
- }
- int nand_wait_ready(unsigned int delay_ms)
- {
- uint8_t status;
- int ret;
- uint64_t timeout;
- /* Wait before reading status */
- udelay(1);
- ret = nand_status(NULL);
- if (ret != 0) {
- return ret;
- }
- timeout = timeout_init_us(delay_ms * 1000U);
- while (!timeout_elapsed(timeout)) {
- ret = nand_read_data(&status, 1U, true);
- if (ret != 0) {
- return ret;
- }
- if ((status & NAND_STATUS_READY) != 0U) {
- return nand_send_cmd(NAND_CMD_READ_1ST, 0U);
- }
- udelay(10);
- }
- return -ETIMEDOUT;
- }
- static int nand_reset(void)
- {
- int ret;
- ret = nand_send_cmd(NAND_CMD_RESET, NAND_TWB_MAX);
- if (ret != 0) {
- return ret;
- }
- return nand_send_wait(PSEC_TO_MSEC(NAND_TRST_MAX), 0U);
- }
- #if NAND_ONFI_DETECT
- static uint16_t nand_check_crc(uint16_t crc, uint8_t *data_in,
- unsigned int data_len)
- {
- uint32_t i;
- uint32_t j;
- uint32_t bit;
- for (i = 0U; i < data_len; i++) {
- uint8_t cur_param = *data_in++;
- for (j = BIT(7); j != 0U; j >>= 1) {
- bit = crc & BIT(15);
- crc <<= 1;
- if ((cur_param & j) != 0U) {
- bit ^= BIT(15);
- }
- if (bit != 0U) {
- crc ^= CRC_POLYNOM;
- }
- }
- crc &= GENMASK(15, 0);
- }
- return crc;
- }
- static int nand_read_id(uint8_t addr, uint8_t *id, unsigned int size)
- {
- int ret;
- ret = nand_send_cmd(NAND_CMD_READID, 0U);
- if (ret != 0) {
- return ret;
- }
- ret = nand_send_addr(addr, NAND_TWHR_MIN);
- if (ret != 0) {
- return ret;
- }
- return nand_read_data(id, size, true);
- }
- static int nand_read_param_page(void)
- {
- struct nand_param_page page;
- uint8_t addr = 0U;
- int ret;
- ret = nand_send_cmd(NAND_CMD_READ_PARAM_PAGE, 0U);
- if (ret != 0) {
- return ret;
- }
- ret = nand_send_addr(addr, NAND_TWB_MAX);
- if (ret != 0) {
- return ret;
- }
- ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
- if (ret != 0) {
- return ret;
- }
- ret = nand_read_data((uint8_t *)&page, sizeof(page), true);
- if (ret != 0) {
- return ret;
- }
- if (strncmp((char *)&page.page_sig, "ONFI", 4) != 0) {
- WARN("Error ONFI detection\n");
- return -EINVAL;
- }
- if (nand_check_crc(CRC_INIT_VALUE, (uint8_t *)&page, 254U) !=
- page.crc16) {
- WARN("Error reading param\n");
- return -EINVAL;
- }
- if ((page.features & ONFI_FEAT_BUS_WIDTH_16) != 0U) {
- rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_16;
- } else {
- rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_8;
- }
- rawnand_dev.nand_dev->block_size = page.num_pages_per_blk *
- page.bytes_per_page;
- rawnand_dev.nand_dev->page_size = page.bytes_per_page;
- rawnand_dev.nand_dev->size = page.num_pages_per_blk *
- page.bytes_per_page *
- page.num_blk_in_lun * page.num_lun;
- if (page.nb_ecc_bits != GENMASK_32(7, 0)) {
- rawnand_dev.nand_dev->ecc.max_bit_corr = page.nb_ecc_bits;
- rawnand_dev.nand_dev->ecc.size = SZ_512;
- }
- VERBOSE("Page size %u, block_size %u, Size %llu, ecc %u, buswidth %u\n",
- rawnand_dev.nand_dev->page_size,
- rawnand_dev.nand_dev->block_size, rawnand_dev.nand_dev->size,
- rawnand_dev.nand_dev->ecc.max_bit_corr,
- rawnand_dev.nand_dev->buswidth);
- return 0;
- }
- static int detect_onfi(void)
- {
- int ret;
- char id[4];
- ret = nand_read_id(ONFI_SIGNATURE_ADDR, (uint8_t *)id, sizeof(id));
- if (ret != 0) {
- return ret;
- }
- if (strncmp(id, "ONFI", sizeof(id)) != 0) {
- WARN("NAND Non ONFI detected\n");
- return -ENODEV;
- }
- return nand_read_param_page();
- }
- #endif
- static int nand_mtd_block_is_bad(unsigned int block)
- {
- unsigned int nbpages_per_block = rawnand_dev.nand_dev->block_size /
- rawnand_dev.nand_dev->page_size;
- uint8_t bbm_marker[2];
- uint8_t page;
- int ret;
- for (page = 0U; page < 2U; page++) {
- ret = nand_read_page_cmd(block * nbpages_per_block,
- rawnand_dev.nand_dev->page_size,
- (uintptr_t)bbm_marker,
- sizeof(bbm_marker));
- if (ret != 0) {
- return ret;
- }
- if ((bbm_marker[0] != GENMASK_32(7, 0)) ||
- (bbm_marker[1] != GENMASK_32(7, 0))) {
- WARN("Block %u is bad\n", block);
- return 1;
- }
- }
- return 0;
- }
- static int nand_mtd_read_page_raw(struct nand_device *nand, unsigned int page,
- uintptr_t buffer)
- {
- return nand_read_page_cmd(page, 0U, buffer,
- rawnand_dev.nand_dev->page_size);
- }
- void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops)
- {
- rawnand_dev.ops = ops;
- }
- int nand_raw_init(unsigned long long *size, unsigned int *erase_size)
- {
- int ret;
- rawnand_dev.nand_dev = get_nand_device();
- if (rawnand_dev.nand_dev == NULL) {
- return -EINVAL;
- }
- rawnand_dev.nand_dev->mtd_block_is_bad = nand_mtd_block_is_bad;
- rawnand_dev.nand_dev->mtd_read_page = nand_mtd_read_page_raw;
- rawnand_dev.nand_dev->ecc.mode = NAND_ECC_NONE;
- if ((rawnand_dev.ops->setup == NULL) ||
- (rawnand_dev.ops->exec == NULL)) {
- return -ENODEV;
- }
- ret = nand_reset();
- if (ret != 0) {
- return ret;
- }
- #if NAND_ONFI_DETECT
- if (detect_onfi() != 0) {
- WARN("Detect ONFI failed\n");
- }
- #endif
- if (plat_get_raw_nand_data(&rawnand_dev) != 0) {
- return -EINVAL;
- }
- assert((rawnand_dev.nand_dev->page_size != 0U) &&
- (rawnand_dev.nand_dev->block_size != 0U) &&
- (rawnand_dev.nand_dev->size != 0U));
- *size = rawnand_dev.nand_dev->size;
- *erase_size = rawnand_dev.nand_dev->block_size;
- rawnand_dev.ops->setup(rawnand_dev.nand_dev);
- return 0;
- }
|