--- zzzz-none-000/linux-4.4.271/drivers/staging/mt29f_spinand/mt29f_spinand.c 2021-06-03 06:22:09.000000000 +0000 +++ hawkeye-5590-750/linux-4.4.271/drivers/staging/mt29f_spinand/mt29f_spinand.c 2023-04-19 10:22:29.000000000 +0000 @@ -20,11 +20,296 @@ #include #include #include +#include +#include #include "mt29f_spinand.h" +#include "giga_spinand.h" + +#define BUFSIZE (10 * 64 * 4096) + +static bool use_dummybyte_in_readid; + +static int spinand_disable_ecc(struct spi_device *spi_nand); +static int spinand_lock_block(struct spi_device *spi_nand, u8 lock); + +struct spinand_ops spinand_dev[] = { +#ifdef CONFIG_MTD_SPINAND_GIGADEVICE + { + .maf_id = NAND_MFR_GIGA, + .no_of_dies = 1, + .dev_id = 0xb1, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = gigadevice_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id, + .spinand_verify_ecc = gigadevice_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_GIGA, + .no_of_dies = 1, + .dev_id = 0xb4, + .prev_die_id = INT_MAX, + .pages_per_die = 0x20000, + .spinand_set_defaults = gigadevice_set_defaults_512mb, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id, + .spinand_verify_ecc = gigadevice_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_GIGA, + .no_of_dies = 1, + .dev_id = 0xa1, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = gigadevice_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id, + .spinand_verify_ecc = gigadevice_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_GIGA, + .no_of_dies = 1, + .dev_id = 0xd1, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = gigadevice_set_defaults_128mb, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data_v2, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id_v2, + .spinand_verify_ecc = gigadevice_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_GIGA, + .no_of_dies = 1, + .dev_id = 0xc1, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = gigadevice_set_defaults_128mb, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data_v2, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id_v3, + .spinand_verify_ecc = gigadevice_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_ATO, + .no_of_dies = 1, + .dev_id = 0x12, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = gigadevice_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = gigadevice_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = gigadevice_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = gigadevice_parse_id, + .spinand_verify_ecc = dummy_verify_ecc, + .spinand_die_select = NULL, + }, +#endif + { + .maf_id = NAND_MFR_MACRONIX, + .no_of_dies = 1, + .dev_id = 0x12, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = macronix_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = macronix_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = macronix_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = macronix_parse_id, + .spinand_verify_ecc = macronix_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_WINBOND, + .no_of_dies = 1, + .dev_id = 0xaa, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = winbond_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = winbond_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = winbond_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = winbond_parse_id, + .spinand_verify_ecc = macronix_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_WINBOND, + .no_of_dies = 2, + .dev_id = 0xab, + .prev_die_id = INT_MAX, + .pages_per_die = 0x10000, + .spinand_set_defaults = winbond_set_defaults, + .spinand_read_cmd = gigadevice_read_cmd, + .spinand_read_data = winbond_read_data, + .spinand_write_cmd = gigadevice_write_cmd, + .spinand_write_data = winbond_write_data, + .spinand_erase_blk = gigadevice_erase_blk, + .spinand_parse_id = winbond_parse_id, + .spinand_verify_ecc = macronix_verify_ecc, + .spinand_die_select = winbond_die_select, + }, + { + .maf_id = NAND_MFR_TOSHIBA, + .no_of_dies = 1, + .dev_id = 0xcd, + .prev_die_id = INT_MAX, + .pages_per_die = 0x20000, + .spinand_set_defaults = gigadevice_set_defaults_512mb, + .spinand_read_cmd = toshiba_read_cmd, + .spinand_read_data = toshiba_read_data, + .spinand_write_cmd = toshiba_write_cmd, + .spinand_write_data = toshiba_write_data, + .spinand_erase_blk = toshiba_erase_blk, + .spinand_parse_id = toshiba_parse_id, + .spinand_verify_ecc = toshiba_verify_ecc, + .spinand_die_select = NULL, + }, + { + .maf_id = NAND_MFR_FIDELIX, + .no_of_dies = 2, /*No of planes*/ + .dev_id = 0x72, + .prev_die_id = INT_MAX, + .pages_per_die = FIDELIX_PAGES_PER_PLANE, + .spinand_set_defaults = fidelix_set_defaults, + .spinand_read_cmd = fidelix_read_cmd, + .spinand_read_data = fidelix_read_data, + .spinand_write_cmd = fidelix_write_cmd, + .spinand_write_data = fidelix_write_data, + .spinand_erase_blk = fidelix_erase_blk, + .spinand_parse_id = fidelix_parse_id, + .spinand_verify_ecc = macronix_verify_ecc, + .spinand_die_select = NULL, + }, + { }, +}; + +void mt29f_read_page_to_cache(struct spinand_cmd *cmd, u32 page_id) +{ + cmd->addr[1] = (u8)((page_id & 0xff00) >> 8); + cmd->addr[2] = (u8)(page_id & 0x00ff); +} + +void mt29f_read_from_cache(struct spinand_cmd *cmd, u16 column, u32 page_id) +{ + cmd->addr[0] = (u8)((column & 0xff00) >> 8); + cmd->addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); + cmd->addr[1] = (u8)(column & 0x00ff); + cmd->addr[2] = (u8)(0xff); +} + +void mt29f_program_data_to_cache(struct spinand_cmd *cmd, u16 column, + u32 page_id) +{ + cmd->addr[0] = (u8)((column & 0xff00) >> 8); + cmd->addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); + cmd->addr[1] = (u8)(column & 0x00ff); +} + +void mt29f_program_execute(struct spinand_cmd *cmd, u32 column) +{ + cmd->addr[1] = (u8)((column & 0xff00) >> 8); + cmd->addr[2] = (u8)(column & 0x00ff); +} + +void mt29f_erase_block_erase(struct spinand_cmd *cmd, u32 page_id) +{ + cmd->addr[1] = (u8)((page_id & 0xff00) >> 8); + cmd->addr[2] = (u8)(page_id & 0x00ff); +} + +int mt29f_verify_ecc(u8 status) +{ + int ecc_status = (status & STATUS_ECC_MASK); + + if (ecc_status == STATUS_ECC_ERROR) + return SPINAND_ECC_ERROR; + else if (ecc_status == STATUS_ECC_1BIT_CORRECTED) + return SPINAND_ECC_CORRECTED; + else + return 0; +} + +struct spinand_ops mt29f_spinand_ops = { + NAND_MFR_MICRON, + 1, + 0x0, + INT_MAX, + 0x0, + NULL, + mt29f_read_page_to_cache, + mt29f_read_from_cache, + mt29f_program_execute, + mt29f_program_data_to_cache, + mt29f_erase_block_erase, + NULL, + mt29f_verify_ecc, + NULL, +}; + +static inline struct spinand_ops *get_dev_ops(struct spi_device *spi_nand) +{ + struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata + (&spi_nand->dev); + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct spinand_info *info = (struct spinand_info *)chip->priv; + struct spinand_ops *dev_ops = info->dev_ops; + + return dev_ops; +} + +void spinand_parse_id(struct spi_device *spi_nand, u8 *nand_id, u8 *id) +{ + int tmp; + struct spinand_ops *tmp_ops; + struct mtd_info *mtd = (struct mtd_info *) + dev_get_drvdata(&spi_nand->dev); + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct spinand_info *info = (struct spinand_info *)chip->priv; + + for (tmp = 0; tmp < ARRAY_SIZE(spinand_dev) - 1; tmp++) { + tmp_ops = &spinand_dev[tmp]; + if (tmp_ops->spinand_parse_id(spi_nand, tmp_ops, + nand_id, id) == 0) { + info->dev_ops = &spinand_dev[tmp]; + info->dev_ops->spinand_set_defaults(spi_nand); + return; + } + } + info->dev_ops = &mt29f_spinand_ops; +} -#define BUFSIZE (10 * 64 * 2048) -#define CACHE_BUF 2112 /* * OOB area specification layout: Total 32 available free bytes. */ @@ -110,6 +395,60 @@ return spi_sync(spi, &message); } +static int get_die_id(struct spinand_ops *dev_ops, u32 _page_id) +{ + u64 page_id = _page_id; + + do_div(page_id, dev_ops->pages_per_die); + if (page_id > dev_ops->no_of_dies) { + pr_info("invalid die id : %lld\n", page_id); + return -EINVAL; + } + + return page_id; +} + +/* + * winbond_die_select - send command 0xc2 to select die + * Description: + * Die select function. + * Die ID is given as either 0 or 1 to select die 0 or 1 + * respectively + */ +int winbond_die_select(struct spi_device *spi_nand, + struct spinand_ops *dev_ops, u8 die_id) +{ + int retval; + struct spinand_cmd cmd = {0}; + + if (die_id < 0) + return -1; + + if (dev_ops->prev_die_id == die_id) + return 0; + + cmd.cmd = CMD_DIE_SELECT, + cmd.n_addr = 1, + cmd.addr[0] = die_id, + retval = spinand_cmd(spi_nand, &cmd); + if (retval < 0) + dev_err(&spi_nand->dev, "error %d in die select\n", retval); + else + dev_ops->prev_die_id = die_id; + + return retval; +} + +static inline int select_die(struct spi_device *spi_nand, + struct spinand_ops *dev_ops, int die) +{ + if (!dev_ops->spinand_die_select) + return 0; + + return dev_ops->spinand_die_select(spi_nand, + dev_ops, die); +} + /* * spinand_read_id- Read SPI Nand ID * Description: @@ -118,12 +457,19 @@ static int spinand_read_id(struct spi_device *spi_nand, u8 *id) { int retval; - u8 nand_id[3]; + int i; + u8 nand_id[4]; struct spinand_cmd cmd = {0}; + struct spinand_ops *dev_ops; cmd.cmd = CMD_READ_ID; cmd.n_rx = 3; cmd.rx_buf = &nand_id[0]; + if (use_dummybyte_in_readid) { + cmd.n_addr = 1; + cmd.addr[0] = 0x0; + cmd.n_rx += 1; + } retval = spinand_cmd(spi_nand, &cmd); if (retval < 0) { @@ -132,6 +478,20 @@ } id[0] = nand_id[1]; id[1] = nand_id[2]; + spinand_parse_id(spi_nand, nand_id, id); + dev_ops = get_dev_ops(spi_nand); + if (dev_ops->spinand_die_select) { + for (i = 0; i < dev_ops->no_of_dies; i++) { + retval = dev_ops->spinand_die_select(spi_nand, + dev_ops, i); + if (retval < 0) + return retval; + spinand_lock_block(spi_nand, BL_ALL_UNLOCKED); + if (spinand_disable_ecc(spi_nand) < 0) + pr_info("%s: disable ecc failed!\n", __func__); + } + } + return retval; } @@ -247,19 +607,31 @@ static int spinand_enable_ecc(struct spi_device *spi_nand) { int retval; + int i; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); u8 otp = 0; - retval = spinand_get_otp(spi_nand, &otp); - if (retval < 0) - return retval; + for (i = 0; i < dev_ops->no_of_dies; i++) { + retval = select_die(spi_nand, dev_ops, i); + if (retval < 0) + return retval; - if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) - return 0; - otp |= OTP_ECC_MASK; - retval = spinand_set_otp(spi_nand, &otp); - if (retval < 0) - return retval; - return spinand_get_otp(spi_nand, &otp); + retval = spinand_get_otp(spi_nand, &otp); + if (retval < 0) + return retval; + + if ((otp & OTP_ECC_MASK) != OTP_ECC_MASK) { + otp |= OTP_ECC_MASK; + retval = spinand_set_otp(spi_nand, &otp); + if (retval < 0) + return retval; + retval = spinand_get_otp(spi_nand, &otp); + if (retval < 0) + return retval; + } + } + + return 0; } #endif @@ -282,33 +654,67 @@ return 0; } +static int spinand_enable_buffer_mode(struct spi_device *spi_nand) +{ + int retval; + u8 otp = 0; + + retval = spinand_get_otp(spi_nand, &otp); + if (retval < 0) + return retval; + + if ((otp & OTP_WINBOND_BUFFERMODE) != OTP_WINBOND_BUFFERMODE) { + otp |= OTP_WINBOND_BUFFERMODE; + retval = spinand_set_otp(spi_nand, &otp); + if (retval < 0) + return retval; + return spinand_get_otp(spi_nand, &otp); + } + return 0; +} + /** - * spinand_write_enable- send command 0x06 to enable write or erase the + * spinand_write_config- send command 0x06 to enable write or erase the + * Nand cells or send command 0x04 to disable write or erase the * Nand cells + * * Description: * Before write and erase the Nand cells, the write enable has to be set. * After the write or erase, the write enable bit is automatically * cleared (status register bit 2) * Set the bit 2 of the status register has the same effect + * After write and erase the Nand cells, the write enable has to be disabled. */ -static int spinand_write_enable(struct spi_device *spi_nand) +static int spinand_write_config(struct spi_device *spi_nand, u8 opcode) { + int ret = 0; + int i; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); struct spinand_cmd cmd = {0}; - cmd.cmd = CMD_WR_ENABLE; - return spinand_cmd(spi_nand, &cmd); + for (i = 0; i < dev_ops->no_of_dies; i++) { + ret = select_die(spi_nand, dev_ops, i); + if (ret < 0) + return ret; + cmd.cmd = opcode; + ret = spinand_cmd(spi_nand, &cmd); + if (ret < 0) + return ret; + } + + return ret; } -static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id) +static int spinand_read_page_to_cache(struct spi_device *spi_nand, u32 page_id) { struct spinand_cmd cmd = {0}; - u16 row; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); + + select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id)); - row = page_id; cmd.cmd = CMD_READ; cmd.n_addr = 3; - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); + dev_ops->spinand_read_cmd(&cmd, page_id); return spinand_cmd(spi_nand, &cmd); } @@ -321,19 +727,17 @@ * locations. * No tRd delay. */ -static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id, +static int spinand_read_from_cache(struct spi_device *spi_nand, u32 page_id, u16 byte_id, u16 len, u8 *rbuf) { struct spinand_cmd cmd = {0}; u16 column; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); column = byte_id; cmd.cmd = CMD_READ_RDM; cmd.n_addr = 3; - cmd.addr[0] = (u8)((column & 0xff00) >> 8); - cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); - cmd.addr[1] = (u8)(column & 0x00ff); - cmd.addr[2] = (u8)(0xff); + dev_ops->spinand_read_data(&cmd, column, page_id); cmd.n_dummy = 0; cmd.n_rx = len; cmd.rx_buf = rbuf; @@ -352,21 +756,31 @@ * The read includes two commands to the Nand: 0x13 and 0x03 commands * Poll to read status to wait for tRD time. */ -static int spinand_read_page(struct spi_device *spi_nand, u16 page_id, - u16 offset, u16 len, u8 *rbuf) +static int spinand_read_page(struct spi_device *spi_nand, u32 page_id, + u32 offset, u32 len, u8 *rbuf) { - int ret; + int ret, ecc_corrected = 0; u8 status = 0; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); + struct mtd_info *mtd = (struct mtd_info *) + dev_get_drvdata(&spi_nand->dev); + struct nand_chip *chip = (struct nand_chip *)mtd->priv; #ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_read_hw_ecc) { - if (spinand_enable_ecc(spi_nand) < 0) - dev_err(&spi_nand->dev, "enable HW ECC failed!"); + ret = spinand_enable_ecc(spi_nand); + if (ret < 0) { + dev_err(&spi_nand->dev, "enable HW ECC failed! err %d", ret); + return -EIO; + } } #endif + ret = spinand_read_page_to_cache(spi_nand, page_id); - if (ret < 0) + if (ret < 0) { + dev_err(&spi_nand->dev, "read to cache failed err %d\n", ret); return ret; + } if (wait_till_ready(spi_nand)) dev_err(&spi_nand->dev, "WAIT timedout!!!\n"); @@ -380,10 +794,15 @@ } if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { + int ecc_status = dev_ops->spinand_verify_ecc(status); + + if (ecc_status == SPINAND_ECC_ERROR) { dev_err(&spi_nand->dev, "ecc error, page=%d\n", page_id); - return 0; + mtd->ecc_stats.failed++; + } else if (ecc_status == SPINAND_ECC_CORRECTED) { + mtd->ecc_stats.corrected += chip->ecc.strength; + ecc_corrected = 1; } break; } @@ -391,7 +810,7 @@ ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf); if (ret < 0) { - dev_err(&spi_nand->dev, "read from cache failed!!\n"); + dev_err(&spi_nand->dev, "read from cache failed!! err %d\n", ret); return ret; } @@ -399,13 +818,16 @@ if (enable_read_hw_ecc) { ret = spinand_disable_ecc(spi_nand); if (ret < 0) { - dev_err(&spi_nand->dev, "disable ecc failed!!\n"); + dev_err(&spi_nand->dev, "disable HW ECC failed! err %d", ret); return ret; - } + } enable_read_hw_ecc = 0; } #endif - return ret; + if (ecc_corrected) + return chip->ecc.strength; + + return 0; } /* @@ -420,20 +842,21 @@ * Since it is writing the data to cache, there is no tPROG time. */ static int spinand_program_data_to_cache(struct spi_device *spi_nand, - u16 page_id, u16 byte_id, + u32 page_id, u16 byte_id, u16 len, u8 *wbuf) { struct spinand_cmd cmd = {0}; u16 column; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); + + select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id)); column = byte_id; cmd.cmd = CMD_PROG_PAGE_CLRCACHE; cmd.n_addr = 2; - cmd.addr[0] = (u8)((column & 0xff00) >> 8); - cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4); - cmd.addr[1] = (u8)(column & 0x00ff); + dev_ops->spinand_write_data(&cmd, column, page_id); cmd.n_tx = len; - cmd.tx_buf = wbuf; + cmd.tx_buf = wbuf + column; return spinand_cmd(spi_nand, &cmd); } @@ -447,16 +870,16 @@ * the Nand array. * Need to wait for tPROG time to finish the transaction. */ -static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id) +static int spinand_program_execute(struct spi_device *spi_nand, u32 page_id) { struct spinand_cmd cmd = {0}; - u16 row; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); + + select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id)); - row = page_id; cmd.cmd = CMD_PROG_PAGE_EXC; cmd.n_addr = 3; - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); + dev_ops->spinand_write_cmd(&cmd, page_id); return spinand_cmd(spi_nand, &cmd); } @@ -475,75 +898,72 @@ * Poll to wait for the tPROG time to finish the transaction. */ static int spinand_program_page(struct spi_device *spi_nand, - u16 page_id, u16 offset, u16 len, u8 *buf) + u32 page_id, u16 offset, u16 len, u8 *buf) { - int retval; + int ret = 0, retval; u8 status = 0; - u8 *wbuf; -#ifdef CONFIG_MTD_SPINAND_ONDIEECC - unsigned int i, j; - enable_read_hw_ecc = 0; - wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL); - spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf); - - for (i = offset, j = 0; i < len; i++, j++) - wbuf[i] &= buf[j]; +#ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_hw_ecc) { retval = spinand_enable_ecc(spi_nand); if (retval < 0) { - dev_err(&spi_nand->dev, "enable ecc failed!!\n"); - return retval; + dev_err(&spi_nand->dev, "enable ecc failed!! err %d\n", retval); + return -EIO; } } -#else - wbuf = buf; #endif - retval = spinand_write_enable(spi_nand); + retval = spinand_write_config(spi_nand, CMD_WR_ENABLE); if (retval < 0) { - dev_err(&spi_nand->dev, "write enable failed!!\n"); - return retval; + dev_err(&spi_nand->dev, "write enable failed!! err %d\n", retval); + goto exit; } - if (wait_till_ready(spi_nand)) - dev_err(&spi_nand->dev, "wait timedout!!!\n"); retval = spinand_program_data_to_cache(spi_nand, page_id, - offset, len, wbuf); - if (retval < 0) - return retval; + offset, len, buf); + if (retval < 0) { + dev_err(&spi_nand->dev, "write to cache failed!! err %d\n", retval); + goto exit; + } + retval = spinand_program_execute(spi_nand, page_id); - if (retval < 0) - return retval; + if (retval < 0) { + dev_err(&spi_nand->dev, "write exec failed!! err %d\n", retval); + goto exit; + } + while (1) { retval = spinand_read_status(spi_nand, &status); if (retval < 0) { dev_err(&spi_nand->dev, "error %d reading status register\n", retval); - return retval; + goto exit; } if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) { dev_err(&spi_nand->dev, "program error, page %d\n", page_id); - return -1; + ret = -EIO; } break; } } + +exit: #ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_hw_ecc) { retval = spinand_disable_ecc(spi_nand); - if (retval < 0) { - dev_err(&spi_nand->dev, "disable ecc failed!!\n"); - return retval; - } + if (retval < 0) + dev_err(&spi_nand->dev, "disable ecc failed!! err %d\n", retval); enable_hw_ecc = 0; } #endif + retval = spinand_write_config(spi_nand, CMD_WR_DISABLE); + if (retval < 0) + dev_err(&spi_nand->dev, "write disable failed!! err %d\n", retval); - return 0; + return ret; } /** @@ -555,16 +975,16 @@ * one block--64 pages * Need to wait for tERS. */ -static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id) +static int spinand_erase_block_erase(struct spi_device *spi_nand, u32 block_id) { struct spinand_cmd cmd = {0}; - u16 row; + struct spinand_ops *dev_ops = get_dev_ops(spi_nand); + + select_die(spi_nand, dev_ops, get_die_id(dev_ops, block_id)); - row = block_id; cmd.cmd = CMD_ERASE_BLK; cmd.n_addr = 3; - cmd.addr[1] = (u8)((row & 0xff00) >> 8); - cmd.addr[2] = (u8)(row & 0x00ff); + dev_ops->spinand_erase_blk(&cmd, block_id); return spinand_cmd(spi_nand, &cmd); } @@ -580,12 +1000,16 @@ * and then send the 0xd8 erase command * Poll to wait for the tERS time to complete the tranaction. */ -static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id) +static int spinand_erase_block(struct spi_device *spi_nand, u32 block_id) { int retval; u8 status = 0; - retval = spinand_write_enable(spi_nand); + retval = spinand_write_config(spi_nand, CMD_WR_ENABLE); + if (retval < 0) { + dev_err(&spi_nand->dev, "write enable failed!!\n"); + return retval; + } if (wait_till_ready(spi_nand)) dev_err(&spi_nand->dev, "wait timedout!!!\n"); @@ -602,11 +1026,18 @@ if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) { dev_err(&spi_nand->dev, "erase error, block %d\n", block_id); - return -1; + return -EIO; } break; } } + retval = spinand_write_config(spi_nand, CMD_WR_DISABLE); + if (retval < 0) { + dev_err(&spi_nand->dev, "write disable failed!!\n"); + return retval; + } + if (wait_till_ready(spi_nand)) + dev_err(&spi_nand->dev, "wait timedout!!!\n"); return 0; } @@ -629,36 +1060,34 @@ u8 *buf, int oob_required, int page) { int retval; - u8 status; u8 *p = buf; int eccsize = chip->ecc.size; int eccsteps = chip->ecc.steps; struct spinand_info *info = (struct spinand_info *)chip->priv; + struct spinand_state *state = (struct spinand_state *)info->priv; enable_read_hw_ecc = 1; + retval = spinand_read_page(info->spi, page, state->col, + (mtd->writesize + mtd->oobsize), state->buf); chip->read_buf(mtd, p, eccsize * eccsteps); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - while (1) { - retval = spinand_read_status(info->spi, &status); - if (retval < 0) { - dev_err(&mtd->dev, - "error %d reading status register\n", retval); - return retval; - } + return retval; +} - if ((status & STATUS_OIP_MASK) == STATUS_READY) { - if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { - pr_info("spinand: ECC error\n"); - mtd->ecc_stats.failed++; - } else if ((status & STATUS_ECC_MASK) == - STATUS_ECC_1BIT_CORRECTED) - mtd->ecc_stats.corrected++; - break; - } - } +static int spinand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct spinand_info *info = (struct spinand_info *)chip->priv; + struct spinand_state *state = (struct spinand_state *)info->priv; + + spinand_read_page(info->spi, page, state->col, + (mtd->writesize + mtd->oobsize), state->buf); + chip->read_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } #endif @@ -683,7 +1112,7 @@ unsigned long timeo = jiffies; int retval, state = chip->state; - u8 status; + u8 status = 0; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; @@ -754,13 +1183,15 @@ */ case NAND_CMD_READ1: case NAND_CMD_READ0: + state->col = column; + state->row = page; state->buf_ptr = 0; - spinand_read_page(info->spi, page, 0x0, 0x840, state->buf); break; /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: state->buf_ptr = 0; - spinand_read_page(info->spi, page, 0x800, 0x40, state->buf); + spinand_read_page(info->spi, page, (mtd->writesize + column), + mtd->oobsize, state->buf); break; case NAND_CMD_RNDOUT: state->buf_ptr = column; @@ -837,6 +1268,47 @@ return ret; } +/* SPI NAND ID Table */ +struct nand_flash_dev spinand_flash_ids[] = { + {"ATO25D1GA 128MiB 3.3V", + { .id = {0x9b, 0x12} }, SZ_2K, 128, SZ_128K, 0, 2, 64}, + + {"GD5F4GQ4UC 512MiB 3.3V", + { .id = {0xc8, 0xB4} }, SZ_4K, 512, SZ_256K, 0, 2, 256}, + + {"GD5F1GQ1UC 128MiB 3.3V", + { .id = {0xc8, 0xB1} }, SZ_2K, 128, SZ_128K, 0, 2, 128}, + + {"GD5F1GQ1RC 128MiB 1.8V", + { .id = {0xc8, 0xA1} }, SZ_2K, 128, SZ_128K, 0, 2, 128}, + + {"MX35LFxGE4AB 128MiB 3.3V", + { .id = {0xc2, 0x12} }, SZ_2K, 128, SZ_128K, 0, 2, 64}, + + {"W25N512G 64MiB 3.3V", + { .id = {0xef, 0xaa, 0x20 } }, SZ_2K, 64, SZ_128K, 0, 3, 64}, + + {"W25N01GV 128MiB 3.3V", + { .id = {0xef, 0xaa} }, SZ_2K, 128, SZ_128K, 0, 2, 64}, + + {"W25M02GV 256MiB 3.3V(Dual die)", + { .id = {0xef, 0xab} }, SZ_2K, 256, SZ_128K, 0, 2, 64}, + + {"TC58CVG2S0F 4G 3.3V 8-bit", + { .id = {0x98, 0xcd} }, SZ_4K, 512, SZ_256K, 0, 2, 128}, + + {"GD5F1GQ4UB 128MiB 3.3V", + { .id = {0xc8, 0xd1} }, SZ_2K, 128, SZ_128K, 0, 2, 128}, + + {"GD5F1GQ4R 128MiB 3.3V", + { .id = {0xc8, 0xc1} }, SZ_2K, 128, SZ_128K, 0, 2, 128}, + + {"FM35X2GA 256MiB 3.3V", + { .id = {0xe5, 0x72} }, SZ_2K, 256, SZ_128K, 0, 2, 64}, + + {NULL} +}; + /* * spinand_probe - [spinand Interface] * @spi_nand: registered device driver. @@ -877,6 +1349,8 @@ if (!chip) return -ENOMEM; + use_dummybyte_in_readid = of_property_read_bool(spi_nand->dev.of_node, + "use-dummybyte-in-readid"); #ifdef CONFIG_MTD_SPINAND_ONDIEECC chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 0x200; @@ -887,6 +1361,7 @@ chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; chip->ecc.layout = &spinand_oob_64; chip->ecc.read_page = spinand_read_page_hwecc; + chip->ecc.read_page_raw = spinand_read_page_raw; chip->ecc.write_page = spinand_write_page_hwecc; #else chip->ecc.mode = NAND_ECC_SOFT; @@ -900,12 +1375,10 @@ chip->read_byte = spinand_read_byte; chip->cmdfunc = spinand_cmdfunc; chip->waitfunc = spinand_wait; - chip->options |= NAND_CACHEPRG; + chip->options |= NAND_NO_SUBPAGE_WRITE; chip->select_chip = spinand_select_chip; - mtd = devm_kzalloc(&spi_nand->dev, sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd) - return -ENOMEM; + mtd = &chip->mtd; dev_set_drvdata(&spi_nand->dev, mtd); @@ -913,9 +1386,15 @@ mtd->dev.parent = &spi_nand->dev; mtd->oobsize = 64; - if (nand_scan(mtd, 1)) + if (nand_scan_ident(mtd, 1, spinand_flash_ids)) return -ENXIO; + if (nand_scan_tail(mtd)) + return -ENXIO; + + if (info->dev_ops->maf_id == NAND_MFR_WINBOND && spinand_enable_buffer_mode(spi_nand) < 0) + dev_err(&spi_nand->dev, "enable buffer mode failed!"); + ppdata.of_node = spi_nand->dev.of_node; return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); }