|
@@ -428,6 +428,28 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
|
|
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @timeo: Timeout
|
|
|
+ *
|
|
|
+ * Helper function for nand_wait_ready used when needing to wait in interrupt
|
|
|
+ * context.
|
|
|
+ */
|
|
|
+static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Wait for the device to get ready */
|
|
|
+ for (i = 0; i < timeo; i++) {
|
|
|
+ if (chip->dev_ready(mtd))
|
|
|
+ break;
|
|
|
+ touch_softlockup_watchdog();
|
|
|
+ mdelay(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Wait for the ready pin, after a command
|
|
|
* The timeout is catched later.
|
|
@@ -437,6 +459,10 @@ void nand_wait_ready(struct mtd_info *mtd)
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
unsigned long timeo = jiffies + 2;
|
|
|
|
|
|
+ /* 400ms timeout */
|
|
|
+ if (in_interrupt() || oops_in_progress)
|
|
|
+ return panic_nand_wait_ready(mtd, 400);
|
|
|
+
|
|
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
|
|
/* wait until command is processed or timeout occures */
|
|
|
do {
|
|
@@ -671,6 +697,22 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|
|
nand_wait_ready(mtd);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * panic_nand_get_device - [GENERIC] Get chip for selected access
|
|
|
+ * @chip: the nand chip descriptor
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @new_state: the state which is requested
|
|
|
+ *
|
|
|
+ * Used when in panic, no locks are taken.
|
|
|
+ */
|
|
|
+static void panic_nand_get_device(struct nand_chip *chip,
|
|
|
+ struct mtd_info *mtd, int new_state)
|
|
|
+{
|
|
|
+ /* Hardware controller shared among independend devices */
|
|
|
+ chip->controller->active = chip;
|
|
|
+ chip->state = new_state;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* nand_get_device - [GENERIC] Get chip for selected access
|
|
|
* @chip: the nand chip descriptor
|
|
@@ -709,6 +751,32 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
|
|
goto retry;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * panic_nand_wait - [GENERIC] wait until the command is done
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @chip: NAND chip structure
|
|
|
+ * @timeo: Timeout
|
|
|
+ *
|
|
|
+ * Wait for command done. This is a helper function for nand_wait used when
|
|
|
+ * we are in interrupt context. May happen when in panic and trying to write
|
|
|
+ * an oops trough mtdoops.
|
|
|
+ */
|
|
|
+static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ unsigned long timeo)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < timeo; i++) {
|
|
|
+ if (chip->dev_ready) {
|
|
|
+ if (chip->dev_ready(mtd))
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mdelay(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* nand_wait - [DEFAULT] wait until the command is done
|
|
|
* @mtd: MTD device structure
|
|
@@ -740,15 +808,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|
|
else
|
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
|
|
|
|
- while (time_before(jiffies, timeo)) {
|
|
|
- if (chip->dev_ready) {
|
|
|
- if (chip->dev_ready(mtd))
|
|
|
- break;
|
|
|
- } else {
|
|
|
- if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
- break;
|
|
|
+ if (in_interrupt() || oops_in_progress)
|
|
|
+ panic_nand_wait(mtd, chip, timeo);
|
|
|
+ else {
|
|
|
+ while (time_before(jiffies, timeo)) {
|
|
|
+ if (chip->dev_ready) {
|
|
|
+ if (chip->dev_ready(mtd))
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ cond_resched();
|
|
|
}
|
|
|
- cond_resched();
|
|
|
}
|
|
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
|
|
|
|
@@ -1948,6 +2020,45 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * panic_nand_write - [MTD Interface] NAND write with ECC
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @to: offset to write to
|
|
|
+ * @len: number of bytes to write
|
|
|
+ * @retlen: pointer to variable to store the number of written bytes
|
|
|
+ * @buf: the data to write
|
|
|
+ *
|
|
|
+ * NAND write with ECC. Used when performing writes in interrupt context, this
|
|
|
+ * may for example be called by mtdoops when writing an oops while in panic.
|
|
|
+ */
|
|
|
+static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|
|
+ size_t *retlen, const uint8_t *buf)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Do not allow reads past end of device */
|
|
|
+ if ((to + len) > mtd->size)
|
|
|
+ return -EINVAL;
|
|
|
+ if (!len)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Wait for the device to get ready. */
|
|
|
+ panic_nand_wait(mtd, chip, 400);
|
|
|
+
|
|
|
+ /* Grab the device. */
|
|
|
+ panic_nand_get_device(chip, mtd, FL_WRITING);
|
|
|
+
|
|
|
+ chip->ops.len = len;
|
|
|
+ chip->ops.datbuf = (uint8_t *)buf;
|
|
|
+ chip->ops.oobbuf = NULL;
|
|
|
+
|
|
|
+ ret = nand_do_write_ops(mtd, to, &chip->ops);
|
|
|
+
|
|
|
+ *retlen = chip->ops.retlen;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* nand_write - [MTD Interface] NAND write with ECC
|
|
|
* @mtd: MTD device structure
|
|
@@ -2877,6 +2988,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
mtd->unpoint = NULL;
|
|
|
mtd->read = nand_read;
|
|
|
mtd->write = nand_write;
|
|
|
+ mtd->panic_write = panic_nand_write;
|
|
|
mtd->read_oob = nand_read_oob;
|
|
|
mtd->write_oob = nand_write_oob;
|
|
|
mtd->sync = nand_sync;
|