--- zzzz-none-000/linux-3.10.107/drivers/mtd/nand/nand_base.c 2017-06-27 09:49:32.000000000 +0000 +++ vr9-7490-729/linux-3.10.107/drivers/mtd/nand/nand_base.c 2021-11-10 11:53:55.000000000 +0000 @@ -29,6 +29,12 @@ * */ +/** + * Some part of this file is modified by Ikanos Communications. + * + * Copyright (C) 2013-2014 Ikanos Communications. + */ + #include #include #include @@ -45,6 +51,20 @@ #include #include #include +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) +#include +#include +#if defined(CONFIG_FUSIV_VX185) +extern unsigned int reset_reg_val; +#endif +#endif + +// AVM/TKL: check if mtd has been switched into panic mode +static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf); +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo); +#define IS_PANIC_MODE(mtd) (unlikely((mtd)->_write == panic_nand_write)) /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -132,6 +152,12 @@ { struct nand_chip *chip = mtd->priv; + if(IS_PANIC_MODE(mtd)){ + chip->controller->active = NULL; + chip->state = FL_READY; + return; + } + /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; @@ -352,7 +378,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; + uint8_t buf[2] = { 2, 2 }; // hbl: 2 == user bad marked (kernel, urlader nimmt die 1) int block, res, ret = 0, i = 0; int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); @@ -436,7 +462,11 @@ /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + return (fusiv_nfc_readl(NFC_STATUS_0) & NAND_STATUS_WP) ? 0 : 1; +#else return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +#endif } /** @@ -490,7 +520,7 @@ unsigned long timeo = jiffies + msecs_to_jiffies(20); /* 400ms timeout */ - if (in_interrupt() || oops_in_progress) + if (in_interrupt() || oops_in_progress || IS_PANIC_MODE(mtd)) return panic_nand_wait_ready(mtd, 400); led_trigger_event(nand_led_trigger, LED_FULL); @@ -631,6 +661,11 @@ chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + chip->cmd_ctrl(mtd, column | (page_addr << chip->page_shift), + NAND_NCE | NAND_ALE); + +#else int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; /* Serially input address */ @@ -651,6 +686,7 @@ chip->cmd_ctrl(mtd, page_addr >> 16, NAND_NCE | NAND_ALE); } +#endif } chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); @@ -677,8 +713,11 @@ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) - ; +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + while (!(fusiv_nfc_readl(NFC_STATUS_0) & NAND_STATUS_READY)); +#else + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; +#endif return; case NAND_CMD_RNDOUT: @@ -746,6 +785,18 @@ spinlock_t *lock = &chip->controller->lock; wait_queue_head_t *wq = &chip->controller->wq; DECLARE_WAITQUEUE(wait, current); + + // AVM/TKL: use non-blocking override when mtd is in panic mode + if(IS_PANIC_MODE(mtd)){ + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + + chip->controller->active = chip; + chip->state = new_state; + return 0; + } + retry: spin_lock(lock); @@ -792,8 +843,13 @@ if (chip->dev_ready(mtd)) break; } else { +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + if (fusiv_nfc_readl(NFC_STATUS_0) & NAND_STATUS_READY) + break; +#else if (chip->read_byte(mtd) & NAND_STATUS_READY) break; +#endif } mdelay(1); } @@ -824,7 +880,7 @@ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - if (in_interrupt() || oops_in_progress) + if (in_interrupt() || oops_in_progress || IS_PANIC_MODE(mtd)) panic_nand_wait(mtd, chip, timeo); else { timeo = jiffies + msecs_to_jiffies(timeo); @@ -833,17 +889,31 @@ if (chip->dev_ready(mtd)) break; } else { +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + if (fusiv_nfc_readl(NFC_STATUS_0) & NAND_STATUS_READY) + break; +#else if (chip->read_byte(mtd) & NAND_STATUS_READY) break; +#endif } cond_resched(); } } led_trigger_event(nand_led_trigger, LED_OFF); +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + /* IKANOS: issue status register read command, Read status */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + status = fusiv_nfc_readl(NFC_STATUS_0); + if (!(status & NAND_STATUS_TRUE_READY)) + printk(KERN_ERR "nand status is %x \n", status); +#else status = (int)chip->read_byte(mtd); +#endif /* This can happen if in case of timeout or buggy dev_ready */ - WARN_ON(!(status & NAND_STATUS_READY)); + // AVM/TKL: MERGE removed by Ikanos, check why + //WARN_ON(!(status & NAND_STATUS_READY)); return status; } @@ -1648,9 +1718,10 @@ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); chip->write_buf(mtd, buf, length); +#if !defined(CONFIG_FUSIV_VX185) && !defined(CONFIG_FUSIV_VX585) /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - +#endif status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0; @@ -1726,6 +1797,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + unsigned int max_bitflips = 0; int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; @@ -1786,6 +1858,8 @@ nand_wait_ready(mtd); } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + readlen -= len; if (!readlen) break; @@ -1811,7 +1885,7 @@ if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** @@ -2132,8 +2206,9 @@ cached = 0; if (!cached || !NAND_HAS_CACHEPROG(chip)) { - +#if !defined(CONFIG_FUSIV_VX185) && !defined(CONFIG_FUSIV_VX585) chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +#endif status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are @@ -2528,7 +2603,11 @@ { struct nand_chip *chip = mtd->priv; /* Send commands to erase a block */ +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + chip->cmdfunc(mtd, NAND_CMD_ERASE1, 0, page); +#else chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); +#endif chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); } @@ -2594,8 +2673,9 @@ while (len) { /* Check if we have a bad block, we do not erase bad blocks! */ - if (nand_block_checkbad(mtd, ((loff_t) page) << - chip->page_shift, 0, allowbbt)) { + if ((instr->priv != 1) && /*--- 1 signals force to mtd->erase ---*/ + (nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, 0, allowbbt))) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; @@ -3099,6 +3179,23 @@ extid >>= 2; /* Get buswidth information */ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; +#if defined(CONFIG_FUSIV_VX185) + if ((reset_reg_val & NAND_RESET_1GB_4KB_MASK)== NAND_RESET_1GB_4KB) + { + mtd->writesize = NAND_PAGE_SIZE_4KB; + mtd->oobsize = NAND_OOB_224; + mtd->erasesize = NAND_BLOCKSIZE_512KB; + busw = 0; + } +#endif //VX185 +#if defined(CONFIG_FUSIV_VX585) + if (id_data[1] == 0xda || id_data[1] == 0xdc) + { + mtd->oobsize = 112; + } else if (id_data[1] == 0xd3) { + mtd->oobsize = 224; + } +#endif } } @@ -3214,6 +3311,7 @@ */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); +#if !defined(CONFIG_FUSIV_VX185) && !defined(CONFIG_FUSIV_VX585) /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); @@ -3233,7 +3331,15 @@ /* Read entire ID string */ for (i = 0; i < 8; i++) id_data[i] = chip->read_byte(mtd); - +#else + *maf_id = fusiv_nfc_readl(NFC_ID_0) & 0xFF; + *dev_id = fusiv_nfc_readl(NFC_ID_2) & 0xFF; + id_data[0] = fusiv_nfc_readl(NFC_ID_0) & 0xFF; + id_data[1] = fusiv_nfc_readl(NFC_ID_2) & 0xFF; + id_data[2] = fusiv_nfc_readl(NFC_ID_4) & 0xFF; + id_data[3] = fusiv_nfc_readl(NFC_ID_6) & 0xFF; + mdelay(1); +#endif if (id_data[0] != *maf_id || id_data[1] != *dev_id) { pr_info("%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, @@ -3293,7 +3399,7 @@ if (nand_manuf_ids[maf_idx].id == *maf_id) break; } - +#if !defined(CONFIG_FUSIV_VX585) if (chip->options & NAND_BUSWIDTH_AUTO) { WARN_ON(chip->options & NAND_BUSWIDTH_16); chip->options |= busw; @@ -3311,7 +3417,7 @@ busw ? 16 : 8); return ERR_PTR(-EINVAL); } - +#endif //VX585_PRESILICON nand_decode_bbm_options(mtd, chip, id_data); /* Calculate the address shift from the page size */ @@ -3385,6 +3491,12 @@ chip->select_chip(mtd, i); /* See comment in nand_get_flash_type for reset */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); +#if defined(CONFIG_FUSIV_VX185) || defined(CONFIG_FUSIV_VX585) + /* Read manufacturer and device IDs */ + if (nand_maf_id != (fusiv_nfc_readl(NFC_ID_0) & 0xff) || + nand_dev_id != (fusiv_nfc_readl(NFC_ID_2) & 0xff)) + break; +#else /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ @@ -3393,6 +3505,8 @@ chip->select_chip(mtd, -1); break; } +#endif + chip->select_chip(mtd, -1); } if (i > 1) @@ -3527,10 +3641,12 @@ chip->ecc.write_oob = nand_write_oob_syndrome; if (mtd->writesize >= chip->ecc.size) { +#if !defined(CONFIG_MACH_FUSIV) // AVM/TKL: MERGE: deactivated by Ikanos, check why if (!chip->ecc.strength) { pr_warn("Driver must set ecc.strength when using hardware ECC\n"); BUG(); } +#endif break; } pr_warn("%d byte HW ECC not possible on " @@ -3779,6 +3895,23 @@ led_trigger_unregister_simple(nand_led_trigger); } +/* + * AVM/TKL + * nand_base_panic_reinit + * function can be called to make the NAND base use only functions fit + * for using during kernel oops or panic. + * Currently just replace the standard nand_write() function pointer with + * pointer to panic_nand_write(). Critical sections will have to check if + * mtd->_write points to panic_nand_write() and act accordingly + */ +void nand_base_panic_reinit(struct mtd_info *mtd) +{ + if(mtd->_write == nand_write){ + mtd->_write = panic_nand_write; + } +} +EXPORT_SYMBOL_GPL(nand_base_panic_reinit); + module_init(nand_base_init); module_exit(nand_base_exit);