diff options
author | Brian Norris <computersforpeace@gmail.com> | 2015-11-10 15:15:27 -0500 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2015-11-19 16:34:44 -0500 |
commit | c67cbb839da9cc2757eabfa128556db6a2baf160 (patch) | |
tree | 1a8f6a07ef4ec5af82bb9af9d04c309ca34bb7bb | |
parent | 1e6460abf739310fe695bafac30c2be39d18c8f9 (diff) |
mtd: spi-nor: provide default erase_sector implementation
Some spi-nor drivers perform sector erase by duplicating their
write_reg() command. Let's not require that the driver fill this out,
and provide a default instead.
Tested on m25p80.c and Medatek's MT8173 SPI NOR flash driver.
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-rw-r--r-- | drivers/mtd/spi-nor/spi-nor.c | 37 | ||||
-rw-r--r-- | include/linux/mtd/spi-nor.h | 3 |
2 files changed, 35 insertions, 5 deletions
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 16c9f5522b8f..a38ec01a1e06 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) | 38 | #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) |
39 | 39 | ||
40 | #define SPI_NOR_MAX_ID_LEN 6 | 40 | #define SPI_NOR_MAX_ID_LEN 6 |
41 | #define SPI_NOR_MAX_ADDR_WIDTH 4 | ||
41 | 42 | ||
42 | struct flash_info { | 43 | struct flash_info { |
43 | char *name; | 44 | char *name; |
@@ -313,6 +314,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) | |||
313 | } | 314 | } |
314 | 315 | ||
315 | /* | 316 | /* |
317 | * Initiate the erasure of a single sector | ||
318 | */ | ||
319 | static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) | ||
320 | { | ||
321 | u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; | ||
322 | int i; | ||
323 | |||
324 | if (nor->erase) | ||
325 | return nor->erase(nor, addr); | ||
326 | |||
327 | /* | ||
328 | * Default implementation, if driver doesn't have a specialized HW | ||
329 | * control | ||
330 | */ | ||
331 | for (i = nor->addr_width - 1; i >= 0; i--) { | ||
332 | buf[i] = addr & 0xff; | ||
333 | addr >>= 8; | ||
334 | } | ||
335 | |||
336 | return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); | ||
337 | } | ||
338 | |||
339 | /* | ||
316 | * Erase an address range on the nor chip. The address range may extend | 340 | * Erase an address range on the nor chip. The address range may extend |
317 | * one or more erase sectors. Return an error is there is a problem erasing. | 341 | * one or more erase sectors. Return an error is there is a problem erasing. |
318 | */ | 342 | */ |
@@ -371,10 +395,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
371 | while (len) { | 395 | while (len) { |
372 | write_enable(nor); | 396 | write_enable(nor); |
373 | 397 | ||
374 | if (nor->erase(nor, addr)) { | 398 | ret = spi_nor_erase_sector(nor, addr); |
375 | ret = -EIO; | 399 | if (ret) |
376 | goto erase_err; | 400 | goto erase_err; |
377 | } | ||
378 | 401 | ||
379 | addr += mtd->erasesize; | 402 | addr += mtd->erasesize; |
380 | len -= mtd->erasesize; | 403 | len -= mtd->erasesize; |
@@ -1138,7 +1161,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) | |||
1138 | static int spi_nor_check(struct spi_nor *nor) | 1161 | static int spi_nor_check(struct spi_nor *nor) |
1139 | { | 1162 | { |
1140 | if (!nor->dev || !nor->read || !nor->write || | 1163 | if (!nor->dev || !nor->read || !nor->write || |
1141 | !nor->read_reg || !nor->write_reg || !nor->erase) { | 1164 | !nor->read_reg || !nor->write_reg) { |
1142 | pr_err("spi-nor: please fill all the necessary fields!\n"); | 1165 | pr_err("spi-nor: please fill all the necessary fields!\n"); |
1143 | return -EINVAL; | 1166 | return -EINVAL; |
1144 | } | 1167 | } |
@@ -1340,6 +1363,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) | |||
1340 | nor->addr_width = 3; | 1363 | nor->addr_width = 3; |
1341 | } | 1364 | } |
1342 | 1365 | ||
1366 | if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { | ||
1367 | dev_err(dev, "address width is too large: %u\n", | ||
1368 | nor->addr_width); | ||
1369 | return -EINVAL; | ||
1370 | } | ||
1371 | |||
1343 | nor->read_dummy = spi_nor_read_dummy_cycles(nor); | 1372 | nor->read_dummy = spi_nor_read_dummy_cycles(nor); |
1344 | 1373 | ||
1345 | dev_info(dev, "%s (%lld Kbytes)\n", info->name, | 1374 | dev_info(dev, "%s (%lld Kbytes)\n", info->name, |
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 955f268d159a..7bed97471e53 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h | |||
@@ -143,7 +143,8 @@ struct mtd_info; | |||
143 | * @read: [DRIVER-SPECIFIC] read data from the SPI NOR | 143 | * @read: [DRIVER-SPECIFIC] read data from the SPI NOR |
144 | * @write: [DRIVER-SPECIFIC] write data to the SPI NOR | 144 | * @write: [DRIVER-SPECIFIC] write data to the SPI NOR |
145 | * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR | 145 | * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR |
146 | * at the offset @offs | 146 | * at the offset @offs; if not provided by the driver, |
147 | * spi-nor will send the erase opcode via write_reg() | ||
147 | * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR | 148 | * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR |
148 | * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR | 149 | * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR |
149 | * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is | 150 | * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is |