Commit 245131e2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mtd/fixes-for-4.17-rc3' of git://git.infradead.org/linux-mtd

Pull mtd fixes from Boris Brezillon:

 - Fix nanddev_mtd_erase() function to match the changes done in
   e7bfb3fd ("mtd: Stop updating erase_info->state and calling
   mtd_erase_callback()")

 - Fix a memory leak in the Tango NAND controller driver

 - Fix read/write to a suspended erase block in the CFI driver

 - Fix the DT parsing logic in the Marvell NAND controller driver

* tag 'mtd/fixes-for-4.17-rc3' of git://git.infradead.org/linux-mtd:
  mtd: rawnand: marvell: fix the chip-select DT parsing logic
  mtd: cfi: cmdset_0002: Do not allow read/write to suspend erase block.
  mtd: cfi: cmdset_0001: Workaround Micron Erase suspend bug.
  mtd: cfi: cmdset_0001: Do not allow read/write to suspend erase block.
  mtd: spi-nor: cadence-quadspi: Fix page fault kernel panic
  mtd: nand: Fix nanddev_mtd_erase()
  mtd: rawnand: tango: Fix struct clk memory leak
parents 0f940fac f6997bec
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#define I82802AB 0x00ad #define I82802AB 0x00ad
#define I82802AC 0x00ac #define I82802AC 0x00ac
#define PF38F4476 0x881c #define PF38F4476 0x881c
#define M28F00AP30 0x8963
/* STMicroelectronics chips */ /* STMicroelectronics chips */
#define M50LPW080 0x002F #define M50LPW080 0x002F
#define M50FLW080A 0x0080 #define M50FLW080A 0x0080
...@@ -375,6 +376,17 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi, ...@@ -375,6 +376,17 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
extp->MinorVersion = '1'; extp->MinorVersion = '1';
} }
static int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip)
{
/*
* Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t
* Erase Supend for their small Erase Blocks(0x8000)
*/
if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30)
return 1;
return 0;
}
static inline struct cfi_pri_intelext * static inline struct cfi_pri_intelext *
read_pri_intelext(struct map_info *map, __u16 adr) read_pri_intelext(struct map_info *map, __u16 adr)
{ {
...@@ -831,21 +843,30 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long ...@@ -831,21 +843,30 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long
(mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
goto sleep; goto sleep;
/* Do not allow suspend iff read/write to EB address */
if ((adr & chip->in_progress_block_mask) ==
chip->in_progress_block_addr)
goto sleep;
/* do not suspend small EBs, buggy Micron Chips */
if (cfi_is_micron_28F00AP30(cfi, chip) &&
(chip->in_progress_block_mask == ~(0x8000-1)))
goto sleep;
/* Erase suspend */ /* Erase suspend */
map_write(map, CMD(0xB0), adr); map_write(map, CMD(0xB0), chip->in_progress_block_addr);
/* If the flash has finished erasing, then 'erase suspend' /* If the flash has finished erasing, then 'erase suspend'
* appears to make some (28F320) flash devices switch to * appears to make some (28F320) flash devices switch to
* 'read' mode. Make sure that we switch to 'read status' * 'read' mode. Make sure that we switch to 'read status'
* mode so we get the right data. --rmk * mode so we get the right data. --rmk
*/ */
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_ERASING; chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING; chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1; chip->erase_suspended = 1;
for (;;) { for (;;) {
status = map_read(map, adr); status = map_read(map, chip->in_progress_block_addr);
if (map_word_andequal(map, status, status_OK, status_OK)) if (map_word_andequal(map, status, status_OK, status_OK))
break; break;
...@@ -1041,8 +1062,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad ...@@ -1041,8 +1062,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
sending the 0x70 (Read Status) command to an erasing sending the 0x70 (Read Status) command to an erasing
chip and expecting it to be ignored, that's what we chip and expecting it to be ignored, that's what we
do. */ do. */
map_write(map, CMD(0xd0), adr); map_write(map, CMD(0xd0), chip->in_progress_block_addr);
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_READY; chip->oldstate = FL_READY;
chip->state = FL_ERASING; chip->state = FL_ERASING;
break; break;
...@@ -1933,6 +1954,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, ...@@ -1933,6 +1954,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0xD0), adr); map_write(map, CMD(0xD0), adr);
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
chip->in_progress_block_mask = ~(len - 1);
ret = INVAL_CACHE_AND_WAIT(map, chip, adr, ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
adr, len, adr, len,
......
...@@ -816,9 +816,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -816,9 +816,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
(mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
goto sleep; goto sleep;
/* We could check to see if we're trying to access the sector /* Do not allow suspend iff read/write to EB address */
* that is currently being erased. However, no user will try if ((adr & chip->in_progress_block_mask) ==
* anything like that so we just wait for the timeout. */ chip->in_progress_block_addr)
goto sleep;
/* Erase suspend */ /* Erase suspend */
/* It's harmless to issue the Erase-Suspend and Erase-Resume /* It's harmless to issue the Erase-Suspend and Erase-Resume
...@@ -2267,6 +2268,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) ...@@ -2267,6 +2268,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr; chip->in_progress_block_addr = adr;
chip->in_progress_block_mask = ~(map->size - 1);
INVALIDATE_CACHE_UDELAY(map, chip, INVALIDATE_CACHE_UDELAY(map, chip,
adr, map->size, adr, map->size,
...@@ -2356,6 +2358,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, ...@@ -2356,6 +2358,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr; chip->in_progress_block_addr = adr;
chip->in_progress_block_mask = ~(len - 1);
INVALIDATE_CACHE_UDELAY(map, chip, INVALIDATE_CACHE_UDELAY(map, chip,
adr, len, adr, len,
......
...@@ -162,7 +162,6 @@ int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) ...@@ -162,7 +162,6 @@ int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
ret = nanddev_erase(nand, &pos); ret = nanddev_erase(nand, &pos);
if (ret) { if (ret) {
einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);
einfo->state = MTD_ERASE_FAILED;
return ret; return ret;
} }
...@@ -170,8 +169,6 @@ int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) ...@@ -170,8 +169,6 @@ int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
nanddev_pos_next_eraseblock(nand, &pos); nanddev_pos_next_eraseblock(nand, &pos);
} }
einfo->state = MTD_ERASE_DONE;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nanddev_mtd_erase); EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
......
...@@ -2299,29 +2299,20 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, ...@@ -2299,29 +2299,20 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
/* /*
* The legacy "num-cs" property indicates the number of CS on the only * The legacy "num-cs" property indicates the number of CS on the only
* chip connected to the controller (legacy bindings does not support * chip connected to the controller (legacy bindings does not support
* more than one chip). CS are only incremented one by one while the RB * more than one chip). The CS and RB pins are always the #0.
* pin is always the #0.
* *
* When not using legacy bindings, a couple of "reg" and "nand-rb" * When not using legacy bindings, a couple of "reg" and "nand-rb"
* properties must be filled. For each chip, expressed as a subnode, * properties must be filled. For each chip, expressed as a subnode,
* "reg" points to the CS lines and "nand-rb" to the RB line. * "reg" points to the CS lines and "nand-rb" to the RB line.
*/ */
if (pdata) { if (pdata || nfc->caps->legacy_of_bindings) {
nsels = 1; nsels = 1;
} else if (nfc->caps->legacy_of_bindings && } else {
!of_get_property(np, "num-cs", &nsels)) { nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
dev_err(dev, "missing num-cs property\n"); if (nsels <= 0) {
return -EINVAL; dev_err(dev, "missing/invalid reg property\n");
} else if (!of_get_property(np, "reg", &nsels)) { return -EINVAL;
dev_err(dev, "missing reg property\n"); }
return -EINVAL;
}
if (!pdata)
nsels /= sizeof(u32);
if (!nsels) {
dev_err(dev, "invalid reg property size\n");
return -EINVAL;
} }
/* Alloc the nand chip structure */ /* Alloc the nand chip structure */
......
...@@ -645,7 +645,7 @@ static int tango_nand_probe(struct platform_device *pdev) ...@@ -645,7 +645,7 @@ static int tango_nand_probe(struct platform_device *pdev)
writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE);
clk = clk_get(&pdev->dev, NULL); clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) if (IS_ERR(clk))
return PTR_ERR(clk); return PTR_ERR(clk);
......
...@@ -501,7 +501,9 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf, ...@@ -501,7 +501,9 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf,
void __iomem *reg_base = cqspi->iobase; void __iomem *reg_base = cqspi->iobase;
void __iomem *ahb_base = cqspi->ahb_base; void __iomem *ahb_base = cqspi->ahb_base;
unsigned int remaining = n_rx; unsigned int remaining = n_rx;
unsigned int mod_bytes = n_rx % 4;
unsigned int bytes_to_read = 0; unsigned int bytes_to_read = 0;
u8 *rxbuf_end = rxbuf + n_rx;
int ret = 0; int ret = 0;
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
...@@ -530,11 +532,24 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf, ...@@ -530,11 +532,24 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf,
} }
while (bytes_to_read != 0) { while (bytes_to_read != 0) {
unsigned int word_remain = round_down(remaining, 4);
bytes_to_read *= cqspi->fifo_width; bytes_to_read *= cqspi->fifo_width;
bytes_to_read = bytes_to_read > remaining ? bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read; remaining : bytes_to_read;
ioread32_rep(ahb_base, rxbuf, bytes_to_read = round_down(bytes_to_read, 4);
DIV_ROUND_UP(bytes_to_read, 4)); /* Read 4 byte word chunks then single bytes */
if (bytes_to_read) {
ioread32_rep(ahb_base, rxbuf,
(bytes_to_read / 4));
} else if (!word_remain && mod_bytes) {
unsigned int temp = ioread32(ahb_base);
bytes_to_read = mod_bytes;
memcpy(rxbuf, &temp, min((unsigned int)
(rxbuf_end - rxbuf),
bytes_to_read));
}
rxbuf += bytes_to_read; rxbuf += bytes_to_read;
remaining -= bytes_to_read; remaining -= bytes_to_read;
bytes_to_read = cqspi_get_rd_sram_level(cqspi); bytes_to_read = cqspi_get_rd_sram_level(cqspi);
......
...@@ -85,6 +85,7 @@ struct flchip { ...@@ -85,6 +85,7 @@ struct flchip {
unsigned int write_suspended:1; unsigned int write_suspended:1;
unsigned int erase_suspended:1; unsigned int erase_suspended:1;
unsigned long in_progress_block_addr; unsigned long in_progress_block_addr;
unsigned long in_progress_block_mask;
struct mutex mutex; struct mutex mutex;
wait_queue_head_t wq; /* Wait on here when we're waiting for the chip wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment