diff options
Diffstat (limited to 'drivers/mtd')
101 files changed, 8938 insertions, 5269 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fb31a7f649a3..42d401ea60ee 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -23,73 +23,6 @@ config MTD_TESTS WARNING: some of the tests will ERASE entire MTD device which they test. Do not use these tests unless you really know what you do. -config MTD_CMDLINE_PARTS - tristate "Command line partition table parsing" - depends on MTD - help - Allow generic configuration of the MTD partition tables via the kernel - command line. Multiple flash resources are supported for hardware where - different kinds of flash memory are available. - - You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The - SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for - example. - - The format for the command line is as follows: - - mtdparts=<mtddef>[;<mtddef] - <mtddef> := <mtd-id>:<partdef>[,<partdef>] - <partdef> := <size>[@offset][<name>][ro] - <mtd-id> := unique id used in mapping driver/device - <size> := standard linux memsize OR "-" to denote all - remaining space - <name> := (NAME) - - Due to the way Linux handles the command line, no spaces are - allowed in the partition definition, including mtd id's and partition - names. - - Examples: - - 1 flash resource (mtd-id "sa1100"), with 1 single writable partition: - mtdparts=sa1100:- - - Same flash, but 2 named partitions, the first one being read-only: - mtdparts=sa1100:256k(ARMboot)ro,-(root) - - If unsure, say 'N'. - -config MTD_OF_PARTS - tristate "OpenFirmware partitioning information support" - default y - depends on OF - help - This provides a partition parsing function which derives - the partition map from the children of the flash node, - as described in Documentation/devicetree/bindings/mtd/partition.txt. - -config MTD_AR7_PARTS - tristate "TI AR7 partitioning support" - help - TI AR7 partitioning support - -config MTD_BCM63XX_PARTS - tristate "BCM63XX CFE partitioning support" - depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST - select CRC32 - select MTD_PARSER_IMAGETAG - help - This provides partition parsing for BCM63xx devices with CFE - bootloaders. - -config MTD_BCM47XX_PARTS - tristate "BCM47XX partitioning support" - depends on BCM47XX || ARCH_BCM_5301X - help - This provides partitions parser for devices based on BCM47xx - boards. - menu "Partition parsers" source "drivers/mtd/parsers/Kconfig" endmenu @@ -274,4 +207,6 @@ source "drivers/mtd/spi-nor/Kconfig" source "drivers/mtd/ubi/Kconfig" +source "drivers/mtd/hyperbus/Kconfig" + endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 806287e80e84..56cc60ccc477 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -7,11 +7,6 @@ obj-$(CONFIG_MTD) += mtd.o mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o -obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o -obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o -obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o -obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o -obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o obj-y += parsers/ # 'Users' - code which presents functionality to userspace. @@ -34,3 +29,4 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ +obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 79a53cb8507b..00a79489067c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1353,7 +1353,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a { unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; + int ret; adr += chip->start; @@ -1383,7 +1383,7 @@ static int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len, struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs, last_end = 0; int chipnum; - int ret = 0; + int ret; if (!map->virt) return -EINVAL; @@ -1550,7 +1550,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, { struct cfi_private *cfi = map->fldrv_priv; map_word status, write_cmd; - int ret=0; + int ret; adr += chip->start; @@ -1624,7 +1624,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; + int ret; int chipnum; unsigned long ofs; @@ -1871,7 +1871,7 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs, struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; - int ret = 0; + int ret; int chipnum; unsigned long ofs, vec_seek, i; size_t len = 0; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c8fa5906bdf9..04b383bc3947 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -49,9 +49,21 @@ #define SST49LF008A 0x005a #define AT49BV6416 0x00d6 +/* + * Status Register bit description. Used by flash devices that don't + * support DQ polling (e.g. HyperFlash) + */ +#define CFI_SR_DRB BIT(7) +#define CFI_SR_ESB BIT(5) +#define CFI_SR_PSB BIT(4) +#define CFI_SR_WBASB BIT(3) +#define CFI_SR_SLSB BIT(1) + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +#if !FORCE_WORD_WRITE static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +#endif static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_amdstd_sync (struct mtd_info *); @@ -97,6 +109,59 @@ static struct mtd_chip_driver cfi_amdstd_chipdrv = { .module = THIS_MODULE }; +/* + * Use status register to poll for Erase/write completion when DQ is not + * supported. This is indicated by Bit[1:0] of SoftwareFeatures field in + * CFI Primary Vendor-Specific Extended Query table 1.5 + */ +static int cfi_use_status_reg(struct cfi_private *cfi) +{ + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + u8 poll_mask = CFI_POLL_STATUS_REG | CFI_POLL_DQ; + + return extp->MinorVersion >= '5' && + (extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG; +} + +static int cfi_check_err_status(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + map_word status; + + if (!cfi_use_status_reg(cfi)) + return 0; + + cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + status = map_read(map, adr); + + /* The error bits are invalid while the chip's busy */ + if (!map_word_bitsset(map, status, CMD(CFI_SR_DRB))) + return 0; + + if (map_word_bitsset(map, status, CMD(0x3a))) { + unsigned long chipstatus = MERGESTATUS(status); + + if (chipstatus & CFI_SR_ESB) + pr_err("%s erase operation failed, status %lx\n", + map->name, chipstatus); + if (chipstatus & CFI_SR_PSB) + pr_err("%s program operation failed, status %lx\n", + map->name, chipstatus); + if (chipstatus & CFI_SR_WBASB) + pr_err("%s buffer program command aborted, status %lx\n", + map->name, chipstatus); + if (chipstatus & CFI_SR_SLSB) + pr_err("%s sector write protected, status %lx\n", + map->name, chipstatus); + + /* Erase/Program status bits are set on the operation failure */ + if (chipstatus & (CFI_SR_ESB | CFI_SR_PSB)) + return 1; + } + return 0; +} /* #define DEBUG_CFI_FEATURES */ @@ -202,6 +267,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd) } #endif +#if !FORCE_WORD_WRITE static void fixup_use_write_buffers(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -211,6 +277,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd) mtd->_write = cfi_amdstd_write_buffers; } } +#endif /* !FORCE_WORD_WRITE */ /* Atmel chips don't use the same PRI format as AMD chips */ static void fixup_convert_atmel_pri(struct mtd_info *mtd) @@ -727,7 +794,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) kfree(mtd->eraseregions); kfree(mtd); kfree(cfi->cmdset_priv); - kfree(cfi->cfiq); return NULL; } @@ -742,10 +808,25 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) * correctly and is therefore not done (particularly with interleaved chips * as each chip must be checked independently of the others). */ -static int __xipram chip_ready(struct map_info *map, unsigned long addr) +static int __xipram chip_ready(struct map_info *map, struct flchip *chip, + unsigned long addr) { + struct cfi_private *cfi = map->fldrv_priv; map_word d, t; + if (cfi_use_status_reg(cfi)) { + map_word ready = CMD(CFI_SR_DRB); + /* + * For chips that support status register, check device + * ready bit + */ + cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + d = map_read(map, addr); + + return map_word_andequal(map, d, ready, ready); + } + d = map_read(map, addr); t = map_read(map, addr); @@ -767,10 +848,26 @@ static int __xipram chip_ready(struct map_info *map, unsigned long addr) * as each chip must be checked independently of the others). * */ -static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected) +static int __xipram chip_good(struct map_info *map, struct flchip *chip, + unsigned long addr, map_word expected) { + struct cfi_private *cfi = map->fldrv_priv; map_word oldd, curd; + if (cfi_use_status_reg(cfi)) { + map_word ready = CMD(CFI_SR_DRB); + + /* + * For chips that support status register, check device + * ready bit + */ + cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + curd = map_read(map, addr); + + return map_word_andequal(map, curd, ready, ready); + } + oldd = map_read(map, addr); curd = map_read(map, addr); @@ -792,7 +889,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr case FL_STATUS: for (;;) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) break; if (time_after(jiffies, timeo)) { @@ -830,7 +927,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) break; if (time_after(jiffies, timeo)) { @@ -1362,7 +1459,7 @@ static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr, /* wait for chip to become ready */ timeo = jiffies + msecs_to_jiffies(2); for (;;) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) break; if (time_after(jiffies, timeo)) { @@ -1548,11 +1645,11 @@ static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, do_otp_lock, 1); } -static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, - unsigned long adr, map_word datum, - int mode) +static int __xipram do_write_oneword_once(struct map_info *map, + struct flchip *chip, + unsigned long adr, map_word datum, + int mode, struct cfi_private *cfi) { - struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; /* * We use a 1ms + 1 jiffies generic timeout for writes (most devices @@ -1565,42 +1662,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, */ unsigned long uWriteTimeout = (HZ / 1000) + 1; int ret = 0; - map_word oldd; - int retry_cnt = 0; - - adr += chip->start; - - mutex_lock(&chip->mutex); - ret = get_chip(map, chip, adr, mode); - if (ret) { - mutex_unlock(&chip->mutex); - return ret; - } - pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", - __func__, adr, datum.x[0]); - - if (mode == FL_OTP_WRITE) - otp_enter(map, chip, adr, map_bankwidth(map)); - - /* - * Check for a NOP for the case when the datum to write is already - * present - it saves time and works around buggy chips that corrupt - * data at other locations when 0xff is written to a location that - * already contains 0xff. - */ - oldd = map_read(map, adr); - if (map_word_equal(map, oldd, datum)) { - pr_debug("MTD %s(): NOP\n", - __func__); - goto op_done; - } - - XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); - ENABLE_VPP(map); - xip_disable(map, chip, adr); - - retry: cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -1628,38 +1690,127 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, continue; } - if (time_after(jiffies, timeo) && !chip_ready(map, adr)){ + /* + * We check "time_after" and "!chip_good" before checking + * "chip_good" to avoid the failure due to scheduling. + */ + if (time_after(jiffies, timeo) && + !chip_good(map, chip, adr, datum)) { xip_enable(map, chip, adr); printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); xip_disable(map, chip, adr); + ret = -EIO; break; } - if (chip_ready(map, adr)) + if (chip_good(map, chip, adr, datum)) { + if (cfi_check_err_status(map, chip, adr)) + ret = -EIO; break; + } /* Latency issues. Drop the lock, wait a while and retry */ UDELAY(map, chip, adr, 1); } - /* Did we succeed? */ - if (!chip_good(map, adr, datum)) { - /* reset on all failures. */ - map_write(map, CMD(0xF0), chip->start); - /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_RETRIES) - goto retry; + return ret; +} - ret = -EIO; +static int __xipram do_write_oneword_start(struct map_info *map, + struct flchip *chip, + unsigned long adr, int mode) +{ + int ret; + + mutex_lock(&chip->mutex); + + ret = get_chip(map, chip, adr, mode); + if (ret) { + mutex_unlock(&chip->mutex); + return ret; } - xip_enable(map, chip, adr); - op_done: + + if (mode == FL_OTP_WRITE) + otp_enter(map, chip, adr, map_bankwidth(map)); + + return ret; +} + +static void __xipram do_write_oneword_done(struct map_info *map, + struct flchip *chip, + unsigned long adr, int mode) +{ if (mode == FL_OTP_WRITE) otp_exit(map, chip, adr, map_bankwidth(map)); + chip->state = FL_READY; DISABLE_VPP(map); put_chip(map, chip, adr); + mutex_unlock(&chip->mutex); +} + +static int __xipram do_write_oneword_retry(struct map_info *map, + struct flchip *chip, + unsigned long adr, map_word datum, + int mode) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + map_word oldd; + int retry_cnt = 0; + + /* + * Check for a NOP for the case when the datum to write is already + * present - it saves time and works around buggy chips that corrupt + * data at other locations when 0xff is written to a location that + * already contains 0xff. + */ + oldd = map_read(map, adr); + if (map_word_equal(map, oldd, datum)) { + pr_debug("MTD %s(): NOP\n", __func__); + return ret; + } + + XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); + ENABLE_VPP(map); + xip_disable(map, chip, adr); + + retry: + ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi); + if (ret) { + /* reset on all failures. */ + map_write(map, CMD(0xF0), chip->start); + /* FIXME - should have reset delay before continuing */ + + if (++retry_cnt <= MAX_RETRIES) { + ret = 0; + goto retry; + } + } + xip_enable(map, chip, adr); + + return ret; +} + +static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, map_word datum, + int mode) +{ + int ret; + + adr += chip->start; + + pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr, + datum.x[0]); + + ret = do_write_oneword_start(map, chip, adr, mode); + if (ret) + return ret; + + ret = do_write_oneword_retry(map, chip, adr, datum, mode); + + do_write_oneword_done(map, chip, adr, mode); return ret; } @@ -1670,7 +1821,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; + int ret; int chipnum; unsigned long ofs, chipstart; DECLARE_WAITQUEUE(wait, current); @@ -1788,6 +1939,83 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, return 0; } +#if !FORCE_WORD_WRITE +static int __xipram do_write_buffer_wait(struct map_info *map, + struct flchip *chip, unsigned long adr, + map_word datum) +{ + unsigned long timeo; + unsigned long u_write_timeout; + int ret = 0; + + /* + * Timeout is calculated according to CFI data, if available. + * See more comments in cfi_cmdset_0002(). + */ + u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max); + timeo = jiffies + u_write_timeout; + + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + mutex_unlock(&chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ + mutex_lock(&chip->mutex); + continue; + } + + /* + * We check "time_after" and "!chip_good" before checking + * "chip_good" to avoid the failure due to scheduling. + */ + if (time_after(jiffies, timeo) && + !chip_good(map, chip, adr, datum)) { + pr_err("MTD %s(): software timeout, address:0x%.8lx.\n", + __func__, adr); + ret = -EIO; + break; + } + + if (chip_good(map, chip, adr, datum)) { + if (cfi_check_err_status(map, chip, adr)) + ret = -EIO; + break; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + UDELAY(map, chip, adr, 1); + } + + return ret; +} + +static void __xipram do_write_buffer_reset(struct map_info *map, + struct flchip *chip, + struct cfi_private *cfi) +{ + /* + * Recovery from write-buffer programming failures requires + * the write-to-buffer-reset sequence. Since the last part + * of the sequence also works as a normal reset, we can run + * the same commands regardless of why we are here. + * See e.g. + * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf + */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, + cfi->device_type, NULL); + cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi, + cfi->device_type, NULL); + + /* FIXME - should have reset delay before continuing */ +} /* * FIXME: interleaved mode not tested, and probably not supported! @@ -1797,14 +2025,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, int len) { struct cfi_private *cfi = map->fldrv_priv; - unsigned long timeo = jiffies + HZ; - /* - * Timeout is calculated according to CFI data, if available. - * See more comments in cfi_cmdset_0002(). - */ - unsigned long uWriteTimeout = - usecs_to_jiffies(chip->buffer_write_time_max); - int ret = -EIO; + int ret; unsigned long cmd_adr; int z, words; map_word datum; @@ -1860,61 +2081,12 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, adr, map_bankwidth(map), chip->word_write_time); - timeo = jiffies + uWriteTimeout; - - for (;;) { - if (chip->state != FL_WRITING) { - /* Someone's suspended the write. Sleep */ - DECLARE_WAITQUEUE(wait, current); - - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - mutex_unlock(&chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + (HZ / 2); /* FIXME */ - mutex_lock(&chip->mutex); - continue; - } - - /* - * We check "time_after" and "!chip_good" before checking "chip_good" to avoid - * the failure due to scheduling. - */ - if (time_after(jiffies, timeo) && !chip_good(map, adr, datum)) - break; - - if (chip_good(map, adr, datum)) { - xip_enable(map, chip, adr); - goto op_done; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - UDELAY(map, chip, adr, 1); - } + ret = do_write_buffer_wait(map, chip, adr, datum); + if (ret) + do_write_buffer_reset(map, chip, cfi); - /* - * Recovery from write-buffer programming failures requires - * the write-to-buffer-reset sequence. Since the last part - * of the sequence also works as a normal reset, we can run - * the same commands regardless of why we are here. - * See e.g. - * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf - */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, - cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, - cfi->device_type, NULL); - cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi, - cfi->device_type, NULL); xip_enable(map, chip, adr); - /* FIXME - should have reset delay before continuing */ - - printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n", - __func__, adr); - ret = -EIO; - op_done: chip->state = FL_READY; DISABLE_VPP(map); put_chip(map, chip, adr); @@ -1930,7 +2102,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; - int ret = 0; + int ret; int chipnum; unsigned long ofs; @@ -1998,6 +2170,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, return 0; } +#endif /* !FORCE_WORD_WRITE */ /* * Wait for the flash chip to become ready to write data @@ -2018,7 +2191,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip, * If the driver thinks the chip is idle, and no toggle bits * are changing, then the chip is actually idle for sure. */ - if (chip->state == FL_READY && chip_ready(map, adr)) + if (chip->state == FL_READY && chip_ready(map, chip, adr)) return 0; /* @@ -2035,7 +2208,7 @@ static int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip, /* wait for the chip to become ready */ for (i = 0; i < jiffies_to_usecs(timeo); i++) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) return 0; udelay(1); @@ -2066,7 +2239,7 @@ static int do_panic_write_oneword(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int retry_cnt = 0; map_word oldd; - int ret = 0; + int ret; int i; adr += chip->start; @@ -2099,13 +2272,14 @@ retry: map_write(map, datum, adr); for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) break; udelay(1); } - if (!chip_good(map, adr, datum)) { + if (!chip_good(map, chip, adr, datum) || + cfi_check_err_status(map, chip, adr)) { /* reset on all failures. */ map_write(map, CMD(0xF0), chip->start); /* FIXME - should have reset delay before continuing */ @@ -2140,7 +2314,7 @@ static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs, chipstart; - int ret = 0; + int ret; int chipnum; chipnum = to >> cfi->chipshift; @@ -2244,13 +2418,13 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) unsigned long timeo = jiffies + HZ; unsigned long int adr; DECLARE_WAITQUEUE(wait, current); - int ret = 0; + int ret; int retry_cnt = 0; adr = cfi->addr_unlock1; mutex_lock(&chip->mutex); - ret = get_chip(map, chip, adr, FL_WRITING); + ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { mutex_unlock(&chip->mutex); return ret; @@ -2300,8 +2474,11 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->erase_suspended = 0; } - if (chip_good(map, adr, map_word_ff(map))) + if (chip_good(map, chip, adr, map_word_ff(map))) { + if (cfi_check_err_status(map, chip, adr)) + ret = -EIO; break; + } if (time_after(jiffies, timeo)) { printk(KERN_WARNING "MTD %s(): software timeout\n", @@ -2340,7 +2517,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); - int ret = 0; + int ret; int retry_cnt = 0; adr += chip->start; @@ -2396,8 +2573,11 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->erase_suspended = 0; } - if (chip_good(map, adr, map_word_ff(map))) + if (chip_good(map, chip, adr, map_word_ff(map))) { + if (cfi_check_err_status(map, chip, adr)) + ret = -EIO; break; + } if (time_after(jiffies, timeo)) { printk(KERN_WARNING "MTD %s(): software timeout\n", @@ -2533,8 +2713,6 @@ struct ppb_lock { int locked; }; -#define MAX_SECTORS 512 - #define DO_XXLOCK_ONEBLOCK_LOCK ((void *)1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *)2) #define DO_XXLOCK_ONEBLOCK_GETLOCK ((void *)3) @@ -2589,7 +2767,7 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map, */ timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */ for (;;) { - if (chip_ready(map, adr)) + if (chip_ready(map, chip, adr)) break; if (time_after(jiffies, timeo)) { @@ -2633,6 +2811,7 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, int i; int sectors; int ret; + int max_sectors; /* * PPB unlocking always unlocks all sectors of the flash chip. @@ -2640,7 +2819,11 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, * first check the locking status of all sectors and save * it for future use. */ - sect = kcalloc(MAX_SECTORS, sizeof(struct ppb_lock), GFP_KERNEL); + max_sectors = 0; + for (i = 0; i < mtd->numeraseregions; i++) + max_sectors += regions[i].numblocks; + + sect = kcalloc(max_sectors, sizeof(struct ppb_lock), GFP_KERNEL); if (!sect) return -ENOMEM; @@ -2689,9 +2872,9 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, } sectors++; - if (sectors >= MAX_SECTORS) { + if (sectors >= max_sectors) { printk(KERN_ERR "Only %d sectors for PPB locking supported!\n", - MAX_SECTORS); + max_sectors); kfree(sect); return -EINVAL; } diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index e752067526a5..54edae63b92d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -611,7 +611,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; - int ret = 0; + int ret; int chipnum; unsigned long ofs; @@ -895,7 +895,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd, { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr, len; - int chipnum, ret = 0; + int chipnum, ret; int i, first; struct mtd_erase_region_info *regions = mtd->eraseregions; @@ -1132,7 +1132,7 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr; - int chipnum, ret = 0; + int chipnum, ret; #ifdef DEBUG_LOCK_BITS int ofs_factor = cfi->interleave * cfi->device_type; #endif @@ -1279,7 +1279,7 @@ static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr; - int chipnum, ret = 0; + int chipnum, ret; #ifdef DEBUG_LOCK_BITS int ofs_factor = cfi->interleave * cfi->device_type; #endif diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index e3b266ee06af..e2d4db05aeb3 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -26,7 +26,7 @@ void cfi_udelay(int us) { if (us >= 1000) { - msleep((us+999)/1000); + msleep(DIV_ROUND_UP(us, 1000)); } else { udelay(us); cond_resched(); diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 839ed40625d6..e5bd3c2bc3b2 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -20,7 +20,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) { - struct mtd_info *mtd = NULL; + struct mtd_info *mtd; struct cfi_private *cfi; /* First probe the map to see if we have CFI stuff there. */ diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ef0e476b2525..f96287c4b789 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -48,7 +48,7 @@ config MTD_MS02NV If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/kbuild/modules.txt>. + say M here and read <file:Documentation/kbuild/modules.rst>. The module will be called ms02-nv. config MTD_DATAFLASH @@ -79,24 +79,6 @@ config MTD_DATAFLASH_OTP other key product data. The second half is programmed with a unique-to-each-chip bit pattern at the factory. -config MTD_M25P80 - tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" - depends on SPI_MASTER && MTD_SPI_NOR - select SPI_MEM - help - This enables access to most modern SPI flash chips, used for - program and data storage. Series supported include Atmel AT26DF, - Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips - are supported as well. See the driver source for the current list, - or to add other chips. - - Note that the original DataFlash chips (AT45 series, not AT26DF), - need an entirely different driver. - - Set up your spi devices with the right board-specific platform data, - if you want to specify device partitioning or to use a device which - doesn't support the JEDEC ID instruction. - config MTD_MCHP23K256 tristate "Microchip 23K256 SRAM" depends on SPI_MASTER diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 94895eab3066..991c8d12c016 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o -obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SST25L) += sst25l.o diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c deleted file mode 100644 index c50888670250..000000000000 --- a/drivers/mtd/devices/m25p80.c +++ /dev/null @@ -1,347 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * MTD SPI driver for ST M25Pxx (and similar) serial flash chips - * - * Author: Mike Lavender, mike@steroidmicros.com - * - * Copyright (c) 2005, Intec Automation Inc. - * - * Some parts are based on lart.c by Abraham Van Der Merwe - * - * Cleaned up and generalized based on mtd_dataflash.c - */ - -#include <linux/err.h> -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/device.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> - -#include <linux/spi/spi.h> -#include <linux/spi/spi-mem.h> -#include <linux/spi/flash.h> -#include <linux/mtd/spi-nor.h> - -struct m25p { - struct spi_mem *spimem; - struct spi_nor spi_nor; -}; - -static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) -{ - struct m25p *flash = nor->priv; - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(len, NULL, 1)); - void *scratchbuf; - int ret; - - scratchbuf = kmalloc(len, GFP_KERNEL); - if (!scratchbuf) - return -ENOMEM; - - op.data.buf.in = scratchbuf; - ret = spi_mem_exec_op(flash->spimem, &op); - if (ret < 0) - dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret, - code); - else - memcpy(val, scratchbuf, len); - - kfree(scratchbuf); - - return ret; -} - -static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) -{ - struct m25p *flash = nor->priv; - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, NULL, 1)); - void *scratchbuf; - int ret; - - scratchbuf = kmemdup(buf, len, GFP_KERNEL); - if (!scratchbuf) - return -ENOMEM; - - op.data.buf.out = scratchbuf; - ret = spi_mem_exec_op(flash->spimem, &op); - kfree(scratchbuf); - - return ret; -} - -static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, - const u_char *buf) -{ - struct m25p *flash = nor->priv; - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, to, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, buf, 1)); - int ret; - - /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) - op.addr.nbytes = 0; - - ret = spi_mem_adjust_op_size(flash->spimem, &op); - if (ret) - return ret; - op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; - - ret = spi_mem_exec_op(flash->spimem, &op); - if (ret) - return ret; - - return op.data.nbytes; -} - -/* - * Read an address range from the nor chip. The address range - * may be any size provided it is within the physical boundaries. - */ -static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, - u_char *buf) -{ - struct m25p *flash = nor->priv; - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, from, 1), - SPI_MEM_OP_DUMMY(nor->read_dummy, 1), - SPI_MEM_OP_DATA_IN(len, buf, 1)); - size_t remaining = len; - int ret; - - /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op.dummy.buswidth = op.addr.buswidth; - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); - - /* convert the dummy cycles to the number of bytes */ - op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; - - while (remaining) { - op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; - ret = spi_mem_adjust_op_size(flash->spimem, &op); - if (ret) - return ret; - - ret = spi_mem_exec_op(flash->spimem, &op); - if (ret) - return ret; - - op.addr.val += op.data.nbytes; - remaining -= op.data.nbytes; - op.data.buf.in += op.data.nbytes; - } - - return len; -} - -/* - * board specific setup should have ensured the SPI clock used here - * matches what the READ command supports, at least until this driver - * understands FAST_READ (for clocks over 25 MHz). - */ -static int m25p_probe(struct spi_mem *spimem) -{ - struct spi_device *spi = spimem->spi; - struct flash_platform_data *data; - struct m25p *flash; - struct spi_nor *nor; - struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; - char *flash_name; - int ret; - - data = dev_get_platdata(&spimem->spi->dev); - - flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL); - if (!flash) - return -ENOMEM; - - nor = &flash->spi_nor; - - /* install the hooks */ - nor->read = m25p80_read; - nor->write = m25p80_write; - nor->write_reg = m25p80_write_reg; - nor->read_reg = m25p80_read_reg; - - nor->dev = &spimem->spi->dev; - spi_nor_set_flash_node(nor, spi->dev.of_node); - nor->priv = flash; - - spi_mem_set_drvdata(spimem, flash); - flash->spimem = spimem; - - if (spi->mode & SPI_RX_OCTAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; - - if (spi->mode & SPI_TX_OCTAL) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 | - SNOR_HWCAPS_PP_1_1_8 | - SNOR_HWCAPS_PP_1_8_8); - } else if (spi->mode & SPI_RX_QUAD) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; - - if (spi->mode & SPI_TX_QUAD) - hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | - SNOR_HWCAPS_PP_1_1_4 | - SNOR_HWCAPS_PP_1_4_4); - } else if (spi->mode & SPI_RX_DUAL) { - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; - - if (spi->mode & SPI_TX_DUAL) - hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; - } - - if (data && data->name) - nor->mtd.name = data->name; - - if (!nor->mtd.name) - nor->mtd.name = spi_mem_get_name(spimem); - - /* For some (historical?) reason many platforms provide two different - * names in flash_platform_data: "name" and "type". Quite often name is - * set to "m25p80" and then "type" provides a real chip name. - * If that's the case, respect "type" and ignore a "name". - */ - if (data && data->type) - flash_name = data->type; - else if (!strcmp(spi->modalias, "spi-nor")) - flash_name = NULL; /* auto-detect */ - else - flash_name = spi->modalias; - - ret = spi_nor_scan(nor, flash_name, &hwcaps); - if (ret) - return ret; - - return mtd_device_register(&nor->mtd, data ? data->parts : NULL, - data ? data->nr_parts : 0); -} - - -static int m25p_remove(struct spi_mem *spimem) -{ - struct m25p *flash = spi_mem_get_drvdata(spimem); - - spi_nor_restore(&flash->spi_nor); - - /* Clean up MTD stuff. */ - return mtd_device_unregister(&flash->spi_nor.mtd); -} - -static void m25p_shutdown(struct spi_mem *spimem) -{ - struct m25p *flash = spi_mem_get_drvdata(spimem); - - spi_nor_restore(&flash->spi_nor); -} -/* - * Do NOT add to this array without reading the following: - * - * Historically, many flash devices are bound to this driver by their name. But - * since most of these flash are compatible to some extent, and their - * differences can often be differentiated by the JEDEC read-ID command, we - * encourage new users to add support to the spi-nor library, and simply bind - * against a generic string here (e.g., "jedec,spi-nor"). - * - * Many flash names are kept here in this list (as well as in spi-nor.c) to - * keep them available as module aliases for existing platforms. - */ -static const struct spi_device_id m25p_ids[] = { - /* - * Allow non-DT platform devices to bind to the "spi-nor" modalias, and - * hack around the fact that the SPI core does not provide uevent - * matching for .of_match_table - */ - {"spi-nor"}, - - /* - * Entries not used in DTs that should be safe to drop after replacing - * them with "spi-nor" in platform data. - */ - {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"}, - - /* - * Entries that were used in DTs without "jedec,spi-nor" fallback and - * should be kept for backward compatibility. - */ - {"at25df321a"}, {"at25df641"}, {"at26df081a"}, - {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"}, - {"mx25l25635e"},{"mx66l51235l"}, - {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"}, - {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"}, - {"s25fl064k"}, - {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"}, - {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"}, - {"m25p64"}, {"m25p128"}, - {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"}, - {"w25q80bl"}, {"w25q128"}, {"w25q256"}, - - /* Flashes that can't be detected using JEDEC */ - {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"}, - {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, - {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, - - /* Everspin MRAMs (non-JEDEC) */ - { "mr25h128" }, /* 128 Kib, 40 MHz */ - { "mr25h256" }, /* 256 Kib, 40 MHz */ - { "mr25h10" }, /* 1 Mib, 40 MHz */ - { "mr25h40" }, /* 4 Mib, 40 MHz */ - - { }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); - -static const struct of_device_id m25p_of_table[] = { - /* - * Generic compatibility for SPI NOR that can be identified by the - * JEDEC READ ID opcode (0x9F). Use this, if possible. - */ - { .compatible = "jedec,spi-nor" }, - {} -}; -MODULE_DEVICE_TABLE(of, m25p_of_table); - -static struct spi_mem_driver m25p80_driver = { - .spidrv = { - .driver = { - .name = "m25p80", - .of_match_table = m25p_of_table, - }, - .id_table = m25p_ids, - }, - .probe = m25p_probe, - .remove = m25p_remove, - .shutdown = m25p_shutdown, - - /* REVISIT: many of these chips have deep power-down modes, which - * should clearly be entered on suspend() to minimize power use. - * And also when they're otherwise idle... - */ -}; - -module_spi_mem_driver(m25p80_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mike Lavender"); -MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips"); diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index b20d02b4f830..77c872fd3d83 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -64,15 +64,17 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_transfer transfer[2] = {}; struct spi_message message; unsigned char command[MAX_CMD_SIZE]; - int ret; + int ret, cmd_len; spi_message_init(&message); + cmd_len = mchp23k256_cmdsz(flash); + command[0] = MCHP23K256_CMD_WRITE; mchp23k256_addr2cmd(flash, to, command); transfer[0].tx_buf = command; - transfer[0].len = mchp23k256_cmdsz(flash); + transfer[0].len = cmd_len; spi_message_add_tail(&transfer[0], &message); transfer[1].tx_buf = buf; @@ -88,8 +90,8 @@ static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len, if (ret) return ret; - if (retlen && message.actual_length > sizeof(command)) - *retlen += message.actual_length - sizeof(command); + if (retlen && message.actual_length > cmd_len) + *retlen += message.actual_length - cmd_len; return 0; } @@ -101,16 +103,18 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_transfer transfer[2] = {}; struct spi_message message; unsigned char command[MAX_CMD_SIZE]; - int ret; + int ret, cmd_len; spi_message_init(&message); + cmd_len = mchp23k256_cmdsz(flash); + memset(&transfer, 0, sizeof(transfer)); command[0] = MCHP23K256_CMD_READ; mchp23k256_addr2cmd(flash, from, command); transfer[0].tx_buf = command; - transfer[0].len = mchp23k256_cmdsz(flash); + transfer[0].len = cmd_len; spi_message_add_tail(&transfer[0], &message); transfer[1].rx_buf = buf; @@ -126,8 +130,8 @@ static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len, if (ret) return ret; - if (retlen && message.actual_length > sizeof(command)) - *retlen += message.actual_length - sizeof(command); + if (retlen && message.actual_length > cmd_len) + *retlen += message.actual_length - cmd_len; return 0; } diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index c467286ca007..931e5c2481b5 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -294,7 +294,7 @@ static int phram_param_call(const char *val, const struct kernel_param *kp) #endif } -module_param_call(phram, phram_param_call, NULL, NULL, 000); +module_param_call(phram, phram_param_call, NULL, NULL, 0200); MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 3b89ab24688b..6597fc2aad34 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -135,7 +135,7 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len, static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 soff_hi; /* start address offset hi */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; u_char *ptr; @@ -150,7 +150,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr) eoff_hi = end & ~(priv->asize - 1); soff_hi = instr->addr & ~(priv->asize - 1); eoff_lo = end & (priv->asize - 1); - soff_lo = instr->addr & (priv->asize - 1); pmc551_point(mtd, instr->addr, instr->len, &retlen, (void **)&ptr, NULL); @@ -225,7 +224,7 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 soff_hi; /* start address offset hi */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; u_char *ptr; @@ -239,7 +238,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len, end = from + len - 1; soff_hi = from & ~(priv->asize - 1); eoff_hi = end & ~(priv->asize - 1); - soff_lo = from & (priv->asize - 1); eoff_lo = end & (priv->asize - 1); pmc551_point(mtd, from, len, retlen, (void **)&ptr, NULL); @@ -282,7 +280,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) { struct mypriv *priv = mtd->priv; - u32 soff_hi, soff_lo; /* start address offset hi/lo */ + u32 soff_hi; /* start address offset hi */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; u_char *ptr; @@ -296,7 +294,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len, end = to + len - 1; soff_hi = to & ~(priv->asize - 1); eoff_hi = end & ~(priv->asize - 1); - soff_lo = to & (priv->asize - 1); eoff_lo = end & (priv->asize - 1); pmc551_point(mtd, to, len, retlen, (void **)&ptr, NULL); diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 986f81d2f93e..79dcca16481d 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -592,6 +592,26 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } +/* + * The purpose of this function is to ensure a memcpy_toio() with byte writes + * only. Its structure is inspired from the ARM implementation of _memcpy_toio() + * which also does single byte writes but cannot be used here as this is just an + * implementation detail and not part of the API. Not mentioning the comment + * stating that _memcpy_toio() should be optimized. + */ +static void spear_smi_memcpy_toio_b(volatile void __iomem *dest, + const void *src, size_t len) +{ + const unsigned char *from = src; + + while (len) { + len--; + writeb(*from, dest); + from++; + dest++; + } +} + static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, void __iomem *dest, const void *src, size_t len) { @@ -614,7 +634,23 @@ static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, ctrlreg1 = readl(dev->io_base + SMI_CR1); writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1); - memcpy_toio(dest, src, len); + /* + * In Write Burst mode (WB_MODE), the specs states that writes must be: + * - incremental + * - of the same size + * The ARM implementation of memcpy_toio() will optimize the number of + * I/O by using as much 4-byte writes as possible, surrounded by + * 2-byte/1-byte access if: + * - the destination is not 4-byte aligned + * - the length is not a multiple of 4-byte. + * Avoid this alternance of write access size by using our own 'byte + * access' helper if at least one of the two conditions above is true. + */ + if (IS_ALIGNED(len, sizeof(u32)) && + IS_ALIGNED((uintptr_t)dest, sizeof(u32))) + memcpy_toio(dest, src, len); + else + spear_smi_memcpy_toio_b(dest, src, len); writel(ctrlreg1, dev->io_base + SMI_CR1); @@ -777,9 +813,6 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev, /* Fill structs for each subnode (flash device) */ while ((pp = of_get_next_child(np, pp))) { - struct spear_smi_flash_info *flash_info; - - flash_info = &pdata->board_flash_info[i]; pdata->np[i] = pp; /* Read base-addr and size from DT */ @@ -933,7 +966,6 @@ static int spear_smi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENODEV; - dev_err(&pdev->dev, "invalid smi irq\n"); goto err; } diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index f4d1667daaf9..1888523d9745 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -255,7 +255,6 @@ struct stfsm_seq { struct stfsm { struct device *dev; void __iomem *base; - struct resource *region; struct mtd_info mtd; struct mutex lock; struct flash_info *info; diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig new file mode 100644 index 000000000000..a4d8968d133d --- /dev/null +++ b/drivers/mtd/hyperbus/Kconfig @@ -0,0 +1,25 @@ +menuconfig MTD_HYPERBUS + tristate "HyperBus support" + depends on HAS_IOMEM + select MTD_CFI + select MTD_MAP_BANK_WIDTH_2 + select MTD_CFI_AMDSTD + select MTD_COMPLEX_MAPPINGS + help + This is the framework for the HyperBus which can be used by + the HyperBus Controller driver to communicate with + HyperFlash. See Cypress HyperBus specification for more + details + +if MTD_HYPERBUS + +config HBMC_AM654 + tristate "HyperBus controller driver for AM65x SoC" + depends on ARM64 || COMPILE_TEST + select MULTIPLEXER + imply MUX_MMIO + help + This is the driver for HyperBus controller on TI's AM65x and + other SoCs + +endif # MTD_HYPERBUS diff --git a/drivers/mtd/hyperbus/Makefile b/drivers/mtd/hyperbus/Makefile new file mode 100644 index 000000000000..8a936e066f48 --- /dev/null +++ b/drivers/mtd/hyperbus/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o +obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c new file mode 100644 index 000000000000..08d543b124cd --- /dev/null +++ b/drivers/mtd/hyperbus/hbmc-am654.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Vignesh Raghavendra <vigneshr@ti.com> + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/cfi.h> +#include <linux/mtd/hyperbus.h> +#include <linux/mtd/mtd.h> +#include <linux/mux/consumer.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> + +#define AM654_HBMC_CALIB_COUNT 25 + +struct am654_hbmc_priv { + struct hyperbus_ctlr ctlr; + struct hyperbus_device hbdev; + struct mux_control *mux_ctrl; +}; + +static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) +{ + struct map_info *map = &hbdev->map; + struct cfi_private cfi; + int count = AM654_HBMC_CALIB_COUNT; + int pass_count = 0; + int ret; + + cfi.interleave = 1; + cfi.device_type = CFI_DEVICETYPE_X16; + cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL); + cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL); + + while (count--) { + ret = cfi_qry_present(map, 0, &cfi); + if (ret) + pass_count++; + else + pass_count = 0; + if (pass_count == 5) + break; + } + + cfi_qry_mode_off(0, map, &cfi); + + return ret; +} + +static const struct hyperbus_ops am654_hbmc_ops = { + .calibrate = am654_hbmc_calibrate, +}; + +static int am654_hbmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct am654_hbmc_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + if (of_property_read_bool(dev->of_node, "mux-controls")) { + struct mux_control *control = devm_mux_control_get(dev, NULL); + + if (IS_ERR(control)) + return PTR_ERR(control); + + ret = mux_control_select(control, 1); + if (ret) { + dev_err(dev, "Failed to select HBMC mux\n"); + return ret; + } + priv->mux_ctrl = control; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto disable_pm; + } + + priv->ctlr.dev = dev; + priv->ctlr.ops = &am654_hbmc_ops; + priv->hbdev.ctlr = &priv->ctlr; + priv->hbdev.np = of_get_next_child(dev->of_node, NULL); + ret = hyperbus_register_device(&priv->hbdev); + if (ret) { + dev_err(dev, "failed to register controller\n"); + pm_runtime_put_sync(&pdev->dev); + goto disable_pm; + } + + return 0; +disable_pm: + pm_runtime_disable(dev); + if (priv->mux_ctrl) + mux_control_deselect(priv->mux_ctrl); + return ret; +} + +static int am654_hbmc_remove(struct platform_device *pdev) +{ + struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); + int ret; + + ret = hyperbus_unregister_device(&priv->hbdev); + if (priv->mux_ctrl) + mux_control_deselect(priv->mux_ctrl); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static const struct of_device_id am654_hbmc_dt_ids[] = { + { + .compatible = "ti,am654-hbmc", + }, + { /* end of table */ } +}; + +MODULE_DEVICE_TABLE(of, am654_hbmc_dt_ids); + +static struct platform_driver am654_hbmc_platform_driver = { + .probe = am654_hbmc_probe, + .remove = am654_hbmc_remove, + .driver = { + .name = "hbmc-am654", + .of_match_table = am654_hbmc_dt_ids, + }, +}; + +module_platform_driver(am654_hbmc_platform_driver); + +MODULE_DESCRIPTION("HBMC driver for AM654 SoC"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:hbmc-am654"); +MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c new file mode 100644 index 000000000000..6af9ea34117d --- /dev/null +++ b/drivers/mtd/hyperbus/hyperbus-core.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Vignesh Raghavendra <vigneshr@ti.com> + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/hyperbus.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/types.h> + +static struct hyperbus_device *map_to_hbdev(struct map_info *map) +{ + return container_of(map, struct hyperbus_device, map); +} + +static map_word hyperbus_read16(struct map_info *map, unsigned long addr) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + map_word read_data; + + read_data.x[0] = ctlr->ops->read16(hbdev, addr); + + return read_data; +} + +static void hyperbus_write16(struct map_info *map, map_word d, + unsigned long addr) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->write16(hbdev, addr, d.x[0]); +} + +static void hyperbus_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->copy_from(hbdev, to, from, len); +} + +static void hyperbus_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->copy_to(hbdev, to, from, len); +} + +int hyperbus_register_device(struct hyperbus_device *hbdev) +{ + const struct hyperbus_ops *ops; + struct hyperbus_ctlr *ctlr; + struct device_node *np; + struct map_info *map; + struct resource res; + struct device *dev; + int ret; + + if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) { + pr_err("hyperbus: please fill all the necessary fields!\n"); + return -EINVAL; + } + + np = hbdev->np; + ctlr = hbdev->ctlr; + if (!of_device_is_compatible(np, "cypress,hyperflash")) + return -ENODEV; + + hbdev->memtype = HYPERFLASH; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + dev = ctlr->dev; + map = &hbdev->map; + map->size = resource_size(&res); + map->virt = devm_ioremap_resource(dev, &res); + if (IS_ERR(map->virt)) + return PTR_ERR(map->virt); + + map->name = dev_name(dev); + map->bankwidth = 2; + map->device_node = np; + + simple_map_init(map); + ops = ctlr->ops; + if (ops) { + if (ops->read16) + map->read = hyperbus_read16; + if (ops->write16) + map->write = hyperbus_write16; + if (ops->copy_to) + map->copy_to = hyperbus_copy_to; + if (ops->copy_from) + map->copy_from = hyperbus_copy_from; + + if (ops->calibrate && !ctlr->calibrated) { + ret = ops->calibrate(hbdev); + if (!ret) { + dev_err(dev, "Calibration failed\n"); + return -ENODEV; + } + ctlr->calibrated = true; + } + } + + hbdev->mtd = do_map_probe("cfi_probe", map); + if (!hbdev->mtd) { + dev_err(dev, "probing of hyperbus device failed\n"); + return -ENODEV; + } + + hbdev->mtd->dev.parent = dev; + mtd_set_of_node(hbdev->mtd, np); + + ret = mtd_device_register(hbdev->mtd, NULL, 0); + if (ret) { + dev_err(dev, "failed to register mtd device\n"); + map_destroy(hbdev->mtd); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hyperbus_register_device); + +int hyperbus_unregister_device(struct hyperbus_device *hbdev) +{ + int ret = 0; + + if (hbdev && hbdev->mtd) { + ret = mtd_device_unregister(hbdev->mtd); + map_destroy(hbdev->mtd); + } + + return ret; +} +EXPORT_SYMBOL_GPL(hyperbus_unregister_device); + +MODULE_DESCRIPTION("HyperBus Framework"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index bc82305ebb4c..b28225a7c4f3 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -96,6 +96,17 @@ config MTD_PHYSMAP_GEMINI platforms, some detection and setting up parallel mode on the external interface. +config MTD_PHYSMAP_IXP4XX + bool "Intel IXP4xx OF-based physical memory map handling" + depends on MTD_PHYSMAP_OF + depends on ARM + select MTD_COMPLEX_MAPPINGS + select MTD_CFI_BE_BYTE_SWAP if CPU_BIG_ENDIAN + default ARCH_IXP4XX + help + This provides some extra DT physmap parsing for the Intel IXP4xx + platforms, some elaborate endianness handling in particular. + config MTD_PHYSMAP_GPIO_ADDR bool "GPIO-assisted Flash Chip Support" depends on MTD_PHYSMAP diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 1146009f41df..c0da86a5d26f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o physmap-objs-y += physmap-core.o physmap-objs-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o physmap-objs-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o +physmap-objs-$(CONFIG_MTD_PHYSMAP_IXP4XX) += physmap-ixp4xx.o physmap-objs := $(physmap-objs-y) obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PISMO) += pismo.o diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 876f12f40018..0eeadfeb620d 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -86,7 +86,7 @@ static int __init init_l440gx(void) return -ENOMEM; } simple_map_init(&l440gx_map); - printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); + pr_debug("window_addr = %p\n", l440gx_map.virt); /* Setup the pm iobase resource * This code should move into some kind of generic bridge diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 21b556afc305..a9f7964e2edb 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -41,6 +41,7 @@ #include <linux/gpio/consumer.h> #include "physmap-gemini.h" +#include "physmap-ixp4xx.h" #include "physmap-versatile.h" struct physmap_flash_info { @@ -370,6 +371,10 @@ static int physmap_flash_of_init(struct platform_device *dev) if (err) return err; + err = of_flash_probe_ixp4xx(dev, dp, &info->maps[i]); + if (err) + return err; + err = of_flash_probe_versatile(dev, dp, &info->maps[i]); if (err) return err; diff --git a/drivers/mtd/maps/physmap-ixp4xx.c b/drivers/mtd/maps/physmap-ixp4xx.c new file mode 100644 index 000000000000..6a054229a8a0 --- /dev/null +++ b/drivers/mtd/maps/physmap-ixp4xx.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel IXP4xx OF physmap add-on + * Copyright (C) 2019 Linus Walleij <linus.walleij@linaro.org> + * + * Based on the ixp4xx.c map driver, originally written by: + * Intel Corporation + * Deepak Saxena <dsaxena@mvista.com> + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003-2004 MontaVista Software, Inc. + */ +#include <linux/export.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/mtd/map.h> +#include <linux/mtd/xip.h> +#include "physmap-ixp4xx.h" + +/* + * Read/write a 16 bit word from flash address 'addr'. + * + * When the cpu is in little-endian mode it swizzles the address lines + * ('address coherency') so we need to undo the swizzling to ensure commands + * and the like end up on the correct flash address. + * + * To further complicate matters, due to the way the expansion bus controller + * handles 32 bit reads, the byte stream ABCD is stored on the flash as: + * D15 D0 + * +---+---+ + * | A | B | 0 + * +---+---+ + * | C | D | 2 + * +---+---+ + * This means that on LE systems each 16 bit word must be swapped. Note that + * this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI + * data and other flash commands which are always in D7-D0. + */ +#ifndef CONFIG_CPU_BIG_ENDIAN + +static inline u16 flash_read16(void __iomem *addr) +{ + return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2))); +} + +static inline void flash_write16(u16 d, void __iomem *addr) +{ + __raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2)); +} + +#define BYTE0(h) ((h) & 0xFF) +#define BYTE1(h) (((h) >> 8) & 0xFF) + +#else + +static inline u16 flash_read16(const void __iomem *addr) +{ + return __raw_readw(addr); +} + +static inline void flash_write16(u16 d, void __iomem *addr) +{ + __raw_writew(d, addr); +} + +#define BYTE0(h) (((h) >> 8) & 0xFF) +#define BYTE1(h) ((h) & 0xFF) +#endif + +static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) +{ + map_word val; + + val.x[0] = flash_read16(map->virt + ofs); + return val; +} + +/* + * The IXP4xx expansion bus only allows 16-bit wide acceses + * when attached to a 16-bit wide device (such as the 28F128J3A), + * so we can't just memcpy_fromio(). + */ +static void ixp4xx_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + u8 *dest = (u8 *) to; + void __iomem *src = map->virt + from; + + if (len <= 0) + return; + + if (from & 1) { + *dest++ = BYTE1(flash_read16(src-1)); + src++; + --len; + } + + while (len >= 2) { + u16 data = flash_read16(src); + *dest++ = BYTE0(data); + *dest++ = BYTE1(data); + src += 2; + len -= 2; + } + + if (len > 0) + *dest++ = BYTE0(flash_read16(src)); +} + +static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) +{ + flash_write16(d.x[0], map->virt + adr); +} + +int of_flash_probe_ixp4xx(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + struct device *dev = &pdev->dev; + + /* Multiplatform guard */ + if (!of_device_is_compatible(np, "intel,ixp4xx-flash")) + return 0; + + map->read = ixp4xx_read16; + map->write = ixp4xx_write16; + map->copy_from = ixp4xx_copy_from; + map->copy_to = NULL; + + dev_info(dev, "initialized Intel IXP4xx-specific physmap control\n"); + + return 0; +} diff --git a/drivers/mtd/maps/physmap-ixp4xx.h b/drivers/mtd/maps/physmap-ixp4xx.h new file mode 100644 index 000000000000..b0fc49b7f3ed --- /dev/null +++ b/drivers/mtd/maps/physmap-ixp4xx.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/of.h> +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_PHYSMAP_IXP4XX +int of_flash_probe_ixp4xx(struct platform_device *pdev, + struct device_node *np, + struct map_info *map); +#else +static inline +int of_flash_probe_ixp4xx(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + return 0; +} +#endif diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index 788d4996e2c1..946ba80f9758 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -211,13 +211,12 @@ static int pismo_remove(struct i2c_client *client) static int pismo_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct pismo_pdata *pdata = client->dev.platform_data; struct pismo_eeprom eeprom; struct pismo_data *pismo; int ret, i; - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "functionality mismatch\n"); return -EIO; } diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index cebb346877a9..7d96758a8f04 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -68,8 +68,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev) info->map.name); return -ENOMEM; } - info->map.cached = - ioremap_cached(info->map.phys, info->map.size); + info->map.cached = ioremap_cache(info->map.phys, info->map.size); if (!info->map.cached) printk(KERN_WARNING "Failed to ioremap cached %s\n", info->map.name); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 895510d40ce4..47602af4ee34 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -81,6 +81,7 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r default: printk(KERN_WARNING "SA1100 flash: unknown base address " "0x%08lx, assuming CS0\n", phys); + /* Fall through */ case SA1100_CS0_PHYS: subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 975aed94f06c..b841008a9eb7 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -174,7 +174,7 @@ static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count, break; case MTD_FILE_MODE_RAW: { - struct mtd_oob_ops ops; + struct mtd_oob_ops ops = {}; ops.mode = MTD_OPS_RAW; ops.datbuf = kbuf; @@ -268,7 +268,7 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c case MTD_FILE_MODE_RAW: { - struct mtd_oob_ops ops; + struct mtd_oob_ops ops = {}; ops.mode = MTD_OPS_RAW; ops.datbuf = kbuf; @@ -350,7 +350,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, uint32_t __user *retp) { struct mtd_file_info *mfi = file->private_data; - struct mtd_oob_ops ops; + struct mtd_oob_ops ops = {}; uint32_t retlen; int ret = 0; @@ -394,7 +394,7 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, uint32_t __user *retp) { struct mtd_file_info *mfi = file->private_data; - struct mtd_oob_ops ops; + struct mtd_oob_ops ops = {}; int ret = 0; if (length > 4096) @@ -587,7 +587,7 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp) { struct mtd_write_req req; - struct mtd_oob_ops ops; + struct mtd_oob_ops ops = {}; const void __user *usr_data, *usr_oob; int ret; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 7324ff832b41..170a7221b35f 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -437,7 +437,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) return err; } -static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, + bool is_lock) { struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; @@ -456,7 +457,10 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) else size = len; - err = mtd_lock(subdev, ofs, size); + if (is_lock) + err = mtd_lock(subdev, ofs, size); + else + err = mtd_unlock(subdev, ofs, size); if (err) break; @@ -471,35 +475,33 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return err; } +static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + return concat_xxlock(mtd, ofs, len, true); +} + static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { + return concat_xxlock(mtd, ofs, len, false); +} + +static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ struct mtd_concat *concat = CONCAT(mtd); - int i, err = 0; + int i, err = -EINVAL; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - uint64_t size; if (ofs >= subdev->size) { - size = 0; ofs -= subdev->size; continue; } - if (ofs + len > subdev->size) - size = subdev->size - ofs; - else - size = len; - - err = mtd_unlock(subdev, ofs, size); - if (err) - break; - len -= size; - if (len == 0) + if (ofs + len > subdev->size) break; - err = -EINVAL; - ofs = 0; + return mtd_is_locked(subdev, ofs, len); } return err; @@ -704,6 +706,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd._sync = concat_sync; concat->mtd._lock = concat_lock; concat->mtd._unlock = concat_unlock; + concat->mtd._is_locked = concat_is_locked; concat->mtd._suspend = concat_suspend; concat->mtd._resume = concat_resume; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 453242d6cf56..5fac4355b9c2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -335,6 +335,70 @@ static const struct device_type mtd_devtype = { .release = mtd_release, }; +static int mtd_partid_show(struct seq_file *s, void *p) +{ + struct mtd_info *mtd = s->private; + + seq_printf(s, "%s\n", mtd->dbg.partid); + + return 0; +} + +static int mtd_partid_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtd_partid_show, inode->i_private); +} + +static const struct file_operations mtd_partid_debug_fops = { + .open = mtd_partid_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mtd_partname_show(struct seq_file *s, void *p) +{ + struct mtd_info *mtd = s->private; + + seq_printf(s, "%s\n", mtd->dbg.partname); + + return 0; +} + +static int mtd_partname_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtd_partname_show, inode->i_private); +} + +static const struct file_operations mtd_partname_debug_fops = { + .open = mtd_partname_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *dfs_dir_mtd; + +static void mtd_debugfs_populate(struct mtd_info *mtd) +{ + struct device *dev = &mtd->dev; + struct dentry *root; + + if (IS_ERR_OR_NULL(dfs_dir_mtd)) + return; + + root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd); + mtd->dbg.dfs_dir = root; + + if (mtd->dbg.partid) + debugfs_create_file("partid", 0400, root, mtd, + &mtd_partid_debug_fops); + + if (mtd->dbg.partname) + debugfs_create_file("partname", 0400, root, mtd, + &mtd_partname_debug_fops); +} + #ifndef CONFIG_MMU unsigned mtd_mmap_capabilities(struct mtd_info *mtd) { @@ -512,8 +576,6 @@ static int mtd_nvmem_add(struct mtd_info *mtd) return 0; } -static struct dentry *dfs_dir_mtd; - /** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure @@ -607,13 +669,7 @@ int add_mtd_device(struct mtd_info *mtd) if (error) goto fail_nvmem_add; - if (!IS_ERR_OR_NULL(dfs_dir_mtd)) { - mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd); - if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) { - pr_debug("mtd device %s won't show data in debugfs\n", - dev_name(&mtd->dev)); - } - } + mtd_debugfs_populate(mtd); device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, "mtd%dro", i); @@ -1124,6 +1180,9 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return -EROFS; if (!len) return 0; + if (!mtd->oops_panic_write) + mtd->oops_panic_write = true; + return mtd->_panic_write(mtd, to, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_panic_write); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index b31c868019ad..b5eefeabf310 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -5,6 +5,7 @@ */ extern struct mutex mtd_table_mutex; +extern struct backing_dev_info *mtd_bdi; struct mtd_info *__mtd_next_device(int i); int __must_check add_mtd_device(struct mtd_info *mtd); diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 4f042a3653ce..c3e2098372f2 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -15,112 +15,109 @@ #include <linux/slab.h> #include <linux/major.h> #include <linux/backing-dev.h> +#include <linux/fs_context.h> +#include "mtdcore.h" /* * compare superblocks to see if they're equivalent * - they are if the underlying MTD device is the same */ -static int get_sb_mtd_compare(struct super_block *sb, void *_mtd) +static int mtd_test_super(struct super_block *sb, struct fs_context *fc) { - struct mtd_info *mtd = _mtd; + struct mtd_info *mtd = fc->sget_key; - if (sb->s_mtd == mtd) { + if (sb->s_mtd == fc->sget_key) { pr_debug("MTDSB: Match on device %d (\"%s\")\n", - mtd->index, mtd->name); + mtd->index, mtd->name); return 1; } pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", - sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); + sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); return 0; } -extern struct backing_dev_info *mtd_bdi; - /* * mark the superblock by the MTD device it is using * - set the device number to be the correct MTD block device for pesuperstence * of NFS exports */ -static int get_sb_mtd_set(struct super_block *sb, void *_mtd) +static int mtd_set_super(struct super_block *sb, struct fs_context *fc) { - struct mtd_info *mtd = _mtd; - - sb->s_mtd = mtd; - sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index); + sb->s_mtd = fc->sget_key; + sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index); sb->s_bdi = bdi_get(mtd_bdi); - return 0; } /* * get a superblock on an MTD-backed filesystem */ -static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - struct mtd_info *mtd, - int (*fill_super)(struct super_block *, void *, int)) +static int mtd_get_sb(struct fs_context *fc, + struct mtd_info *mtd, + int (*fill_super)(struct super_block *, + struct fs_context *)) { struct super_block *sb; int ret; - sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, flags, mtd); + fc->sget_key = mtd; + sb = sget_fc(fc, mtd_test_super, mtd_set_super); if (IS_ERR(sb)) - goto out_error; - - if (sb->s_root) - goto already_mounted; - - /* fresh new superblock */ - pr_debug("MTDSB: New superblock for device %d (\"%s\")\n", - mtd->index, mtd->name); - - ret = fill_super(sb, data, flags & SB_SILENT ? 1 : 0); - if (ret < 0) { - deactivate_locked_super(sb); - return ERR_PTR(ret); + return PTR_ERR(sb); + + if (sb->s_root) { + /* new mountpoint for an already mounted superblock */ + pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n", + mtd->index, mtd->name); + put_mtd_device(mtd); + } else { + /* fresh new superblock */ + pr_debug("MTDSB: New superblock for device %d (\"%s\")\n", + mtd->index, mtd->name); + + ret = fill_super(sb, fc); + if (ret < 0) + goto error_sb; + + sb->s_flags |= SB_ACTIVE; } - /* go */ - sb->s_flags |= SB_ACTIVE; - return dget(sb->s_root); - - /* new mountpoint for an already mounted superblock */ -already_mounted: - pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n", - mtd->index, mtd->name); - put_mtd_device(mtd); - return dget(sb->s_root); + BUG_ON(fc->root); + fc->root = dget(sb->s_root); + return 0; -out_error: - put_mtd_device(mtd); - return ERR_CAST(sb); +error_sb: + deactivate_locked_super(sb); + return ret; } /* * get a superblock on an MTD-backed filesystem by MTD device number */ -static struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, int mtdnr, - int (*fill_super)(struct super_block *, void *, int)) +static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr, + int (*fill_super)(struct super_block *, + struct fs_context *)) { struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); if (IS_ERR(mtd)) { - pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr); - return ERR_CAST(mtd); + errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr); + return PTR_ERR(mtd); } - return mount_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super); + return mtd_get_sb(fc, mtd, fill_super); } -/* - * set up an MTD-based superblock +/** + * get_tree_mtd - Get a superblock based on a single MTD device + * @fc: The filesystem context holding the parameters + * @fill_super: Helper to initialise a new superblock */ -struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct super_block *, void *, int)) +int get_tree_mtd(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc)) { #ifdef CONFIG_BLOCK struct block_device *bdev; @@ -128,46 +125,42 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, #endif int mtdnr; - if (!dev_name) - return ERR_PTR(-EINVAL); + if (!fc->source) + return invalf(fc, "No source specified"); - pr_debug("MTDSB: dev_name \"%s\"\n", dev_name); + pr_debug("MTDSB: dev_name \"%s\"\n", fc->source); /* the preferred way of mounting in future; especially when * CONFIG_BLOCK=n - we specify the underlying MTD device by number or * by name, so that we don't require block device support to be present - * in the kernel. */ - if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { - if (dev_name[3] == ':') { + * in the kernel. + */ + if (fc->source[0] == 'm' && + fc->source[1] == 't' && + fc->source[2] == 'd') { + if (fc->source[3] == ':') { struct mtd_info *mtd; /* mount by MTD device name */ pr_debug("MTDSB: mtd:%%s, name \"%s\"\n", - dev_name + 4); + fc->source + 4); - mtd = get_mtd_device_nm(dev_name + 4); + mtd = get_mtd_device_nm(fc->source + 4); if (!IS_ERR(mtd)) - return mount_mtd_aux( - fs_type, flags, - dev_name, data, mtd, - fill_super); + return mtd_get_sb(fc, mtd, fill_super); - printk(KERN_NOTICE "MTD:" - " MTD device with name \"%s\" not found.\n", - dev_name + 4); + errorf(fc, "MTD: MTD device with name \"%s\" not found", + fc->source + 4); - } else if (isdigit(dev_name[3])) { + } else if (isdigit(fc->source[3])) { /* mount by MTD device number name */ char *endptr; - mtdnr = simple_strtoul(dev_name + 3, &endptr, 0); + mtdnr = simple_strtoul(fc->source + 3, &endptr, 0); if (!*endptr) { /* It was a valid number */ - pr_debug("MTDSB: mtd%%d, mtdnr %d\n", - mtdnr); - return mount_mtd_nr(fs_type, flags, - dev_name, data, - mtdnr, fill_super); + pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr); + return mtd_get_sb_by_nr(fc, mtdnr, fill_super); } } } @@ -176,36 +169,29 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, /* try the old way - the hack where we allowed users to mount * /dev/mtdblock$(n) but didn't actually _use_ the blockdev */ - bdev = lookup_bdev(dev_name); + bdev = lookup_bdev(fc->source); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); - pr_debug("MTDSB: lookup_bdev() returned %d\n", ret); - return ERR_PTR(ret); + errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret); + return ret; } pr_debug("MTDSB: lookup_bdev() returned 0\n"); - ret = -EINVAL; - major = MAJOR(bdev->bd_dev); mtdnr = MINOR(bdev->bd_dev); bdput(bdev); - if (major != MTD_BLOCK_MAJOR) - goto not_an_MTD_device; - - return mount_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super); + if (major == MTD_BLOCK_MAJOR) + return mtd_get_sb_by_nr(fc, mtdnr, fill_super); -not_an_MTD_device: #endif /* CONFIG_BLOCK */ - if (!(flags & SB_SILENT)) - printk(KERN_NOTICE - "MTD: Attempt to mount non-MTD device \"%s\"\n", - dev_name); - return ERR_PTR(-EINVAL); + if (!(fc->sb_flags & SB_SILENT)) + errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"", + fc->source); + return -EINVAL; } - -EXPORT_SYMBOL_GPL(mount_mtd); +EXPORT_SYMBOL_GPL(get_tree_mtd); /* * destroy an MTD-based superblock diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index f92414eb4c86..58eefa43af14 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1257,7 +1257,6 @@ DEFINE_SHOW_ATTRIBUTE(mtdswap); static int mtdswap_add_debugfs(struct mtdswap_dev *d) { struct dentry *root = d->mtd->dbg.dfs_dir; - struct dentry *dent; if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; @@ -1265,12 +1264,7 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d) if (IS_ERR_OR_NULL(root)) return -1; - dent = debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, - &mtdswap_fops); - if (!dent) { - dev_err(d->dev, "debugfs_create_file failed\n"); - return -1; - } + debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops); return 0; } diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index d759c02d9cb2..77bd32a683e1 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3257,6 +3257,9 @@ static void onenand_check_features(struct mtd_info *mtd) /* Lock scheme */ switch (density) { + case ONENAND_DEVICE_DENSITY_8Gb: + this->options |= ONENAND_HAS_NOP_1; + /* fall through */ case ONENAND_DEVICE_DENSITY_4Gb: if (ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; @@ -3277,12 +3280,15 @@ static void onenand_check_features(struct mtd_info *mtd) if ((this->version_id & 0xf) == 0xe) this->options |= ONENAND_HAS_NOP_1; } + this->options |= ONENAND_HAS_UNLOCK_ALL; + break; case ONENAND_DEVICE_DENSITY_2Gb: /* 2Gb DDP does not have 2 plane */ if (!ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_UNLOCK_ALL; + break; case ONENAND_DEVICE_DENSITY_1Gb: /* A-Die has all block unlock */ @@ -3874,6 +3880,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->oob_buf) { if (this->options & ONENAND_PAGEBUF_ALLOC) { this->options &= ~ONENAND_PAGEBUF_ALLOC; +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif kfree(this->page_buf); } return -ENOMEM; diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 5a711d8beaca..74fb91adeb46 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -351,14 +351,6 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. -config MTD_NAND_NUC900 - tristate "Nuvoton NUC9xx/w90p910 NAND controller" - depends on ARCH_W90X900 || COMPILE_TEST - depends on HAS_IOMEM - help - This enables the driver for the NAND Flash on evaluation board based - on w90p910 / NUC9xx. - source "drivers/mtd/nand/raw/ingenic/Kconfig" config MTD_NAND_FSMC @@ -407,6 +399,12 @@ config MTD_NAND_MTK Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. +config MTD_NAND_MXIC + tristate "Macronix raw NAND controller" + depends on HAS_IOMEM || COMPILE_TEST + help + This selects the Macronix raw NAND controller driver. + config MTD_NAND_TEGRA tristate "NVIDIA Tegra NAND controller" depends on ARCH_TEGRA || COMPILE_TEST @@ -452,6 +450,13 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_NAND_CADENCE + tristate "Support Cadence NAND (HPNFC) controller" + depends on OF || COMPILE_TEST + help + Enable the driver for NAND flash on platforms using a Cadence NAND + controller. + comment "Misc" config MTD_SM_COMMON diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index efaf5cd25edc..2d136b158fb7 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o @@ -54,9 +53,11 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o +obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o +obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_onfi.o diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 97a97a9ccc36..e10b76089048 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -134,16 +134,15 @@ static void au_write_buf16(struct nand_chip *this, const u_char *buf, int len) /** * au_read_buf16 - read chip data into buffer - * @mtd: MTD device structure + * @this: NAND chip object * @buf: buffer to store date * @len: number of bytes to read * * read function for 16bit buswidth */ -static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void au_read_buf16(struct nand_chip *this, u_char *buf, int len) { int i; - struct nand_chip *this = mtd_to_nand(mtd); u16 *p = (u16 *) buf; len >>= 1; diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 873527753f52..1a66b1cd51c0 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -84,6 +84,12 @@ struct brcm_nand_dma_desc { #define FLASH_DMA_ECC_ERROR (1 << 8) #define FLASH_DMA_CORR_ERROR (1 << 9) +/* Bitfields for DMA_MODE */ +#define FLASH_DMA_MODE_STOP_ON_ERROR BIT(1) /* stop in Uncorr ECC error */ +#define FLASH_DMA_MODE_MODE BIT(0) /* link list */ +#define FLASH_DMA_MODE_MASK (FLASH_DMA_MODE_STOP_ON_ERROR | \ + FLASH_DMA_MODE_MODE) + /* 512B flash cache in the NAND controller HW */ #define FC_SHIFT 9U #define FC_BYTES 512U @@ -96,6 +102,63 @@ struct brcm_nand_dma_desc { #define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) #define NAND_POLL_STATUS_TIMEOUT_MS 100 +/* flash_dma registers */ +enum flash_dma_reg { + FLASH_DMA_REVISION = 0, + FLASH_DMA_FIRST_DESC, + FLASH_DMA_FIRST_DESC_EXT, + FLASH_DMA_CTRL, + FLASH_DMA_MODE, + FLASH_DMA_STATUS, + FLASH_DMA_INTERRUPT_DESC, + FLASH_DMA_INTERRUPT_DESC_EXT, + FLASH_DMA_ERROR_STATUS, + FLASH_DMA_CURRENT_DESC, + FLASH_DMA_CURRENT_DESC_EXT, +}; + +/* flash_dma registers v0*/ +static const u16 flash_dma_regs_v0[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x04, + [FLASH_DMA_CTRL] = 0x08, + [FLASH_DMA_MODE] = 0x0c, + [FLASH_DMA_STATUS] = 0x10, + [FLASH_DMA_INTERRUPT_DESC] = 0x14, + [FLASH_DMA_ERROR_STATUS] = 0x18, + [FLASH_DMA_CURRENT_DESC] = 0x1c, +}; + +/* flash_dma registers v1*/ +static const u16 flash_dma_regs_v1[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x04, + [FLASH_DMA_FIRST_DESC_EXT] = 0x08, + [FLASH_DMA_CTRL] = 0x0c, + [FLASH_DMA_MODE] = 0x10, + [FLASH_DMA_STATUS] = 0x14, + [FLASH_DMA_INTERRUPT_DESC] = 0x18, + [FLASH_DMA_INTERRUPT_DESC_EXT] = 0x1c, + [FLASH_DMA_ERROR_STATUS] = 0x20, + [FLASH_DMA_CURRENT_DESC] = 0x24, + [FLASH_DMA_CURRENT_DESC_EXT] = 0x28, +}; + +/* flash_dma registers v4 */ +static const u16 flash_dma_regs_v4[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x08, + [FLASH_DMA_FIRST_DESC_EXT] = 0x0c, + [FLASH_DMA_CTRL] = 0x10, + [FLASH_DMA_MODE] = 0x14, + [FLASH_DMA_STATUS] = 0x18, + [FLASH_DMA_INTERRUPT_DESC] = 0x20, + [FLASH_DMA_INTERRUPT_DESC_EXT] = 0x24, + [FLASH_DMA_ERROR_STATUS] = 0x28, + [FLASH_DMA_CURRENT_DESC] = 0x30, + [FLASH_DMA_CURRENT_DESC_EXT] = 0x34, +}; + /* Controller feature flags */ enum { BRCMNAND_HAS_1K_SECTORS = BIT(0), @@ -128,6 +191,8 @@ struct brcmnand_controller { /* List of NAND hosts (one for each chip-select) */ struct list_head host_list; + /* flash_dma reg */ + const u16 *flash_dma_offsets; struct brcm_nand_dma_desc *dma_desc; dma_addr_t dma_pa; @@ -151,6 +216,7 @@ struct brcmnand_controller { u32 nand_cs_nand_xor; u32 corr_stat_threshold; u32 flash_dma_mode; + bool pio_poll_mode; }; struct brcmnand_cfg { @@ -462,7 +528,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) /* Register offsets */ if (ctrl->nand_version >= 0x0702) ctrl->reg_offsets = brcmnand_regs_v72; - else if (ctrl->nand_version >= 0x0701) + else if (ctrl->nand_version == 0x0701) ctrl->reg_offsets = brcmnand_regs_v71; else if (ctrl->nand_version >= 0x0600) ctrl->reg_offsets = brcmnand_regs_v60; @@ -507,7 +573,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) } /* Maximum spare area sector size (per 512B) */ - if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) ctrl->max_oob = 128; else if (ctrl->nand_version >= 0x0600) ctrl->max_oob = 64; @@ -538,6 +604,17 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) return 0; } +static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl) +{ + /* flash_dma register offsets */ + if (ctrl->nand_version >= 0x0703) + ctrl->flash_dma_offsets = flash_dma_regs_v4; + else if (ctrl->nand_version == 0x0602) + ctrl->flash_dma_offsets = flash_dma_regs_v0; + else + ctrl->flash_dma_offsets = flash_dma_regs_v1; +} + static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, enum brcmnand_reg reg) { @@ -580,6 +657,54 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, __raw_writel(val, ctrl->nand_fc + word * 4); } +static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl) +{ + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); +} + +static u64 brcmnand_get_uncorrecc_addr(struct brcmnand_controller *ctrl) +{ + u64 err_addr; + + err_addr = brcmnand_read_reg(ctrl, BRCMNAND_UNCORR_ADDR); + err_addr |= ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + + return err_addr; +} + +static u64 brcmnand_get_correcc_addr(struct brcmnand_controller *ctrl) +{ + u64 err_addr; + + err_addr = brcmnand_read_reg(ctrl, BRCMNAND_CORR_ADDR); + err_addr |= ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + + return err_addr; +} + +static void brcmnand_set_cmd_addr(struct mtd_info *mtd, u64 addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); +} + static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, enum brcmnand_cs_reg reg) { @@ -612,7 +737,7 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; int cs = host->cs; - if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) bits = 7; else if (ctrl->nand_version >= 0x0600) bits = 6; @@ -666,7 +791,7 @@ enum { static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) { - if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) return GENMASK(7, 0); else if (ctrl->nand_version >= 0x0600) return GENMASK(6, 0); @@ -796,39 +921,44 @@ static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) * Flash DMA ***********************************************************************/ -enum flash_dma_reg { - FLASH_DMA_REVISION = 0x00, - FLASH_DMA_FIRST_DESC = 0x04, - FLASH_DMA_FIRST_DESC_EXT = 0x08, - FLASH_DMA_CTRL = 0x0c, - FLASH_DMA_MODE = 0x10, - FLASH_DMA_STATUS = 0x14, - FLASH_DMA_INTERRUPT_DESC = 0x18, - FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, - FLASH_DMA_ERROR_STATUS = 0x20, - FLASH_DMA_CURRENT_DESC = 0x24, - FLASH_DMA_CURRENT_DESC_EXT = 0x28, -}; - static inline bool has_flash_dma(struct brcmnand_controller *ctrl) { return ctrl->flash_dma_base; } +static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl) +{ + if (ctrl->pio_poll_mode) + return; + + if (has_flash_dma(ctrl)) { + ctrl->flash_dma_base = NULL; + disable_irq(ctrl->dma_irq); + } + + disable_irq(ctrl->irq); + ctrl->pio_poll_mode = true; +} + static inline bool flash_dma_buf_ok(const void *buf) { return buf && !is_vmalloc_addr(buf) && likely(IS_ALIGNED((uintptr_t)buf, 4)); } -static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, - u32 val) +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, + enum flash_dma_reg dma_reg, u32 val) { + u16 offs = ctrl->flash_dma_offsets[dma_reg]; + brcmnand_writel(val, ctrl->flash_dma_base + offs); } -static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, + enum flash_dma_reg dma_reg) { + u16 offs = ctrl->flash_dma_offsets[dma_reg]; + return brcmnand_readl(ctrl->flash_dma_base + offs); } @@ -931,7 +1061,7 @@ static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, if (section >= sectors) return -ERANGE; - oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->offset = ((section + 1) * sas) - chip->ecc.bytes; oobregion->length = chip->ecc.bytes; return 0; @@ -1205,9 +1335,12 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) { struct brcmnand_controller *ctrl = host->ctrl; int ret; + u64 cmd_addr; + + cmd_addr = brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + dev_dbg(ctrl->dev, "send native cmd %d addr 0x%llx\n", cmd, cmd_addr); - dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, - brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); BUG_ON(ctrl->cmd_pending != 0); ctrl->cmd_pending = cmd; @@ -1229,15 +1362,42 @@ static void brcmnand_cmd_ctrl(struct nand_chip *chip, int dat, /* intentionally left blank */ } +static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + struct mtd_info *mtd = nand_to_mtd(chip); + bool err = false; + int sts; + + if (mtd->oops_panic_write) { + /* switch to interrupt polling and PIO mode */ + disable_ctrl_irqs(ctrl); + sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, + NAND_CTRL_RDY, 0); + err = (sts < 0) ? true : false; + } else { + unsigned long timeo = msecs_to_jiffies( + NAND_POLL_STATUS_TIMEOUT_MS); + /* wait for completion interrupt */ + sts = wait_for_completion_timeout(&ctrl->done, timeo); + err = (sts <= 0) ? true : false; + } + + return err; +} + static int brcmnand_waitfunc(struct nand_chip *chip) { struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_controller *ctrl = host->ctrl; - unsigned long timeo = msecs_to_jiffies(100); + bool err = false; dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); - if (ctrl->cmd_pending && - wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { + if (ctrl->cmd_pending) + err = brcmstb_nand_wait_for_completion(chip); + + if (err) { u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) >> brcmnand_cmd_shift(ctrl); @@ -1366,12 +1526,7 @@ static void brcmnand_cmdfunc(struct nand_chip *chip, unsigned command, if (!native_cmd) return; - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - + brcmnand_set_cmd_addr(mtd, addr); brcmnand_send_cmd(host, native_cmd); brcmnand_waitfunc(chip); @@ -1532,8 +1687,11 @@ static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); - flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); - (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + if (ctrl->nand_version > 0x0602) { + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, + upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + } /* Start FLASH_DMA engine */ ctrl->dma_pending = true; @@ -1589,20 +1747,10 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, struct brcmnand_controller *ctrl = host->ctrl; int i, j, ret = 0; - /* Clear error addresses */ - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); - - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_clear_ecc_addr(ctrl); for (i = 0; i < trans; i++, addr += FC_BYTES) { - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + brcmnand_set_cmd_addr(mtd, addr); /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ brcmnand_send_cmd(host, CMD_PAGE_READ); brcmnand_waitfunc(chip); @@ -1622,21 +1770,15 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, host->hwcfg.sector_size_1k); if (!ret) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_EXT_ADDR) - & 0xffff) << 32); + *err_addr = brcmnand_get_uncorrecc_addr(ctrl); + if (*err_addr) ret = -EBADMSG; } if (!ret) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_CORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_CORR_EXT_ADDR) - & 0xffff) << 32); + *err_addr = brcmnand_get_correcc_addr(ctrl); + if (*err_addr) ret = -EUCLEAN; } @@ -1667,6 +1809,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, int bitflips = 0; int page = addr >> chip->page_shift; int ret; + void *ecc_chunk; if (!buf) buf = nand_get_data_buf(chip); @@ -1679,7 +1822,9 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, return ret; for (i = 0; i < chip->ecc.steps; i++, oob += sas) { - ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, + ecc_chunk = buf + chip->ecc.size * i; + ret = nand_check_erased_ecc_chunk(ecc_chunk, + chip->ecc.size, oob, sas, NULL, 0, chip->ecc.strength); if (ret < 0) @@ -1703,7 +1848,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); try_dmaread: - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + brcmnand_clear_ecc_addr(ctrl); if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, @@ -1850,15 +1995,9 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, goto out; } - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - for (i = 0; i < trans; i++, addr += FC_BYTES) { /* full address MUST be set before populating FC */ - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + brcmnand_set_cmd_addr(mtd, addr); if (buf) { brcmnand_soc_data_bus_prepare(ctrl->soc, false); @@ -2136,6 +2275,17 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) return -EINVAL; } + if (chip->ecc.mode != NAND_ECC_NONE && + (!chip->ecc.size || !chip->ecc.strength)) { + if (chip->base.eccreq.step_size && chip->base.eccreq.strength) { + /* use detected ECC parameters */ + chip->ecc.size = chip->base.eccreq.step_size; + chip->ecc.strength = chip->base.eccreq.strength; + dev_info(ctrl->dev, "Using ECC step-size %d, strength %d\n", + chip->ecc.size, chip->ecc.strength); + } + } + switch (chip->ecc.size) { case 512: if (chip->ecc.algo == NAND_ECC_HAMMING) @@ -2395,6 +2545,7 @@ static const struct of_device_id brcmnand_of_match[] = { { .compatible = "brcm,brcmnand-v7.0" }, { .compatible = "brcm,brcmnand-v7.1" }, { .compatible = "brcm,brcmnand-v7.2" }, + { .compatible = "brcm,brcmnand-v7.3" }, {}, }; MODULE_DEVICE_TABLE(of, brcmnand_of_match); @@ -2481,7 +2632,11 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) goto err; } - flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + /* initialize the dma version */ + brcmnand_flash_dma_revision_init(ctrl); + + /* linked-list and stop on error */ + flash_dma_writel(ctrl, FLASH_DMA_MODE, FLASH_DMA_MODE_MASK); flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); /* Allocate descriptor(s) */ diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c new file mode 100644 index 000000000000..3a36285a8d8a --- /dev/null +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -0,0 +1,3030 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cadence NAND flash controller driver + * + * Copyright (C) 2019 Cadence + * + * Author: Piotr Sroka <piotrs@cadence.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> +#include <linux/of_device.h> +#include <linux/iopoll.h> + +/* + * HPNFC can work in 3 modes: + * - PIO - can work in master or slave DMA + * - CDMA - needs Master DMA for accessing command descriptors. + * - Generic mode - can use only slave DMA. + * CDMA and PIO modes can be used to execute only base commands. + * Generic mode can be used to execute any command + * on NAND flash memory. Driver uses CDMA mode for + * block erasing, page reading, page programing. + * Generic mode is used for executing rest of commands. + */ + +#define MAX_OOB_SIZE_PER_SECTOR 32 +#define MAX_ADDRESS_CYC 6 +#define MAX_ERASE_ADDRESS_CYC 3 +#define MAX_DATA_SIZE 0xFFFC +#define DMA_DATA_SIZE_ALIGN 8 + +/* Register definition. */ +/* + * Command register 0. + * Writing data to this register will initiate a new transaction + * of the NF controller. + */ +#define CMD_REG0 0x0000 +/* Command type field mask. */ +#define CMD_REG0_CT GENMASK(31, 30) +/* Command type CDMA. */ +#define CMD_REG0_CT_CDMA 0uL +/* Command type generic. */ +#define CMD_REG0_CT_GEN 3uL +/* Command thread number field mask. */ +#define CMD_REG0_TN GENMASK(27, 24) + +/* Command register 2. */ +#define CMD_REG2 0x0008 +/* Command register 3. */ +#define CMD_REG3 0x000C +/* Pointer register to select which thread status will be selected. */ +#define CMD_STATUS_PTR 0x0010 +/* Command status register for selected thread. */ +#define CMD_STATUS 0x0014 + +/* Interrupt status register. */ +#define INTR_STATUS 0x0110 +#define INTR_STATUS_SDMA_ERR BIT(22) +#define INTR_STATUS_SDMA_TRIGG BIT(21) +#define INTR_STATUS_UNSUPP_CMD BIT(19) +#define INTR_STATUS_DDMA_TERR BIT(18) +#define INTR_STATUS_CDMA_TERR BIT(17) +#define INTR_STATUS_CDMA_IDL BIT(16) + +/* Interrupt enable register. */ +#define INTR_ENABLE 0x0114 +#define INTR_ENABLE_INTR_EN BIT(31) +#define INTR_ENABLE_SDMA_ERR_EN BIT(22) +#define INTR_ENABLE_SDMA_TRIGG_EN BIT(21) +#define INTR_ENABLE_UNSUPP_CMD_EN BIT(19) +#define INTR_ENABLE_DDMA_TERR_EN BIT(18) +#define INTR_ENABLE_CDMA_TERR_EN BIT(17) +#define INTR_ENABLE_CDMA_IDLE_EN BIT(16) + +/* Controller internal state. */ +#define CTRL_STATUS 0x0118 +#define CTRL_STATUS_INIT_COMP BIT(9) +#define CTRL_STATUS_CTRL_BUSY BIT(8) + +/* Command Engine threads state. */ +#define TRD_STATUS 0x0120 + +/* Command Engine interrupt thread error status. */ +#define TRD_ERR_INT_STATUS 0x0128 +/* Command Engine interrupt thread error enable. */ +#define TRD_ERR_INT_STATUS_EN 0x0130 +/* Command Engine interrupt thread complete status. */ +#define TRD_COMP_INT_STATUS 0x0138 + +/* + * Transfer config 0 register. + * Configures data transfer parameters. + */ +#define TRAN_CFG_0 0x0400 +/* Offset value from the beginning of the page. */ +#define TRAN_CFG_0_OFFSET GENMASK(31, 16) +/* Numbers of sectors to transfer within singlNF device's page. */ +#define TRAN_CFG_0_SEC_CNT GENMASK(7, 0) + +/* + * Transfer config 1 register. + * Configures data transfer parameters. + */ +#define TRAN_CFG_1 0x0404 +/* Size of last data sector. */ +#define TRAN_CFG_1_LAST_SEC_SIZE GENMASK(31, 16) +/* Size of not-last data sector. */ +#define TRAN_CFG_1_SECTOR_SIZE GENMASK(15, 0) + +/* ECC engine configuration register 0. */ +#define ECC_CONFIG_0 0x0428 +/* Correction strength. */ +#define ECC_CONFIG_0_CORR_STR GENMASK(10, 8) +/* Enable erased pages detection mechanism. */ +#define ECC_CONFIG_0_ERASE_DET_EN BIT(1) +/* Enable controller ECC check bits generation and correction. */ +#define ECC_CONFIG_0_ECC_EN BIT(0) + +/* ECC engine configuration register 1. */ +#define ECC_CONFIG_1 0x042C + +/* Multiplane settings register. */ +#define MULTIPLANE_CFG 0x0434 +/* Cache operation settings. */ +#define CACHE_CFG 0x0438 + +/* DMA settings register. */ +#define DMA_SETINGS 0x043C +/* Enable SDMA error report on access unprepared slave DMA interface. */ +#define DMA_SETINGS_SDMA_ERR_RSP BIT(17) + +/* Transferred data block size for the slave DMA module. */ +#define SDMA_SIZE 0x0440 + +/* Thread number associated with transferred data block + * for the slave DMA module. + */ +#define SDMA_TRD_NUM 0x0444 +/* Thread number mask. */ +#define SDMA_TRD_NUM_SDMA_TRD GENMASK(2, 0) + +#define CONTROL_DATA_CTRL 0x0494 +/* Thread number mask. */ +#define CONTROL_DATA_CTRL_SIZE GENMASK(15, 0) + +#define CTRL_VERSION 0x800 +#define CTRL_VERSION_REV GENMASK(7, 0) + +/* Available hardware features of the controller. */ +#define CTRL_FEATURES 0x804 +/* Support for NV-DDR2/3 work mode. */ +#define CTRL_FEATURES_NVDDR_2_3 BIT(28) +/* Support for NV-DDR work mode. */ +#define CTRL_FEATURES_NVDDR BIT(27) +/* Support for asynchronous work mode. */ +#define CTRL_FEATURES_ASYNC BIT(26) +/* Support for asynchronous work mode. */ +#define CTRL_FEATURES_N_BANKS GENMASK(25, 24) +/* Slave and Master DMA data width. */ +#define CTRL_FEATURES_DMA_DWITH64 BIT(21) +/* Availability of Control Data feature.*/ +#define CTRL_FEATURES_CONTROL_DATA BIT(10) + +/* BCH Engine identification register 0 - correction strengths. */ +#define BCH_CFG_0 0x838 +#define BCH_CFG_0_CORR_CAP_0 GENMASK(7, 0) +#define BCH_CFG_0_CORR_CAP_1 GENMASK(15, 8) +#define BCH_CFG_0_CORR_CAP_2 GENMASK(23, 16) +#define BCH_CFG_0_CORR_CAP_3 GENMASK(31, 24) + +/* BCH Engine identification register 1 - correction strengths. */ +#define BCH_CFG_1 0x83C +#define BCH_CFG_1_CORR_CAP_4 GENMASK(7, 0) +#define BCH_CFG_1_CORR_CAP_5 GENMASK(15, 8) +#define BCH_CFG_1_CORR_CAP_6 GENMASK(23, 16) +#define BCH_CFG_1_CORR_CAP_7 GENMASK(31, 24) + +/* BCH Engine identification register 2 - sector sizes. */ +#define BCH_CFG_2 0x840 +#define BCH_CFG_2_SECT_0 GENMASK(15, 0) +#define BCH_CFG_2_SECT_1 GENMASK(31, 16) + +/* BCH Engine identification register 3. */ +#define BCH_CFG_3 0x844 + +/* Ready/Busy# line status. */ +#define RBN_SETINGS 0x1004 + +/* Common settings. */ +#define COMMON_SET 0x1008 +/* 16 bit device connected to the NAND Flash interface. */ +#define COMMON_SET_DEVICE_16BIT BIT(8) + +/* Skip_bytes registers. */ +#define SKIP_BYTES_CONF 0x100C +#define SKIP_BYTES_MARKER_VALUE GENMASK(31, 16) +#define SKIP_BYTES_NUM_OF_BYTES GENMASK(7, 0) + +#define SKIP_BYTES_OFFSET 0x1010 +#define SKIP_BYTES_OFFSET_VALUE GENMASK(23, 0) + +/* Timings configuration. */ +#define ASYNC_TOGGLE_TIMINGS 0x101c +#define ASYNC_TOGGLE_TIMINGS_TRH GENMASK(28, 24) +#define ASYNC_TOGGLE_TIMINGS_TRP GENMASK(20, 16) +#define ASYNC_TOGGLE_TIMINGS_TWH GENMASK(12, 8) +#define ASYNC_TOGGLE_TIMINGS_TWP GENMASK(4, 0) + +#define TIMINGS0 0x1024 +#define TIMINGS0_TADL GENMASK(31, 24) +#define TIMINGS0_TCCS GENMASK(23, 16) +#define TIMINGS0_TWHR GENMASK(15, 8) +#define TIMINGS0_TRHW GENMASK(7, 0) + +#define TIMINGS1 0x1028 +#define TIMINGS1_TRHZ GENMASK(31, 24) +#define TIMINGS1_TWB GENMASK(23, 16) +#define TIMINGS1_TVDLY GENMASK(7, 0) + +#define TIMINGS2 0x102c +#define TIMINGS2_TFEAT GENMASK(25, 16) +#define TIMINGS2_CS_HOLD_TIME GENMASK(13, 8) +#define TIMINGS2_CS_SETUP_TIME GENMASK(5, 0) + +/* Configuration of the resynchronization of slave DLL of PHY. */ +#define DLL_PHY_CTRL 0x1034 +#define DLL_PHY_CTRL_DLL_RST_N BIT(24) +#define DLL_PHY_CTRL_EXTENDED_WR_MODE BIT(17) +#define DLL_PHY_CTRL_EXTENDED_RD_MODE BIT(16) +#define DLL_PHY_CTRL_RS_HIGH_WAIT_CNT GENMASK(11, 8) +#define DLL_PHY_CTRL_RS_IDLE_CNT GENMASK(7, 0) + +/* Register controlling DQ related timing. */ +#define PHY_DQ_TIMING 0x2000 +/* Register controlling DSQ related timing. */ +#define PHY_DQS_TIMING 0x2004 +#define PHY_DQS_TIMING_DQS_SEL_OE_END GENMASK(3, 0) +#define PHY_DQS_TIMING_PHONY_DQS_SEL BIT(16) +#define PHY_DQS_TIMING_USE_PHONY_DQS BIT(20) + +/* Register controlling the gate and loopback control related timing. */ +#define PHY_GATE_LPBK_CTRL 0x2008 +#define PHY_GATE_LPBK_CTRL_RDS GENMASK(24, 19) + +/* Register holds the control for the master DLL logic. */ +#define PHY_DLL_MASTER_CTRL 0x200C +#define PHY_DLL_MASTER_CTRL_BYPASS_MODE BIT(23) + +/* Register holds the control for the slave DLL logic. */ +#define PHY_DLL_SLAVE_CTRL 0x2010 + +/* This register handles the global control settings for the PHY. */ +#define PHY_CTRL 0x2080 +#define PHY_CTRL_SDR_DQS BIT(14) +#define PHY_CTRL_PHONY_DQS GENMASK(9, 4) + +/* + * This register handles the global control settings + * for the termination selects for reads. + */ +#define PHY_TSEL 0x2084 + +/* Generic command layout. */ +#define GCMD_LAY_CS GENMASK_ULL(11, 8) +/* + * This bit informs the minicotroller if it has to wait for tWB + * after sending the last CMD/ADDR/DATA in the sequence. + */ +#define GCMD_LAY_TWB BIT_ULL(6) +/* Type of generic instruction. */ +#define GCMD_LAY_INSTR GENMASK_ULL(5, 0) + +/* Generic CMD sequence type. */ +#define GCMD_LAY_INSTR_CMD 0 +/* Generic ADDR sequence type. */ +#define GCMD_LAY_INSTR_ADDR 1 +/* Generic data transfer sequence type. */ +#define GCMD_LAY_INSTR_DATA 2 + +/* Input part of generic command type of input is command. */ +#define GCMD_LAY_INPUT_CMD GENMASK_ULL(23, 16) + +/* Generic command address sequence - address fields. */ +#define GCMD_LAY_INPUT_ADDR GENMASK_ULL(63, 16) +/* Generic command address sequence - address size. */ +#define GCMD_LAY_INPUT_ADDR_SIZE GENMASK_ULL(13, 11) + +/* Transfer direction field of generic command data sequence. */ +#define GCMD_DIR BIT_ULL(11) +/* Read transfer direction of generic command data sequence. */ +#define GCMD_DIR_READ 0 +/* Write transfer direction of generic command data sequence. */ +#define GCMD_DIR_WRITE 1 + +/* ECC enabled flag of generic command data sequence - ECC enabled. */ +#define GCMD_ECC_EN BIT_ULL(12) +/* Generic command data sequence - sector size. */ +#define GCMD_SECT_SIZE GENMASK_ULL(31, 16) +/* Generic command data sequence - sector count. */ +#define GCMD_SECT_CNT GENMASK_ULL(39, 32) +/* Generic command data sequence - last sector size. */ +#define GCMD_LAST_SIZE GENMASK_ULL(55, 40) + +/* CDMA descriptor fields. */ +/* Erase command type of CDMA descriptor. */ +#define CDMA_CT_ERASE 0x1000 +/* Program page command type of CDMA descriptor. */ +#define CDMA_CT_WR 0x2100 +/* Read page command type of CDMA descriptor. */ +#define CDMA_CT_RD 0x2200 + +/* Flash pointer memory shift. */ +#define CDMA_CFPTR_MEM_SHIFT 24 +/* Flash pointer memory mask. */ +#define CDMA_CFPTR_MEM GENMASK(26, 24) + +/* + * Command DMA descriptor flags. If set causes issue interrupt after + * the completion of descriptor processing. + */ +#define CDMA_CF_INT BIT(8) +/* + * Command DMA descriptor flags - the next descriptor + * address field is valid and descriptor processing should continue. + */ +#define CDMA_CF_CONT BIT(9) +/* DMA master flag of command DMA descriptor. */ +#define CDMA_CF_DMA_MASTER BIT(10) + +/* Operation complete status of command descriptor. */ +#define CDMA_CS_COMP BIT(15) +/* Operation complete status of command descriptor. */ +/* Command descriptor status - operation fail. */ +#define CDMA_CS_FAIL BIT(14) +/* Command descriptor status - page erased. */ +#define CDMA_CS_ERP BIT(11) +/* Command descriptor status - timeout occurred. */ +#define CDMA_CS_TOUT BIT(10) +/* + * Maximum amount of correction applied to one ECC sector. + * It is part of command descriptor status. + */ +#define CDMA_CS_MAXERR GENMASK(9, 2) +/* Command descriptor status - uncorrectable ECC error. */ +#define CDMA_CS_UNCE BIT(1) +/* Command descriptor status - descriptor error. */ +#define CDMA_CS_ERR BIT(0) + +/* Status of operation - OK. */ +#define STAT_OK 0 +/* Status of operation - FAIL. */ +#define STAT_FAIL 2 +/* Status of operation - uncorrectable ECC error. */ +#define STAT_ECC_UNCORR 3 +/* Status of operation - page erased. */ +#define STAT_ERASED 5 +/* Status of operation - correctable ECC error. */ +#define STAT_ECC_CORR 6 +/* Status of operation - unsuspected state. */ +#define STAT_UNKNOWN 7 +/* Status of operation - operation is not completed yet. */ +#define STAT_BUSY 0xFF + +#define BCH_MAX_NUM_CORR_CAPS 8 +#define BCH_MAX_NUM_SECTOR_SIZES 2 + +struct cadence_nand_timings { + u32 async_toggle_timings; + u32 timings0; + u32 timings1; + u32 timings2; + u32 dll_phy_ctrl; + u32 phy_ctrl; + u32 phy_dqs_timing; + u32 phy_gate_lpbk_ctrl; +}; + +/* Command DMA descriptor. */ +struct cadence_nand_cdma_desc { + /* Next descriptor address. */ + u64 next_pointer; + + /* Flash address is a 32-bit address comprising of BANK and ROW ADDR. */ + u32 flash_pointer; + /*field appears in HPNFC version 13*/ + u16 bank; + u16 rsvd0; + + /* Operation the controller needs to perform. */ + u16 command_type; + u16 rsvd1; + /* Flags for operation of this command. */ + u16 command_flags; + u16 rsvd2; + + /* System/host memory address required for data DMA commands. */ + u64 memory_pointer; + + /* Status of operation. */ + u32 status; + u32 rsvd3; + + /* Address pointer to sync buffer location. */ + u64 sync_flag_pointer; + + /* Controls the buffer sync mechanism. */ + u32 sync_arguments; + u32 rsvd4; + + /* Control data pointer. */ + u64 ctrl_data_ptr; +}; + +/* Interrupt status. */ +struct cadence_nand_irq_status { + /* Thread operation complete status. */ + u32 trd_status; + /* Thread operation error. */ + u32 trd_error; + /* Controller status. */ + u32 status; +}; + +/* Cadence NAND flash controller capabilities get from driver data. */ +struct cadence_nand_dt_devdata { + /* Skew value of the output signals of the NAND Flash interface. */ + u32 if_skew; + /* It informs if slave DMA interface is connected to DMA engine. */ + unsigned int has_dma:1; +}; + +/* Cadence NAND flash controller capabilities read from registers. */ +struct cdns_nand_caps { + /* Maximum number of banks supported by hardware. */ + u8 max_banks; + /* Slave and Master DMA data width in bytes (4 or 8). */ + u8 data_dma_width; + /* Control Data feature supported. */ + bool data_control_supp; + /* Is PHY type DLL. */ + bool is_phy_type_dll; +}; + +struct cdns_nand_ctrl { + struct device *dev; + struct nand_controller controller; + struct cadence_nand_cdma_desc *cdma_desc; + /* IP capability. */ + const struct cadence_nand_dt_devdata *caps1; + struct cdns_nand_caps caps2; + u8 ctrl_rev; + dma_addr_t dma_cdma_desc; + u8 *buf; + u32 buf_size; + u8 curr_corr_str_idx; + + /* Register interface. */ + void __iomem *reg; + + struct { + void __iomem *virt; + dma_addr_t dma; + } io; + + int irq; + /* Interrupts that have happened. */ + struct cadence_nand_irq_status irq_status; + /* Interrupts we are waiting for. */ + struct cadence_nand_irq_status irq_mask; + struct completion complete; + /* Protect irq_mask and irq_status. */ + spinlock_t irq_lock; + + int ecc_strengths[BCH_MAX_NUM_CORR_CAPS]; + struct nand_ecc_step_info ecc_stepinfos[BCH_MAX_NUM_SECTOR_SIZES]; + struct nand_ecc_caps ecc_caps; + + int curr_trans_type; + + struct dma_chan *dmac; + + u32 nf_clk_rate; + /* + * Estimated Board delay. The value includes the total + * round trip delay for the signals and is used for deciding on values + * associated with data read capture. + */ + u32 board_delay; + + struct nand_chip *selected_chip; + + unsigned long assigned_cs; + struct list_head chips; +}; + +struct cdns_nand_chip { + struct cadence_nand_timings timings; + struct nand_chip chip; + u8 nsels; + struct list_head node; + + /* + * part of oob area of NAND flash memory page. + * This part is available for user to read or write. + */ + u32 avail_oob_size; + + /* Sector size. There are few sectors per mtd->writesize */ + u32 sector_size; + u32 sector_count; + + /* Offset of BBM. */ + u8 bbm_offs; + /* Number of bytes reserved for BBM. */ + u8 bbm_len; + /* ECC strength index. */ + u8 corr_str_idx; + + u8 cs[]; +}; + +struct ecc_info { + int (*calc_ecc_bytes)(int step_size, int strength); + int max_step_size; +}; + +static inline struct +cdns_nand_chip *to_cdns_nand_chip(struct nand_chip *chip) +{ + return container_of(chip, struct cdns_nand_chip, chip); +} + +static inline struct +cdns_nand_ctrl *to_cdns_nand_ctrl(struct nand_controller *controller) +{ + return container_of(controller, struct cdns_nand_ctrl, controller); +} + +static bool +cadence_nand_dma_buf_ok(struct cdns_nand_ctrl *cdns_ctrl, const void *buf, + u32 buf_len) +{ + u8 data_dma_width = cdns_ctrl->caps2.data_dma_width; + + return buf && virt_addr_valid(buf) && + likely(IS_ALIGNED((uintptr_t)buf, data_dma_width)) && + likely(IS_ALIGNED(buf_len, DMA_DATA_SIZE_ALIGN)); +} + +static int cadence_nand_wait_for_value(struct cdns_nand_ctrl *cdns_ctrl, + u32 reg_offset, u32 timeout_us, + u32 mask, bool is_clear) +{ + u32 val; + int ret; + + ret = readl_relaxed_poll_timeout(cdns_ctrl->reg + reg_offset, + val, !(val & mask) == is_clear, + 10, timeout_us); + + if (ret < 0) { + dev_err(cdns_ctrl->dev, + "Timeout while waiting for reg %x with mask %x is clear %d\n", + reg_offset, mask, is_clear); + } + + return ret; +} + +static int cadence_nand_set_ecc_enable(struct cdns_nand_ctrl *cdns_ctrl, + bool enable) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ECC_EN; + else + reg &= ~ECC_CONFIG_0_ECC_EN; + + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + + return 0; +} + +static void cadence_nand_set_ecc_strength(struct cdns_nand_ctrl *cdns_ctrl, + u8 corr_str_idx) +{ + u32 reg; + + if (cdns_ctrl->curr_corr_str_idx == corr_str_idx) + return; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + reg &= ~ECC_CONFIG_0_CORR_STR; + reg |= FIELD_PREP(ECC_CONFIG_0_CORR_STR, corr_str_idx); + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + + cdns_ctrl->curr_corr_str_idx = corr_str_idx; +} + +static int cadence_nand_get_ecc_strength_idx(struct cdns_nand_ctrl *cdns_ctrl, + u8 strength) +{ + int i, corr_str_idx = -1; + + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cdns_ctrl->ecc_strengths[i] == strength) { + corr_str_idx = i; + break; + } + } + + return corr_str_idx; +} + +static int cadence_nand_set_skip_marker_val(struct cdns_nand_ctrl *cdns_ctrl, + u16 marker_value) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_MARKER_VALUE; + reg |= FIELD_PREP(SKIP_BYTES_MARKER_VALUE, + marker_value); + + writel_relaxed(reg, cdns_ctrl->reg + SKIP_BYTES_CONF); + + return 0; +} + +static int cadence_nand_set_skip_bytes_conf(struct cdns_nand_ctrl *cdns_ctrl, + u8 num_of_bytes, + u32 offset_value, + int enable) +{ + u32 reg, skip_bytes_offset; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + if (!enable) { + num_of_bytes = 0; + offset_value = 0; + } + + reg = readl_relaxed(cdns_ctrl->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_NUM_OF_BYTES; + reg |= FIELD_PREP(SKIP_BYTES_NUM_OF_BYTES, + num_of_bytes); + skip_bytes_offset = FIELD_PREP(SKIP_BYTES_OFFSET_VALUE, + offset_value); + + writel_relaxed(reg, cdns_ctrl->reg + SKIP_BYTES_CONF); + writel_relaxed(skip_bytes_offset, cdns_ctrl->reg + SKIP_BYTES_OFFSET); + + return 0; +} + +/* Functions enables/disables hardware detection of erased data */ +static void cadence_nand_set_erase_detection(struct cdns_nand_ctrl *cdns_ctrl, + bool enable, + u8 bitflips_threshold) +{ + u32 reg; + + reg = readl_relaxed(cdns_ctrl->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ERASE_DET_EN; + else + reg &= ~ECC_CONFIG_0_ERASE_DET_EN; + + writel_relaxed(reg, cdns_ctrl->reg + ECC_CONFIG_0); + writel_relaxed(bitflips_threshold, cdns_ctrl->reg + ECC_CONFIG_1); +} + +static int cadence_nand_set_access_width16(struct cdns_nand_ctrl *cdns_ctrl, + bool bit_bus16) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cdns_ctrl->reg + COMMON_SET); + + if (!bit_bus16) + reg &= ~COMMON_SET_DEVICE_16BIT; + else + reg |= COMMON_SET_DEVICE_16BIT; + writel_relaxed(reg, cdns_ctrl->reg + COMMON_SET); + + return 0; +} + +static void +cadence_nand_clear_interrupt(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + writel_relaxed(irq_status->status, cdns_ctrl->reg + INTR_STATUS); + writel_relaxed(irq_status->trd_status, + cdns_ctrl->reg + TRD_COMP_INT_STATUS); + writel_relaxed(irq_status->trd_error, + cdns_ctrl->reg + TRD_ERR_INT_STATUS); +} + +static void +cadence_nand_read_int_status(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + irq_status->status = readl_relaxed(cdns_ctrl->reg + INTR_STATUS); + irq_status->trd_status = readl_relaxed(cdns_ctrl->reg + + TRD_COMP_INT_STATUS); + irq_status->trd_error = readl_relaxed(cdns_ctrl->reg + + TRD_ERR_INT_STATUS); +} + +static u32 irq_detected(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_status) +{ + cadence_nand_read_int_status(cdns_ctrl, irq_status); + + return irq_status->status || irq_status->trd_status || + irq_status->trd_error; +} + +static void cadence_nand_reset_irq(struct cdns_nand_ctrl *cdns_ctrl) +{ + unsigned long flags; + + spin_lock_irqsave(&cdns_ctrl->irq_lock, flags); + memset(&cdns_ctrl->irq_status, 0, sizeof(cdns_ctrl->irq_status)); + memset(&cdns_ctrl->irq_mask, 0, sizeof(cdns_ctrl->irq_mask)); + spin_unlock_irqrestore(&cdns_ctrl->irq_lock, flags); +} + +/* + * This is the interrupt service routine. It handles all interrupts + * sent to this device. + */ +static irqreturn_t cadence_nand_isr(int irq, void *dev_id) +{ + struct cdns_nand_ctrl *cdns_ctrl = dev_id; + struct cadence_nand_irq_status irq_status; + irqreturn_t result = IRQ_NONE; + + spin_lock(&cdns_ctrl->irq_lock); + + if (irq_detected(cdns_ctrl, &irq_status)) { + /* Handle interrupt. */ + /* First acknowledge it. */ + cadence_nand_clear_interrupt(cdns_ctrl, &irq_status); + /* Status in the device context for someone to read. */ + cdns_ctrl->irq_status.status |= irq_status.status; + cdns_ctrl->irq_status.trd_status |= irq_status.trd_status; + cdns_ctrl->irq_status.trd_error |= irq_status.trd_error; + /* Notify anyone who cares that it happened. */ + complete(&cdns_ctrl->complete); + /* Tell the OS that we've handled this. */ + result = IRQ_HANDLED; + } + spin_unlock(&cdns_ctrl->irq_lock); + + return result; +} + +static void cadence_nand_set_irq_mask(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_mask) +{ + writel_relaxed(INTR_ENABLE_INTR_EN | irq_mask->status, + cdns_ctrl->reg + INTR_ENABLE); + + writel_relaxed(irq_mask->trd_error, + cdns_ctrl->reg + TRD_ERR_INT_STATUS_EN); +} + +static void +cadence_nand_wait_for_irq(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_irq_status *irq_mask, + struct cadence_nand_irq_status *irq_status) +{ + unsigned long timeout = msecs_to_jiffies(10000); + unsigned long time_left; + + time_left = wait_for_completion_timeout(&cdns_ctrl->complete, + timeout); + + *irq_status = cdns_ctrl->irq_status; + if (time_left == 0) { + /* Timeout error. */ + dev_err(cdns_ctrl->dev, "timeout occurred:\n"); + dev_err(cdns_ctrl->dev, "\tstatus = 0x%x, mask = 0x%x\n", + irq_status->status, irq_mask->status); + dev_err(cdns_ctrl->dev, + "\ttrd_status = 0x%x, trd_status mask = 0x%x\n", + irq_status->trd_status, irq_mask->trd_status); + dev_err(cdns_ctrl->dev, + "\t trd_error = 0x%x, trd_error mask = 0x%x\n", + irq_status->trd_error, irq_mask->trd_error); + } +} + +/* Execute generic command on NAND controller. */ +static int cadence_nand_generic_cmd_send(struct cdns_nand_ctrl *cdns_ctrl, + u8 chip_nr, + u64 mini_ctrl_cmd) +{ + u32 mini_ctrl_cmd_l, mini_ctrl_cmd_h, reg; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_CS, chip_nr); + mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF; + mini_ctrl_cmd_h = mini_ctrl_cmd >> 32; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_reset_irq(cdns_ctrl); + + writel_relaxed(mini_ctrl_cmd_l, cdns_ctrl->reg + CMD_REG2); + writel_relaxed(mini_ctrl_cmd_h, cdns_ctrl->reg + CMD_REG3); + + /* Select generic command. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_GEN); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, 0); + + /* Issue command. */ + writel_relaxed(reg, cdns_ctrl->reg + CMD_REG0); + + return 0; +} + +/* Wait for data on slave DMA interface. */ +static int cadence_nand_wait_on_sdma(struct cdns_nand_ctrl *cdns_ctrl, + u8 *out_sdma_trd, + u32 *out_sdma_size) +{ + struct cadence_nand_irq_status irq_mask, irq_status; + + irq_mask.trd_status = 0; + irq_mask.trd_error = 0; + irq_mask.status = INTR_STATUS_SDMA_TRIGG + | INTR_STATUS_SDMA_ERR + | INTR_STATUS_UNSUPP_CMD; + + cadence_nand_set_irq_mask(cdns_ctrl, &irq_mask); + cadence_nand_wait_for_irq(cdns_ctrl, &irq_mask, &irq_status); + if (irq_status.status == 0) { + dev_err(cdns_ctrl->dev, "Timeout while waiting for SDMA\n"); + return -ETIMEDOUT; + } + + if (irq_status.status & INTR_STATUS_SDMA_TRIGG) { + *out_sdma_size = readl_relaxed(cdns_ctrl->reg + SDMA_SIZE); + *out_sdma_trd = readl_relaxed(cdns_ctrl->reg + SDMA_TRD_NUM); + *out_sdma_trd = + FIELD_GET(SDMA_TRD_NUM_SDMA_TRD, *out_sdma_trd); + } else { + dev_err(cdns_ctrl->dev, "SDMA error - irq_status %x\n", + irq_status.status); + return -EIO; + } + + return 0; +} + +static void cadence_nand_get_caps(struct cdns_nand_ctrl *cdns_ctrl) +{ + u32 reg; + + reg = readl_relaxed(cdns_ctrl->reg + CTRL_FEATURES); + + cdns_ctrl->caps2.max_banks = 1 << FIELD_GET(CTRL_FEATURES_N_BANKS, reg); + + if (FIELD_GET(CTRL_FEATURES_DMA_DWITH64, reg)) + cdns_ctrl->caps2.data_dma_width = 8; + else + cdns_ctrl->caps2.data_dma_width = 4; + + if (reg & CTRL_FEATURES_CONTROL_DATA) + cdns_ctrl->caps2.data_control_supp = true; + + if (reg & (CTRL_FEATURES_NVDDR_2_3 + | CTRL_FEATURES_NVDDR)) + cdns_ctrl->caps2.is_phy_type_dll = true; +} + +/* Prepare CDMA descriptor. */ +static void +cadence_nand_cdma_desc_prepare(struct cdns_nand_ctrl *cdns_ctrl, + char nf_mem, u32 flash_ptr, char *mem_ptr, + char *ctrl_data_ptr, u16 ctype) +{ + struct cadence_nand_cdma_desc *cdma_desc = cdns_ctrl->cdma_desc; + + memset(cdma_desc, 0, sizeof(struct cadence_nand_cdma_desc)); + + /* Set fields for one descriptor. */ + cdma_desc->flash_pointer = flash_ptr; + if (cdns_ctrl->ctrl_rev >= 13) + cdma_desc->bank = nf_mem; + else + cdma_desc->flash_pointer |= (nf_mem << CDMA_CFPTR_MEM_SHIFT); + + cdma_desc->command_flags |= CDMA_CF_DMA_MASTER; + cdma_desc->command_flags |= CDMA_CF_INT; + + cdma_desc->memory_pointer = (uintptr_t)mem_ptr; + cdma_desc->status = 0; + cdma_desc->sync_flag_pointer = 0; + cdma_desc->sync_arguments = 0; + + cdma_desc->command_type = ctype; + cdma_desc->ctrl_data_ptr = (uintptr_t)ctrl_data_ptr; +} + +static u8 cadence_nand_check_desc_error(struct cdns_nand_ctrl *cdns_ctrl, + u32 desc_status) +{ + if (desc_status & CDMA_CS_ERP) + return STAT_ERASED; + + if (desc_status & CDMA_CS_UNCE) + return STAT_ECC_UNCORR; + + if (desc_status & CDMA_CS_ERR) { + dev_err(cdns_ctrl->dev, ":CDMA desc error flag detected.\n"); + return STAT_FAIL; + } + + if (FIELD_GET(CDMA_CS_MAXERR, desc_status)) + return STAT_ECC_CORR; + + return STAT_FAIL; +} + +static int cadence_nand_cdma_finish(struct cdns_nand_ctrl *cdns_ctrl) +{ + struct cadence_nand_cdma_desc *desc_ptr = cdns_ctrl->cdma_desc; + u8 status = STAT_BUSY; + + if (desc_ptr->status & CDMA_CS_FAIL) { + status = cadence_nand_check_desc_error(cdns_ctrl, + desc_ptr->status); + dev_err(cdns_ctrl->dev, ":CDMA error %x\n", desc_ptr->status); + } else if (desc_ptr->status & CDMA_CS_COMP) { + /* Descriptor finished with no errors. */ + if (desc_ptr->command_flags & CDMA_CF_CONT) { + dev_info(cdns_ctrl->dev, "DMA unsupported flag is set"); + status = STAT_UNKNOWN; + } else { + /* Last descriptor. */ + status = STAT_OK; + } + } + + return status; +} + +static int cadence_nand_cdma_send(struct cdns_nand_ctrl *cdns_ctrl, + u8 thread) +{ + u32 reg; + int status; + + /* Wait for thread ready. */ + status = cadence_nand_wait_for_value(cdns_ctrl, TRD_STATUS, + 1000000, + BIT(thread), true); + if (status) + return status; + + cadence_nand_reset_irq(cdns_ctrl); + + writel_relaxed((u32)cdns_ctrl->dma_cdma_desc, + cdns_ctrl->reg + CMD_REG2); + writel_relaxed(0, cdns_ctrl->reg + CMD_REG3); + + /* Select CDMA mode. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_CDMA); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, thread); + /* Issue command. */ + writel_relaxed(reg, cdns_ctrl->reg + CMD_REG0); + + return 0; +} + +/* Send SDMA command and wait for finish. */ +static u32 +cadence_nand_cdma_send_and_wait(struct cdns_nand_ctrl *cdns_ctrl, + u8 thread) +{ + struct cadence_nand_irq_status irq_mask, irq_status = {0}; + int status; + + irq_mask.trd_status = BIT(thread); + irq_mask.trd_error = BIT(thread); + irq_mask.status = INTR_STATUS_CDMA_TERR; + + cadence_nand_set_irq_mask(cdns_ctrl, &irq_mask); + + status = cadence_nand_cdma_send(cdns_ctrl, thread); + if (status) + return status; + + cadence_nand_wait_for_irq(cdns_ctrl, &irq_mask, &irq_status); + + if (irq_status.status == 0 && irq_status.trd_status == 0 && + irq_status.trd_error == 0) { + dev_err(cdns_ctrl->dev, "CDMA command timeout\n"); + return -ETIMEDOUT; + } + if (irq_status.status & irq_mask.status) { + dev_err(cdns_ctrl->dev, "CDMA command failed\n"); + return -EIO; + } + + return 0; +} + +/* + * ECC size depends on configured ECC strength and on maximum supported + * ECC step size. + */ +static int cadence_nand_calc_ecc_bytes(int max_step_size, int strength) +{ + int nbytes = DIV_ROUND_UP(fls(8 * max_step_size) * strength, 8); + + return ALIGN(nbytes, 2); +} + +#define CADENCE_NAND_CALC_ECC_BYTES(max_step_size) \ + static int \ + cadence_nand_calc_ecc_bytes_##max_step_size(int step_size, \ + int strength)\ + {\ + return cadence_nand_calc_ecc_bytes(max_step_size, strength);\ + } + +CADENCE_NAND_CALC_ECC_BYTES(256) +CADENCE_NAND_CALC_ECC_BYTES(512) +CADENCE_NAND_CALC_ECC_BYTES(1024) +CADENCE_NAND_CALC_ECC_BYTES(2048) +CADENCE_NAND_CALC_ECC_BYTES(4096) + +/* Function reads BCH capabilities. */ +static int cadence_nand_read_bch_caps(struct cdns_nand_ctrl *cdns_ctrl) +{ + struct nand_ecc_caps *ecc_caps = &cdns_ctrl->ecc_caps; + int max_step_size = 0, nstrengths, i; + u32 reg; + + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_0); + cdns_ctrl->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg); + cdns_ctrl->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg); + cdns_ctrl->ecc_strengths[2] = FIELD_GET(BCH_CFG_0_CORR_CAP_2, reg); + cdns_ctrl->ecc_strengths[3] = FIELD_GET(BCH_CFG_0_CORR_CAP_3, reg); + + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_1); + cdns_ctrl->ecc_strengths[4] = FIELD_GET(BCH_CFG_1_CORR_CAP_4, reg); + cdns_ctrl->ecc_strengths[5] = FIELD_GET(BCH_CFG_1_CORR_CAP_5, reg); + cdns_ctrl->ecc_strengths[6] = FIELD_GET(BCH_CFG_1_CORR_CAP_6, reg); + cdns_ctrl->ecc_strengths[7] = FIELD_GET(BCH_CFG_1_CORR_CAP_7, reg); + + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_2); + cdns_ctrl->ecc_stepinfos[0].stepsize = + FIELD_GET(BCH_CFG_2_SECT_0, reg); + + cdns_ctrl->ecc_stepinfos[1].stepsize = + FIELD_GET(BCH_CFG_2_SECT_1, reg); + + nstrengths = 0; + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cdns_ctrl->ecc_strengths[i] != 0) + nstrengths++; + } + + ecc_caps->nstepinfos = 0; + for (i = 0; i < BCH_MAX_NUM_SECTOR_SIZES; i++) { + /* ECC strengths are common for all step infos. */ + cdns_ctrl->ecc_stepinfos[i].nstrengths = nstrengths; + cdns_ctrl->ecc_stepinfos[i].strengths = + cdns_ctrl->ecc_strengths; + + if (cdns_ctrl->ecc_stepinfos[i].stepsize != 0) + ecc_caps->nstepinfos++; + + if (cdns_ctrl->ecc_stepinfos[i].stepsize > max_step_size) + max_step_size = cdns_ctrl->ecc_stepinfos[i].stepsize; + } + ecc_caps->stepinfos = &cdns_ctrl->ecc_stepinfos[0]; + + switch (max_step_size) { + case 256: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_256; + break; + case 512: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_512; + break; + case 1024: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_1024; + break; + case 2048: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_2048; + break; + case 4096: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_4096; + break; + default: + dev_err(cdns_ctrl->dev, + "Unsupported sector size(ecc step size) %d\n", + max_step_size); + return -EIO; + } + + return 0; +} + +/* Hardware initialization. */ +static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl) +{ + int status; + u32 reg; + + status = cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_INIT_COMP, false); + if (status) + return status; + + reg = readl_relaxed(cdns_ctrl->reg + CTRL_VERSION); + cdns_ctrl->ctrl_rev = FIELD_GET(CTRL_VERSION_REV, reg); + + dev_info(cdns_ctrl->dev, + "%s: cadence nand controller version reg %x\n", + __func__, reg); + + /* Disable cache and multiplane. */ + writel_relaxed(0, cdns_ctrl->reg + MULTIPLANE_CFG); + writel_relaxed(0, cdns_ctrl->reg + CACHE_CFG); + + /* Clear all interrupts. */ + writel_relaxed(0xFFFFFFFF, cdns_ctrl->reg + INTR_STATUS); + + cadence_nand_get_caps(cdns_ctrl); + cadence_nand_read_bch_caps(cdns_ctrl); + + /* + * Set IO width access to 8. + * It is because during SW device discovering width access + * is expected to be 8. + */ + status = cadence_nand_set_access_width16(cdns_ctrl, false); + + return status; +} + +#define TT_MAIN_OOB_AREAS 2 +#define TT_RAW_PAGE 3 +#define TT_BBM 4 +#define TT_MAIN_OOB_AREA_EXT 5 + +/* Prepare size of data to transfer. */ +static void +cadence_nand_prepare_data_size(struct nand_chip *chip, + int transfer_type) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + u32 sec_size = 0, offset = 0, sec_cnt = 1; + u32 last_sec_size = cdns_chip->sector_size; + u32 data_ctrl_size = 0; + u32 reg = 0; + + if (cdns_ctrl->curr_trans_type == transfer_type) + return; + + switch (transfer_type) { + case TT_MAIN_OOB_AREA_EXT: + sec_cnt = cdns_chip->sector_count; + sec_size = cdns_chip->sector_size; + data_ctrl_size = cdns_chip->avail_oob_size; + break; + case TT_MAIN_OOB_AREAS: + sec_cnt = cdns_chip->sector_count; + last_sec_size = cdns_chip->sector_size + + cdns_chip->avail_oob_size; + sec_size = cdns_chip->sector_size; + break; + case TT_RAW_PAGE: + last_sec_size = mtd->writesize + mtd->oobsize; + break; + case TT_BBM: + offset = mtd->writesize + cdns_chip->bbm_offs; + last_sec_size = 8; + break; + } + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_0_OFFSET, offset); + reg |= FIELD_PREP(TRAN_CFG_0_SEC_CNT, sec_cnt); + writel_relaxed(reg, cdns_ctrl->reg + TRAN_CFG_0); + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_1_LAST_SEC_SIZE, last_sec_size); + reg |= FIELD_PREP(TRAN_CFG_1_SECTOR_SIZE, sec_size); + writel_relaxed(reg, cdns_ctrl->reg + TRAN_CFG_1); + + if (cdns_ctrl->caps2.data_control_supp) { + reg = readl_relaxed(cdns_ctrl->reg + CONTROL_DATA_CTRL); + reg &= ~CONTROL_DATA_CTRL_SIZE; + reg |= FIELD_PREP(CONTROL_DATA_CTRL_SIZE, data_ctrl_size); + writel_relaxed(reg, cdns_ctrl->reg + CONTROL_DATA_CTRL); + } + + cdns_ctrl->curr_trans_type = transfer_type; +} + +static int +cadence_nand_cdma_transfer(struct cdns_nand_ctrl *cdns_ctrl, u8 chip_nr, + int page, void *buf, void *ctrl_dat, u32 buf_size, + u32 ctrl_dat_size, enum dma_data_direction dir, + bool with_ecc) +{ + dma_addr_t dma_buf, dma_ctrl_dat = 0; + u8 thread_nr = chip_nr; + int status; + u16 ctype; + + if (dir == DMA_FROM_DEVICE) + ctype = CDMA_CT_RD; + else + ctype = CDMA_CT_WR; + + cadence_nand_set_ecc_enable(cdns_ctrl, with_ecc); + + dma_buf = dma_map_single(cdns_ctrl->dev, buf, buf_size, dir); + if (dma_mapping_error(cdns_ctrl->dev, dma_buf)) { + dev_err(cdns_ctrl->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + + if (ctrl_dat && ctrl_dat_size) { + dma_ctrl_dat = dma_map_single(cdns_ctrl->dev, ctrl_dat, + ctrl_dat_size, dir); + if (dma_mapping_error(cdns_ctrl->dev, dma_ctrl_dat)) { + dma_unmap_single(cdns_ctrl->dev, dma_buf, + buf_size, dir); + dev_err(cdns_ctrl->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + } + + cadence_nand_cdma_desc_prepare(cdns_ctrl, chip_nr, page, + (void *)dma_buf, (void *)dma_ctrl_dat, + ctype); + + status = cadence_nand_cdma_send_and_wait(cdns_ctrl, thread_nr); + + dma_unmap_single(cdns_ctrl->dev, dma_buf, + buf_size, dir); + + if (ctrl_dat && ctrl_dat_size) + dma_unmap_single(cdns_ctrl->dev, dma_ctrl_dat, + ctrl_dat_size, dir); + if (status) + return status; + + return cadence_nand_cdma_finish(cdns_ctrl); +} + +static void cadence_nand_set_timings(struct cdns_nand_ctrl *cdns_ctrl, + struct cadence_nand_timings *t) +{ + writel_relaxed(t->async_toggle_timings, + cdns_ctrl->reg + ASYNC_TOGGLE_TIMINGS); + writel_relaxed(t->timings0, cdns_ctrl->reg + TIMINGS0); + writel_relaxed(t->timings1, cdns_ctrl->reg + TIMINGS1); + writel_relaxed(t->timings2, cdns_ctrl->reg + TIMINGS2); + + if (cdns_ctrl->caps2.is_phy_type_dll) + writel_relaxed(t->dll_phy_ctrl, cdns_ctrl->reg + DLL_PHY_CTRL); + + writel_relaxed(t->phy_ctrl, cdns_ctrl->reg + PHY_CTRL); + + if (cdns_ctrl->caps2.is_phy_type_dll) { + writel_relaxed(0, cdns_ctrl->reg + PHY_TSEL); + writel_relaxed(2, cdns_ctrl->reg + PHY_DQ_TIMING); + writel_relaxed(t->phy_dqs_timing, + cdns_ctrl->reg + PHY_DQS_TIMING); + writel_relaxed(t->phy_gate_lpbk_ctrl, + cdns_ctrl->reg + PHY_GATE_LPBK_CTRL); + writel_relaxed(PHY_DLL_MASTER_CTRL_BYPASS_MODE, + cdns_ctrl->reg + PHY_DLL_MASTER_CTRL); + writel_relaxed(0, cdns_ctrl->reg + PHY_DLL_SLAVE_CTRL); + } +} + +static int cadence_nand_select_target(struct nand_chip *chip) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (chip == cdns_ctrl->selected_chip) + return 0; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_timings(cdns_ctrl, &cdns_chip->timings); + + cadence_nand_set_ecc_strength(cdns_ctrl, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cdns_ctrl, true, + chip->ecc.strength); + + cdns_ctrl->curr_trans_type = -1; + cdns_ctrl->selected_chip = chip; + + return 0; +} + +static int cadence_nand_erase(struct nand_chip *chip, u32 page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + u8 thread_nr = cdns_chip->cs[chip->cur_cs]; + + cadence_nand_cdma_desc_prepare(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, NULL, NULL, + CDMA_CT_ERASE); + status = cadence_nand_cdma_send_and_wait(cdns_ctrl, thread_nr); + if (status) { + dev_err(cdns_ctrl->dev, "erase operation failed\n"); + return -EIO; + } + + status = cadence_nand_cdma_finish(cdns_ctrl); + if (status) + return status; + + return 0; +} + +static int cadence_nand_read_bbm(struct nand_chip *chip, int page, u8 *buf) +{ + int status; + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + + cadence_nand_prepare_data_size(chip, TT_BBM); + + cadence_nand_set_skip_bytes_conf(cdns_ctrl, 0, 0, 0); + + /* + * Read only bad block marker from offset + * defined by a memory manufacturer. + */ + status = cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, cdns_ctrl->buf, NULL, + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + if (status) { + dev_err(cdns_ctrl->dev, "read BBM failed\n"); + return -EIO; + } + + memcpy(buf + cdns_chip->bbm_offs, cdns_ctrl->buf, cdns_chip->bbm_len); + + return 0; +} + +static int cadence_nand_write_page(struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + u16 marker_val = 0xFFFF; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cdns_ctrl, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, + 1); + + if (oob_required) { + marker_val = *(u16 *)(chip->oob_poi + + cdns_chip->bbm_offs); + } else { + /* Set oob data to 0xFF. */ + memset(cdns_ctrl->buf + mtd->writesize, 0xFF, + cdns_chip->avail_oob_size); + } + + cadence_nand_set_skip_marker_val(cdns_ctrl, marker_val); + + cadence_nand_prepare_data_size(chip, TT_MAIN_OOB_AREA_EXT); + + if (cadence_nand_dma_buf_ok(cdns_ctrl, buf, mtd->writesize) && + cdns_ctrl->caps2.data_control_supp) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cdns_ctrl->buf + mtd->writesize; + + status = cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, (void *)buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_TO_DEVICE, true); + if (status) { + dev_err(cdns_ctrl->dev, "write page failed\n"); + return -EIO; + } + + return 0; + } + + if (oob_required) { + /* Transfer the data to the oob area. */ + memcpy(cdns_ctrl->buf + mtd->writesize, chip->oob_poi, + cdns_chip->avail_oob_size); + } + + memcpy(cdns_ctrl->buf, buf, mtd->writesize); + + cadence_nand_prepare_data_size(chip, TT_MAIN_OOB_AREAS); + + return cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, cdns_ctrl->buf, NULL, + mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_TO_DEVICE, true); +} + +static int cadence_nand_write_oob(struct nand_chip *chip, int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct mtd_info *mtd = nand_to_mtd(chip); + + memset(cdns_ctrl->buf, 0xFF, mtd->writesize); + + return cadence_nand_write_page(chip, cdns_ctrl->buf, 1, page); +} + +static int cadence_nand_write_page_raw(struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cdns_ctrl->buf; + int oob_skip = cdns_chip->bbm_len; + size_t size = writesize + oobsize; + int i, pos, len; + int status = 0; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + /* + * Fill the buffer with 0xff first except the full page transfer. + * This simplifies the logic. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, size); + + cadence_nand_set_skip_bytes_conf(cdns_ctrl, 0, 0, 0); + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, buf, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(tmp_buf + writesize + oob_skip, buf, + len); + buf += len; + } + } + } + + if (oob_required) { + const u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* BBM at the beginning of the OOB area. */ + memcpy(tmp_buf + writesize, oob, oob_skip); + + /* OOB free. */ + memcpy(tmp_buf + oob_data_offset, oob, + cdns_chip->avail_oob_size); + oob += cdns_chip->avail_oob_size; + + /* OOB ECC. */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, oob, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(tmp_buf + writesize + oob_skip, oob, + len); + oob += len; + } + } + } + + cadence_nand_prepare_data_size(chip, TT_RAW_PAGE); + + return cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, cdns_ctrl->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_TO_DEVICE, false); +} + +static int cadence_nand_write_oob_raw(struct nand_chip *chip, + int page) +{ + return cadence_nand_write_page_raw(chip, NULL, true, page); +} + +static int cadence_nand_read_page(struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int status = 0; + int ecc_err_count = 0; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cdns_ctrl, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, 1); + + /* + * If data buffer can be accessed by DMA and data_control feature + * is supported then transfer data and oob directly. + */ + if (cadence_nand_dma_buf_ok(cdns_ctrl, buf, mtd->writesize) && + cdns_ctrl->caps2.data_control_supp) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cdns_ctrl->buf + mtd->writesize; + + cadence_nand_prepare_data_size(chip, TT_MAIN_OOB_AREA_EXT); + status = cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_FROM_DEVICE, true); + /* Otherwise use bounce buffer. */ + } else { + cadence_nand_prepare_data_size(chip, TT_MAIN_OOB_AREAS); + status = cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, cdns_ctrl->buf, + NULL, mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_FROM_DEVICE, true); + + memcpy(buf, cdns_ctrl->buf, mtd->writesize); + if (oob_required) + memcpy(chip->oob_poi, + cdns_ctrl->buf + mtd->writesize, + mtd->oobsize); + } + + switch (status) { + case STAT_ECC_UNCORR: + mtd->ecc_stats.failed++; + ecc_err_count++; + break; + case STAT_ECC_CORR: + ecc_err_count = FIELD_GET(CDMA_CS_MAXERR, + cdns_ctrl->cdma_desc->status); + mtd->ecc_stats.corrected += ecc_err_count; + break; + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cdns_ctrl->dev, "read page failed\n"); + return -EIO; + } + + if (oob_required) + if (cadence_nand_read_bbm(chip, page, chip->oob_poi)) + return -EIO; + + return ecc_err_count; +} + +/* Reads OOB data from the device. */ +static int cadence_nand_read_oob(struct nand_chip *chip, int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + + return cadence_nand_read_page(chip, cdns_ctrl->buf, 1, page); +} + +static int cadence_nand_read_page_raw(struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int oob_skip = cdns_chip->bbm_len; + int writesize = mtd->writesize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cdns_ctrl->buf; + int i, pos, len; + int status = 0; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cdns_ctrl, 0, 0, 0); + + cadence_nand_prepare_data_size(chip, TT_RAW_PAGE); + status = cadence_nand_cdma_transfer(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + page, cdns_ctrl->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + + switch (status) { + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cdns_ctrl->dev, "read raw page failed\n"); + return -EIO; + } + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(buf, tmp_buf + pos, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(buf, tmp_buf + writesize + oob_skip, + len); + buf += len; + } + } + } + + if (oob_required) { + u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* OOB free. */ + memcpy(oob, tmp_buf + oob_data_offset, + cdns_chip->avail_oob_size); + + /* BBM at the beginning of the OOB area. */ + memcpy(oob, tmp_buf + writesize, oob_skip); + + oob += cdns_chip->avail_oob_size; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(oob, tmp_buf + pos, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(oob, tmp_buf + writesize + oob_skip, + len); + oob += len; + } + } + } + + return 0; +} + +static int cadence_nand_read_oob_raw(struct nand_chip *chip, + int page) +{ + return cadence_nand_read_page_raw(chip, NULL, true, page); +} + +static void cadence_nand_slave_dma_transfer_finished(void *data) +{ + struct completion *finished = data; + + complete(finished); +} + +static int cadence_nand_slave_dma_transfer(struct cdns_nand_ctrl *cdns_ctrl, + void *buf, + dma_addr_t dev_dma, size_t len, + enum dma_data_direction dir) +{ + DECLARE_COMPLETION_ONSTACK(finished); + struct dma_chan *chan; + struct dma_device *dma_dev; + dma_addr_t src_dma, dst_dma, buf_dma; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + + chan = cdns_ctrl->dmac; + dma_dev = chan->device; + + buf_dma = dma_map_single(dma_dev->dev, buf, len, dir); + if (dma_mapping_error(dma_dev->dev, buf_dma)) { + dev_err(cdns_ctrl->dev, "Failed to map DMA buffer\n"); + goto err; + } + + if (dir == DMA_FROM_DEVICE) { + src_dma = cdns_ctrl->io.dma; + dst_dma = buf_dma; + } else { + src_dma = buf_dma; + dst_dma = cdns_ctrl->io.dma; + } + + tx = dmaengine_prep_dma_memcpy(cdns_ctrl->dmac, dst_dma, src_dma, len, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(cdns_ctrl->dev, "Failed to prepare DMA memcpy\n"); + goto err_unmap; + } + + tx->callback = cadence_nand_slave_dma_transfer_finished; + tx->callback_param = &finished; + + cookie = dmaengine_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(cdns_ctrl->dev, "Failed to do DMA tx_submit\n"); + goto err_unmap; + } + + dma_async_issue_pending(cdns_ctrl->dmac); + wait_for_completion(&finished); + + dma_unmap_single(cdns_ctrl->dev, buf_dma, len, dir); + + return 0; + +err_unmap: + dma_unmap_single(cdns_ctrl->dev, buf_dma, len, dir); + +err: + dev_dbg(cdns_ctrl->dev, "Fall back to CPU I/O\n"); + + return -EIO; +} + +static int cadence_nand_read_buf(struct cdns_nand_ctrl *cdns_ctrl, + u8 *buf, int len) +{ + u8 thread_nr = 0; + u32 sdma_size; + int status; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cdns_ctrl, &thread_nr, &sdma_size); + if (status) + return status; + + if (!cdns_ctrl->caps1->has_dma) { + int len_in_words = len >> 2; + + /* read alingment data */ + ioread32_rep(cdns_ctrl->io.virt, buf, len_in_words); + if (sdma_size > len) { + /* read rest data from slave DMA interface if any */ + ioread32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf, + sdma_size / 4 - len_in_words); + /* copy rest of data */ + memcpy(buf + (len_in_words << 2), cdns_ctrl->buf, + len - (len_in_words << 2)); + } + return 0; + } + + if (cadence_nand_dma_buf_ok(cdns_ctrl, buf, len)) { + status = cadence_nand_slave_dma_transfer(cdns_ctrl, buf, + cdns_ctrl->io.dma, + len, DMA_FROM_DEVICE); + if (status == 0) + return 0; + + dev_warn(cdns_ctrl->dev, + "Slave DMA transfer failed. Try again using bounce buffer."); + } + + /* If DMA transfer is not possible or failed then use bounce buffer. */ + status = cadence_nand_slave_dma_transfer(cdns_ctrl, cdns_ctrl->buf, + cdns_ctrl->io.dma, + sdma_size, DMA_FROM_DEVICE); + + if (status) { + dev_err(cdns_ctrl->dev, "Slave DMA transfer failed"); + return status; + } + + memcpy(buf, cdns_ctrl->buf, len); + + return 0; +} + +static int cadence_nand_write_buf(struct cdns_nand_ctrl *cdns_ctrl, + const u8 *buf, int len) +{ + u8 thread_nr = 0; + u32 sdma_size; + int status; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cdns_ctrl, &thread_nr, &sdma_size); + if (status) + return status; + + if (!cdns_ctrl->caps1->has_dma) { + int len_in_words = len >> 2; + + iowrite32_rep(cdns_ctrl->io.virt, buf, len_in_words); + if (sdma_size > len) { + /* copy rest of data */ + memcpy(cdns_ctrl->buf, buf + (len_in_words << 2), + len - (len_in_words << 2)); + /* write all expected by nand controller data */ + iowrite32_rep(cdns_ctrl->io.virt, cdns_ctrl->buf, + sdma_size / 4 - len_in_words); + } + + return 0; + } + + if (cadence_nand_dma_buf_ok(cdns_ctrl, buf, len)) { + status = cadence_nand_slave_dma_transfer(cdns_ctrl, (void *)buf, + cdns_ctrl->io.dma, + len, DMA_TO_DEVICE); + if (status == 0) + return 0; + + dev_warn(cdns_ctrl->dev, + "Slave DMA transfer failed. Try again using bounce buffer."); + } + + /* If DMA transfer is not possible or failed then use bounce buffer. */ + memcpy(cdns_ctrl->buf, buf, len); + + status = cadence_nand_slave_dma_transfer(cdns_ctrl, cdns_ctrl->buf, + cdns_ctrl->io.dma, + sdma_size, DMA_TO_DEVICE); + + if (status) + dev_err(cdns_ctrl->dev, "Slave DMA transfer failed"); + + return status; +} + +static int cadence_nand_force_byte_access(struct nand_chip *chip, + bool force_8bit) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + int status; + + /* + * Callers of this function do not verify if the NAND is using a 16-bit + * an 8-bit bus for normal operations, so we need to take care of that + * here by leaving the configuration unchanged if the NAND does not have + * the NAND_BUSWIDTH_16 flag set. + */ + if (!(chip->options & NAND_BUSWIDTH_16)) + return 0; + + status = cadence_nand_set_access_width16(cdns_ctrl, !force_8bit); + + return status; +} + +static int cadence_nand_cmd_opcode(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + const struct nand_op_instr *instr; + unsigned int op_id = 0; + u64 mini_ctrl_cmd = 0; + int ret; + + instr = &subop->instrs[op_id]; + + if (instr->delay_ns > 0) + mini_ctrl_cmd |= GCMD_LAY_TWB; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_CMD); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_CMD, + instr->ctx.cmd.opcode); + + ret = cadence_nand_generic_cmd_send(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + if (ret) + dev_err(cdns_ctrl->dev, "send cmd %x failed\n", + instr->ctx.cmd.opcode); + + return ret; +} + +static int cadence_nand_cmd_address(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + const struct nand_op_instr *instr; + unsigned int op_id = 0; + u64 mini_ctrl_cmd = 0; + unsigned int offset, naddrs; + u64 address = 0; + const u8 *addrs; + int ret; + int i; + + instr = &subop->instrs[op_id]; + + if (instr->delay_ns > 0) + mini_ctrl_cmd |= GCMD_LAY_TWB; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_ADDR); + + offset = nand_subop_get_addr_start_off(subop, op_id); + naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + addrs = &instr->ctx.addr.addrs[offset]; + + for (i = 0; i < naddrs; i++) + address |= (u64)addrs[i] << (8 * i); + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR, + address); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR_SIZE, + naddrs - 1); + + ret = cadence_nand_generic_cmd_send(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + if (ret) + dev_err(cdns_ctrl->dev, "send address %llx failed\n", address); + + return ret; +} + +static int cadence_nand_cmd_erase(struct nand_chip *chip, + const struct nand_subop *subop) +{ + unsigned int op_id; + + if (subop->instrs[0].ctx.cmd.opcode == NAND_CMD_ERASE1) { + int i; + const struct nand_op_instr *instr = NULL; + unsigned int offset, naddrs; + const u8 *addrs; + u32 page = 0; + + instr = &subop->instrs[1]; + offset = nand_subop_get_addr_start_off(subop, 1); + naddrs = nand_subop_get_num_addr_cyc(subop, 1); + addrs = &instr->ctx.addr.addrs[offset]; + + for (i = 0; i < naddrs; i++) + page |= (u32)addrs[i] << (8 * i); + + return cadence_nand_erase(chip, page); + } + + /* + * If it is not an erase operation then handle operation + * by calling exec_op function. + */ + for (op_id = 0; op_id < subop->ninstrs; op_id++) { + int ret; + const struct nand_operation nand_op = { + .cs = chip->cur_cs, + .instrs = &subop->instrs[op_id], + .ninstrs = 1}; + ret = chip->controller->ops->exec_op(chip, &nand_op, false); + if (ret) + return ret; + } + + return 0; +} + +static int cadence_nand_cmd_data(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + const struct nand_op_instr *instr; + unsigned int offset, op_id = 0; + u64 mini_ctrl_cmd = 0; + int len = 0; + int ret; + + instr = &subop->instrs[op_id]; + + if (instr->delay_ns > 0) + mini_ctrl_cmd |= GCMD_LAY_TWB; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_DATA); + + if (instr->type == NAND_OP_DATA_OUT_INSTR) + mini_ctrl_cmd |= FIELD_PREP(GCMD_DIR, + GCMD_DIR_WRITE); + + len = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + mini_ctrl_cmd |= FIELD_PREP(GCMD_SECT_CNT, 1); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAST_SIZE, len); + if (instr->ctx.data.force_8bit) { + ret = cadence_nand_force_byte_access(chip, true); + if (ret) { + dev_err(cdns_ctrl->dev, + "cannot change byte access generic data cmd failed\n"); + return ret; + } + } + + ret = cadence_nand_generic_cmd_send(cdns_ctrl, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + if (ret) { + dev_err(cdns_ctrl->dev, "send generic data cmd failed\n"); + return ret; + } + + if (instr->type == NAND_OP_DATA_IN_INSTR) { + void *buf = instr->ctx.data.buf.in + offset; + + ret = cadence_nand_read_buf(cdns_ctrl, buf, len); + } else { + const void *buf = instr->ctx.data.buf.out + offset; + + ret = cadence_nand_write_buf(cdns_ctrl, buf, len); + } + + if (ret) { + dev_err(cdns_ctrl->dev, "data transfer failed for generic command\n"); + return ret; + } + + if (instr->ctx.data.force_8bit) { + ret = cadence_nand_force_byte_access(chip, false); + if (ret) { + dev_err(cdns_ctrl->dev, + "cannot change byte access generic data cmd failed\n"); + } + } + + return ret; +} + +static int cadence_nand_cmd_waitrdy(struct nand_chip *chip, + const struct nand_subop *subop) +{ + int status; + unsigned int op_id = 0; + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + const struct nand_op_instr *instr = &subop->instrs[op_id]; + u32 timeout_us = instr->ctx.waitrdy.timeout_ms * 1000; + + status = cadence_nand_wait_for_value(cdns_ctrl, RBN_SETINGS, + timeout_us, + BIT(cdns_chip->cs[chip->cur_cs]), + false); + return status; +} + +static const struct nand_op_parser cadence_nand_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_erase, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ERASE_ADDRESS_CYC), + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_opcode, + NAND_OP_PARSER_PAT_CMD_ELEM(false)), + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_address, + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYC)), + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_data, + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, MAX_DATA_SIZE)), + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_data, + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, MAX_DATA_SIZE)), + NAND_OP_PARSER_PATTERN( + cadence_nand_cmd_waitrdy, + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)) + ); + +static int cadence_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + int status = cadence_nand_select_target(chip); + + if (status) + return status; + + return nand_op_parser_exec_op(chip, &cadence_nand_op_parser, op, + check_only); +} + +static int cadence_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->bbm_len; + oobregion->length = cdns_chip->avail_oob_size + - cdns_chip->bbm_len; + + return 0; +} + +static int cadence_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->avail_oob_size; + oobregion->length = chip->ecc.total; + + return 0; +} + +static const struct mtd_ooblayout_ops cadence_nand_ooblayout_ops = { + .free = cadence_nand_ooblayout_free, + .ecc = cadence_nand_ooblayout_ecc, +}; + +static int calc_cycl(u32 timing, u32 clock) +{ + if (timing == 0 || clock == 0) + return 0; + + if ((timing % clock) > 0) + return timing / clock; + else + return timing / clock - 1; +} + +/* Calculate max data valid window. */ +static inline u32 calc_tdvw_max(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 board_delay_skew_min, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min + + board_delay_skew_min; +} + +/* Calculate data valid window. */ +static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 trea_max, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min - trea_max; +} + +static int +cadence_nand_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + const struct nand_sdr_timings *sdr; + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct cadence_nand_timings *t = &cdns_chip->timings; + u32 reg; + u32 board_delay = cdns_ctrl->board_delay; + u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL, + cdns_ctrl->nf_clk_rate); + u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt; + u32 tfeat_cnt, trhz_cnt, tvdly_cnt; + u32 trhw_cnt, twb_cnt, twh_cnt = 0, twhr_cnt; + u32 twp_cnt = 0, trp_cnt = 0, trh_cnt = 0; + u32 if_skew = cdns_ctrl->caps1->if_skew; + u32 board_delay_skew_min = board_delay - if_skew; + u32 board_delay_skew_max = board_delay + if_skew; + u32 dqs_sampl_res, phony_dqs_mod; + u32 tdvw, tdvw_min, tdvw_max; + u32 ext_rd_mode, ext_wr_mode; + u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0; + u32 sampling_point; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + memset(t, 0, sizeof(*t)); + /* Sampling point calculation. */ + + if (cdns_ctrl->caps2.is_phy_type_dll) + phony_dqs_mod = 2; + else + phony_dqs_mod = 1; + + dqs_sampl_res = clk_period / phony_dqs_mod; + + tdvw_min = sdr->tREA_max + board_delay_skew_max; + /* + * The idea of those calculation is to get the optimum value + * for tRP and tRH timings. If it is NOT possible to sample data + * with optimal tRP/tRH settings, the parameters will be extended. + * If clk_period is 50ns (the lowest value) this condition is met + * for asynchronous timing modes 1, 2, 3, 4 and 5. + * If clk_period is 20ns the condition is met only + * for asynchronous timing mode 5. + */ + if (sdr->tRC_min <= clk_period && + sdr->tRP_min <= (clk_period / 2) && + sdr->tREH_min <= (clk_period / 2)) { + /* Performance mode. */ + ext_rd_mode = 0; + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * and is not on the edge (ie. we have hold margin). + * If not extend the tRP timings. + */ + if (tdvw > 0) { + if (tdvw_max <= tdvw_min || + (tdvw_max % dqs_sampl_res) == 0) { + /* + * No valid sampling point so the RE pulse need + * to be widen widening by half clock cycle. + */ + ext_rd_mode = 1; + } + } else { + /* + * There is no valid window + * to be able to sample data the tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + ext_rd_mode = 1; + } + + } else { + /* Extended read mode. */ + u32 trh; + + ext_rd_mode = 1; + trp_cnt = calc_cycl(sdr->tRP_min, clk_period); + trh = sdr->tRC_min - ((trp_cnt + 1) * clk_period); + if (sdr->tREH_min >= trh) + trh_cnt = calc_cycl(sdr->tREH_min, clk_period); + else + trh_cnt = calc_cycl(trh, clk_period); + + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * or if it is at the edge check if previous is valid + * - if not extend the tRP timings. + */ + if (tdvw > 0) { + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + + if ((((tdvw_max / dqs_sampl_res) + * dqs_sampl_res) <= tdvw_min) || + (((tdvw_max % dqs_sampl_res) == 0) && + (((tdvw_max / dqs_sampl_res - 1) + * dqs_sampl_res) <= tdvw_min))) { + /* + * Data valid window width is lower than + * sampling resolution and do not hit any + * sampling point to be sure the sampling point + * will be found the RE low pulse width will be + * extended by one clock cycle. + */ + trp_cnt = trp_cnt + 1; + } + } else { + /* + * There is no valid window to be able to sample data. + * The tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + } + } + + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, ext_rd_mode); + + if (sdr->tWC_min <= clk_period && + (sdr->tWP_min + if_skew) <= (clk_period / 2) && + (sdr->tWH_min + if_skew) <= (clk_period / 2)) { + ext_wr_mode = 0; + } else { + u32 twh; + + ext_wr_mode = 1; + twp_cnt = calc_cycl(sdr->tWP_min + if_skew, clk_period); + if ((twp_cnt + 1) * clk_period < (sdr->tALS_min + if_skew)) + twp_cnt = calc_cycl(sdr->tALS_min + if_skew, + clk_period); + + twh = (sdr->tWC_min - (twp_cnt + 1) * clk_period); + if (sdr->tWH_min >= twh) + twh = sdr->tWH_min; + + twh_cnt = calc_cycl(twh + if_skew, clk_period); + } + + reg = FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRH, trh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRP, trp_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWH, twh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWP, twp_cnt); + t->async_toggle_timings = reg; + dev_dbg(cdns_ctrl->dev, "ASYNC_TOGGLE_TIMINGS_SDR\t%x\n", reg); + + tadl_cnt = calc_cycl((sdr->tADL_min + if_skew), clk_period); + tccs_cnt = calc_cycl((sdr->tCCS_min + if_skew), clk_period); + twhr_cnt = calc_cycl((sdr->tWHR_min + if_skew), clk_period); + trhw_cnt = calc_cycl((sdr->tRHW_min + if_skew), clk_period); + reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt); + + /* + * If timing exceeds delay field in timing register + * then use maximum value. + */ + if (FIELD_FIT(TIMINGS0_TCCS, tccs_cnt)) + reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt); + else + reg |= TIMINGS0_TCCS; + + reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt); + reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt); + t->timings0 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS0_SDR\t%x\n", reg); + + /* The following is related to single signal so skew is not needed. */ + trhz_cnt = calc_cycl(sdr->tRHZ_max, clk_period); + trhz_cnt = trhz_cnt + 1; + twb_cnt = calc_cycl((sdr->tWB_max + board_delay), clk_period); + /* + * Because of the two stage syncflop the value must be increased by 3 + * first value is related with sync, second value is related + * with output if delay. + */ + twb_cnt = twb_cnt + 3 + 5; + /* + * The following is related to the we edge of the random data input + * sequence so skew is not needed. + */ + tvdly_cnt = calc_cycl(500000 + if_skew, clk_period); + reg = FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt); + reg |= FIELD_PREP(TIMINGS1_TWB, twb_cnt); + reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt); + t->timings1 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS1_SDR\t%x\n", reg); + + tfeat_cnt = calc_cycl(sdr->tFEAT_max, clk_period); + if (tfeat_cnt < twb_cnt) + tfeat_cnt = twb_cnt; + + tceh_cnt = calc_cycl(sdr->tCEH_min, clk_period); + tcs_cnt = calc_cycl((sdr->tCS_min + if_skew), clk_period); + + reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt); + t->timings2 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS2_SDR\t%x\n", reg); + + if (cdns_ctrl->caps2.is_phy_type_dll) { + reg = DLL_PHY_CTRL_DLL_RST_N; + if (ext_wr_mode) + reg |= DLL_PHY_CTRL_EXTENDED_WR_MODE; + if (ext_rd_mode) + reg |= DLL_PHY_CTRL_EXTENDED_RD_MODE; + + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, 7); + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, 7); + t->dll_phy_ctrl = reg; + dev_dbg(cdns_ctrl->dev, "DLL_PHY_CTRL_SDR\t%x\n", reg); + } + + /* Sampling point calculation. */ + if ((tdvw_max % dqs_sampl_res) > 0) + sampling_point = tdvw_max / dqs_sampl_res; + else + sampling_point = (tdvw_max / dqs_sampl_res - 1); + + if (sampling_point * dqs_sampl_res > tdvw_min) { + dll_phy_dqs_timing = + FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, 4); + dll_phy_dqs_timing |= PHY_DQS_TIMING_USE_PHONY_DQS; + phony_dqs_timing = sampling_point / phony_dqs_mod; + + if ((sampling_point % 2) > 0) { + dll_phy_dqs_timing |= PHY_DQS_TIMING_PHONY_DQS_SEL; + if ((tdvw_max % dqs_sampl_res) == 0) + /* + * Calculation for sampling point at the edge + * of data and being odd number. + */ + phony_dqs_timing = (tdvw_max / dqs_sampl_res) + / phony_dqs_mod - 1; + + if (!cdns_ctrl->caps2.is_phy_type_dll) + phony_dqs_timing--; + + } else { + phony_dqs_timing--; + } + rd_del_sel = phony_dqs_timing + 3; + } else { + dev_warn(cdns_ctrl->dev, + "ERROR : cannot find valid sampling point\n"); + } + + reg = FIELD_PREP(PHY_CTRL_PHONY_DQS, phony_dqs_timing); + if (cdns_ctrl->caps2.is_phy_type_dll) + reg |= PHY_CTRL_SDR_DQS; + t->phy_ctrl = reg; + dev_dbg(cdns_ctrl->dev, "PHY_CTRL_REG_SDR\t%x\n", reg); + + if (cdns_ctrl->caps2.is_phy_type_dll) { + dev_dbg(cdns_ctrl->dev, "PHY_TSEL_REG_SDR\t%x\n", 0); + dev_dbg(cdns_ctrl->dev, "PHY_DQ_TIMING_REG_SDR\t%x\n", 2); + dev_dbg(cdns_ctrl->dev, "PHY_DQS_TIMING_REG_SDR\t%x\n", + dll_phy_dqs_timing); + t->phy_dqs_timing = dll_phy_dqs_timing; + + reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel); + dev_dbg(cdns_ctrl->dev, "PHY_GATE_LPBK_CTRL_REG_SDR\t%x\n", + reg); + t->phy_gate_lpbk_ctrl = reg; + + dev_dbg(cdns_ctrl->dev, "PHY_DLL_MASTER_CTRL_REG_SDR\t%lx\n", + PHY_DLL_MASTER_CTRL_BYPASS_MODE); + dev_dbg(cdns_ctrl->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0); + } + + return 0; +} + +int cadence_nand_attach_chip(struct nand_chip *chip) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u32 ecc_size = cdns_chip->sector_count * chip->ecc.bytes; + struct mtd_info *mtd = nand_to_mtd(chip); + u32 max_oob_data_size; + int ret; + + if (chip->options & NAND_BUSWIDTH_16) { + ret = cadence_nand_set_access_width16(cdns_ctrl, true); + if (ret) + return ret; + } + + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->bbt_options |= NAND_BBT_NO_OOB; + chip->ecc.mode = NAND_ECC_HW; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + cdns_chip->bbm_offs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + cdns_chip->bbm_offs &= ~0x01; + cdns_chip->bbm_len = 2; + } else { + cdns_chip->bbm_len = 1; + } + + ret = nand_ecc_choose_conf(chip, + &cdns_ctrl->ecc_caps, + mtd->oobsize - cdns_chip->bbm_len); + if (ret) { + dev_err(cdns_ctrl->dev, "ECC configuration failed\n"); + return ret; + } + + dev_dbg(cdns_ctrl->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + /* Error correction configuration. */ + cdns_chip->sector_size = chip->ecc.size; + cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size; + + cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; + + max_oob_data_size = MAX_OOB_SIZE_PER_SECTOR; + + if (cdns_chip->avail_oob_size > max_oob_data_size) + cdns_chip->avail_oob_size = max_oob_data_size; + + if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size) + > mtd->oobsize) + cdns_chip->avail_oob_size -= 4; + + ret = cadence_nand_get_ecc_strength_idx(cdns_ctrl, chip->ecc.strength); + if (ret < 0) + return -EINVAL; + + cdns_chip->corr_str_idx = (u8)ret; + + if (cadence_nand_wait_for_value(cdns_ctrl, CTRL_STATUS, + 1000000, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_ecc_strength(cdns_ctrl, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cdns_ctrl, true, + chip->ecc.strength); + + /* Override the default read operations. */ + chip->ecc.read_page = cadence_nand_read_page; + chip->ecc.read_page_raw = cadence_nand_read_page_raw; + chip->ecc.write_page = cadence_nand_write_page; + chip->ecc.write_page_raw = cadence_nand_write_page_raw; + chip->ecc.read_oob = cadence_nand_read_oob; + chip->ecc.write_oob = cadence_nand_write_oob; + chip->ecc.read_oob_raw = cadence_nand_read_oob_raw; + chip->ecc.write_oob_raw = cadence_nand_write_oob_raw; + + if ((mtd->writesize + mtd->oobsize) > cdns_ctrl->buf_size) + cdns_ctrl->buf_size = mtd->writesize + mtd->oobsize; + + /* Is 32-bit DMA supported? */ + ret = dma_set_mask(cdns_ctrl->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(cdns_ctrl->dev, "no usable DMA configuration\n"); + return ret; + } + + mtd_set_ooblayout(mtd, &cadence_nand_ooblayout_ops); + + return 0; +} + +static const struct nand_controller_ops cadence_nand_controller_ops = { + .attach_chip = cadence_nand_attach_chip, + .exec_op = cadence_nand_exec_op, + .setup_data_interface = cadence_nand_setup_data_interface, +}; + +static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl, + struct device_node *np) +{ + struct cdns_nand_chip *cdns_chip; + struct mtd_info *mtd; + struct nand_chip *chip; + int nsels, ret, i; + u32 cs; + + nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); + if (nsels <= 0) { + dev_err(cdns_ctrl->dev, "missing/invalid reg property\n"); + return -EINVAL; + } + + /* Allocate the nand chip structure. */ + cdns_chip = devm_kzalloc(cdns_ctrl->dev, sizeof(*cdns_chip) + + (nsels * sizeof(u8)), + GFP_KERNEL); + if (!cdns_chip) { + dev_err(cdns_ctrl->dev, "could not allocate chip structure\n"); + return -ENOMEM; + } + + cdns_chip->nsels = nsels; + + for (i = 0; i < nsels; i++) { + /* Retrieve CS id. */ + ret = of_property_read_u32_index(np, "reg", i, &cs); + if (ret) { + dev_err(cdns_ctrl->dev, + "could not retrieve reg property: %d\n", + ret); + return ret; + } + + if (cs >= cdns_ctrl->caps2.max_banks) { + dev_err(cdns_ctrl->dev, + "invalid reg value: %u (max CS = %d)\n", + cs, cdns_ctrl->caps2.max_banks); + return -EINVAL; + } + + if (test_and_set_bit(cs, &cdns_ctrl->assigned_cs)) { + dev_err(cdns_ctrl->dev, + "CS %d already assigned\n", cs); + return -EINVAL; + } + + cdns_chip->cs[i] = cs; + } + + chip = &cdns_chip->chip; + chip->controller = &cdns_ctrl->controller; + nand_set_flash_node(chip, np); + + mtd = nand_to_mtd(chip); + mtd->dev.parent = cdns_ctrl->dev; + + /* + * Default to HW ECC engine mode. If the nand-ecc-mode property is given + * in the DT node, this entry will be overwritten in nand_scan_ident(). + */ + chip->ecc.mode = NAND_ECC_HW; + + ret = nand_scan(chip, cdns_chip->nsels); + if (ret) { + dev_err(cdns_ctrl->dev, "could not scan the nand chip\n"); + return ret; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(cdns_ctrl->dev, + "failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + list_add_tail(&cdns_chip->node, &cdns_ctrl->chips); + + return 0; +} + +static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl) +{ + struct cdns_nand_chip *entry, *temp; + + list_for_each_entry_safe(entry, temp, &cdns_ctrl->chips, node) { + nand_release(&entry->chip); + list_del(&entry->node); + } +} + +static int cadence_nand_chips_init(struct cdns_nand_ctrl *cdns_ctrl) +{ + struct device_node *np = cdns_ctrl->dev->of_node; + struct device_node *nand_np; + int max_cs = cdns_ctrl->caps2.max_banks; + int nchips, ret; + + nchips = of_get_child_count(np); + + if (nchips > max_cs) { + dev_err(cdns_ctrl->dev, + "too many NAND chips: %d (max = %d CS)\n", + nchips, max_cs); + return -EINVAL; + } + + for_each_child_of_node(np, nand_np) { + ret = cadence_nand_chip_init(cdns_ctrl, nand_np); + if (ret) { + of_node_put(nand_np); + cadence_nand_chips_cleanup(cdns_ctrl); + return ret; + } + } + + return 0; +} + +static void +cadence_nand_irq_cleanup(int irqnum, struct cdns_nand_ctrl *cdns_ctrl) +{ + /* Disable interrupts. */ + writel_relaxed(INTR_ENABLE_INTR_EN, cdns_ctrl->reg + INTR_ENABLE); +} + +static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) +{ + dma_cap_mask_t mask; + int ret; + + cdns_ctrl->cdma_desc = dma_alloc_coherent(cdns_ctrl->dev, + sizeof(*cdns_ctrl->cdma_desc), + &cdns_ctrl->dma_cdma_desc, + GFP_KERNEL); + if (!cdns_ctrl->dma_cdma_desc) + return -ENOMEM; + + cdns_ctrl->buf_size = SZ_16K; + cdns_ctrl->buf = kmalloc(cdns_ctrl->buf_size, GFP_KERNEL); + if (!cdns_ctrl->buf) { + ret = -ENOMEM; + goto free_buf_desc; + } + + if (devm_request_irq(cdns_ctrl->dev, cdns_ctrl->irq, cadence_nand_isr, + IRQF_SHARED, "cadence-nand-controller", + cdns_ctrl)) { + dev_err(cdns_ctrl->dev, "Unable to allocate IRQ\n"); + ret = -ENODEV; + goto free_buf; + } + + spin_lock_init(&cdns_ctrl->irq_lock); + init_completion(&cdns_ctrl->complete); + + ret = cadence_nand_hw_init(cdns_ctrl); + if (ret) + goto disable_irq; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + if (cdns_ctrl->caps1->has_dma) { + cdns_ctrl->dmac = dma_request_channel(mask, NULL, NULL); + if (!cdns_ctrl->dmac) { + dev_err(cdns_ctrl->dev, + "Unable to get a DMA channel\n"); + ret = -EBUSY; + goto disable_irq; + } + } + + nand_controller_init(&cdns_ctrl->controller); + INIT_LIST_HEAD(&cdns_ctrl->chips); + + cdns_ctrl->controller.ops = &cadence_nand_controller_ops; + cdns_ctrl->curr_corr_str_idx = 0xFF; + + ret = cadence_nand_chips_init(cdns_ctrl); + if (ret) { + dev_err(cdns_ctrl->dev, "Failed to register MTD: %d\n", + ret); + goto dma_release_chnl; + } + + kfree(cdns_ctrl->buf); + cdns_ctrl->buf = kzalloc(cdns_ctrl->buf_size, GFP_KERNEL); + if (!cdns_ctrl->buf) { + ret = -ENOMEM; + goto dma_release_chnl; + } + + return 0; + +dma_release_chnl: + if (cdns_ctrl->dmac) + dma_release_channel(cdns_ctrl->dmac); + +disable_irq: + cadence_nand_irq_cleanup(cdns_ctrl->irq, cdns_ctrl); + +free_buf: + kfree(cdns_ctrl->buf); + +free_buf_desc: + dma_free_coherent(cdns_ctrl->dev, sizeof(struct cadence_nand_cdma_desc), + cdns_ctrl->cdma_desc, cdns_ctrl->dma_cdma_desc); + + return ret; +} + +/* Driver exit point. */ +static void cadence_nand_remove(struct cdns_nand_ctrl *cdns_ctrl) +{ + cadence_nand_chips_cleanup(cdns_ctrl); + cadence_nand_irq_cleanup(cdns_ctrl->irq, cdns_ctrl); + kfree(cdns_ctrl->buf); + dma_free_coherent(cdns_ctrl->dev, sizeof(struct cadence_nand_cdma_desc), + cdns_ctrl->cdma_desc, cdns_ctrl->dma_cdma_desc); + + if (cdns_ctrl->dmac) + dma_release_channel(cdns_ctrl->dmac); +} + +struct cadence_nand_dt { + struct cdns_nand_ctrl cdns_ctrl; + struct clk *clk; +}; + +static const struct cadence_nand_dt_devdata cadence_nand_default = { + .if_skew = 0, + .has_dma = 1, +}; + +static const struct of_device_id cadence_nand_dt_ids[] = { + { + .compatible = "cdns,hp-nfc", + .data = &cadence_nand_default + }, {} +}; + +MODULE_DEVICE_TABLE(of, cadence_nand_dt_ids); + +static int cadence_nand_dt_probe(struct platform_device *ofdev) +{ + struct resource *res; + struct cadence_nand_dt *dt; + struct cdns_nand_ctrl *cdns_ctrl; + int ret; + const struct of_device_id *of_id; + const struct cadence_nand_dt_devdata *devdata; + u32 val; + + of_id = of_match_device(cadence_nand_dt_ids, &ofdev->dev); + if (of_id) { + ofdev->id_entry = of_id->data; + devdata = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + + dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + + cdns_ctrl = &dt->cdns_ctrl; + cdns_ctrl->caps1 = devdata; + + cdns_ctrl->dev = &ofdev->dev; + cdns_ctrl->irq = platform_get_irq(ofdev, 0); + if (cdns_ctrl->irq < 0) + return cdns_ctrl->irq; + + dev_info(cdns_ctrl->dev, "IRQ: nr %d\n", cdns_ctrl->irq); + + cdns_ctrl->reg = devm_platform_ioremap_resource(ofdev, 0); + if (IS_ERR(cdns_ctrl->reg)) { + dev_err(&ofdev->dev, "devm_ioremap_resource res 0 failed\n"); + return PTR_ERR(cdns_ctrl->reg); + } + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); + cdns_ctrl->io.dma = res->start; + cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(cdns_ctrl->io.virt)) { + dev_err(cdns_ctrl->dev, "devm_ioremap_resource res 1 failed\n"); + return PTR_ERR(cdns_ctrl->io.virt); + } + + dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk"); + if (IS_ERR(dt->clk)) + return PTR_ERR(dt->clk); + + cdns_ctrl->nf_clk_rate = clk_get_rate(dt->clk); + + ret = of_property_read_u32(ofdev->dev.of_node, + "cdns,board-delay-ps", &val); + if (ret) { + val = 4830; + dev_info(cdns_ctrl->dev, + "missing cdns,board-delay-ps property, %d was set\n", + val); + } + cdns_ctrl->board_delay = val; + + ret = cadence_nand_init(cdns_ctrl); + if (ret) + return ret; + + platform_set_drvdata(ofdev, dt); + return 0; +} + +static int cadence_nand_dt_remove(struct platform_device *ofdev) +{ + struct cadence_nand_dt *dt = platform_get_drvdata(ofdev); + + cadence_nand_remove(&dt->cdns_ctrl); + + return 0; +} + +static struct platform_driver cadence_nand_dt_driver = { + .probe = cadence_nand_dt_probe, + .remove = cadence_nand_dt_remove, + .driver = { + .name = "cadence-nand-controller", + .of_match_table = cadence_nand_dt_ids, + }, +}; + +module_platform_driver(cadence_nand_dt_driver); + +MODULE_AUTHOR("Piotr Sroka <piotrs@cadence.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Driver for Cadence NAND flash controller"); + diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 5e14836f6bd5..8b779a899dcf 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -102,47 +102,6 @@ static int denali_dt_chip_init(struct denali_controller *denali, return denali_chip_init(denali, dchip); } -/* Backward compatibility for old platforms */ -static int denali_dt_legacy_chip_init(struct denali_controller *denali) -{ - struct denali_chip *dchip; - int nsels, i; - - nsels = denali->nbanks; - - dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels), - GFP_KERNEL); - if (!dchip) - return -ENOMEM; - - dchip->nsels = nsels; - - for (i = 0; i < nsels; i++) - dchip->sels[i].bank = i; - - nand_set_flash_node(&dchip->chip, denali->dev->of_node); - - return denali_chip_init(denali, dchip); -} - -/* - * Check the DT binding. - * The new binding expects chip subnodes in the controller node. - * So, #address-cells = <1>; #size-cells = <0>; are required. - * Check the #size-cells to distinguish the binding. - */ -static bool denali_dt_is_legacy_binding(struct device_node *np) -{ - u32 cells; - int ret; - - ret = of_property_read_u32(np, "#size-cells", &cells); - if (ret) - return true; - - return cells != 0; -} - static int denali_dt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -167,10 +126,8 @@ static int denali_dt_probe(struct platform_device *pdev) denali->dev = dev; denali->irq = platform_get_irq(pdev, 0); - if (denali->irq < 0) { - dev_err(dev, "no irq defined\n"); + if (denali->irq < 0) return denali->irq; - } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg"); denali->reg = devm_ioremap_resource(dev, res); @@ -213,17 +170,11 @@ static int denali_dt_probe(struct platform_device *pdev) if (ret) goto out_disable_clk_ecc; - if (denali_dt_is_legacy_binding(dev->of_node)) { - ret = denali_dt_legacy_chip_init(denali); - if (ret) + for_each_child_of_node(dev->of_node, np) { + ret = denali_dt_chip_init(denali, np); + if (ret) { + of_node_put(np); goto out_remove_denali; - } else { - for_each_child_of_node(dev->of_node, np) { - ret = denali_dt_chip_init(denali, np); - if (ret) { - of_node_put(np); - goto out_remove_denali; - } } } diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 6c7ca41354be..a6964feeec77 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -613,28 +613,20 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, for (op_id = 0; op_id < op->ninstrs; op_id++) { instr = &op->instrs[op_id]; + nand_op_trace(" ", instr); + switch (instr->type) { case NAND_OP_CMD_INSTR: - pr_debug(" ->CMD [0x%02x]\n", - instr->ctx.cmd.opcode); - writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va); break; case NAND_OP_ADDR_INSTR: - pr_debug(" ->ADDR [%d cyc]", - instr->ctx.addr.naddrs); - for (i = 0; i < instr->ctx.addr.naddrs; i++) writeb_relaxed(instr->ctx.addr.addrs[i], host->addr_va); break; case NAND_OP_DATA_IN_INSTR: - pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - if (host->mode == USE_DMA_ACCESS) fsmc_read_buf_dma(host, instr->ctx.data.buf.in, instr->ctx.data.len); @@ -644,10 +636,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, break; case NAND_OP_DATA_OUT_INSTR: - pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - if (host->mode == USE_DMA_ACCESS) fsmc_write_buf_dma(host, instr->ctx.data.buf.out, @@ -658,9 +646,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, break; case NAND_OP_WAITRDY_INSTR: - pr_debug(" ->WAITRDY [max %d ms]\n", - instr->ctx.waitrdy.timeout_ms); - ret = nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); break; diff --git a/drivers/mtd/nand/raw/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile index 30ceee9704d1..9bd81a31e02e 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/Makefile +++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o gpmi_nand-objs += gpmi-nand.o -gpmi_nand-objs += gpmi-lib.o diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c deleted file mode 100644 index a8b26d2e793c..000000000000 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Freescale GPMI NAND Flash Driver - * - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. - * Copyright (C) 2008 Embedded Alley Solutions, Inc. - */ -#include <linux/delay.h> -#include <linux/clk.h> -#include <linux/slab.h> - -#include "gpmi-nand.h" -#include "gpmi-regs.h" -#include "bch-regs.h" - -/* Converts time to clock cycles */ -#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) - -#define MXS_SET_ADDR 0x4 -#define MXS_CLR_ADDR 0x8 -/* - * Clear the bit and poll it cleared. This is usually called with - * a reset address and mask being either SFTRST(bit 31) or CLKGATE - * (bit 30). - */ -static int clear_poll_bit(void __iomem *addr, u32 mask) -{ - int timeout = 0x400; - - /* clear the bit */ - writel(mask, addr + MXS_CLR_ADDR); - - /* - * SFTRST needs 3 GPMI clocks to settle, the reference manual - * recommends to wait 1us. - */ - udelay(1); - - /* poll the bit becoming clear */ - while ((readl(addr) & mask) && --timeout) - /* nothing */; - - return !timeout; -} - -#define MODULE_CLKGATE (1 << 30) -#define MODULE_SFTRST (1 << 31) -/* - * The current mxs_reset_block() will do two things: - * [1] enable the module. - * [2] reset the module. - * - * In most of the cases, it's ok. - * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). - * If you try to soft reset the BCH block, it becomes unusable until - * the next hard reset. This case occurs in the NAND boot mode. When the board - * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. - * So If the driver tries to reset the BCH again, the BCH will not work anymore. - * You will see a DMA timeout in this case. The bug has been fixed - * in the following chips, such as MX28. - * - * To avoid this bug, just add a new parameter `just_enable` for - * the mxs_reset_block(), and rewrite it here. - */ -static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) -{ - int ret; - int timeout = 0x400; - - /* clear and poll SFTRST */ - ret = clear_poll_bit(reset_addr, MODULE_SFTRST); - if (unlikely(ret)) - goto error; - - /* clear CLKGATE */ - writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); - - if (!just_enable) { - /* set SFTRST to reset the block */ - writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); - udelay(1); - - /* poll CLKGATE becoming set */ - while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) - /* nothing */; - if (unlikely(!timeout)) - goto error; - } - - /* clear and poll SFTRST */ - ret = clear_poll_bit(reset_addr, MODULE_SFTRST); - if (unlikely(ret)) - goto error; - - /* clear and poll CLKGATE */ - ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); - if (unlikely(ret)) - goto error; - - return 0; - -error: - pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); - return -ETIMEDOUT; -} - -static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) -{ - struct clk *clk; - int ret; - int i; - - for (i = 0; i < GPMI_CLK_MAX; i++) { - clk = this->resources.clock[i]; - if (!clk) - break; - - if (v) { - ret = clk_prepare_enable(clk); - if (ret) - goto err_clk; - } else { - clk_disable_unprepare(clk); - } - } - return 0; - -err_clk: - for (; i > 0; i--) - clk_disable_unprepare(this->resources.clock[i - 1]); - return ret; -} - -int gpmi_enable_clk(struct gpmi_nand_data *this) -{ - return __gpmi_enable_clk(this, true); -} - -int gpmi_disable_clk(struct gpmi_nand_data *this) -{ - return __gpmi_enable_clk(this, false); -} - -int gpmi_init(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - int ret; - - ret = gpmi_enable_clk(this); - if (ret) - return ret; - ret = gpmi_reset_block(r->gpmi_regs, false); - if (ret) - goto err_out; - - /* - * Reset BCH here, too. We got failures otherwise :( - * See later BCH reset for explanation of MX23 and MX28 handling - */ - ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); - if (ret) - goto err_out; - - /* Choose NAND mode. */ - writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); - - /* Set the IRQ polarity. */ - writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, - r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* Disable Write-Protection. */ - writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* Select BCH ECC. */ - writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - /* - * Decouple the chip select from dma channel. We use dma0 for all - * the chips. - */ - writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); - - gpmi_disable_clk(this); - return 0; -err_out: - gpmi_disable_clk(this); - return ret; -} - -/* This function is very useful. It is called only when the bug occur. */ -void gpmi_dump_info(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - struct bch_geometry *geo = &this->bch_geometry; - u32 reg; - int i; - - dev_err(this->dev, "Show GPMI registers :\n"); - for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { - reg = readl(r->gpmi_regs + i * 0x10); - dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); - } - - /* start to print out the BCH info */ - dev_err(this->dev, "Show BCH registers :\n"); - for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { - reg = readl(r->bch_regs + i * 0x10); - dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); - } - dev_err(this->dev, "BCH Geometry :\n" - "GF length : %u\n" - "ECC Strength : %u\n" - "Page Size in Bytes : %u\n" - "Metadata Size in Bytes : %u\n" - "ECC Chunk Size in Bytes: %u\n" - "ECC Chunk Count : %u\n" - "Payload Size in Bytes : %u\n" - "Auxiliary Size in Bytes: %u\n" - "Auxiliary Status Offset: %u\n" - "Block Mark Byte Offset : %u\n" - "Block Mark Bit Offset : %u\n", - geo->gf_len, - geo->ecc_strength, - geo->page_size, - geo->metadata_size, - geo->ecc_chunk_size, - geo->ecc_chunk_count, - geo->payload_size, - geo->auxiliary_size, - geo->auxiliary_status_offset, - geo->block_mark_byte_offset, - geo->block_mark_bit_offset); -} - -/* Configures the geometry for BCH. */ -int bch_set_geometry(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - struct bch_geometry *bch_geo = &this->bch_geometry; - unsigned int block_count; - unsigned int block_size; - unsigned int metadata_size; - unsigned int ecc_strength; - unsigned int page_size; - unsigned int gf_len; - int ret; - - ret = common_nfc_set_geometry(this); - if (ret) - return ret; - - block_count = bch_geo->ecc_chunk_count - 1; - block_size = bch_geo->ecc_chunk_size; - metadata_size = bch_geo->metadata_size; - ecc_strength = bch_geo->ecc_strength >> 1; - page_size = bch_geo->page_size; - gf_len = bch_geo->gf_len; - - ret = gpmi_enable_clk(this); - if (ret) - return ret; - - /* - * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this - * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. - * and MX28. - */ - ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); - if (ret) - goto err_out; - - /* Configure layout 0. */ - writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) - | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) - | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) - | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) - | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), - r->bch_regs + HW_BCH_FLASH0LAYOUT0); - - writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) - | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) - | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) - | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), - r->bch_regs + HW_BCH_FLASH0LAYOUT1); - - /* Set *all* chip selects to use layout 0. */ - writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); - - /* Enable interrupts. */ - writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, - r->bch_regs + HW_BCH_CTRL_SET); - - gpmi_disable_clk(this); - return 0; -err_out: - gpmi_disable_clk(this); - return ret; -} - -/* - * <1> Firstly, we should know what's the GPMI-clock means. - * The GPMI-clock is the internal clock in the gpmi nand controller. - * If you set 100MHz to gpmi nand controller, the GPMI-clock's period - * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. - * - * <2> Secondly, we should know what's the frequency on the nand chip pins. - * The frequency on the nand chip pins is derived from the GPMI-clock. - * We can get it from the following equation: - * - * F = G / (DS + DH) - * - * F : the frequency on the nand chip pins. - * G : the GPMI clock, such as 100MHz. - * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP - * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD - * - * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, - * the nand EDO(extended Data Out) timing could be applied. - * The GPMI implements a feedback read strobe to sample the read data. - * The feedback read strobe can be delayed to support the nand EDO timing - * where the read strobe may deasserts before the read data is valid, and - * read data is valid for some time after read strobe. - * - * The following figure illustrates some aspects of a NAND Flash read: - * - * |<---tREA---->| - * | | - * | | | - * |<--tRP-->| | - * | | | - * __ ___|__________________________________ - * RDN \________/ | - * | - * /---------\ - * Read Data --------------< >--------- - * \---------/ - * | | - * |<-D->| - * FeedbackRDN ________ ____________ - * \___________/ - * - * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. - * - * - * <4> Now, we begin to describe how to compute the right RDN_DELAY. - * - * 4.1) From the aspect of the nand chip pins: - * Delay = (tREA + C - tRP) {1} - * - * tREA : the maximum read access time. - * C : a constant to adjust the delay. default is 4000ps. - * tRP : the read pulse width, which is exactly: - * tRP = (GPMI-clock-period) * DATA_SETUP - * - * 4.2) From the aspect of the GPMI nand controller: - * Delay = RDN_DELAY * 0.125 * RP {2} - * - * RP : the DLL reference period. - * if (GPMI-clock-period > DLL_THRETHOLD) - * RP = GPMI-clock-period / 2; - * else - * RP = GPMI-clock-period; - * - * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period - * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD - * is 16000ps, but in mx6q, we use 12000ps. - * - * 4.3) since {1} equals {2}, we get: - * - * (tREA + 4000 - tRP) * 8 - * RDN_DELAY = ----------------------- {3} - * RP - */ -static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, - const struct nand_sdr_timings *sdr) -{ - struct gpmi_nfc_hardware_timing *hw = &this->hw; - unsigned int dll_threshold_ps = this->devdata->max_chain_delay; - unsigned int period_ps, reference_period_ps; - unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; - unsigned int tRP_ps; - bool use_half_period; - int sample_delay_ps, sample_delay_factor; - u16 busy_timeout_cycles; - u8 wrn_dly_sel; - - if (sdr->tRC_min >= 30000) { - /* ONFI non-EDO modes [0-3] */ - hw->clk_rate = 22000000; - wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; - } else if (sdr->tRC_min >= 25000) { - /* ONFI EDO mode 4 */ - hw->clk_rate = 80000000; - wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; - } else { - /* ONFI EDO mode 5 */ - hw->clk_rate = 100000000; - wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; - } - - /* SDR core timings are given in picoseconds */ - period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); - - addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); - data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); - data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); - busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps); - - hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | - BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | - BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); - hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); - - /* - * Derive NFC ideal delay from {3}: - * - * (tREA + 4000 - tRP) * 8 - * RDN_DELAY = ----------------------- - * RP - */ - if (period_ps > dll_threshold_ps) { - use_half_period = true; - reference_period_ps = period_ps / 2; - } else { - use_half_period = false; - reference_period_ps = period_ps; - } - - tRP_ps = data_setup_cycles * period_ps; - sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; - if (sample_delay_ps > 0) - sample_delay_factor = sample_delay_ps / reference_period_ps; - else - sample_delay_factor = 0; - - hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel); - if (sample_delay_factor) - hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | - BM_GPMI_CTRL1_DLL_ENABLE | - (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); -} - -void gpmi_nfc_apply_timings(struct gpmi_nand_data *this) -{ - struct gpmi_nfc_hardware_timing *hw = &this->hw; - struct resources *r = &this->resources; - void __iomem *gpmi_regs = r->gpmi_regs; - unsigned int dll_wait_time_us; - - clk_set_rate(r->clock[0], hw->clk_rate); - - writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0); - writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1); - - /* - * Clear several CTRL1 fields, DLL must be disabled when setting - * RDN_DELAY or HALF_PERIOD. - */ - writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR); - writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET); - - /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ - dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; - if (!dll_wait_time_us) - dll_wait_time_us = 1; - - /* Wait for the DLL to settle. */ - udelay(dll_wait_time_us); -} - -int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr, - const struct nand_data_interface *conf) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - const struct nand_sdr_timings *sdr; - - /* Retrieve required NAND timings */ - sdr = nand_get_sdr_timings(conf); - if (IS_ERR(sdr)) - return PTR_ERR(sdr); - - /* Only MX6 GPMI controller can reach EDO timings */ - if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) - return -ENOTSUPP; - - /* Stop here if this call was just a check */ - if (chipnr < 0) - return 0; - - /* Do the actual derivation of the controller timings */ - gpmi_nfc_compute_timings(this, sdr); - - this->hw.must_apply_timings = true; - - return 0; -} - -/* Clears a BCH interrupt. */ -void gpmi_clear_bch(struct gpmi_nand_data *this) -{ - struct resources *r = &this->resources; - writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); -} - -/* Returns the Ready/Busy status of the given chip. */ -int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) -{ - struct resources *r = &this->resources; - uint32_t mask = 0; - uint32_t reg = 0; - - if (GPMI_IS_MX23(this)) { - mask = MX23_BM_GPMI_DEBUG_READY0 << chip; - reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); - } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) { - /* - * In the imx6, all the ready/busy pins are bound - * together. So we only need to check chip 0. - */ - if (GPMI_IS_MX6(this)) - chip = 0; - - /* MX28 shares the same R/B register as MX6Q. */ - mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); - reg = readl(r->gpmi_regs + HW_GPMI_STAT); - } else - dev_err(this->dev, "unknown arch.\n"); - return reg & mask; -} - -int gpmi_send_command(struct gpmi_nand_data *this) -{ - struct dma_chan *channel = get_dma_chan(this); - struct dma_async_tx_descriptor *desc; - struct scatterlist *sgl; - int chip = this->current_chip; - int ret; - u32 pio[3]; - - /* [1] send out the PIO words */ - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) - | BM_GPMI_CTRL0_ADDRESS_INCREMENT - | BF_GPMI_CTRL0_XFER_COUNT(this->command_length); - pio[1] = pio[2] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ - sgl = &this->cmd_sgl; - - sg_init_one(sgl, this->cmd_buffer, this->command_length); - dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); - desc = dmaengine_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] submit the DMA */ - ret = start_dma_without_bch_irq(this, desc); - - dma_unmap_sg(this->dev, sgl, 1, DMA_TO_DEVICE); - - return ret; -} - -int gpmi_send_data(struct gpmi_nand_data *this, const void *buf, int len) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - int ret; - uint32_t command_mode; - uint32_t address; - u32 pio[2]; - - /* [1] PIO */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(len); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] send DMA request */ - prepare_data_dma(this, buf, len, DMA_TO_DEVICE); - desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] submit the DMA */ - ret = start_dma_without_bch_irq(this, desc); - - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); - - return ret; -} - -int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) -{ - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - int ret; - u32 pio[2]; - bool direct; - - /* [1] : send PIO */ - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) - | BF_GPMI_CTRL0_XFER_COUNT(len); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] : send DMA request */ - direct = prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); - desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] : submit the DMA */ - - ret = start_dma_without_bch_irq(this, desc); - - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (!direct) - memcpy(buf, this->data_buffer_dma, len); - - return ret; -} - -int gpmi_send_page(struct gpmi_nand_data *this, - dma_addr_t payload, dma_addr_t auxiliary) -{ - struct bch_geometry *geo = &this->bch_geometry; - uint32_t command_mode; - uint32_t address; - uint32_t ecc_command; - uint32_t buffer_mask; - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - u32 pio[6]; - - /* A DMA descriptor that does an ECC page read. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; - buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | - BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(0); - pio[1] = 0; - pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC - | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) - | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; - pio[4] = payload; - pio[5] = auxiliary; - - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, - DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - return start_dma_with_bch_irq(this, desc); -} - -int gpmi_read_page(struct gpmi_nand_data *this, - dma_addr_t payload, dma_addr_t auxiliary) -{ - struct bch_geometry *geo = &this->bch_geometry; - uint32_t command_mode; - uint32_t address; - uint32_t ecc_command; - uint32_t buffer_mask; - struct dma_async_tx_descriptor *desc; - struct dma_chan *channel = get_dma_chan(this); - int chip = this->current_chip; - u32 pio[6]; - - /* [1] Wait for the chip to report ready. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(0); - pio[1] = 0; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, - DMA_TRANS_NONE, 0); - if (!desc) - return -EINVAL; - - /* [2] Enable the BCH block and read. */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; - buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE - | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); - - pio[1] = 0; - pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC - | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) - | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); - pio[3] = geo->page_size; - pio[4] = payload; - pio[5] = auxiliary; - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [3] Disable the BCH block */ - command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; - address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; - - pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) - | BM_GPMI_CTRL0_WORD_LENGTH - | BF_GPMI_CTRL0_CS(chip, this) - | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) - | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); - pio[1] = 0; - pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ - desc = dmaengine_prep_slave_sg(channel, - (struct scatterlist *)pio, 3, - DMA_TRANS_NONE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - return -EINVAL; - - /* [4] submit the DMA */ - return start_dma_with_bch_irq(this, desc); -} - -/** - * gpmi_copy_bits - copy bits from one memory region to another - * @dst: destination buffer - * @dst_bit_off: bit offset we're starting to write at - * @src: source buffer - * @src_bit_off: bit offset we're starting to read from - * @nbits: number of bits to copy - * - * This functions copies bits from one memory region to another, and is used by - * the GPMI driver to copy ECC sections which are not guaranteed to be byte - * aligned. - * - * src and dst should not overlap. - * - */ -void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, - const u8 *src, size_t src_bit_off, - size_t nbits) -{ - size_t i; - size_t nbytes; - u32 src_buffer = 0; - size_t bits_in_src_buffer = 0; - - if (!nbits) - return; - - /* - * Move src and dst pointers to the closest byte pointer and store bit - * offsets within a byte. - */ - src += src_bit_off / 8; - src_bit_off %= 8; - - dst += dst_bit_off / 8; - dst_bit_off %= 8; - - /* - * Initialize the src_buffer value with bits available in the first - * byte of data so that we end up with a byte aligned src pointer. - */ - if (src_bit_off) { - src_buffer = src[0] >> src_bit_off; - if (nbits >= (8 - src_bit_off)) { - bits_in_src_buffer += 8 - src_bit_off; - } else { - src_buffer &= GENMASK(nbits - 1, 0); - bits_in_src_buffer += nbits; - } - nbits -= bits_in_src_buffer; - src++; - } - - /* Calculate the number of bytes that can be copied from src to dst. */ - nbytes = nbits / 8; - - /* Try to align dst to a byte boundary. */ - if (dst_bit_off) { - if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) { - src_buffer |= src[0] << bits_in_src_buffer; - bits_in_src_buffer += 8; - src++; - nbytes--; - } - - if (bits_in_src_buffer >= (8 - dst_bit_off)) { - dst[0] &= GENMASK(dst_bit_off - 1, 0); - dst[0] |= src_buffer << dst_bit_off; - src_buffer >>= (8 - dst_bit_off); - bits_in_src_buffer -= (8 - dst_bit_off); - dst_bit_off = 0; - dst++; - if (bits_in_src_buffer > 7) { - bits_in_src_buffer -= 8; - dst[0] = src_buffer; - dst++; - src_buffer >>= 8; - } - } - } - - if (!bits_in_src_buffer && !dst_bit_off) { - /* - * Both src and dst pointers are byte aligned, thus we can - * just use the optimized memcpy function. - */ - if (nbytes) - memcpy(dst, src, nbytes); - } else { - /* - * src buffer is not byte aligned, hence we have to copy each - * src byte to the src_buffer variable before extracting a byte - * to store in dst. - */ - for (i = 0; i < nbytes; i++) { - src_buffer |= src[i] << bits_in_src_buffer; - dst[i] = src_buffer; - src_buffer >>= 8; - } - } - /* Update dst and src pointers */ - dst += nbytes; - src += nbytes; - - /* - * nbits is the number of remaining bits. It should not exceed 8 as - * we've already copied as much bytes as possible. - */ - nbits %= 8; - - /* - * If there's no more bits to copy to the destination and src buffer - * was already byte aligned, then we're done. - */ - if (!nbits && !bits_in_src_buffer) - return; - - /* Copy the remaining bits to src_buffer */ - if (nbits) - src_buffer |= (*src & GENMASK(nbits - 1, 0)) << - bits_in_src_buffer; - bits_in_src_buffer += nbits; - - /* - * In case there were not enough bits to get a byte aligned dst buffer - * prepare the src_buffer variable to match the dst organization (shift - * src_buffer by dst_bit_off and retrieve the least significant bits - * from dst). - */ - if (dst_bit_off) - src_buffer = (src_buffer << dst_bit_off) | - (*dst & GENMASK(dst_bit_off - 1, 0)); - bits_in_src_buffer += dst_bit_off; - - /* - * Keep most significant bits from dst if we end up with an unaligned - * number of bits. - */ - nbytes = bits_in_src_buffer / 8; - if (bits_in_src_buffer % 8) { - src_buffer |= (dst[nbytes] & - GENMASK(7, bits_in_src_buffer % 8)) << - (nbytes * 8); - nbytes++; - } - - /* Copy the remaining bytes to dst */ - for (i = 0; i < nbytes; i++) { - dst[i] = src_buffer; - src_buffer >>= 8; - } -} diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 40df20d1adf5..334fe3130285 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -6,6 +6,7 @@ * Copyright (C) 2008 Embedded Alley Solutions, Inc. */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/slab.h> #include <linux/sched/task_stack.h> #include <linux/interrupt.h> @@ -13,7 +14,10 @@ #include <linux/mtd/partitions.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/dma/mxs-dma.h> #include "gpmi-nand.h" +#include "gpmi-regs.h" #include "bch-regs.h" /* Resource names for the GPMI NAND driver. */ @@ -21,149 +25,208 @@ #define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" #define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" -/* add our owner bbt descriptor */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr gpmi_bbt_descr = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern -}; +/* Converts time to clock cycles */ +#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 /* - * We may change the layout if we can get the ECC info from the datasheet, - * else we will use all the (page + OOB). + * Clear the bit and poll it cleared. This is usually called with + * a reset address and mask being either SFTRST(bit 31) or CLKGATE + * (bit 30). */ -static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) +static int clear_poll_bit(void __iomem *addr, u32 mask) { - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *geo = &this->bch_geometry; + int timeout = 0x400; - if (section) - return -ERANGE; + /* clear the bit */ + writel(mask, addr + MXS_CLR_ADDR); - oobregion->offset = 0; - oobregion->length = geo->page_size - mtd->writesize; + /* + * SFTRST needs 3 GPMI clocks to settle, the reference manual + * recommends to wait 1us. + */ + udelay(1); - return 0; + /* poll the bit becoming clear */ + while ((readl(addr) & mask) && --timeout) + /* nothing */; + + return !timeout; } -static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) +#define MODULE_CLKGATE (1 << 30) +#define MODULE_SFTRST (1 << 31) +/* + * The current mxs_reset_block() will do two things: + * [1] enable the module. + * [2] reset the module. + * + * In most of the cases, it's ok. + * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). + * If you try to soft reset the BCH block, it becomes unusable until + * the next hard reset. This case occurs in the NAND boot mode. When the board + * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. + * So If the driver tries to reset the BCH again, the BCH will not work anymore. + * You will see a DMA timeout in this case. The bug has been fixed + * in the following chips, such as MX28. + * + * To avoid this bug, just add a new parameter `just_enable` for + * the mxs_reset_block(), and rewrite it here. + */ +static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) { - struct nand_chip *chip = mtd_to_nand(mtd); - struct gpmi_nand_data *this = nand_get_controller_data(chip); - struct bch_geometry *geo = &this->bch_geometry; + int ret; + int timeout = 0x400; + + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear CLKGATE */ + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); + + if (!just_enable) { + /* set SFTRST to reset the block */ + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); + udelay(1); + + /* poll CLKGATE becoming set */ + while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) + /* nothing */; + if (unlikely(!timeout)) + goto error; + } - if (section) - return -ERANGE; + /* clear and poll SFTRST */ + ret = clear_poll_bit(reset_addr, MODULE_SFTRST); + if (unlikely(ret)) + goto error; - /* The available oob size we have. */ - if (geo->page_size < mtd->writesize + mtd->oobsize) { - oobregion->offset = geo->page_size - mtd->writesize; - oobregion->length = mtd->oobsize - oobregion->offset; - } + /* clear and poll CLKGATE */ + ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); + if (unlikely(ret)) + goto error; return 0; + +error: + pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); + return -ETIMEDOUT; } -static const char * const gpmi_clks_for_mx2x[] = { - "gpmi_io", -}; +static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) +{ + struct clk *clk; + int ret; + int i; -static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { - .ecc = gpmi_ooblayout_ecc, - .free = gpmi_ooblayout_free, -}; + for (i = 0; i < GPMI_CLK_MAX; i++) { + clk = this->resources.clock[i]; + if (!clk) + break; -static const struct gpmi_devdata gpmi_devdata_imx23 = { - .type = IS_MX23, - .bch_max_ecc_strength = 20, - .max_chain_delay = 16000, - .clks = gpmi_clks_for_mx2x, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), -}; + if (v) { + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk; + } else { + clk_disable_unprepare(clk); + } + } + return 0; -static const struct gpmi_devdata gpmi_devdata_imx28 = { - .type = IS_MX28, - .bch_max_ecc_strength = 20, - .max_chain_delay = 16000, - .clks = gpmi_clks_for_mx2x, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), -}; +err_clk: + for (; i > 0; i--) + clk_disable_unprepare(this->resources.clock[i - 1]); + return ret; +} -static const char * const gpmi_clks_for_mx6[] = { - "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", -}; +static int gpmi_init(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + int ret; -static const struct gpmi_devdata gpmi_devdata_imx6q = { - .type = IS_MX6Q, - .bch_max_ecc_strength = 40, - .max_chain_delay = 12000, - .clks = gpmi_clks_for_mx6, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), -}; + ret = gpmi_reset_block(r->gpmi_regs, false); + if (ret) + goto err_out; -static const struct gpmi_devdata gpmi_devdata_imx6sx = { - .type = IS_MX6SX, - .bch_max_ecc_strength = 62, - .max_chain_delay = 12000, - .clks = gpmi_clks_for_mx6, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), -}; + /* + * Reset BCH here, too. We got failures otherwise :( + * See later BCH reset for explanation of MX23 and MX28 handling + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); + if (ret) + goto err_out; -static const char * const gpmi_clks_for_mx7d[] = { - "gpmi_io", "gpmi_bch_apb", -}; + /* Choose NAND mode. */ + writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); -static const struct gpmi_devdata gpmi_devdata_imx7d = { - .type = IS_MX7D, - .bch_max_ecc_strength = 62, - .max_chain_delay = 12000, - .clks = gpmi_clks_for_mx7d, - .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), -}; + /* Set the IRQ polarity. */ + writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + r->gpmi_regs + HW_GPMI_CTRL1_SET); -static irqreturn_t bch_irq(int irq, void *cookie) -{ - struct gpmi_nand_data *this = cookie; + /* Disable Write-Protection. */ + writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); - gpmi_clear_bch(this); - complete(&this->bch_done); - return IRQ_HANDLED; + /* Select BCH ECC. */ + writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * Decouple the chip select from dma channel. We use dma0 for all + * the chips. + */ + writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); + + return 0; +err_out: + return ret; } -/* - * Calculate the ECC strength by hand: - * E : The ECC strength. - * G : the length of Galois Field. - * N : The chunk count of per page. - * O : the oobsize of the NAND chip. - * M : the metasize of per page. - * - * The formula is : - * E * G * N - * ------------ <= (O - M) - * 8 - * - * So, we get E by: - * (O - M) * 8 - * E <= ------------- - * G * N - */ -static inline int get_ecc_strength(struct gpmi_nand_data *this) +/* This function is very useful. It is called only when the bug occur. */ +static void gpmi_dump_info(struct gpmi_nand_data *this) { + struct resources *r = &this->resources; struct bch_geometry *geo = &this->bch_geometry; - struct mtd_info *mtd = nand_to_mtd(&this->nand); - int ecc_strength; + u32 reg; + int i; - ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) - / (geo->gf_len * geo->ecc_chunk_count); + dev_err(this->dev, "Show GPMI registers :\n"); + for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { + reg = readl(r->gpmi_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } - /* We need the minor even number. */ - return round_down(ecc_strength, 2); + /* start to print out the BCH info */ + dev_err(this->dev, "Show BCH registers :\n"); + for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { + reg = readl(r->bch_regs + i * 0x10); + dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); + } + dev_err(this->dev, "BCH Geometry :\n" + "GF length : %u\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n", + geo->gf_len, + geo->ecc_strength, + geo->page_size, + geo->metadata_size, + geo->ecc_chunk_size, + geo->ecc_chunk_count, + geo->payload_size, + geo->auxiliary_size, + geo->auxiliary_status_offset, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset); } static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) @@ -296,6 +359,37 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, return 0; } +/* + * Calculate the ECC strength by hand: + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * O : the oobsize of the NAND chip. + * M : the metasize of per page. + * + * The formula is : + * E * G * N + * ------------ <= (O - M) + * 8 + * + * So, we get E by: + * (O - M) * 8 + * E <= ------------- + * G * N + */ +static inline int get_ecc_strength(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = nand_to_mtd(&this->nand); + int ecc_strength; + + ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + + /* We need the minor even number. */ + return round_down(ecc_strength, 2); +} + static int legacy_set_geometry(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; @@ -408,7 +502,7 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) return 0; } -int common_nfc_set_geometry(struct gpmi_nand_data *this) +static int common_nfc_set_geometry(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; @@ -430,18 +524,288 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) return 0; } -struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) +/* Configures the geometry for BCH. */ +static int bch_set_geometry(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + int ret; + + ret = common_nfc_set_geometry(this); + if (ret) + return ret; + + ret = pm_runtime_get_sync(this->dev); + if (ret < 0) + return ret; + + /* + * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this + * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. + * and MX28. + */ + ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); + if (ret) + goto err_out; + + /* Set *all* chip selects to use layout 0. */ + writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); + + ret = 0; +err_out: + pm_runtime_mark_last_busy(this->dev); + pm_runtime_put_autosuspend(this->dev); + + return ret; +} + +/* + * <1> Firstly, we should know what's the GPMI-clock means. + * The GPMI-clock is the internal clock in the gpmi nand controller. + * If you set 100MHz to gpmi nand controller, the GPMI-clock's period + * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. + * + * <2> Secondly, we should know what's the frequency on the nand chip pins. + * The frequency on the nand chip pins is derived from the GPMI-clock. + * We can get it from the following equation: + * + * F = G / (DS + DH) + * + * F : the frequency on the nand chip pins. + * G : the GPMI clock, such as 100MHz. + * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP + * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD + * + * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, + * the nand EDO(extended Data Out) timing could be applied. + * The GPMI implements a feedback read strobe to sample the read data. + * The feedback read strobe can be delayed to support the nand EDO timing + * where the read strobe may deasserts before the read data is valid, and + * read data is valid for some time after read strobe. + * + * The following figure illustrates some aspects of a NAND Flash read: + * + * |<---tREA---->| + * | | + * | | | + * |<--tRP-->| | + * | | | + * __ ___|__________________________________ + * RDN \________/ | + * | + * /---------\ + * Read Data --------------< >--------- + * \---------/ + * | | + * |<-D->| + * FeedbackRDN ________ ____________ + * \___________/ + * + * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. + * + * + * <4> Now, we begin to describe how to compute the right RDN_DELAY. + * + * 4.1) From the aspect of the nand chip pins: + * Delay = (tREA + C - tRP) {1} + * + * tREA : the maximum read access time. + * C : a constant to adjust the delay. default is 4000ps. + * tRP : the read pulse width, which is exactly: + * tRP = (GPMI-clock-period) * DATA_SETUP + * + * 4.2) From the aspect of the GPMI nand controller: + * Delay = RDN_DELAY * 0.125 * RP {2} + * + * RP : the DLL reference period. + * if (GPMI-clock-period > DLL_THRETHOLD) + * RP = GPMI-clock-period / 2; + * else + * RP = GPMI-clock-period; + * + * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period + * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD + * is 16000ps, but in mx6q, we use 12000ps. + * + * 4.3) since {1} equals {2}, we get: + * + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- {3} + * RP + */ +static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, + const struct nand_sdr_timings *sdr) +{ + struct gpmi_nfc_hardware_timing *hw = &this->hw; + unsigned int dll_threshold_ps = this->devdata->max_chain_delay; + unsigned int period_ps, reference_period_ps; + unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; + unsigned int tRP_ps; + bool use_half_period; + int sample_delay_ps, sample_delay_factor; + u16 busy_timeout_cycles; + u8 wrn_dly_sel; + + if (sdr->tRC_min >= 30000) { + /* ONFI non-EDO modes [0-3] */ + hw->clk_rate = 22000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; + } else if (sdr->tRC_min >= 25000) { + /* ONFI EDO mode 4 */ + hw->clk_rate = 80000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + } else { + /* ONFI EDO mode 5 */ + hw->clk_rate = 100000000; + wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + } + + /* SDR core timings are given in picoseconds */ + period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); + + addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); + data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); + data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); + busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps); + + hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); + hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); + + /* + * Derive NFC ideal delay from {3}: + * + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- + * RP + */ + if (period_ps > dll_threshold_ps) { + use_half_period = true; + reference_period_ps = period_ps / 2; + } else { + use_half_period = false; + reference_period_ps = period_ps; + } + + tRP_ps = data_setup_cycles * period_ps; + sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; + if (sample_delay_ps > 0) + sample_delay_factor = sample_delay_ps / reference_period_ps; + else + sample_delay_factor = 0; + + hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel); + if (sample_delay_factor) + hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | + BM_GPMI_CTRL1_DLL_ENABLE | + (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); +} + +static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this) +{ + struct gpmi_nfc_hardware_timing *hw = &this->hw; + struct resources *r = &this->resources; + void __iomem *gpmi_regs = r->gpmi_regs; + unsigned int dll_wait_time_us; + + clk_set_rate(r->clock[0], hw->clk_rate); + + writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0); + writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1); + + /* + * Clear several CTRL1 fields, DLL must be disabled when setting + * RDN_DELAY or HALF_PERIOD. + */ + writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR); + writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ + dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; + if (!dll_wait_time_us) + dll_wait_time_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_us); +} + +static int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + const struct nand_sdr_timings *sdr; + + /* Retrieve required NAND timings */ + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + /* Only MX6 GPMI controller can reach EDO timings */ + if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) + return -ENOTSUPP; + + /* Stop here if this call was just a check */ + if (chipnr < 0) + return 0; + + /* Do the actual derivation of the controller timings */ + gpmi_nfc_compute_timings(this, sdr); + + this->hw.must_apply_timings = true; + + return 0; +} + +/* Clears a BCH interrupt. */ +static void gpmi_clear_bch(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); +} + +static struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) { /* We use the DMA channel 0 to access all the nand chips. */ return this->dma_chans[0]; } +/* This will be called after the DMA operation is finished. */ +static void dma_irq_callback(void *param) +{ + struct gpmi_nand_data *this = param; + struct completion *dma_c = &this->dma_done; + + complete(dma_c); +} + +static irqreturn_t bch_irq(int irq, void *cookie) +{ + struct gpmi_nand_data *this = cookie; + + gpmi_clear_bch(this); + complete(&this->bch_done); + return IRQ_HANDLED; +} + +static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len) +{ + /* + * raw_len is the length to read/write including bch data which + * we are passed in exec_op. Calculate the data length from it. + */ + if (this->bch) + return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size); + else + return raw_len; +} + /* Can we use the upper's buffer directly for DMA? */ -bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, - enum dma_data_direction dr) +static bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, + int raw_len, struct scatterlist *sgl, + enum dma_data_direction dr) { - struct scatterlist *sgl = &this->data_sgl; int ret; + int len = gpmi_raw_len_to_len(this, raw_len); /* first try to map the upper buffer directly */ if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { @@ -457,7 +821,7 @@ map_fail: /* We have to use our own DMA buffer. */ sg_init_one(sgl, this->data_buffer_dma, len); - if (dr == DMA_TO_DEVICE) + if (dr == DMA_TO_DEVICE && buf != this->data_buffer_dma) memcpy(this->data_buffer_dma, buf, len); dma_map_sg(this->dev, sgl, 1, dr); @@ -465,67 +829,263 @@ map_fail: return false; } -/* This will be called after the DMA operation is finished. */ -static void dma_irq_callback(void *param) +/** + * gpmi_copy_bits - copy bits from one memory region to another + * @dst: destination buffer + * @dst_bit_off: bit offset we're starting to write at + * @src: source buffer + * @src_bit_off: bit offset we're starting to read from + * @nbits: number of bits to copy + * + * This functions copies bits from one memory region to another, and is used by + * the GPMI driver to copy ECC sections which are not guaranteed to be byte + * aligned. + * + * src and dst should not overlap. + * + */ +static void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, const u8 *src, + size_t src_bit_off, size_t nbits) { - struct gpmi_nand_data *this = param; - struct completion *dma_c = &this->dma_done; + size_t i; + size_t nbytes; + u32 src_buffer = 0; + size_t bits_in_src_buffer = 0; - complete(dma_c); -} + if (!nbits) + return; -int start_dma_without_bch_irq(struct gpmi_nand_data *this, - struct dma_async_tx_descriptor *desc) -{ - struct completion *dma_c = &this->dma_done; - unsigned long timeout; + /* + * Move src and dst pointers to the closest byte pointer and store bit + * offsets within a byte. + */ + src += src_bit_off / 8; + src_bit_off %= 8; - init_completion(dma_c); + dst += dst_bit_off / 8; + dst_bit_off %= 8; - desc->callback = dma_irq_callback; - desc->callback_param = this; - dmaengine_submit(desc); - dma_async_issue_pending(get_dma_chan(this)); + /* + * Initialize the src_buffer value with bits available in the first + * byte of data so that we end up with a byte aligned src pointer. + */ + if (src_bit_off) { + src_buffer = src[0] >> src_bit_off; + if (nbits >= (8 - src_bit_off)) { + bits_in_src_buffer += 8 - src_bit_off; + } else { + src_buffer &= GENMASK(nbits - 1, 0); + bits_in_src_buffer += nbits; + } + nbits -= bits_in_src_buffer; + src++; + } - /* Wait for the interrupt from the DMA block. */ - timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); - if (!timeout) { - dev_err(this->dev, "DMA timeout, last DMA\n"); - gpmi_dump_info(this); - return -ETIMEDOUT; + /* Calculate the number of bytes that can be copied from src to dst. */ + nbytes = nbits / 8; + + /* Try to align dst to a byte boundary. */ + if (dst_bit_off) { + if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) { + src_buffer |= src[0] << bits_in_src_buffer; + bits_in_src_buffer += 8; + src++; + nbytes--; + } + + if (bits_in_src_buffer >= (8 - dst_bit_off)) { + dst[0] &= GENMASK(dst_bit_off - 1, 0); + dst[0] |= src_buffer << dst_bit_off; + src_buffer >>= (8 - dst_bit_off); + bits_in_src_buffer -= (8 - dst_bit_off); + dst_bit_off = 0; + dst++; + if (bits_in_src_buffer > 7) { + bits_in_src_buffer -= 8; + dst[0] = src_buffer; + dst++; + src_buffer >>= 8; + } + } + } + + if (!bits_in_src_buffer && !dst_bit_off) { + /* + * Both src and dst pointers are byte aligned, thus we can + * just use the optimized memcpy function. + */ + if (nbytes) + memcpy(dst, src, nbytes); + } else { + /* + * src buffer is not byte aligned, hence we have to copy each + * src byte to the src_buffer variable before extracting a byte + * to store in dst. + */ + for (i = 0; i < nbytes; i++) { + src_buffer |= src[i] << bits_in_src_buffer; + dst[i] = src_buffer; + src_buffer >>= 8; + } + } + /* Update dst and src pointers */ + dst += nbytes; + src += nbytes; + + /* + * nbits is the number of remaining bits. It should not exceed 8 as + * we've already copied as much bytes as possible. + */ + nbits %= 8; + + /* + * If there's no more bits to copy to the destination and src buffer + * was already byte aligned, then we're done. + */ + if (!nbits && !bits_in_src_buffer) + return; + + /* Copy the remaining bits to src_buffer */ + if (nbits) + src_buffer |= (*src & GENMASK(nbits - 1, 0)) << + bits_in_src_buffer; + bits_in_src_buffer += nbits; + + /* + * In case there were not enough bits to get a byte aligned dst buffer + * prepare the src_buffer variable to match the dst organization (shift + * src_buffer by dst_bit_off and retrieve the least significant bits + * from dst). + */ + if (dst_bit_off) + src_buffer = (src_buffer << dst_bit_off) | + (*dst & GENMASK(dst_bit_off - 1, 0)); + bits_in_src_buffer += dst_bit_off; + + /* + * Keep most significant bits from dst if we end up with an unaligned + * number of bits. + */ + nbytes = bits_in_src_buffer / 8; + if (bits_in_src_buffer % 8) { + src_buffer |= (dst[nbytes] & + GENMASK(7, bits_in_src_buffer % 8)) << + (nbytes * 8); + nbytes++; + } + + /* Copy the remaining bytes to dst */ + for (i = 0; i < nbytes; i++) { + dst[i] = src_buffer; + src_buffer >>= 8; } - return 0; } +/* add our owner bbt descriptor */ +static uint8_t scan_ff_pattern[] = { 0xff }; +static struct nand_bbt_descr gpmi_bbt_descr = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; + /* - * This function is used in BCH reading or BCH writing pages. - * It will wait for the BCH interrupt as long as ONE second. - * Actually, we must wait for two interrupts : - * [1] firstly the DMA interrupt and - * [2] secondly the BCH interrupt. + * We may change the layout if we can get the ECC info from the datasheet, + * else we will use all the (page + OOB). */ -int start_dma_with_bch_irq(struct gpmi_nand_data *this, - struct dma_async_tx_descriptor *desc) +static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) { - struct completion *bch_c = &this->bch_done; - unsigned long timeout; + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; - /* Prepare to receive an interrupt from the BCH block. */ - init_completion(bch_c); + if (section) + return -ERANGE; - /* start the DMA */ - start_dma_without_bch_irq(this, desc); + oobregion->offset = 0; + oobregion->length = geo->page_size - mtd->writesize; - /* Wait for the interrupt from the BCH block. */ - timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); - if (!timeout) { - dev_err(this->dev, "BCH timeout\n"); - gpmi_dump_info(this); - return -ETIMEDOUT; + return 0; +} + +static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; + + if (section) + return -ERANGE; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + oobregion->offset = geo->page_size - mtd->writesize; + oobregion->length = mtd->oobsize - oobregion->offset; } + return 0; } +static const char * const gpmi_clks_for_mx2x[] = { + "gpmi_io", +}; + +static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { + .ecc = gpmi_ooblayout_ecc, + .free = gpmi_ooblayout_free, +}; + +static const struct gpmi_devdata gpmi_devdata_imx23 = { + .type = IS_MX23, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16000, + .clks = gpmi_clks_for_mx2x, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), +}; + +static const struct gpmi_devdata gpmi_devdata_imx28 = { + .type = IS_MX28, + .bch_max_ecc_strength = 20, + .max_chain_delay = 16000, + .clks = gpmi_clks_for_mx2x, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), +}; + +static const char * const gpmi_clks_for_mx6[] = { + "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", +}; + +static const struct gpmi_devdata gpmi_devdata_imx6q = { + .type = IS_MX6Q, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + +static const struct gpmi_devdata gpmi_devdata_imx6sx = { + .type = IS_MX6SX, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + +static const char * const gpmi_clks_for_mx7d[] = { + "gpmi_io", "gpmi_bch_apb", +}; + +static const struct gpmi_devdata gpmi_devdata_imx7d = { + .type = IS_MX7D, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx7d, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), +}; + static int acquire_register_block(struct gpmi_nand_data *this, const char *res_name) { @@ -667,68 +1227,20 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int send_page_prepare(struct gpmi_nand_data *this, - const void *source, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - const void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(source)) { - dma_addr_t source_phys; - - source_phys = dma_map_single(dev, (void *)source, length, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, source_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = source; - *use_phys = source_phys; - return 0; - } -map_failed: - /* - * Copy the content of the source buffer into the alternate - * buffer and set up the return values accordingly. - */ - memcpy(alt_virt, source, length); - - *use_virt = alt_virt; - *use_phys = alt_phys; - return 0; -} - -static void send_page_end(struct gpmi_nand_data *this, - const void *source, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - const void *used_virt, dma_addr_t used_phys) -{ - struct device *dev = this->dev; - if (used_virt == source) - dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE); -} - static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) { struct device *dev = this->dev; + struct bch_geometry *geo = &this->bch_geometry; - if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt)) - dma_free_coherent(dev, this->page_buffer_size, - this->page_buffer_virt, - this->page_buffer_phys); - kfree(this->cmd_buffer); + if (this->auxiliary_virt && virt_addr_valid(this->auxiliary_virt)) + dma_free_coherent(dev, geo->auxiliary_size, + this->auxiliary_virt, + this->auxiliary_phys); kfree(this->data_buffer_dma); kfree(this->raw_buffer); - this->cmd_buffer = NULL; this->data_buffer_dma = NULL; this->raw_buffer = NULL; - this->page_buffer_virt = NULL; - this->page_buffer_size = 0; } /* Allocate the DMA buffers */ @@ -738,11 +1250,6 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) struct device *dev = this->dev; struct mtd_info *mtd = nand_to_mtd(&this->nand); - /* [1] Allocate a command buffer. PAGE_SIZE is enough. */ - this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL); - if (this->cmd_buffer == NULL) - goto error_alloc; - /* * [2] Allocate a read/write data buffer. * The gpmi_alloc_dma_buffer can be called twice. @@ -756,29 +1263,15 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) if (this->data_buffer_dma == NULL) goto error_alloc; - /* - * [3] Allocate the page buffer. - * - * Both the payload buffer and the auxiliary buffer must appear on - * 32-bit boundaries. We presume the size of the payload buffer is a - * power of two and is much larger than four, which guarantees the - * auxiliary buffer will appear on a 32-bit boundary. - */ - this->page_buffer_size = geo->payload_size + geo->auxiliary_size; - this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size, - &this->page_buffer_phys, GFP_DMA); - if (!this->page_buffer_virt) + this->auxiliary_virt = dma_alloc_coherent(dev, geo->auxiliary_size, + &this->auxiliary_phys, GFP_DMA); + if (!this->auxiliary_virt) goto error_alloc; - this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + this->raw_buffer = kzalloc((mtd->writesize ?: PAGE_SIZE) + mtd->oobsize, GFP_KERNEL); if (!this->raw_buffer) goto error_alloc; - /* Slice up the page buffer. */ - this->payload_virt = this->page_buffer_virt; - this->payload_phys = this->page_buffer_phys; - this->auxiliary_virt = this->payload_virt + geo->payload_size; - this->auxiliary_phys = this->payload_phys + geo->payload_size; return 0; error_alloc: @@ -786,106 +1279,6 @@ error_alloc: return -ENOMEM; } -static void gpmi_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctrl) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - int ret; - - /* - * Every operation begins with a command byte and a series of zero or - * more address bytes. These are distinguished by either the Address - * Latch Enable (ALE) or Command Latch Enable (CLE) signals being - * asserted. When MTD is ready to execute the command, it will deassert - * both latch enables. - * - * Rather than run a separate DMA operation for every single byte, we - * queue them up and run a single DMA operation for the entire series - * of command and data bytes. NAND_CMD_NONE means the END of the queue. - */ - if ((ctrl & (NAND_ALE | NAND_CLE))) { - if (data != NAND_CMD_NONE) - this->cmd_buffer[this->command_length++] = data; - return; - } - - if (!this->command_length) - return; - - ret = gpmi_send_command(this); - if (ret) - dev_err(this->dev, "Chip: %u, Error %d\n", - this->current_chip, ret); - - this->command_length = 0; -} - -static int gpmi_dev_ready(struct nand_chip *chip) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - return gpmi_is_ready(this, this->current_chip); -} - -static void gpmi_select_chip(struct nand_chip *chip, int chipnr) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - int ret; - - /* - * For power consumption matters, disable/enable the clock each time a - * die is selected/unselected. - */ - if (this->current_chip < 0 && chipnr >= 0) { - ret = gpmi_enable_clk(this); - if (ret) - dev_err(this->dev, "Failed to enable the clock\n"); - } else if (this->current_chip >= 0 && chipnr < 0) { - ret = gpmi_disable_clk(this); - if (ret) - dev_err(this->dev, "Failed to disable the clock\n"); - } - - /* - * This driver currently supports only one NAND chip. Plus, dies share - * the same configuration. So once timings have been applied on the - * controller side, they will not change anymore. When the time will - * come, the check on must_apply_timings will have to be dropped. - */ - if (chipnr >= 0 && this->hw.must_apply_timings) { - this->hw.must_apply_timings = false; - gpmi_nfc_apply_timings(this); - } - - this->current_chip = chipnr; -} - -static void gpmi_read_buf(struct nand_chip *chip, uint8_t *buf, int len) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - dev_dbg(this->dev, "len is %d\n", len); - - gpmi_read_data(this, buf, len); -} - -static void gpmi_write_buf(struct nand_chip *chip, const uint8_t *buf, int len) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - - dev_dbg(this->dev, "len is %d\n", len); - - gpmi_send_data(this, buf, len); -} - -static uint8_t gpmi_read_byte(struct nand_chip *chip) -{ - struct gpmi_nand_data *this = nand_get_controller_data(chip); - uint8_t *buf = this->data_buffer_dma; - - gpmi_read_buf(chip, buf, 1); - return buf[0]; -} - /* * Handles block mark swapping. * It can be called in swapping the block mark, or swapping it back, @@ -934,54 +1327,20 @@ static void block_mark_swapping(struct gpmi_nand_data *this, p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); } -static int gpmi_ecc_read_page_data(struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) +static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, + int last, int meta) { struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; struct mtd_info *mtd = nand_to_mtd(chip); - dma_addr_t payload_phys; - unsigned int i; + int i; unsigned char *status; - unsigned int max_bitflips = 0; - int ret; - bool direct = false; - - dev_dbg(this->dev, "page number is : %d\n", page); - - payload_phys = this->payload_phys; - - if (virt_addr_valid(buf)) { - dma_addr_t dest_phys; - - dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size, - DMA_FROM_DEVICE); - if (!dma_mapping_error(this->dev, dest_phys)) { - payload_phys = dest_phys; - direct = true; - } - } - - /* go! */ - ret = gpmi_read_page(this, payload_phys, this->auxiliary_phys); - - if (direct) - dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, - DMA_FROM_DEVICE); - - if (ret) { - dev_err(this->dev, "Error in ECC-based read: %d\n", ret); - return ret; - } + unsigned int max_bitflips = 0; /* Loop over status bytes, accumulating ECC status. */ - status = this->auxiliary_virt + nfc_geo->auxiliary_status_offset; - - if (!direct) - memcpy(buf, this->payload_virt, nfc_geo->payload_size); + status = this->auxiliary_virt + ALIGN(meta, 4); - for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { + for (i = first; i < last; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) continue; @@ -1061,6 +1420,50 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, *status); } + return max_bitflips; +} + +static void gpmi_bch_layout_std(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + unsigned int ecc_strength = geo->ecc_strength >> 1; + unsigned int gf_len = geo->gf_len; + unsigned int block_size = geo->ecc_chunk_size; + + this->bch_flashlayout0 = + BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) | + BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) | + BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | + BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this); + + this->bch_flashlayout1 = + BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) | + BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | + BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this); +} + +static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct bch_geometry *geo = &this->bch_geometry; + unsigned int max_bitflips; + int ret; + + gpmi_bch_layout_std(this); + this->bch = true; + + ret = nand_read_page_op(chip, page, 0, buf, geo->page_size); + if (ret) + return ret; + + max_bitflips = gpmi_count_bitflips(chip, buf, 0, + geo->ecc_chunk_count, + geo->auxiliary_status_offset); + /* handle the block mark swapping */ block_mark_swapping(this, buf, this->auxiliary_virt); @@ -1082,30 +1485,20 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, return max_bitflips; } -static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - nand_read_page_op(chip, page, 0, NULL, 0); - - return gpmi_ecc_read_page_data(chip, buf, oob_required, page); -} - /* Fake a virtual small page for the subpage read */ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, uint32_t len, uint8_t *buf, int page) { struct gpmi_nand_data *this = nand_get_controller_data(chip); - void __iomem *bch_regs = this->resources.bch_regs; - struct bch_geometry old_geo = this->bch_geometry; struct bch_geometry *geo = &this->bch_geometry; int size = chip->ecc.size; /* ECC chunk size */ int meta, n, page_size; - u32 r1_old, r2_old, r1_new, r2_new; unsigned int max_bitflips; + unsigned int ecc_strength; int first, last, marker_pos; int ecc_parity_size; int col = 0; - int old_swap_block_mark = this->swap_block_mark; + int ret; /* The size of ECC parity */ ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; @@ -1138,43 +1531,33 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, buf = buf + first * size; } - nand_read_page_op(chip, page, col, NULL, 0); - - /* Save the old environment */ - r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); - r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); + ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; - /* change the BCH registers and bch_geometry{} */ n = last - first + 1; page_size = meta + (size + ecc_parity_size) * n; + ecc_strength = geo->ecc_strength >> 1; - r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | - BM_BCH_FLASH0LAYOUT0_META_SIZE); - r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) - | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); - writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); + this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) | + BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | + BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | + BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this); - r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; - r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); - writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); + this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | + BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | + BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) | + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this); - geo->ecc_chunk_count = n; - geo->payload_size = n * size; - geo->page_size = page_size; - geo->auxiliary_status_offset = ALIGN(meta, 4); + this->bch = true; + + ret = nand_read_page_op(chip, page, col, buf, page_size); + if (ret) + return ret; dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", page, offs, len, col, first, n, page_size); - /* Read the subpage now */ - this->swap_block_mark = false; - max_bitflips = gpmi_ecc_read_page_data(chip, buf, 0, page); - - /* Restore */ - writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); - writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); - this->bch_geometry = old_geo; - this->swap_block_mark = old_swap_block_mark; + max_bitflips = gpmi_count_bitflips(chip, buf, first, last, meta); return max_bitflips; } @@ -1185,81 +1568,29 @@ static int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; - const void *payload_virt; - dma_addr_t payload_phys; - const void *auxiliary_virt; - dma_addr_t auxiliary_phys; - int ret; + int ret; dev_dbg(this->dev, "ecc write page.\n"); - nand_prog_page_begin_op(chip, page, 0, NULL, 0); + gpmi_bch_layout_std(this); + this->bch = true; + + memcpy(this->auxiliary_virt, chip->oob_poi, nfc_geo->auxiliary_size); if (this->swap_block_mark) { /* - * If control arrives here, we're doing block mark swapping. - * Since we can't modify the caller's buffers, we must copy them - * into our own. - */ - memcpy(this->payload_virt, buf, mtd->writesize); - payload_virt = this->payload_virt; - payload_phys = this->payload_phys; - - memcpy(this->auxiliary_virt, chip->oob_poi, - nfc_geo->auxiliary_size); - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; - - /* Handle block mark swapping. */ - block_mark_swapping(this, - (void *)payload_virt, (void *)auxiliary_virt); - } else { - /* - * If control arrives here, we're not doing block mark swapping, - * so we can to try and use the caller's buffers. + * When doing bad block marker swapping we must always copy the + * input buffer as we can't modify the const buffer. */ - ret = send_page_prepare(this, - buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate payload DMA buffer\n"); - return 0; - } - - ret = send_page_prepare(this, - chip->oob_poi, mtd->oobsize, - this->auxiliary_virt, this->auxiliary_phys, - nfc_geo->auxiliary_size, - &auxiliary_virt, &auxiliary_phys); - if (ret) { - dev_err(this->dev, "Inadequate auxiliary DMA buffer\n"); - goto exit_auxiliary; - } + memcpy(this->data_buffer_dma, buf, mtd->writesize); + buf = this->data_buffer_dma; + block_mark_swapping(this, this->data_buffer_dma, + this->auxiliary_virt); } - /* Ask the NFC. */ - ret = gpmi_send_page(this, payload_phys, auxiliary_phys); - if (ret) - dev_err(this->dev, "Error in ECC-based write: %d\n", ret); - - if (!this->swap_block_mark) { - send_page_end(this, chip->oob_poi, mtd->oobsize, - this->auxiliary_virt, this->auxiliary_phys, - nfc_geo->auxiliary_size, - auxiliary_virt, auxiliary_phys); -exit_auxiliary: - send_page_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); - } + ret = nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size); - if (ret) - return ret; - - return nand_prog_page_end_op(chip); + return ret; } /* @@ -1326,14 +1657,16 @@ static int gpmi_ecc_read_oob(struct nand_chip *chip, int page) { struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret; - dev_dbg(this->dev, "page number is %d\n", page); /* clear the OOB buffer */ memset(chip->oob_poi, ~0, mtd->oobsize); /* Read out the conventional OOB. */ - nand_read_page_op(chip, page, mtd->writesize, NULL, 0); - chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); + ret = nand_read_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); + if (ret) + return ret; /* * Now, we want to make sure the block mark is correct. In the @@ -1342,8 +1675,9 @@ static int gpmi_ecc_read_oob(struct nand_chip *chip, int page) */ if (GPMI_IS_MX23(this)) { /* Read the block mark into the first byte of the OOB buffer. */ - nand_read_page_op(chip, page, 0, NULL, 0); - chip->oob_poi[0] = chip->legacy.read_byte(chip); + ret = nand_read_page_op(chip, page, 0, chip->oob_poi, 1); + if (ret) + return ret; } return 0; @@ -1392,9 +1726,12 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf, size_t oob_byte_off; uint8_t *oob = chip->oob_poi; int step; + int ret; - nand_read_page_op(chip, page, 0, tmp_buf, - mtd->writesize + mtd->oobsize); + ret = nand_read_page_op(chip, page, 0, tmp_buf, + mtd->writesize + mtd->oobsize); + if (ret) + return ret; /* * If required, swap the bad block marker and the data stored in the @@ -1606,13 +1943,12 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) unsigned int stride; unsigned int page; u8 *buffer = nand_get_data_buf(chip); - int saved_chip_number; int found_an_ncb_fingerprint = false; + int ret; /* Compute the number of strides in a search area. */ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; - saved_chip_number = this->current_chip; nand_select_target(chip, 0); /* @@ -1630,8 +1966,10 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) * Read the NCB fingerprint. The fingerprint is four bytes long * and starts in the 12th byte of the page. */ - nand_read_page_op(chip, page, 12, NULL, 0); - chip->legacy.read_buf(chip, buffer, strlen(fingerprint)); + ret = nand_read_page_op(chip, page, 12, buffer, + strlen(fingerprint)); + if (ret) + continue; /* Look for the fingerprint. */ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { @@ -1641,10 +1979,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) } - if (saved_chip_number >= 0) - nand_select_target(chip, saved_chip_number); - else - nand_deselect_target(chip); + nand_deselect_target(chip); if (found_an_ncb_fingerprint) dev_dbg(dev, "\tFound a fingerprint\n"); @@ -1668,7 +2003,6 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) unsigned int stride; unsigned int page; u8 *buffer = nand_get_data_buf(chip); - int saved_chip_number; int status; /* Compute the search area geometry. */ @@ -1685,8 +2019,6 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); - /* Select chip 0. */ - saved_chip_number = this->current_chip; nand_select_target(chip, 0); /* Loop over blocks in the first search area, erasing them. */ @@ -1718,11 +2050,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) dev_err(dev, "[%s] Write failed.\n", __func__); } - /* Deselect chip 0. */ - if (saved_chip_number >= 0) - nand_select_target(chip, saved_chip_number); - else - nand_deselect_target(chip); + nand_deselect_target(chip); return 0; } @@ -1773,10 +2101,13 @@ static int mx23_boot_init(struct gpmi_nand_data *this) /* Send the command to read the conventional block mark. */ nand_select_target(chip, chipnr); - nand_read_page_op(chip, page, mtd->writesize, NULL, 0); - block_mark = chip->legacy.read_byte(chip); + ret = nand_read_page_op(chip, page, mtd->writesize, &block_mark, + 1); nand_deselect_target(chip); + if (ret) + continue; + /* * Check if the block is marked bad. If so, we need to mark it * again, but this time the result will be a mark in the @@ -1890,9 +2221,330 @@ static int gpmi_nand_attach_chip(struct nand_chip *chip) return 0; } +static struct gpmi_transfer *get_next_transfer(struct gpmi_nand_data *this) +{ + struct gpmi_transfer *transfer = &this->transfers[this->ntransfers]; + + this->ntransfers++; + + if (this->ntransfers == GPMI_MAX_TRANSFERS) + return NULL; + + return transfer; +} + +static struct dma_async_tx_descriptor *gpmi_chain_command( + struct gpmi_nand_data *this, u8 cmd, const u8 *addr, int naddr) +{ + struct dma_chan *channel = get_dma_chan(this); + struct dma_async_tx_descriptor *desc; + struct gpmi_transfer *transfer; + int chip = this->nand.cur_cs; + u32 pio[3]; + + /* [1] send out the PIO words */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) + | BM_GPMI_CTRL0_ADDRESS_INCREMENT + | BF_GPMI_CTRL0_XFER_COUNT(naddr + 1); + pio[1] = 0; + pio[2] = 0; + desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), + DMA_TRANS_NONE, 0); + if (!desc) + return NULL; + + transfer = get_next_transfer(this); + if (!transfer) + return NULL; + + transfer->cmdbuf[0] = cmd; + if (naddr) + memcpy(&transfer->cmdbuf[1], addr, naddr); + + sg_init_one(&transfer->sgl, transfer->cmdbuf, naddr + 1); + dma_map_sg(this->dev, &transfer->sgl, 1, DMA_TO_DEVICE); + + transfer->direction = DMA_TO_DEVICE; + + desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, DMA_MEM_TO_DEV, + MXS_DMA_CTRL_WAIT4END); + return desc; +} + +static struct dma_async_tx_descriptor *gpmi_chain_wait_ready( + struct gpmi_nand_data *this) +{ + struct dma_chan *channel = get_dma_chan(this); + u32 pio[2]; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + + return mxs_dmaengine_prep_pio(channel, pio, 2, DMA_TRANS_NONE, + MXS_DMA_CTRL_WAIT4END | MXS_DMA_CTRL_WAIT4RDY); +} + +static struct dma_async_tx_descriptor *gpmi_chain_data_read( + struct gpmi_nand_data *this, void *buf, int raw_len, bool *direct) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct gpmi_transfer *transfer; + u32 pio[6] = {}; + + transfer = get_next_transfer(this); + if (!transfer) + return NULL; + + transfer->direction = DMA_FROM_DEVICE; + + *direct = prepare_data_dma(this, buf, raw_len, &transfer->sgl, + DMA_FROM_DEVICE); + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(raw_len); + + if (this->bch) { + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE) + | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); + pio[3] = raw_len; + pio[4] = transfer->sgl.dma_address; + pio[5] = this->auxiliary_phys; + } + + desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), + DMA_TRANS_NONE, 0); + if (!desc) + return NULL; + + if (!this->bch) + desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, + DMA_DEV_TO_MEM, + MXS_DMA_CTRL_WAIT4END); + + return desc; +} + +static struct dma_async_tx_descriptor *gpmi_chain_data_write( + struct gpmi_nand_data *this, const void *buf, int raw_len) +{ + struct dma_chan *channel = get_dma_chan(this); + struct dma_async_tx_descriptor *desc; + struct gpmi_transfer *transfer; + u32 pio[6] = {}; + + transfer = get_next_transfer(this); + if (!transfer) + return NULL; + + transfer->direction = DMA_TO_DEVICE; + + prepare_data_dma(this, buf, raw_len, &transfer->sgl, DMA_TO_DEVICE); + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) + | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(raw_len); + + if (this->bch) { + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE) + | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); + pio[3] = raw_len; + pio[4] = transfer->sgl.dma_address; + pio[5] = this->auxiliary_phys; + } + + desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), + DMA_TRANS_NONE, + (this->bch ? MXS_DMA_CTRL_WAIT4END : 0)); + if (!desc) + return NULL; + + if (!this->bch) + desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, + DMA_MEM_TO_DEV, + MXS_DMA_CTRL_WAIT4END); + + return desc; +} + +static int gpmi_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + const struct nand_op_instr *instr; + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct dma_async_tx_descriptor *desc = NULL; + int i, ret, buf_len = 0, nbufs = 0; + u8 cmd = 0; + void *buf_read = NULL; + const void *buf_write = NULL; + bool direct = false; + struct completion *completion; + unsigned long to; + + this->ntransfers = 0; + for (i = 0; i < GPMI_MAX_TRANSFERS; i++) + this->transfers[i].direction = DMA_NONE; + + ret = pm_runtime_get_sync(this->dev); + if (ret < 0) + return ret; + + /* + * This driver currently supports only one NAND chip. Plus, dies share + * the same configuration. So once timings have been applied on the + * controller side, they will not change anymore. When the time will + * come, the check on must_apply_timings will have to be dropped. + */ + if (this->hw.must_apply_timings) { + this->hw.must_apply_timings = false; + gpmi_nfc_apply_timings(this); + } + + dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs); + + for (i = 0; i < op->ninstrs; i++) { + instr = &op->instrs[i]; + + nand_op_trace(" ", instr); + + switch (instr->type) { + case NAND_OP_WAITRDY_INSTR: + desc = gpmi_chain_wait_ready(this); + break; + case NAND_OP_CMD_INSTR: + cmd = instr->ctx.cmd.opcode; + + /* + * When this command has an address cycle chain it + * together with the address cycle + */ + if (i + 1 != op->ninstrs && + op->instrs[i + 1].type == NAND_OP_ADDR_INSTR) + continue; + + desc = gpmi_chain_command(this, cmd, NULL, 0); + + break; + case NAND_OP_ADDR_INSTR: + desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs, + instr->ctx.addr.naddrs); + break; + case NAND_OP_DATA_OUT_INSTR: + buf_write = instr->ctx.data.buf.out; + buf_len = instr->ctx.data.len; + nbufs++; + + desc = gpmi_chain_data_write(this, buf_write, buf_len); + + break; + case NAND_OP_DATA_IN_INSTR: + if (!instr->ctx.data.len) + break; + buf_read = instr->ctx.data.buf.in; + buf_len = instr->ctx.data.len; + nbufs++; + + desc = gpmi_chain_data_read(this, buf_read, buf_len, + &direct); + break; + } + + if (!desc) { + ret = -ENXIO; + goto unmap; + } + } + + dev_dbg(this->dev, "%s setup done\n", __func__); + + if (nbufs > 1) { + dev_err(this->dev, "Multiple data instructions not supported\n"); + ret = -EINVAL; + goto unmap; + } + + if (this->bch) { + writel(this->bch_flashlayout0, + this->resources.bch_regs + HW_BCH_FLASH0LAYOUT0); + writel(this->bch_flashlayout1, + this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1); + } + + if (this->bch && buf_read) { + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + this->resources.bch_regs + HW_BCH_CTRL_SET); + completion = &this->bch_done; + } else { + desc->callback = dma_irq_callback; + desc->callback_param = this; + completion = &this->dma_done; + } + + init_completion(completion); + + dmaengine_submit(desc); + dma_async_issue_pending(get_dma_chan(this)); + + to = wait_for_completion_timeout(completion, msecs_to_jiffies(1000)); + if (!to) { + dev_err(this->dev, "DMA timeout, last DMA\n"); + gpmi_dump_info(this); + ret = -ETIMEDOUT; + goto unmap; + } + + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + this->resources.bch_regs + HW_BCH_CTRL_CLR); + gpmi_clear_bch(this); + + ret = 0; + +unmap: + for (i = 0; i < this->ntransfers; i++) { + struct gpmi_transfer *transfer = &this->transfers[i]; + + if (transfer->direction != DMA_NONE) + dma_unmap_sg(this->dev, &transfer->sgl, 1, + transfer->direction); + } + + if (!ret && buf_read && !direct) + memcpy(buf_read, this->data_buffer_dma, + gpmi_raw_len_to_len(this, buf_len)); + + this->bch = false; + + pm_runtime_mark_last_busy(this->dev); + pm_runtime_put_autosuspend(this->dev); + + return ret; +} + static const struct nand_controller_ops gpmi_nand_controller_ops = { .attach_chip = gpmi_nand_attach_chip, .setup_data_interface = gpmi_setup_data_interface, + .exec_op = gpmi_nfc_exec_op, }; static int gpmi_nand_init(struct gpmi_nand_data *this) @@ -1901,9 +2553,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) struct mtd_info *mtd = nand_to_mtd(chip); int ret; - /* init current chip */ - this->current_chip = -1; - /* init the MTD data structures */ mtd->name = "gpmi-nand"; mtd->dev.parent = this->dev; @@ -1911,14 +2560,8 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); - chip->legacy.select_chip = gpmi_select_chip; - chip->legacy.cmd_ctrl = gpmi_cmd_ctrl; - chip->legacy.dev_ready = gpmi_dev_ready; - chip->legacy.read_byte = gpmi_read_byte; - chip->legacy.read_buf = gpmi_read_buf; - chip->legacy.write_buf = gpmi_write_buf; - chip->badblock_pattern = &gpmi_bbt_descr; chip->legacy.block_markbad = gpmi_block_markbad; + chip->badblock_pattern = &gpmi_bbt_descr; chip->options |= NAND_NO_SUBPAGE_WRITE; /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ @@ -1934,7 +2577,10 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) if (ret) goto err_out; - chip->legacy.dummy_controller.ops = &gpmi_nand_controller_ops; + nand_controller_init(&this->base); + this->base.ops = &gpmi_nand_controller_ops; + chip->controller = &this->base; + ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); if (ret) goto err_out; @@ -2004,6 +2650,16 @@ static int gpmi_nand_probe(struct platform_device *pdev) if (ret) goto exit_acquire_resources; + ret = __gpmi_enable_clk(this, true); + if (ret) + goto exit_nfc_init; + + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + ret = gpmi_init(this); if (ret) goto exit_nfc_init; @@ -2012,11 +2668,16 @@ static int gpmi_nand_probe(struct platform_device *pdev) if (ret) goto exit_nfc_init; + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + dev_info(this->dev, "driver registered.\n"); return 0; exit_nfc_init: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); release_resources(this); exit_acquire_resources: @@ -2027,6 +2688,9 @@ static int gpmi_nand_remove(struct platform_device *pdev) { struct gpmi_nand_data *this = platform_get_drvdata(pdev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + nand_release(&this->nand); gpmi_free_dma_buffer(this); release_resources(this); @@ -2069,8 +2733,23 @@ static int gpmi_pm_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ +static int __maybe_unused gpmi_runtime_suspend(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + + return __gpmi_enable_clk(this, false); +} + +static int __maybe_unused gpmi_runtime_resume(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + + return __gpmi_enable_clk(this, true); +} + static const struct dev_pm_ops gpmi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) + SET_RUNTIME_PM_OPS(gpmi_runtime_suspend, gpmi_runtime_resume, NULL) }; static struct platform_driver gpmi_nand_driver = { diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index a804a4a5bd46..fdc5ed7de083 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -103,6 +103,14 @@ struct gpmi_nfc_hardware_timing { u32 ctrl1n; }; +#define GPMI_MAX_TRANSFERS 8 + +struct gpmi_transfer { + u8 cmdbuf[8]; + struct scatterlist sgl; + enum dma_data_direction direction; +}; + struct gpmi_nand_data { /* Devdata */ const struct gpmi_devdata *devdata; @@ -126,25 +134,18 @@ struct gpmi_nand_data { struct boot_rom_geometry rom_geometry; /* MTD / NAND */ + struct nand_controller base; struct nand_chip nand; - /* General-use Variables */ - int current_chip; - unsigned int command_length; + struct gpmi_transfer transfers[GPMI_MAX_TRANSFERS]; + int ntransfers; - struct scatterlist cmd_sgl; - char *cmd_buffer; + bool bch; + uint32_t bch_flashlayout0; + uint32_t bch_flashlayout1; - struct scatterlist data_sgl; char *data_buffer_dma; - void *page_buffer_virt; - dma_addr_t page_buffer_phys; - unsigned int page_buffer_size; - - void *payload_virt; - dma_addr_t payload_phys; - void *auxiliary_virt; dma_addr_t auxiliary_phys; @@ -154,45 +155,8 @@ struct gpmi_nand_data { #define DMA_CHANS 8 struct dma_chan *dma_chans[DMA_CHANS]; struct completion dma_done; - - /* private */ - void *private; }; -/* Common Services */ -int common_nfc_set_geometry(struct gpmi_nand_data *); -struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -bool prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, - enum dma_data_direction dr); -int start_dma_without_bch_irq(struct gpmi_nand_data *, - struct dma_async_tx_descriptor *); -int start_dma_with_bch_irq(struct gpmi_nand_data *, - struct dma_async_tx_descriptor *); - -/* GPMI-NAND helper function library */ -int gpmi_init(struct gpmi_nand_data *); -void gpmi_clear_bch(struct gpmi_nand_data *); -void gpmi_dump_info(struct gpmi_nand_data *); -int bch_set_geometry(struct gpmi_nand_data *); -int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip); -int gpmi_send_command(struct gpmi_nand_data *); -int gpmi_enable_clk(struct gpmi_nand_data *this); -int gpmi_disable_clk(struct gpmi_nand_data *this); -int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr, - const struct nand_data_interface *conf); -void gpmi_nfc_apply_timings(struct gpmi_nand_data *this); -int gpmi_read_data(struct gpmi_nand_data *, void *buf, int len); -int gpmi_send_data(struct gpmi_nand_data *, const void *buf, int len); - -int gpmi_send_page(struct gpmi_nand_data *, - dma_addr_t payload, dma_addr_t auxiliary); -int gpmi_read_page(struct gpmi_nand_data *, - dma_addr_t payload, dma_addr_t auxiliary); - -void gpmi_copy_bits(u8 *dst, size_t dst_bit_off, - const u8 *src, size_t src_bit_off, - size_t nbits); - /* BCH : Status Block Completion Codes */ #define STATUS_GOOD 0x00 #define STATUS_ERASED 0xff diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 6a4626a8bf95..0b48be54ba6f 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -751,10 +751,8 @@ static int hisi_nfc_probe(struct platform_device *pdev) mtd = nand_to_mtd(chip); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no IRQ resource defined\n"); + if (irq < 0) return -ENXIO; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->iobase = devm_ioremap_resource(dev, res); diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig index 66b7cffdb0c2..e30feb56b650 100644 --- a/drivers/mtd/nand/raw/ingenic/Kconfig +++ b/drivers/mtd/nand/raw/ingenic/Kconfig @@ -1,11 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -config MTD_NAND_JZ4740 - tristate "JZ4740 NAND controller" - depends on MACH_JZ4740 || COMPILE_TEST - depends on HAS_IOMEM - help - Enables support for NAND Flash on JZ4740 SoC based boards. - config MTD_NAND_JZ4780 tristate "JZ4780 NAND controller" depends on JZ4780_NEMC diff --git a/drivers/mtd/nand/raw/ingenic/Makefile b/drivers/mtd/nand/raw/ingenic/Makefile index b63d36889263..4c53f5e759c3 100644 --- a/drivers/mtd/nand/raw/ingenic/Makefile +++ b/drivers/mtd/nand/raw/ingenic/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_JZ4780) += ingenic_nand.o ingenic_nand-y += ingenic_nand_drv.o diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index d7b7c0f13909..49afebee50db 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -310,7 +310,6 @@ static int ingenic_nand_init_chip(struct platform_device *pdev, struct device *dev = &pdev->dev; struct ingenic_nand *nand; struct ingenic_nand_cs *cs; - struct resource *res; struct nand_chip *chip; struct mtd_info *mtd; const __be32 *reg; @@ -326,8 +325,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev, jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); - res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); - cs->base = devm_ioremap_resource(dev, res); + cs->base = devm_platform_ioremap_resource(pdev, chipnr); if (IS_ERR(cs->base)) return PTR_ERR(cs->base); @@ -418,6 +416,7 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc, ret = ingenic_nand_init_chip(pdev, nfc, np, i); if (ret) { ingenic_nand_cleanup_chips(nfc); + of_node_put(np); return ret; } diff --git a/drivers/mtd/nand/raw/ingenic/jz4740_nand.c b/drivers/mtd/nand/raw/ingenic/jz4740_nand.c deleted file mode 100644 index acdf674fcc87..000000000000 --- a/drivers/mtd/nand/raw/ingenic/jz4740_nand.c +++ /dev/null @@ -1,536 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> - * JZ4740 SoC NAND controller driver - */ - -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/rawnand.h> -#include <linux/mtd/partitions.h> - -#include <linux/gpio/consumer.h> - -#include <linux/platform_data/jz4740/jz4740_nand.h> - -#define JZ_REG_NAND_CTRL 0x50 -#define JZ_REG_NAND_ECC_CTRL 0x100 -#define JZ_REG_NAND_DATA 0x104 -#define JZ_REG_NAND_PAR0 0x108 -#define JZ_REG_NAND_PAR1 0x10C -#define JZ_REG_NAND_PAR2 0x110 -#define JZ_REG_NAND_IRQ_STAT 0x114 -#define JZ_REG_NAND_IRQ_CTRL 0x118 -#define JZ_REG_NAND_ERR(x) (0x11C + ((x) << 2)) - -#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) -#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) -#define JZ_NAND_ECC_CTRL_RS BIT(2) -#define JZ_NAND_ECC_CTRL_RESET BIT(1) -#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) - -#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) -#define JZ_NAND_STATUS_PAD_FINISH BIT(4) -#define JZ_NAND_STATUS_DEC_FINISH BIT(3) -#define JZ_NAND_STATUS_ENC_FINISH BIT(2) -#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) -#define JZ_NAND_STATUS_ERROR BIT(0) - -#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) -#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) -#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa - -#define JZ_NAND_MEM_CMD_OFFSET 0x08000 -#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 - -struct jz_nand { - struct nand_chip chip; - void __iomem *base; - struct resource *mem; - - unsigned char banks[JZ_NAND_NUM_BANKS]; - void __iomem *bank_base[JZ_NAND_NUM_BANKS]; - struct resource *bank_mem[JZ_NAND_NUM_BANKS]; - - int selected_bank; - - struct gpio_desc *busy_gpio; - bool is_reading; -}; - -static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct jz_nand, chip); -} - -static void jz_nand_select_chip(struct nand_chip *chip, int chipnr) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - uint32_t ctrl; - int banknr; - - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; - - if (chipnr == -1) { - banknr = -1; - } else { - banknr = nand->banks[chipnr] - 1; - chip->legacy.IO_ADDR_R = nand->bank_base[banknr]; - chip->legacy.IO_ADDR_W = nand->bank_base[banknr]; - } - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - nand->selected_bank = banknr; -} - -static void jz_nand_cmd_ctrl(struct nand_chip *chip, int dat, - unsigned int ctrl) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - uint32_t reg; - void __iomem *bank_base = nand->bank_base[nand->selected_bank]; - - BUG_ON(nand->selected_bank < 0); - - if (ctrl & NAND_CTRL_CHANGE) { - BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); - if (ctrl & NAND_ALE) - bank_base += JZ_NAND_MEM_ADDR_OFFSET; - else if (ctrl & NAND_CLE) - bank_base += JZ_NAND_MEM_CMD_OFFSET; - chip->legacy.IO_ADDR_W = bank_base; - - reg = readl(nand->base + JZ_REG_NAND_CTRL); - if (ctrl & NAND_NCE) - reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); - else - reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); - writel(reg, nand->base + JZ_REG_NAND_CTRL); - } - if (dat != NAND_CMD_NONE) - writeb(dat, chip->legacy.IO_ADDR_W); -} - -static int jz_nand_dev_ready(struct nand_chip *chip) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - return gpiod_get_value_cansleep(nand->busy_gpio); -} - -static void jz_nand_hwctl(struct nand_chip *chip, int mode) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - uint32_t reg; - - writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - - reg |= JZ_NAND_ECC_CTRL_RESET; - reg |= JZ_NAND_ECC_CTRL_ENABLE; - reg |= JZ_NAND_ECC_CTRL_RS; - - switch (mode) { - case NAND_ECC_READ: - reg &= ~JZ_NAND_ECC_CTRL_ENCODING; - nand->is_reading = true; - break; - case NAND_ECC_WRITE: - reg |= JZ_NAND_ECC_CTRL_ENCODING; - nand->is_reading = false; - break; - default: - break; - } - - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); -} - -static int jz_nand_calculate_ecc_rs(struct nand_chip *chip, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - uint32_t reg, status; - int i; - unsigned int timeout = 1000; - static uint8_t empty_block_ecc[] = {0xcd, 0x9d, 0x90, 0x58, 0xf4, - 0x8b, 0xff, 0xb7, 0x6f}; - - if (nand->is_reading) - return 0; - - do { - status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); - } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout); - - if (timeout == 0) - return -1; - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg &= ~JZ_NAND_ECC_CTRL_ENABLE; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - for (i = 0; i < 9; ++i) - ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); - - /* If the written data is completly 0xff, we also want to write 0xff as - * ecc, otherwise we will get in trouble when doing subpage writes. */ - if (memcmp(ecc_code, empty_block_ecc, 9) == 0) - memset(ecc_code, 0xff, 9); - - return 0; -} - -static void jz_nand_correct_data(uint8_t *dat, int index, int mask) -{ - int offset = index & 0x7; - uint16_t data; - - index += (index >> 3); - - data = dat[index]; - data |= dat[index+1] << 8; - - mask ^= (data >> offset) & 0x1ff; - data &= ~(0x1ff << offset); - data |= (mask << offset); - - dat[index] = data & 0xff; - dat[index+1] = (data >> 8) & 0xff; -} - -static int jz_nand_correct_ecc_rs(struct nand_chip *chip, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct jz_nand *nand = mtd_to_jz_nand(nand_to_mtd(chip)); - int i, error_count, index; - uint32_t reg, status, error; - unsigned int timeout = 1000; - - for (i = 0; i < 9; ++i) - writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg |= JZ_NAND_ECC_CTRL_PAR_READY; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - do { - status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); - } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); - - if (timeout == 0) - return -ETIMEDOUT; - - reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); - reg &= ~JZ_NAND_ECC_CTRL_ENABLE; - writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); - - if (status & JZ_NAND_STATUS_ERROR) { - if (status & JZ_NAND_STATUS_UNCOR_ERROR) - return -EBADMSG; - - error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; - - for (i = 0; i < error_count; ++i) { - error = readl(nand->base + JZ_REG_NAND_ERR(i)); - index = ((error >> 16) & 0x1ff) - 1; - if (index >= 0 && index < 512) - jz_nand_correct_data(dat, index, error & 0x1ff); - } - - return error_count; - } - - return 0; -} - -static int jz_nand_ioremap_resource(struct platform_device *pdev, - const char *name, struct resource **res, void __iomem **base) -{ - int ret; - - *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!*res) { - dev_err(&pdev->dev, "Failed to get platform %s memory\n", name); - ret = -ENXIO; - goto err; - } - - *res = request_mem_region((*res)->start, resource_size(*res), - pdev->name); - if (!*res) { - dev_err(&pdev->dev, "Failed to request %s memory region\n", name); - ret = -EBUSY; - goto err; - } - - *base = ioremap((*res)->start, resource_size(*res)); - if (!*base) { - dev_err(&pdev->dev, "Failed to ioremap %s memory region\n", name); - ret = -EBUSY; - goto err_release_mem; - } - - return 0; - -err_release_mem: - release_mem_region((*res)->start, resource_size(*res)); -err: - *res = NULL; - *base = NULL; - return ret; -} - -static inline void jz_nand_iounmap_resource(struct resource *res, - void __iomem *base) -{ - iounmap(base); - release_mem_region(res->start, resource_size(res)); -} - -static int jz_nand_detect_bank(struct platform_device *pdev, - struct jz_nand *nand, unsigned char bank, - size_t chipnr, uint8_t *nand_maf_id, - uint8_t *nand_dev_id) -{ - int ret; - char res_name[6]; - uint32_t ctrl; - struct nand_chip *chip = &nand->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_memory_organization *memorg; - u8 id[2]; - - memorg = nanddev_get_memorg(&chip->base); - - /* Request I/O resource. */ - sprintf(res_name, "bank%d", bank); - ret = jz_nand_ioremap_resource(pdev, res_name, - &nand->bank_mem[bank - 1], - &nand->bank_base[bank - 1]); - if (ret) - return ret; - - /* Enable chip in bank. */ - ctrl = readl(nand->base + JZ_REG_NAND_CTRL); - ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - - if (chipnr == 0) { - /* Detect first chip. */ - ret = nand_scan(chip, 1); - if (ret) - goto notfound_id; - - /* Retrieve the IDs from the first chip. */ - nand_select_target(chip, 0); - nand_reset_op(chip); - nand_readid_op(chip, 0, id, sizeof(id)); - *nand_maf_id = id[0]; - *nand_dev_id = id[1]; - } else { - /* Detect additional chip. */ - nand_select_target(chip, chipnr); - nand_reset_op(chip); - nand_readid_op(chip, 0, id, sizeof(id)); - if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { - ret = -ENODEV; - goto notfound_id; - } - - /* Update size of the MTD. */ - memorg->ntargets++; - mtd->size += nanddev_target_size(&chip->base); - } - - dev_info(&pdev->dev, "Found chip %zu on bank %i\n", chipnr, bank); - return 0; - -notfound_id: - dev_info(&pdev->dev, "No chip found on bank %i\n", bank); - ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); - writel(ctrl, nand->base + JZ_REG_NAND_CTRL); - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - return ret; -} - -static int jz_nand_attach_chip(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct device *dev = mtd->dev.parent; - struct jz_nand_platform_data *pdata = dev_get_platdata(dev); - struct platform_device *pdev = to_platform_device(dev); - - if (pdata && pdata->ident_callback) - pdata->ident_callback(pdev, mtd, &pdata->partitions, - &pdata->num_partitions); - - return 0; -} - -static const struct nand_controller_ops jz_nand_controller_ops = { - .attach_chip = jz_nand_attach_chip, -}; - -static int jz_nand_probe(struct platform_device *pdev) -{ - int ret; - struct jz_nand *nand; - struct nand_chip *chip; - struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - size_t chipnr, bank_idx; - uint8_t nand_maf_id = 0, nand_dev_id = 0; - - nand = kzalloc(sizeof(*nand), GFP_KERNEL); - if (!nand) - return -ENOMEM; - - ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); - if (ret) - goto err_free; - - nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN); - if (IS_ERR(nand->busy_gpio)) { - ret = PTR_ERR(nand->busy_gpio); - dev_err(&pdev->dev, "Failed to request busy gpio %d\n", - ret); - goto err_iounmap_mmio; - } - - chip = &nand->chip; - mtd = nand_to_mtd(chip); - mtd->dev.parent = &pdev->dev; - mtd->name = "jz4740-nand"; - - chip->ecc.hwctl = jz_nand_hwctl; - chip->ecc.calculate = jz_nand_calculate_ecc_rs; - chip->ecc.correct = jz_nand_correct_ecc_rs; - chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; - chip->ecc.size = 512; - chip->ecc.bytes = 9; - chip->ecc.strength = 4; - chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - - chip->legacy.chip_delay = 50; - chip->legacy.cmd_ctrl = jz_nand_cmd_ctrl; - chip->legacy.select_chip = jz_nand_select_chip; - chip->legacy.dummy_controller.ops = &jz_nand_controller_ops; - - if (nand->busy_gpio) - chip->legacy.dev_ready = jz_nand_dev_ready; - - platform_set_drvdata(pdev, nand); - - /* We are going to autodetect NAND chips in the banks specified in the - * platform data. Although nand_scan_ident() can detect multiple chips, - * it requires those chips to be numbered consecuitively, which is not - * always the case for external memory banks. And a fixed chip-to-bank - * mapping is not practical either, since for example Dingoo units - * produced at different times have NAND chips in different banks. - */ - chipnr = 0; - for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { - unsigned char bank; - - /* If there is no platform data, look for NAND in bank 1, - * which is the most likely bank since it is the only one - * that can be booted from. - */ - bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; - if (bank == 0) - break; - if (bank > JZ_NAND_NUM_BANKS) { - dev_warn(&pdev->dev, - "Skipping non-existing bank: %d\n", bank); - continue; - } - /* The detection routine will directly or indirectly call - * jz_nand_select_chip(), so nand->banks has to contain the - * bank we're checking. - */ - nand->banks[chipnr] = bank; - if (jz_nand_detect_bank(pdev, nand, bank, chipnr, - &nand_maf_id, &nand_dev_id) == 0) - chipnr++; - else - nand->banks[chipnr] = 0; - } - if (chipnr == 0) { - dev_err(&pdev->dev, "No NAND chips found\n"); - goto err_iounmap_mmio; - } - - ret = mtd_device_register(mtd, pdata ? pdata->partitions : NULL, - pdata ? pdata->num_partitions : 0); - - if (ret) { - dev_err(&pdev->dev, "Failed to add mtd device\n"); - goto err_cleanup_nand; - } - - dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); - - return 0; - -err_cleanup_nand: - nand_cleanup(chip); - while (chipnr--) { - unsigned char bank = nand->banks[chipnr]; - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - } - writel(0, nand->base + JZ_REG_NAND_CTRL); -err_iounmap_mmio: - jz_nand_iounmap_resource(nand->mem, nand->base); -err_free: - kfree(nand); - return ret; -} - -static int jz_nand_remove(struct platform_device *pdev) -{ - struct jz_nand *nand = platform_get_drvdata(pdev); - size_t i; - - nand_release(&nand->chip); - - /* Deassert and disable all chips */ - writel(0, nand->base + JZ_REG_NAND_CTRL); - - for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { - unsigned char bank = nand->banks[i]; - if (bank != 0) { - jz_nand_iounmap_resource(nand->bank_mem[bank - 1], - nand->bank_base[bank - 1]); - } - } - - jz_nand_iounmap_resource(nand->mem, nand->base); - - kfree(nand); - - return 0; -} - -static struct platform_driver jz_nand_driver = { - .probe = jz_nand_probe, - .remove = jz_nand_remove, - .driver = { - .name = "jz4740-nand", - }, -}; - -module_platform_driver(jz_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); -MODULE_DESCRIPTION("NAND controller driver for JZ4740 SoC"); -MODULE_ALIAS("platform:jz4740-nand"); diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 78b31f845c50..241b58b83240 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -773,7 +773,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { - dev_err(&pdev->dev, "failed to get platform irq\n"); res = -EINVAL; goto release_dma_chan; } diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index fc49e13d81ec..fb5abdcfb007 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2862,10 +2862,8 @@ static int marvell_nfc_probe(struct platform_device *pdev) return PTR_ERR(nfc->regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq\n"); + if (irq < 0) return irq; - } nfc->core_clk = devm_clk_get(&pdev->dev, "core"); diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index ea57ddcec41e..9f17b5b8efbf 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1320,6 +1320,7 @@ static int meson_nfc_nand_chips_init(struct device *dev, ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); if (ret) { meson_nfc_nand_chip_cleanup(nfc); + of_node_put(nand_np); return ret; } } @@ -1398,10 +1399,8 @@ static int meson_nfc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no NFC IRQ resource\n"); + if (irq < 0) return -EINVAL; - } ret = meson_nfc_clk_init(nfc); if (ret) { diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 0f90e060dae8..75f1fa3d4d35 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 OR MIT /* * MTK ECC controller driver. * Copyright (C) 2016 MediaTek Inc. @@ -527,10 +527,8 @@ static int mtk_ecc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get irq: %d\n", irq); + if (irq < 0) return irq; - } ret = dma_set_mask(dev, DMA_BIT_MASK(32)); if (ret) { @@ -596,4 +594,4 @@ module_platform_driver(mtk_ecc_driver); MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>"); MODULE_DESCRIPTION("MTK Nand ECC Driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h index aa52e94c771d..0e48c36e6ca0 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.h +++ b/drivers/mtd/nand/raw/mtk_ecc.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* * MTK SDG1 ECC controller * diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index dceff28c9a31..b8305e39ab51 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 OR MIT /* * MTK NAND Flash controller driver. * Copyright (C) 2016 MediaTek Inc. @@ -79,6 +79,10 @@ #define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) #define NFI_FDM_MAX_SIZE (8) #define NFI_FDM_MIN_SIZE (1) +#define NFI_DEBUG_CON1 (0x220) +#define STROBE_MASK GENMASK(4, 3) +#define STROBE_SHIFT (3) +#define MAX_STROBE_DLY (3) #define NFI_MASTER_STA (0x224) #define MASTER_STA_MASK (0x0FFF) #define NFI_EMPTY_THRESH (0x23C) @@ -150,6 +154,8 @@ struct mtk_nfc { struct list_head chips; u8 *buffer; + + unsigned long assigned_cs; }; /* @@ -500,7 +506,8 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline, { struct mtk_nfc *nfc = nand_get_controller_data(chip); const struct nand_sdr_timings *timings; - u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; + u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst = 0, trlt = 0; + u32 temp, tsel = 0; timings = nand_get_sdr_timings(conf); if (IS_ERR(timings)) @@ -536,14 +543,53 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline, twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; twh &= 0xf; - twst = timings->tWP_min / 1000; + /* Calculate real WE#/RE# hold time in nanosecond */ + temp = (twh + 1) * 1000000 / rate; + /* nanosecond to picosecond */ + temp *= 1000; + + /* + * WE# low level time should be expaned to meet WE# pulse time + * and WE# cycle time at the same time. + */ + if (temp < timings->tWC_min) + twst = timings->tWC_min - temp; + twst = max(timings->tWP_min, twst) / 1000; twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; twst &= 0xf; - trlt = max(timings->tREA_max, timings->tRP_min) / 1000; + /* + * RE# low level time should be expaned to meet RE# pulse time + * and RE# cycle time at the same time. + */ + if (temp < timings->tRC_min) + trlt = timings->tRC_min - temp; + trlt = max(trlt, timings->tRP_min) / 1000; trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; trlt &= 0xf; + /* Calculate RE# pulse time in nanosecond. */ + temp = (trlt + 1) * 1000000 / rate; + /* nanosecond to picosecond */ + temp *= 1000; + /* + * If RE# access time is bigger than RE# pulse time, + * delay sampling data timing. + */ + if (temp < timings->tREA_max) { + tsel = timings->tREA_max / 1000; + tsel = DIV_ROUND_UP(tsel * rate, 1000000); + tsel -= (trlt + 1); + if (tsel > MAX_STROBE_DLY) { + trlt += tsel - MAX_STROBE_DLY; + tsel = MAX_STROBE_DLY; + } + } + temp = nfi_readl(nfc, NFI_DEBUG_CON1); + temp &= ~STROBE_MASK; + temp |= tsel << STROBE_SHIFT; + nfi_writel(nfc, temp, NFI_DEBUG_CON1); + /* * ACCON: access timing control register * ------------------------------------- @@ -835,19 +881,21 @@ static int mtk_nfc_write_oob_std(struct nand_chip *chip, int page) return mtk_nfc_write_page_raw(chip, NULL, 1, page); } -static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) +static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 start, + u32 sectors) { struct nand_chip *chip = mtd_to_nand(mtd); struct mtk_nfc *nfc = nand_get_controller_data(chip); struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); struct mtk_ecc_stats stats; + u32 reg_size = mtk_nand->fdm.reg_size; int rc, i; rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; if (rc) { memset(buf, 0xff, sectors * chip->ecc.size); for (i = 0; i < sectors; i++) - memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size); + memset(oob_ptr(chip, start + i), 0xff, reg_size); return 0; } @@ -867,7 +915,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, u32 spare = mtk_nand->spare_per_sector; u32 column, sectors, start, end, reg; dma_addr_t addr; - int bitflips; + int bitflips = 0; size_t len; u8 *buf; int rc; @@ -934,14 +982,11 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, if (rc < 0) { dev_err(nfc->dev, "subpage done timeout\n"); bitflips = -EIO; - } else { - bitflips = 0; - if (!raw) { - rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); - bitflips = rc < 0 ? -ETIMEDOUT : - mtk_nfc_update_ecc_stats(mtd, buf, sectors); - mtk_nfc_read_fdm(chip, start, sectors); - } + } else if (!raw) { + rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); + bitflips = rc < 0 ? -ETIMEDOUT : + mtk_nfc_update_ecc_stats(mtd, buf, start, sectors); + mtk_nfc_read_fdm(chip, start, sectors); } dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); @@ -1315,6 +1360,17 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, dev_err(dev, "reg property failure : %d\n", ret); return ret; } + + if (tmp >= MTK_NAND_MAX_NSELS) { + dev_err(dev, "invalid CS: %u\n", tmp); + return -EINVAL; + } + + if (test_and_set_bit(tmp, &nfc->assigned_cs)) { + dev_err(dev, "CS %u already assigned\n", tmp); + return -EINVAL; + } + chip->sels[i] = tmp; } @@ -1484,7 +1540,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "no nfi irq resource\n"); ret = -EINVAL; goto clk_disable; } @@ -1589,6 +1644,6 @@ static struct platform_driver mtk_nfc_driver = { module_platform_driver(mtk_nfc_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual MIT/GPL"); MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>"); MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); diff --git a/drivers/mtd/nand/raw/mxic_nand.c b/drivers/mtd/nand/raw/mxic_nand.c new file mode 100644 index 000000000000..ed7a4e021bf5 --- /dev/null +++ b/drivers/mtd/nand/raw/mxic_nand.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Macronix International Co., Ltd. + * + * Author: + * Mason Yang <masonccyang@mxic.com.tw> + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/platform_device.h> + +#include "internals.h" + +#define HC_CFG 0x0 +#define HC_CFG_IF_CFG(x) ((x) << 27) +#define HC_CFG_DUAL_SLAVE BIT(31) +#define HC_CFG_INDIVIDUAL BIT(30) +#define HC_CFG_NIO(x) (((x) / 4) << 27) +#define HC_CFG_TYPE(s, t) ((t) << (23 + ((s) * 2))) +#define HC_CFG_TYPE_SPI_NOR 0 +#define HC_CFG_TYPE_SPI_NAND 1 +#define HC_CFG_TYPE_SPI_RAM 2 +#define HC_CFG_TYPE_RAW_NAND 3 +#define HC_CFG_SLV_ACT(x) ((x) << 21) +#define HC_CFG_CLK_PH_EN BIT(20) +#define HC_CFG_CLK_POL_INV BIT(19) +#define HC_CFG_BIG_ENDIAN BIT(18) +#define HC_CFG_DATA_PASS BIT(17) +#define HC_CFG_IDLE_SIO_LVL(x) ((x) << 16) +#define HC_CFG_MAN_START_EN BIT(3) +#define HC_CFG_MAN_START BIT(2) +#define HC_CFG_MAN_CS_EN BIT(1) +#define HC_CFG_MAN_CS_ASSERT BIT(0) + +#define INT_STS 0x4 +#define INT_STS_EN 0x8 +#define INT_SIG_EN 0xc +#define INT_STS_ALL GENMASK(31, 0) +#define INT_RDY_PIN BIT(26) +#define INT_RDY_SR BIT(25) +#define INT_LNR_SUSP BIT(24) +#define INT_ECC_ERR BIT(17) +#define INT_CRC_ERR BIT(16) +#define INT_LWR_DIS BIT(12) +#define INT_LRD_DIS BIT(11) +#define INT_SDMA_INT BIT(10) +#define INT_DMA_FINISH BIT(9) +#define INT_RX_NOT_FULL BIT(3) +#define INT_RX_NOT_EMPTY BIT(2) +#define INT_TX_NOT_FULL BIT(1) +#define INT_TX_EMPTY BIT(0) + +#define HC_EN 0x10 +#define HC_EN_BIT BIT(0) + +#define TXD(x) (0x14 + ((x) * 4)) +#define RXD 0x24 + +#define SS_CTRL(s) (0x30 + ((s) * 4)) +#define LRD_CFG 0x44 +#define LWR_CFG 0x80 +#define RWW_CFG 0x70 +#define OP_READ BIT(23) +#define OP_DUMMY_CYC(x) ((x) << 17) +#define OP_ADDR_BYTES(x) ((x) << 14) +#define OP_CMD_BYTES(x) (((x) - 1) << 13) +#define OP_OCTA_CRC_EN BIT(12) +#define OP_DQS_EN BIT(11) +#define OP_ENHC_EN BIT(10) +#define OP_PREAMBLE_EN BIT(9) +#define OP_DATA_DDR BIT(8) +#define OP_DATA_BUSW(x) ((x) << 6) +#define OP_ADDR_DDR BIT(5) +#define OP_ADDR_BUSW(x) ((x) << 3) +#define OP_CMD_DDR BIT(2) +#define OP_CMD_BUSW(x) (x) +#define OP_BUSW_1 0 +#define OP_BUSW_2 1 +#define OP_BUSW_4 2 +#define OP_BUSW_8 3 + +#define OCTA_CRC 0x38 +#define OCTA_CRC_IN_EN(s) BIT(3 + ((s) * 16)) +#define OCTA_CRC_CHUNK(s, x) ((fls((x) / 32)) << (1 + ((s) * 16))) +#define OCTA_CRC_OUT_EN(s) BIT(0 + ((s) * 16)) + +#define ONFI_DIN_CNT(s) (0x3c + (s)) + +#define LRD_CTRL 0x48 +#define RWW_CTRL 0x74 +#define LWR_CTRL 0x84 +#define LMODE_EN BIT(31) +#define LMODE_SLV_ACT(x) ((x) << 21) +#define LMODE_CMD1(x) ((x) << 8) +#define LMODE_CMD0(x) (x) + +#define LRD_ADDR 0x4c +#define LWR_ADDR 0x88 +#define LRD_RANGE 0x50 +#define LWR_RANGE 0x8c + +#define AXI_SLV_ADDR 0x54 + +#define DMAC_RD_CFG 0x58 +#define DMAC_WR_CFG 0x94 +#define DMAC_CFG_PERIPH_EN BIT(31) +#define DMAC_CFG_ALLFLUSH_EN BIT(30) +#define DMAC_CFG_LASTFLUSH_EN BIT(29) +#define DMAC_CFG_QE(x) (((x) + 1) << 16) +#define DMAC_CFG_BURST_LEN(x) (((x) + 1) << 12) +#define DMAC_CFG_BURST_SZ(x) ((x) << 8) +#define DMAC_CFG_DIR_READ BIT(1) +#define DMAC_CFG_START BIT(0) + +#define DMAC_RD_CNT 0x5c +#define DMAC_WR_CNT 0x98 + +#define SDMA_ADDR 0x60 + +#define DMAM_CFG 0x64 +#define DMAM_CFG_START BIT(31) +#define DMAM_CFG_CONT BIT(30) +#define DMAM_CFG_SDMA_GAP(x) (fls((x) / 8192) << 2) +#define DMAM_CFG_DIR_READ BIT(1) +#define DMAM_CFG_EN BIT(0) + +#define DMAM_CNT 0x68 + +#define LNR_TIMER_TH 0x6c + +#define RDM_CFG0 0x78 +#define RDM_CFG0_POLY(x) (x) + +#define RDM_CFG1 0x7c +#define RDM_CFG1_RDM_EN BIT(31) +#define RDM_CFG1_SEED(x) (x) + +#define LWR_SUSP_CTRL 0x90 +#define LWR_SUSP_CTRL_EN BIT(31) + +#define DMAS_CTRL 0x9c +#define DMAS_CTRL_EN BIT(31) +#define DMAS_CTRL_DIR_READ BIT(30) + +#define DATA_STROB 0xa0 +#define DATA_STROB_EDO_EN BIT(2) +#define DATA_STROB_INV_POL BIT(1) +#define DATA_STROB_DELAY_2CYC BIT(0) + +#define IDLY_CODE(x) (0xa4 + ((x) * 4)) +#define IDLY_CODE_VAL(x, v) ((v) << (((x) % 4) * 8)) + +#define GPIO 0xc4 +#define GPIO_PT(x) BIT(3 + ((x) * 16)) +#define GPIO_RESET(x) BIT(2 + ((x) * 16)) +#define GPIO_HOLDB(x) BIT(1 + ((x) * 16)) +#define GPIO_WPB(x) BIT((x) * 16) + +#define HC_VER 0xd0 + +#define HW_TEST(x) (0xe0 + ((x) * 4)) + +#define MXIC_NFC_MAX_CLK_HZ 50000000 +#define IRQ_TIMEOUT 1000 + +struct mxic_nand_ctlr { + struct clk *ps_clk; + struct clk *send_clk; + struct clk *send_dly_clk; + struct completion complete; + void __iomem *regs; + struct nand_controller controller; + struct device *dev; + struct nand_chip chip; +}; + +static int mxic_nfc_clk_enable(struct mxic_nand_ctlr *nfc) +{ + int ret; + + ret = clk_prepare_enable(nfc->ps_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(nfc->send_clk); + if (ret) + goto err_ps_clk; + + ret = clk_prepare_enable(nfc->send_dly_clk); + if (ret) + goto err_send_dly_clk; + + return ret; + +err_send_dly_clk: + clk_disable_unprepare(nfc->send_clk); +err_ps_clk: + clk_disable_unprepare(nfc->ps_clk); + + return ret; +} + +static void mxic_nfc_clk_disable(struct mxic_nand_ctlr *nfc) +{ + clk_disable_unprepare(nfc->send_clk); + clk_disable_unprepare(nfc->send_dly_clk); + clk_disable_unprepare(nfc->ps_clk); +} + +static void mxic_nfc_set_input_delay(struct mxic_nand_ctlr *nfc, u8 idly_code) +{ + writel(IDLY_CODE_VAL(0, idly_code) | + IDLY_CODE_VAL(1, idly_code) | + IDLY_CODE_VAL(2, idly_code) | + IDLY_CODE_VAL(3, idly_code), + nfc->regs + IDLY_CODE(0)); + writel(IDLY_CODE_VAL(4, idly_code) | + IDLY_CODE_VAL(5, idly_code) | + IDLY_CODE_VAL(6, idly_code) | + IDLY_CODE_VAL(7, idly_code), + nfc->regs + IDLY_CODE(1)); +} + +static int mxic_nfc_clk_setup(struct mxic_nand_ctlr *nfc, unsigned long freq) +{ + int ret; + + ret = clk_set_rate(nfc->send_clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(nfc->send_dly_clk, freq); + if (ret) + return ret; + + /* + * A constant delay range from 0x0 ~ 0x1F for input delay, + * the unit is 78 ps, the max input delay is 2.418 ns. + */ + mxic_nfc_set_input_delay(nfc, 0xf); + + /* + * Phase degree = 360 * freq * output-delay + * where output-delay is a constant value 1 ns in FPGA. + * + * Get Phase degree = 360 * freq * 1 ns + * = 360 * freq * 1 sec / 1000000000 + * = 9 * freq / 25000000 + */ + ret = clk_set_phase(nfc->send_dly_clk, 9 * freq / 25000000); + if (ret) + return ret; + + return 0; +} + +static int mxic_nfc_set_freq(struct mxic_nand_ctlr *nfc, unsigned long freq) +{ + int ret; + + if (freq > MXIC_NFC_MAX_CLK_HZ) + freq = MXIC_NFC_MAX_CLK_HZ; + + mxic_nfc_clk_disable(nfc); + ret = mxic_nfc_clk_setup(nfc, freq); + if (ret) + return ret; + + ret = mxic_nfc_clk_enable(nfc); + if (ret) + return ret; + + return 0; +} + +static irqreturn_t mxic_nfc_isr(int irq, void *dev_id) +{ + struct mxic_nand_ctlr *nfc = dev_id; + u32 sts; + + sts = readl(nfc->regs + INT_STS); + if (sts & INT_RDY_PIN) + complete(&nfc->complete); + else + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static void mxic_nfc_hw_init(struct mxic_nand_ctlr *nfc) +{ + writel(HC_CFG_NIO(8) | HC_CFG_TYPE(1, HC_CFG_TYPE_RAW_NAND) | + HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN | + HC_CFG_IDLE_SIO_LVL(1), nfc->regs + HC_CFG); + writel(INT_STS_ALL, nfc->regs + INT_STS_EN); + writel(INT_RDY_PIN, nfc->regs + INT_SIG_EN); + writel(0x0, nfc->regs + ONFI_DIN_CNT(0)); + writel(0, nfc->regs + LRD_CFG); + writel(0, nfc->regs + LRD_CTRL); + writel(0x0, nfc->regs + HC_EN); +} + +static void mxic_nfc_cs_enable(struct mxic_nand_ctlr *nfc) +{ + writel(readl(nfc->regs + HC_CFG) | HC_CFG_MAN_CS_EN, + nfc->regs + HC_CFG); + writel(HC_CFG_MAN_CS_ASSERT | readl(nfc->regs + HC_CFG), + nfc->regs + HC_CFG); +} + +static void mxic_nfc_cs_disable(struct mxic_nand_ctlr *nfc) +{ + writel(~HC_CFG_MAN_CS_ASSERT & readl(nfc->regs + HC_CFG), + nfc->regs + HC_CFG); +} + +static int mxic_nfc_wait_ready(struct nand_chip *chip) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + int ret; + + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(IRQ_TIMEOUT)); + if (!ret) { + dev_err(nfc->dev, "nand device timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int mxic_nfc_data_xfer(struct mxic_nand_ctlr *nfc, const void *txbuf, + void *rxbuf, unsigned int len) +{ + unsigned int pos = 0; + + while (pos < len) { + unsigned int nbytes = len - pos; + u32 data = 0xffffffff; + u32 sts; + int ret; + + if (nbytes > 4) + nbytes = 4; + + if (txbuf) + memcpy(&data, txbuf + pos, nbytes); + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, USEC_PER_SEC); + if (ret) + return ret; + + writel(data, nfc->regs + TXD(nbytes % 4)); + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, USEC_PER_SEC); + if (ret) + return ret; + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_RX_NOT_EMPTY, 0, + USEC_PER_SEC); + if (ret) + return ret; + + data = readl(nfc->regs + RXD); + if (rxbuf) { + data >>= (8 * (4 - nbytes)); + memcpy(rxbuf + pos, &data, nbytes); + } + if (readl(nfc->regs + INT_STS) & INT_RX_NOT_EMPTY) + dev_warn(nfc->dev, "RX FIFO not empty\n"); + + pos += nbytes; + } + + return 0; +} + +static int mxic_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, bool check_only) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + const struct nand_op_instr *instr = NULL; + int ret = 0; + unsigned int op_id; + + mxic_nfc_cs_enable(nfc); + init_completion(&nfc->complete); + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writel(0, nfc->regs + HC_EN); + writel(HC_EN_BIT, nfc->regs + HC_EN); + writel(OP_CMD_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_CMD_BYTES(0), nfc->regs + SS_CTRL(0)); + + ret = mxic_nfc_data_xfer(nfc, + &instr->ctx.cmd.opcode, + NULL, 1); + break; + + case NAND_OP_ADDR_INSTR: + writel(OP_ADDR_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_ADDR_BYTES(instr->ctx.addr.naddrs), + nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, + instr->ctx.addr.addrs, NULL, + instr->ctx.addr.naddrs); + break; + + case NAND_OP_DATA_IN_INSTR: + writel(0x0, nfc->regs + ONFI_DIN_CNT(0)); + writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_READ, nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, NULL, + instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + writel(instr->ctx.data.len, + nfc->regs + ONFI_DIN_CNT(0)); + writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F), + nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, + instr->ctx.data.buf.out, NULL, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = mxic_nfc_wait_ready(chip); + break; + } + } + mxic_nfc_cs_disable(nfc); + + return ret; +} + +static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + const struct nand_sdr_timings *sdr; + unsigned long freq; + int ret; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + freq = NSEC_PER_SEC / (sdr->tRC_min / 1000); + + ret = mxic_nfc_set_freq(nfc, freq); + if (ret) + dev_err(nfc->dev, "set freq:%ld failed\n", freq); + + if (sdr->tRC_min < 30000) + writel(DATA_STROB_EDO_EN, nfc->regs + DATA_STROB); + + return 0; +} + +static const struct nand_controller_ops mxic_nand_controller_ops = { + .exec_op = mxic_nfc_exec_op, + .setup_data_interface = mxic_nfc_setup_data_interface, +}; + +static int mxic_nfc_probe(struct platform_device *pdev) +{ + struct device_node *nand_np, *np = pdev->dev.of_node; + struct mtd_info *mtd; + struct mxic_nand_ctlr *nfc; + struct nand_chip *nand_chip; + int err; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(struct mxic_nand_ctlr), + GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->ps_clk = devm_clk_get(&pdev->dev, "ps"); + if (IS_ERR(nfc->ps_clk)) + return PTR_ERR(nfc->ps_clk); + + nfc->send_clk = devm_clk_get(&pdev->dev, "send"); + if (IS_ERR(nfc->send_clk)) + return PTR_ERR(nfc->send_clk); + + nfc->send_dly_clk = devm_clk_get(&pdev->dev, "send_dly"); + if (IS_ERR(nfc->send_dly_clk)) + return PTR_ERR(nfc->send_dly_clk); + + nfc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + nand_chip = &nfc->chip; + mtd = nand_to_mtd(nand_chip); + mtd->dev.parent = &pdev->dev; + + for_each_child_of_node(np, nand_np) + nand_set_flash_node(nand_chip, nand_np); + + nand_chip->priv = nfc; + nfc->dev = &pdev->dev; + nfc->controller.ops = &mxic_nand_controller_ops; + nand_controller_init(&nfc->controller); + nand_chip->controller = &nfc->controller; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mxic_nfc_hw_init(nfc); + + err = devm_request_irq(&pdev->dev, irq, mxic_nfc_isr, + 0, "mxic-nfc", nfc); + if (err) + goto fail; + + err = nand_scan(nand_chip, 1); + if (err) + goto fail; + + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto fail; + + platform_set_drvdata(pdev, nfc); + return 0; + +fail: + mxic_nfc_clk_disable(nfc); + return err; +} + +static int mxic_nfc_remove(struct platform_device *pdev) +{ + struct mxic_nand_ctlr *nfc = platform_get_drvdata(pdev); + + nand_release(&nfc->chip); + mxic_nfc_clk_disable(nfc); + return 0; +} + +static const struct of_device_id mxic_nfc_of_ids[] = { + { .compatible = "mxic,multi-itfc-v009-nand-controller", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxic_nfc_of_ids); + +static struct platform_driver mxic_nfc_driver = { + .probe = mxic_nfc_probe, + .remove = mxic_nfc_remove, + .driver = { + .name = "mxic-nfc", + .of_match_table = mxic_nfc_of_ids, + }, +}; +module_platform_driver(mxic_nfc_driver); + +MODULE_AUTHOR("Mason Yang <masonccyang@mxic.com.tw>"); +MODULE_DESCRIPTION("Macronix raw NAND controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6eb131292eb2..f64e3b6605c6 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -292,12 +292,16 @@ int nand_bbm_get_next_page(struct nand_chip *chip, int page) struct mtd_info *mtd = nand_to_mtd(chip); int last_page = ((mtd->erasesize - mtd->writesize) >> chip->page_shift) & chip->pagemask; + unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE + | NAND_BBM_LASTPAGE; + if (page == 0 && !(chip->options & bbm_flags)) + return 0; if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE) return 0; - else if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE) + if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE) return 1; - else if (page <= last_page && chip->options & NAND_BBM_LASTPAGE) + if (page <= last_page && chip->options & NAND_BBM_LASTPAGE) return last_page; return -EINVAL; @@ -2111,35 +2115,7 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) if (instr == &ctx->subop.instrs[0]) prefix = " ->"; - switch (instr->type) { - case NAND_OP_CMD_INSTR: - pr_debug("%sCMD [0x%02x]\n", prefix, - instr->ctx.cmd.opcode); - break; - case NAND_OP_ADDR_INSTR: - pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, - instr->ctx.addr.naddrs, - instr->ctx.addr.naddrs < 64 ? - instr->ctx.addr.naddrs : 64, - instr->ctx.addr.addrs); - break; - case NAND_OP_DATA_IN_INSTR: - pr_debug("%sDATA_IN [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_DATA_OUT_INSTR: - pr_debug("%sDATA_OUT [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_WAITRDY_INSTR: - pr_debug("%sWAITRDY [max %d ms]\n", prefix, - instr->ctx.waitrdy.timeout_ms); - break; - } + nand_op_trace(prefix, instr); if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) prefix = " "; @@ -2152,6 +2128,22 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) } #endif +static int nand_op_parser_cmp_ctx(const struct nand_op_parser_ctx *a, + const struct nand_op_parser_ctx *b) +{ + if (a->subop.ninstrs < b->subop.ninstrs) + return -1; + else if (a->subop.ninstrs > b->subop.ninstrs) + return 1; + + if (a->subop.last_instr_end_off < b->subop.last_instr_end_off) + return -1; + else if (a->subop.last_instr_end_off > b->subop.last_instr_end_off) + return 1; + + return 0; +} + /** * nand_op_parser_exec_op - exec_op parser * @chip: the NAND chip @@ -2186,32 +2178,40 @@ int nand_op_parser_exec_op(struct nand_chip *chip, unsigned int i; while (ctx.subop.instrs < op->instrs + op->ninstrs) { - int ret; + const struct nand_op_parser_pattern *pattern; + struct nand_op_parser_ctx best_ctx; + int ret, best_pattern = -1; for (i = 0; i < parser->npatterns; i++) { - const struct nand_op_parser_pattern *pattern; + struct nand_op_parser_ctx test_ctx = ctx; pattern = &parser->patterns[i]; - if (!nand_op_parser_match_pat(pattern, &ctx)) + if (!nand_op_parser_match_pat(pattern, &test_ctx)) continue; - nand_op_parser_trace(&ctx); - - if (check_only) - break; - - ret = pattern->exec(chip, &ctx.subop); - if (ret) - return ret; + if (best_pattern >= 0 && + nand_op_parser_cmp_ctx(&test_ctx, &best_ctx) <= 0) + continue; - break; + best_pattern = i; + best_ctx = test_ctx; } - if (i == parser->npatterns) { + if (best_pattern < 0) { pr_debug("->exec_op() parser: pattern not found!\n"); return -ENOTSUPP; } + ctx = best_ctx; + nand_op_parser_trace(&ctx); + + if (!check_only) { + pattern = &parser->patterns[best_pattern]; + ret = pattern->exec(chip, &ctx.subop); + if (ret) + return ret; + } + /* * Update the context structure by pointing to the start of the * next subop. @@ -4116,7 +4116,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); - int ret = -ENOTSUPP; + int ret; ops->retlen = 0; diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index 2ef15ef94525..96045d60471e 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -1232,7 +1232,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) if (!td) { if ((res = nand_memory_bbt(this, bd))) { pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); - goto err; + goto err_free_bbt; } return 0; } @@ -1245,7 +1245,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) buf = vmalloc(len); if (!buf) { res = -ENOMEM; - goto err; + goto err_free_bbt; } /* Is the bbt at a given page? */ @@ -1258,7 +1258,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) res = check_create(this, buf, bd); if (res) - goto err; + goto err_free_buf; /* Prevent the bbt regions from erasing / writing */ mark_bbt_region(this, td); @@ -1268,7 +1268,9 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) vfree(buf); return 0; -err: +err_free_buf: + vfree(buf); +err_free_bbt: kfree(this->bbt); this->bbt = NULL; return res; diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c index 55aa4c1cd414..17527310c3a1 100644 --- a/drivers/mtd/nand/raw/nand_bch.c +++ b/drivers/mtd/nand/raw/nand_bch.c @@ -170,7 +170,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) goto fail; } - nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); if (!nbc->eccmask || !nbc->errloc) goto fail; @@ -182,7 +182,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) goto fail; memset(erased_page, 0xff, eccsize); - memset(nbc->eccmask, 0, eccbytes); encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); kfree(erased_page); diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c index 223fbd8052b3..09fdced659f5 100644 --- a/drivers/mtd/nand/raw/nand_ecc.c +++ b/drivers/mtd/nand/raw/nand_ecc.c @@ -11,7 +11,7 @@ * Thomas Gleixner (tglx@linutronix.de) * * Information on how this algorithm works and how it was developed - * can be found in Documentation/mtd/nand_ecc.txt + * can be found in Documentation/driver-api/mtd/nand_ecc.rst */ #include <linux/types.h> diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index fad57c378dd2..58511aeb0c9a 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -8,6 +8,50 @@ #include "internals.h" +#define MACRONIX_READ_RETRY_BIT BIT(0) +#define MACRONIX_NUM_READ_RETRY_MODES 6 + +struct nand_onfi_vendor_macronix { + u8 reserved; + u8 reliability_func; +} __packed; + +static int macronix_nand_setup_read_retry(struct nand_chip *chip, int mode) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + + if (!chip->parameters.supports_set_get_features || + !test_bit(ONFI_FEATURE_ADDR_READ_RETRY, + chip->parameters.set_feature_list)) + return -ENOTSUPP; + + feature[0] = mode; + return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); +} + +static void macronix_nand_onfi_init(struct nand_chip *chip) +{ + struct nand_parameters *p = &chip->parameters; + struct nand_onfi_vendor_macronix *mxic; + + if (!p->onfi) + return; + + mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor; + if ((mxic->reliability_func & MACRONIX_READ_RETRY_BIT) == 0) + return; + + chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES; + chip->setup_read_retry = macronix_nand_setup_read_retry; + + if (p->supports_set_get_features) { + bitmap_set(p->set_feature_list, + ONFI_FEATURE_ADDR_READ_RETRY, 1); + bitmap_set(p->get_feature_list, + ONFI_FEATURE_ADDR_READ_RETRY, 1); + } +} + /* * Macronix AC series does not support using SET/GET_FEATURES to change * the timings unlike what is declared in the parameter page. Unflag @@ -56,6 +100,7 @@ static int macronix_nand_init(struct nand_chip *chip) chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; macronix_nand_fix_broken_get_timings(chip); + macronix_nand_onfi_init(chip); return 0; } diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 1622d3145587..56654030ec7f 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -390,6 +390,14 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) (chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2) return MICRON_ON_DIE_UNSUPPORTED; + /* + * It seems that there are devices which do not support ECC officially. + * At least the MT29F2G08ABAGA / MT29F2G08ABBGA devices supports + * enabling the ECC feature but don't reflect that to the READ_ID table. + * So we have to guarantee that we disable the ECC feature directly + * after we did the READ_ID table command. Later we can evaluate the + * ECC_ENABLE support. + */ ret = micron_nand_on_die_ecc_setup(chip, true); if (ret) return MICRON_ON_DIE_UNSUPPORTED; @@ -398,13 +406,13 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - if (!(id[4] & MICRON_ID_ECC_ENABLED)) - return MICRON_ON_DIE_UNSUPPORTED; - ret = micron_nand_on_die_ecc_setup(chip, false); if (ret) return MICRON_ON_DIE_UNSUPPORTED; + if (!(id[4] & MICRON_ID_ECC_ENABLED)) + return MICRON_ON_DIE_UNSUPPORTED; + ret = nand_readid_op(chip, 0, id, sizeof(id)); if (ret) return MICRON_ON_DIE_UNSUPPORTED; @@ -438,8 +446,10 @@ static int micron_nand_init(struct nand_chip *chip) if (ret) goto err_free_manuf_data; + chip->options |= NAND_BBM_FIRSTPAGE; + if (mtd->writesize == 2048) - chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; + chip->options |= NAND_BBM_SECONDPAGE; ondie = micron_supports_on_die_ecc(chip); diff --git a/drivers/mtd/nand/raw/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c deleted file mode 100644 index 13bf7b2894d3..000000000000 --- a/drivers/mtd/nand/raw/nuc900_nand.c +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright © 2009 Nuvoton technology corporation. - * - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/clk.h> -#include <linux/err.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/rawnand.h> -#include <linux/mtd/partitions.h> - -#define REG_FMICSR 0x00 -#define REG_SMCSR 0xa0 -#define REG_SMISR 0xac -#define REG_SMCMD 0xb0 -#define REG_SMADDR 0xb4 -#define REG_SMDATA 0xb8 - -#define RESET_FMI 0x01 -#define NAND_EN 0x08 -#define READYBUSY (0x01 << 18) - -#define SWRST 0x01 -#define PSIZE (0x01 << 3) -#define DMARWEN (0x03 << 1) -#define BUSWID (0x01 << 4) -#define ECC4EN (0x01 << 5) -#define WP (0x01 << 24) -#define NANDCS (0x01 << 25) -#define ENDADDR (0x01 << 31) - -#define read_data_reg(dev) \ - __raw_readl((dev)->reg + REG_SMDATA) - -#define write_data_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMDATA) - -#define write_cmd_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMCMD) - -#define write_addr_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMADDR) - -struct nuc900_nand { - struct nand_chip chip; - void __iomem *reg; - struct clk *clk; - spinlock_t lock; -}; - -static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip); -} - -static const struct mtd_partition partitions[] = { - { - .name = "NAND FS 0", - .offset = 0, - .size = 8 * 1024 * 1024 - }, - { - .name = "NAND FS 1", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; - -static unsigned char nuc900_nand_read_byte(struct nand_chip *chip) -{ - unsigned char ret; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - ret = (unsigned char)read_data_reg(nand); - - return ret; -} - -static void nuc900_nand_read_buf(struct nand_chip *chip, - unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - for (i = 0; i < len; i++) - buf[i] = (unsigned char)read_data_reg(nand); -} - -static void nuc900_nand_write_buf(struct nand_chip *chip, - const unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - for (i = 0; i < len; i++) - write_data_reg(nand, buf[i]); -} - -static int nuc900_check_rb(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - val = __raw_readl(nand->reg + REG_SMISR); - val &= READYBUSY; - spin_unlock(&nand->lock); - - return val; -} - -static int nuc900_nand_devready(struct nand_chip *chip) -{ - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - int ready; - - ready = (nuc900_check_rb(nand)) ? 1 : 0; - return ready; -} - -static void nuc900_nand_command_lp(struct nand_chip *chip, - unsigned int command, - int column, int page_addr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - write_cmd_reg(nand, command & 0xff); - - if (column != -1 || page_addr != -1) { - - if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - write_addr_reg(nand, column); - write_addr_reg(nand, column >> 8 | ENDADDR); - } - if (page_addr != -1) { - write_addr_reg(nand, page_addr); - - if (chip->options & NAND_ROW_ADDR_3) { - write_addr_reg(nand, page_addr >> 8); - write_addr_reg(nand, page_addr >> 16 | ENDADDR); - } else { - write_addr_reg(nand, page_addr >> 8 | ENDADDR); - } - } - } - - switch (command) { - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (chip->legacy.dev_ready) - break; - udelay(chip->legacy.chip_delay); - - write_cmd_reg(nand, NAND_CMD_STATUS); - write_cmd_reg(nand, command); - - while (!nuc900_check_rb(nand)) - ; - - return; - - case NAND_CMD_RNDOUT: - write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); - return; - - case NAND_CMD_READ0: - write_cmd_reg(nand, NAND_CMD_READSTART); - /* fall through */ - - default: - - if (!chip->legacy.dev_ready) { - udelay(chip->legacy.chip_delay); - return; - } - } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - while (!chip->legacy.dev_ready(chip)) - ; -} - - -static void nuc900_nand_enable(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); - - val = __raw_readl(nand->reg + REG_FMICSR); - - if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); - - val = __raw_readl(nand->reg + REG_SMCSR); - - val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); - val |= WP; - - __raw_writel(val, nand->reg + REG_SMCSR); - - spin_unlock(&nand->lock); -} - -static int nuc900_nand_probe(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - - nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), - GFP_KERNEL); - if (!nuc900_nand) - return -ENOMEM; - chip = &(nuc900_nand->chip); - mtd = nand_to_mtd(chip); - - mtd->dev.parent = &pdev->dev; - spin_lock_init(&nuc900_nand->lock); - - nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nuc900_nand->clk)) - return -ENOENT; - clk_enable(nuc900_nand->clk); - - chip->legacy.cmdfunc = nuc900_nand_command_lp; - chip->legacy.dev_ready = nuc900_nand_devready; - chip->legacy.read_byte = nuc900_nand_read_byte; - chip->legacy.write_buf = nuc900_nand_write_buf; - chip->legacy.read_buf = nuc900_nand_read_buf; - chip->legacy.chip_delay = 50; - chip->options = 0; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nuc900_nand->reg)) - return PTR_ERR(nuc900_nand->reg); - - nuc900_nand_enable(nuc900_nand); - - if (nand_scan(chip, 1)) - return -ENXIO; - - mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions)); - - platform_set_drvdata(pdev, nuc900_nand); - - return 0; -} - -static int nuc900_nand_remove(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); - - nand_release(&nuc900_nand->chip); - clk_disable(nuc900_nand->clk); - - return 0; -} - -static struct platform_driver nuc900_nand_driver = { - .probe = nuc900_nand_probe, - .remove = nuc900_nand_remove, - .driver = { - .name = "nuc900-fmi", - }, -}; - -module_platform_driver(nuc900_nand_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 8d881a28140e..ad77c112a78a 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -1501,7 +1501,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data, } /* Update number of correctable errors */ - stat += err_vec[i].error_count; + stat = max_t(unsigned int, stat, err_vec[i].error_count); /* Update page data with sector size */ data += ecc->size; @@ -1967,10 +1967,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case NAND_OMAP_PREFETCH_IRQ: info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); - if (info->gpmc_irq_fifo <= 0) { - dev_err(dev, "Error getting fifo IRQ\n"); + if (info->gpmc_irq_fifo <= 0) return -ENODEV; - } err = devm_request_irq(dev, info->gpmc_irq_fifo, omap_nand_irq, IRQF_SHARED, "gpmc-nand-fifo", info); @@ -1982,10 +1980,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip) } info->gpmc_irq_count = platform_get_irq(info->pdev, 1); - if (info->gpmc_irq_count <= 0) { - dev_err(dev, "Error getting IRQ count\n"); + if (info->gpmc_irq_count <= 0) return -ENODEV; - } err = devm_request_irq(dev, info->gpmc_irq_count, omap_nand_irq, IRQF_SHARED, "gpmc-nand-count", info); diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c index 30c51f772de6..c43cb4d92d3d 100644 --- a/drivers/mtd/nand/raw/oxnas_nand.c +++ b/drivers/mtd/nand/raw/oxnas_nand.c @@ -116,7 +116,7 @@ static int oxnas_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (!chip) { err = -ENOMEM; - goto err_clk_unprepare; + goto err_release_child; } chip->controller = &oxnas->base; @@ -137,12 +137,12 @@ static int oxnas_nand_probe(struct platform_device *pdev) /* Scan to find existence of the device */ err = nand_scan(chip, 1); if (err) - goto err_clk_unprepare; + goto err_release_child; err = mtd_device_register(mtd, NULL, 0); if (err) { nand_release(chip); - goto err_clk_unprepare; + goto err_release_child; } oxnas->chips[nchips] = chip; @@ -159,6 +159,8 @@ static int oxnas_nand_probe(struct platform_device *pdev) return 0; +err_release_child: + of_node_put(nand_np); err_clk_unprepare: clk_disable_unprepare(oxnas->clk); return err; diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index dae0d235bb17..77774250fb11 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -998,7 +998,7 @@ static void r852_shutdown(struct pci_dev *pci_dev) #ifdef CONFIG_PM_SLEEP static int r852_suspend(struct device *device) { - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + struct r852_device *dev = dev_get_drvdata(device); if (dev->ctlreg & R852_CTL_CARDENABLE) return -EBUSY; @@ -1019,7 +1019,7 @@ static int r852_suspend(struct device *device) static int r852_resume(struct device *device) { - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + struct r852_device *dev = dev_get_drvdata(device); r852_disable_irqs(dev); r852_card_update_present(dev); diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index e509c93737c4..058e99d0cbcf 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1129,10 +1129,8 @@ static int flctl_probe(struct platform_device *pdev) flctl->fifo = res->start + 0x24; /* FLDTFIFO */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get flste irq data: %d\n", irq); + if (irq < 0) return irq; - } ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, "flste", flctl); diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 999ca6a66036..9e63800f768a 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -37,6 +37,8 @@ /* Max ECC buffer length */ #define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG) +#define FMC2_TIMEOUT_MS 1000 + /* Timings */ #define FMC2_THIZ 1 #define FMC2_TIO 8000 @@ -530,7 +532,8 @@ static int stm32_fmc2_ham_calculate(struct nand_chip *chip, const u8 *data, int ret; ret = readl_relaxed_poll_timeout(fmc2->io_base + FMC2_SR, - sr, sr & FMC2_SR_NWRF, 10, 1000); + sr, sr & FMC2_SR_NWRF, 10, + FMC2_TIMEOUT_MS); if (ret) { dev_err(fmc2->dev, "ham timeout\n"); return ret; @@ -611,7 +614,7 @@ static int stm32_fmc2_bch_calculate(struct nand_chip *chip, const u8 *data, /* Wait until the BCH code is ready */ if (!wait_for_completion_timeout(&fmc2->complete, - msecs_to_jiffies(1000))) { + msecs_to_jiffies(FMC2_TIMEOUT_MS))) { dev_err(fmc2->dev, "bch timeout\n"); stm32_fmc2_disable_bch_irq(fmc2); return -ETIMEDOUT; @@ -696,7 +699,7 @@ static int stm32_fmc2_bch_correct(struct nand_chip *chip, u8 *dat, /* Wait until the decoding error is ready */ if (!wait_for_completion_timeout(&fmc2->complete, - msecs_to_jiffies(1000))) { + msecs_to_jiffies(FMC2_TIMEOUT_MS))) { dev_err(fmc2->dev, "bch timeout\n"); stm32_fmc2_disable_bch_irq(fmc2); return -ETIMEDOUT; @@ -969,7 +972,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, /* Wait end of sequencer transfer */ if (!wait_for_completion_timeout(&fmc2->complete, - msecs_to_jiffies(1000))) { + msecs_to_jiffies(FMC2_TIMEOUT_MS))) { dev_err(fmc2->dev, "seq timeout\n"); stm32_fmc2_disable_seq_irq(fmc2); dmaengine_terminate_all(dma_ch); @@ -981,7 +984,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, /* Wait DMA data transfer completion */ if (!wait_for_completion_timeout(&fmc2->dma_data_complete, - msecs_to_jiffies(100))) { + msecs_to_jiffies(FMC2_TIMEOUT_MS))) { dev_err(fmc2->dev, "data DMA timeout\n"); dmaengine_terminate_all(dma_ch); ret = -ETIMEDOUT; @@ -990,7 +993,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, /* Wait DMA ECC transfer completion */ if (!write_data && !raw) { if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete, - msecs_to_jiffies(100))) { + msecs_to_jiffies(FMC2_TIMEOUT_MS))) { dev_err(fmc2->dev, "ECC DMA timeout\n"); dmaengine_terminate_all(fmc2->dma_ecc_ch); ret = -ETIMEDOUT; @@ -1424,21 +1427,16 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, struct stm32_fmc2_timings *tims = &nand->timings; unsigned long hclk = clk_get_rate(fmc2->clk); unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); - int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att; - - tar = hclkp; - if (tar < sdrt->tAR_min) - tar = sdrt->tAR_min; - tims->tar = DIV_ROUND_UP(tar, hclkp) - 1; - if (tims->tar > FMC2_PCR_TIMING_MASK) - tims->tar = FMC2_PCR_TIMING_MASK; - - tclr = hclkp; - if (tclr < sdrt->tCLR_min) - tclr = sdrt->tCLR_min; - tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1; - if (tims->tclr > FMC2_PCR_TIMING_MASK) - tims->tclr = FMC2_PCR_TIMING_MASK; + unsigned long timing, tar, tclr, thiz, twait; + unsigned long tset_mem, tset_att, thold_mem, thold_att; + + tar = max_t(unsigned long, hclkp, sdrt->tAR_min); + timing = DIV_ROUND_UP(tar, hclkp) - 1; + tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); + + tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min); + timing = DIV_ROUND_UP(tclr, hclkp) - 1; + tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); tims->thiz = FMC2_THIZ; thiz = (tims->thiz + 1) * hclkp; @@ -1448,18 +1446,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, * tWAIT > tWP * tWAIT > tREA + tIO */ - twait = hclkp; - if (twait < sdrt->tRP_min) - twait = sdrt->tRP_min; - if (twait < sdrt->tWP_min) - twait = sdrt->tWP_min; - if (twait < sdrt->tREA_max + FMC2_TIO) - twait = sdrt->tREA_max + FMC2_TIO; - tims->twait = DIV_ROUND_UP(twait, hclkp); - if (tims->twait == 0) - tims->twait = 1; - else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK) - tims->twait = FMC2_PMEM_PATT_TIMING_MASK; + twait = max_t(unsigned long, hclkp, sdrt->tRP_min); + twait = max_t(unsigned long, twait, sdrt->tWP_min); + twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO); + timing = DIV_ROUND_UP(twait, hclkp); + tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tSETUP_MEM > tCS - tWAIT @@ -1474,20 +1465,15 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if (twait > thiz && (sdrt->tDS_min > twait - thiz) && (tset_mem < sdrt->tDS_min - (twait - thiz))) tset_mem = sdrt->tDS_min - (twait - thiz); - tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp); - if (tims->tset_mem == 0) - tims->tset_mem = 1; - else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK) - tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(tset_mem, hclkp); + tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tHOLD_MEM > tCH * tHOLD_MEM > tREH - tSETUP_MEM * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) */ - thold_mem = hclkp; - if (thold_mem < sdrt->tCH_min) - thold_mem = sdrt->tCH_min; + thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min); if (sdrt->tREH_min > tset_mem && (thold_mem < sdrt->tREH_min - tset_mem)) thold_mem = sdrt->tREH_min - tset_mem; @@ -1497,11 +1483,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if ((sdrt->tWC_min > tset_mem + twait) && (thold_mem < sdrt->tWC_min - (tset_mem + twait))) thold_mem = sdrt->tWC_min - (tset_mem + twait); - tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp); - if (tims->thold_mem == 0) - tims->thold_mem = 1; - else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK) - tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(thold_mem, hclkp); + tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tSETUP_ATT > tCS - tWAIT @@ -1523,11 +1506,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if (twait > thiz && (sdrt->tDS_min > twait - thiz) && (tset_att < sdrt->tDS_min - (twait - thiz))) tset_att = sdrt->tDS_min - (twait - thiz); - tims->tset_att = DIV_ROUND_UP(tset_att, hclkp); - if (tims->tset_att == 0) - tims->tset_att = 1; - else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK) - tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(tset_att, hclkp); + tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tHOLD_ATT > tALH @@ -1542,17 +1522,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) */ - thold_att = hclkp; - if (thold_att < sdrt->tALH_min) - thold_att = sdrt->tALH_min; - if (thold_att < sdrt->tCH_min) - thold_att = sdrt->tCH_min; - if (thold_att < sdrt->tCLH_min) - thold_att = sdrt->tCLH_min; - if (thold_att < sdrt->tCOH_min) - thold_att = sdrt->tCOH_min; - if (thold_att < sdrt->tDH_min) - thold_att = sdrt->tDH_min; + thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min); if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) && (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem)) thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem; @@ -1571,11 +1545,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if ((sdrt->tWC_min > tset_att + twait) && (thold_att < sdrt->tWC_min - (tset_att + twait))) thold_att = sdrt->tWC_min - (tset_att + twait); - tims->thold_att = DIV_ROUND_UP(thold_att, hclkp); - if (tims->thold_att == 0) - tims->thold_att = 1; - else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK) - tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(thold_att, hclkp); + tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); } static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr, @@ -1909,6 +1880,9 @@ static int stm32_fmc2_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0, dev_name(dev), fmc2); if (ret) { diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 89773293c64d..37a4ac0dd85b 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2071,10 +2071,8 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return PTR_ERR(nfc->regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq\n"); + if (irq < 0) return irq; - } nfc->ahb_clk = devm_clk_get(dev, "ahb"); if (IS_ERR(nfc->ahb_clk)) { diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index b3f2cabcc7c0..9acf2de37ee0 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -659,6 +659,7 @@ static int tango_nand_probe(struct platform_device *pdev) err = chip_init(&pdev->dev, np); if (err) { tango_nand_remove(pdev); + of_node_put(np); return err; } } diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index e4fe8c4bc711..6b399a75f9ae 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -862,6 +862,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) dev_err(nfc->dev, "Only one NAND chip supported!\n"); err = -EINVAL; + of_node_put(child); goto err_disable_clk; } diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 753125082640..9662b9c1d5a9 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4c15bb58c623..89f6beefb01c 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -511,12 +511,12 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, if (ret == -EBADMSG) { ecc_failed = true; mtd->ecc_stats.failed++; - ret = 0; } else { mtd->ecc_stats.corrected += ret; max_bitflips = max_t(unsigned int, max_bitflips, ret); } + ret = 0; ops->retlen += iter.req.datalen; ops->oobretlen += iter.req.ooblen; } @@ -757,6 +757,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, }; @@ -845,7 +846,7 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u8 devid) + unsigned int table_size, u16 devid) { struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index e6c646007cda..e99d425aa93f 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -9,11 +9,17 @@ #include <linux/mtd/spinand.h> #define SPINAND_MFR_GIGADEVICE 0xC8 + #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) #define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 +#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) +#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -22,6 +28,14 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); +static SPINAND_OP_VARIANTS(read_cache_variants_f, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -59,6 +73,11 @@ static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, return 0; } +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { + .ecc = gd5fxgq4xa_ooblayout_ecc, + .free = gd5fxgq4xa_ooblayout_free, +}; + static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -83,7 +102,7 @@ static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } -static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, +static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -95,7 +114,7 @@ static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, +static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -108,6 +127,11 @@ static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section, return 0; } +static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { + .ecc = gd5fxgq4_variant2_ooblayout_ecc, + .free = gd5fxgq4_variant2_ooblayout_free, +}; + static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -150,15 +174,25 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } -static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { - .ecc = gd5fxgq4xa_ooblayout_ecc, - .free = gd5fxgq4xa_ooblayout_free, -}; +static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { + case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: + return 0; -static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = { - .ecc = gd5fxgq4uexxg_ooblayout_ecc, - .free = gd5fxgq4uexxg_ooblayout_free, -}; + case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ + return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; + } + + return -EINVAL; +} static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO("GD5F1GQ4xA", 0xF1, @@ -195,25 +229,40 @@ static const struct spinand_info gigadevice_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout, + SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, + gd5fxgq4ufxxg_ecc_get_status)), }; static int gigadevice_spinand_detect(struct spinand_device *spinand) { u8 *id = spinand->id.data; + u16 did; int ret; /* - * For GD NANDs, There is an address byte needed to shift in before IDs - * are read out, so the first byte in raw_id is dummy. + * Earlier GDF5-series devices (A,E) return [0][MID][DID] + * Later (F) devices return [MID][DID1][DID2] */ - if (id[1] != SPINAND_MFR_GIGADEVICE) + + if (id[0] == SPINAND_MFR_GIGADEVICE) + did = (id[1] << 8) + id[2]; + else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) + did = id[2]; + else return 0; ret = spinand_match_and_init(spinand, gigadevice_spinand_table, ARRAY_SIZE(gigadevice_spinand_table), - id[2]); + did); if (ret) return ret; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c new file mode 100644 index 000000000000..52307681cbd0 --- /dev/null +++ b/drivers/mtd/nand/spi/paragon.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Jeff Kletsky + * + * Author: Jeff Kletsky <git-commits@allycomm.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + + +#define SPINAND_MFR_PARAGON 0xa1 + + +#define PN26G0XA_STATUS_ECC_BITMASK (3 << 4) + +#define PN26G0XA_STATUS_ECC_NONE_DETECTED (0 << 4) +#define PN26G0XA_STATUS_ECC_1_7_CORRECTED (1 << 4) +#define PN26G0XA_STATUS_ECC_ERRORED (2 << 4) +#define PN26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) + + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + + +static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 6 + (15 * section); /* 4 BBM + 2 user bytes */ + region->length = 13; + + return 0; +} + +static int pn26g0xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 4) + return -ERANGE; + + if (section == 4) { + region->offset = 64; + region->length = 64; + } else { + region->offset = 4 + (15 * section); + region->length = 2; + } + + return 0; +} + +static int pn26g0xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & PN26G0XA_STATUS_ECC_BITMASK) { + case PN26G0XA_STATUS_ECC_NONE_DETECTED: + return 0; + + case PN26G0XA_STATUS_ECC_1_7_CORRECTED: + return 7; /* Return upper limit by convention */ + + case PN26G0XA_STATUS_ECC_8_CORRECTED: + return 8; + + case PN26G0XA_STATUS_ECC_ERRORED: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { + .ecc = pn26g0xa_ooblayout_ecc, + .free = pn26g0xa_ooblayout_free, +}; + + +static const struct spinand_info paragon_spinand_table[] = { + SPINAND_INFO("PN26G01A", 0xe1, + NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), + SPINAND_INFO("PN26G02A", 0xe2, + NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), +}; + +static int paragon_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* Read ID returns [0][MID][DID] */ + + if (id[1] != SPINAND_MFR_PARAGON) + return 0; + + ret = spinand_match_and_init(spinand, paragon_spinand_table, + ARRAY_SIZE(paragon_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { + .detect = paragon_spinand_detect, +}; + +const struct spinand_manufacturer paragon_spinand_manufacturer = { + .id = SPINAND_MFR_PARAGON, + .name = "Paragon", + .ops = ¶gon_spinand_manuf_ops, +}; diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index 176b75a375b1..f98363c9b363 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -1,4 +1,72 @@ # SPDX-License-Identifier: GPL-2.0-only +config MTD_AR7_PARTS + tristate "TI AR7 partitioning parser" + help + TI AR7 partitioning parser support + +config MTD_BCM47XX_PARTS + tristate "BCM47XX partitioning parser" + depends on BCM47XX || ARCH_BCM_5301X + help + This provides partitions parser for devices based on BCM47xx + boards. + +config MTD_BCM63XX_PARTS + tristate "BCM63XX CFE partitioning parser" + depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST + select CRC32 + select MTD_PARSER_IMAGETAG + help + This provides partition parsing for BCM63xx devices with CFE + bootloaders. + +config MTD_CMDLINE_PARTS + tristate "Command line partition table parsing" + depends on MTD + help + Allow generic configuration of the MTD partition tables via the kernel + command line. Multiple flash resources are supported for hardware where + different kinds of flash memory are available. + + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + example. + + The format for the command line is as follows: + + mtdparts=<mtddef>[;<mtddef] + <mtddef> := <mtd-id>:<partdef>[,<partdef>] + <partdef> := <size>[@offset][<name>][ro] + <mtd-id> := unique id used in mapping driver/device + <size> := standard linux memsize OR "-" to denote all + remaining space + <name> := (NAME) + + Due to the way Linux handles the command line, no spaces are + allowed in the partition definition, including mtd id's and partition + names. + + Examples: + + 1 flash resource (mtd-id "sa1100"), with 1 single writable partition: + mtdparts=sa1100:- + + Same flash, but 2 named partitions, the first one being read-only: + mtdparts=sa1100:256k(ARMboot)ro,-(root) + + If unsure, say 'N'. + +config MTD_OF_PARTS + tristate "OpenFirmware (device tree) partitioning parser" + default y + depends on OF + help + This provides a open firmware device tree partition parser + which derives the partition map from the children of the + flash memory node, as described in + Documentation/devicetree/bindings/mtd/partition.txt. + config MTD_PARSER_IMAGETAG tristate "Parser for BCM963XX Image Tag format partitions" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index dd566bdd16e2..b0c5f62f9e85 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -1,4 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o +obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o +obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o +obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o +obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o diff --git a/drivers/mtd/parsers/afs.c b/drivers/mtd/parsers/afs.c index f24d768eee30..752b6cf005f7 100644 --- a/drivers/mtd/parsers/afs.c +++ b/drivers/mtd/parsers/afs.c @@ -371,8 +371,7 @@ static int parse_afs_partitions(struct mtd_info *mtd, out_free_parts: while (i >= 0) { - if (parts[i].name) - kfree(parts[i].name); + kfree(parts[i].name); i--; } kfree(parts); diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/parsers/ar7part.c index 8cd683711ac6..8cd683711ac6 100644 --- a/drivers/mtd/ar7part.c +++ b/drivers/mtd/parsers/ar7part.c diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/parsers/bcm47xxpart.c index 6012a10f10c8..6012a10f10c8 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/parsers/bcm47xxpart.c diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/parsers/bcm63xxpart.c index 78f90c6c18fd..78f90c6c18fd 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/parsers/bcm63xxpart.c diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/parsers/cmdlinepart.c index c86f2db8c882..c86f2db8c882 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/parsers/cmdlinepart.c diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/parsers/ofpart.c index 3caeabf27987..3caeabf27987 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/parsers/ofpart.c diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index dfc47a444b90..4744bf94ad9a 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -774,8 +774,11 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num) continue; /* Read the oob of first sector */ - if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) + if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) { + kfifo_free(&zone->free_sectors); + kfree(zone->lba_to_phys_table); return -EIO; + } /* Test to see if block is erased. It is enough to test first sector, because erase happens in one shot */ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 8e14248d2720..f237fcdf7f86 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -2,6 +2,8 @@ menuconfig MTD_SPI_NOR tristate "SPI-NOR device support" depends on MTD + depends on MTD && SPI_MASTER + select SPI_MEM help This is the framework for the SPI NOR which can be used by the SPI device drivers and the SPI-NOR device driver. @@ -105,11 +107,4 @@ config SPI_INTEL_SPI_PLATFORM To compile this driver as a module, choose M here: the module will be called intel-spi-platform. -config SPI_STM32_QUADSPI - tristate "STM32 Quad SPI controller" - depends on ARCH_STM32 || COMPILE_TEST - help - This enables support for the STM32 Quad SPI controller. - We only connect the NOR to this controller. - endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 189a15cca3ec..9c5ed03cdc19 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -8,4 +8,3 @@ obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o -obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c index 19b8757325d2..2b7cabbb680c 100644 --- a/drivers/mtd/spi-nor/aspeed-smc.c +++ b/drivers/mtd/spi-nor/aspeed-smc.c @@ -320,7 +320,8 @@ static void aspeed_smc_unprep(struct spi_nor *nor, enum spi_nor_ops ops) mutex_unlock(&chip->controller->mutex); } -static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + size_t len) { struct aspeed_smc_chip *chip = nor->priv; @@ -331,8 +332,8 @@ static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return 0; } -static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, - int len) +static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, + size_t len) { struct aspeed_smc_chip *chip = nor->priv; @@ -746,6 +747,15 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) return 0; } +static const struct spi_nor_controller_ops aspeed_smc_controller_ops = { + .prepare = aspeed_smc_prep, + .unprepare = aspeed_smc_unprep, + .read_reg = aspeed_smc_read_reg, + .write_reg = aspeed_smc_write_reg, + .read = aspeed_smc_read_user, + .write = aspeed_smc_write_user, +}; + static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, struct device_node *np, struct resource *r) { @@ -805,12 +815,7 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, nor->dev = dev; nor->priv = chip; spi_nor_set_flash_node(nor, child); - nor->read = aspeed_smc_read_user; - nor->write = aspeed_smc_write_user; - nor->read_reg = aspeed_smc_read_reg; - nor->write_reg = aspeed_smc_write_reg; - nor->prepare = aspeed_smc_prep; - nor->unprepare = aspeed_smc_unprep; + nor->controller_ops = &aspeed_smc_controller_ops; ret = aspeed_smc_chip_setup_init(chip, r); if (ret) @@ -836,8 +841,10 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, controller->chips[cs] = chip; } - if (ret) + if (ret) { + of_node_put(child); aspeed_smc_unregister(controller); + } return ret; } diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 67ade2c81b21..06f997247d0f 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -13,6 +13,7 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> @@ -23,6 +24,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/sched.h> #include <linux/spi/spi.h> #include <linux/timer.h> @@ -240,23 +242,13 @@ struct cqspi_driver_platdata { #define CQSPI_IRQ_STATUS_MASK 0x1FFFF -static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear) +static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr) { - unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS); u32 val; - while (1) { - val = readl(reg); - if (clear) - val = ~val; - val &= mask; - - if (val == mask) - return 0; - - if (time_after(jiffies, end)) - return -ETIMEDOUT; - } + return readl_relaxed_poll_timeout(reg, val, + (((clr ? ~val : val) & mask) == mask), + 10, CQSPI_TIMEOUT_MS * 1000); } static bool cqspi_is_idle(struct cqspi_st *cqspi) @@ -293,7 +285,7 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) return IRQ_HANDLED; } -static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, const u8 opcode) +static unsigned int cqspi_calc_rdreg(struct spi_nor *nor) { struct cqspi_flash_pdata *f_pdata = nor->priv; u32 rdreg = 0; @@ -362,27 +354,27 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg) return cqspi_wait_idle(cqspi); } -static int cqspi_command_read(struct spi_nor *nor, - const u8 *txbuf, const unsigned n_tx, - u8 *rxbuf, const unsigned n_rx) +static int cqspi_command_read(struct spi_nor *nor, u8 opcode, + u8 *rxbuf, size_t n_rx) { struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; unsigned int rdreg; unsigned int reg; - unsigned int read_len; + size_t read_len; int status; if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { - dev_err(nor->dev, "Invalid input argument, len %d rxbuf 0x%p\n", + dev_err(nor->dev, + "Invalid input argument, len %zu rxbuf 0x%p\n", n_rx, rxbuf); return -EINVAL; } - reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; - rdreg = cqspi_calc_rdreg(nor, txbuf[0]); + rdreg = cqspi_calc_rdreg(nor); writel(rdreg, reg_base + CQSPI_REG_RD_INSTR); reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); @@ -412,19 +404,19 @@ static int cqspi_command_read(struct spi_nor *nor, } static int cqspi_command_write(struct spi_nor *nor, const u8 opcode, - const u8 *txbuf, const unsigned n_tx) + const u8 *txbuf, size_t n_tx) { struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; unsigned int reg; unsigned int data; - u32 write_len; + size_t write_len; int ret; if (n_tx > CQSPI_STIG_DATA_LEN_MAX || (n_tx && !txbuf)) { dev_err(nor->dev, - "Invalid input argument, cmdlen %d txbuf 0x%p\n", + "Invalid input argument, cmdlen %zu txbuf 0x%p\n", n_tx, txbuf); return -EINVAL; } @@ -478,7 +470,7 @@ static int cqspi_read_setup(struct spi_nor *nor) unsigned int reg; reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; - reg |= cqspi_calc_rdreg(nor, nor->read_opcode); + reg |= cqspi_calc_rdreg(nor); /* Setup dummy clock cycles */ dummy_clk = nor->read_dummy; @@ -611,7 +603,7 @@ static int cqspi_write_setup(struct spi_nor *nor) /* Set opcode. */ reg = nor->program_opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; writel(reg, reg_base + CQSPI_REG_WR_INSTR); - reg = cqspi_calc_rdreg(nor, nor->program_opcode); + reg = cqspi_calc_rdreg(nor); writel(reg, reg_base + CQSPI_REG_RD_INSTR); reg = readl(reg_base + CQSPI_REG_SIZE); @@ -1058,7 +1050,7 @@ static int cqspi_erase(struct spi_nor *nor, loff_t offs) return ret; /* Send write enable, then erase commands. */ - ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, NULL, 0); if (ret) return ret; @@ -1088,18 +1080,19 @@ static void cqspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) mutex_unlock(&cqspi->bus_mutex); } -static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len) { int ret; ret = cqspi_set_protocol(nor, 0); if (!ret) - ret = cqspi_command_read(nor, &opcode, 1, buf, len); + ret = cqspi_command_read(nor, opcode, buf, len); return ret; } -static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, + size_t len) { int ret; @@ -1224,6 +1217,16 @@ static void cqspi_request_mmap_dma(struct cqspi_st *cqspi) init_completion(&cqspi->rx_dma_complete); } +static const struct spi_nor_controller_ops cqspi_controller_ops = { + .prepare = cqspi_prep, + .unprepare = cqspi_unprep, + .read_reg = cqspi_read_reg, + .write_reg = cqspi_write_reg, + .read = cqspi_read, + .write = cqspi_write, + .erase = cqspi_erase, +}; + static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) { struct platform_device *pdev = cqspi->pdev; @@ -1273,14 +1276,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) nor->dev = dev; spi_nor_set_flash_node(nor, np); nor->priv = f_pdata; - - nor->read_reg = cqspi_read_reg; - nor->write_reg = cqspi_write_reg; - nor->read = cqspi_read; - nor->write = cqspi_write; - nor->erase = cqspi_erase; - nor->prepare = cqspi_prep; - nor->unprepare = cqspi_unprep; + nor->controller_ops = &cqspi_controller_ops; mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), cs); @@ -1325,6 +1321,7 @@ static int cqspi_probe(struct platform_device *pdev) struct cqspi_st *cqspi; struct resource *res; struct resource *res_ahb; + struct reset_control *rstc, *rstc_ocp; const struct cqspi_driver_platdata *ddata; int ret; int irq; @@ -1373,10 +1370,8 @@ static int cqspi_probe(struct platform_device *pdev) /* Obtain IRQ line. */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "Cannot obtain IRQ.\n"); + if (irq < 0) return -ENXIO; - } pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); @@ -1391,6 +1386,25 @@ static int cqspi_probe(struct platform_device *pdev) goto probe_clk_failed; } + /* Obtain QSPI reset control */ + rstc = devm_reset_control_get_optional_exclusive(dev, "qspi"); + if (IS_ERR(rstc)) { + dev_err(dev, "Cannot get QSPI reset.\n"); + return PTR_ERR(rstc); + } + + rstc_ocp = devm_reset_control_get_optional_exclusive(dev, "qspi-ocp"); + if (IS_ERR(rstc_ocp)) { + dev_err(dev, "Cannot get QSPI OCP reset.\n"); + return PTR_ERR(rstc_ocp); + } + + reset_control_assert(rstc); + reset_control_deassert(rstc); + + reset_control_assert(rstc_ocp); + reset_control_deassert(rstc_ocp); + cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); ddata = of_device_get_match_data(dev); if (ddata && (ddata->quirks & CQSPI_NEEDS_WR_DELAY)) diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c index dea43ea3eea3..a1258216f89d 100644 --- a/drivers/mtd/spi-nor/hisi-sfc.c +++ b/drivers/mtd/spi-nor/hisi-sfc.c @@ -177,7 +177,7 @@ static void hisi_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) } static int hisi_spi_nor_op_reg(struct spi_nor *nor, - u8 opcode, int len, u8 optype) + u8 opcode, size_t len, u8 optype) { struct hifmc_priv *priv = nor->priv; struct hifmc_host *host = priv->host; @@ -200,7 +200,7 @@ static int hisi_spi_nor_op_reg(struct spi_nor *nor, } static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, - int len) + size_t len) { struct hifmc_priv *priv = nor->priv; struct hifmc_host *host = priv->host; @@ -215,7 +215,7 @@ static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, } static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode, - u8 *buf, int len) + const u8 *buf, size_t len) { struct hifmc_priv *priv = nor->priv; struct hifmc_host *host = priv->host; @@ -311,6 +311,15 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to, return len; } +static const struct spi_nor_controller_ops hisi_controller_ops = { + .prepare = hisi_spi_nor_prep, + .unprepare = hisi_spi_nor_unprep, + .read_reg = hisi_spi_nor_read_reg, + .write_reg = hisi_spi_nor_write_reg, + .read = hisi_spi_nor_read, + .write = hisi_spi_nor_write, +}; + /** * Get spi flash device information and register it as a mtd device. */ @@ -357,14 +366,8 @@ static int hisi_spi_nor_register(struct device_node *np, } priv->host = host; nor->priv = priv; + nor->controller_ops = &hisi_controller_ops; - nor->prepare = hisi_spi_nor_prep; - nor->unprepare = hisi_spi_nor_unprep; - nor->read_reg = hisi_spi_nor_read_reg; - nor->write_reg = hisi_spi_nor_write_reg; - nor->read = hisi_spi_nor_read; - nor->write = hisi_spi_nor_write; - nor->erase = NULL; ret = spi_nor_scan(nor, NULL, &hwcaps); if (ret) return ret; @@ -401,6 +404,7 @@ static int hisi_spi_nor_register_all(struct hifmc_host *host) if (host->num_chip == HIFMC_MAX_CHIP_NUM) { dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n"); + of_node_put(np); break; } } diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c index 5e2344768d53..3d8987baea2a 100644 --- a/drivers/mtd/spi-nor/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/intel-spi-pci.c @@ -20,6 +20,10 @@ static const struct intel_spi_boardinfo bxt_info = { .type = INTEL_SPI_BXT, }; +static const struct intel_spi_boardinfo cnl_info = { + .type = INTEL_SPI_CNL, +}; + static int intel_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -61,11 +65,15 @@ static void intel_spi_pci_remove(struct pci_dev *pdev) static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info }, { }, }; MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 1ccf23fe7e4b..61d2a0ad2131 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -108,6 +108,10 @@ #define BXT_FREG_NUM 12 #define BXT_PR_NUM 6 +#define CNL_PR 0x84 +#define CNL_FREG_NUM 6 +#define CNL_PR_NUM 5 + #define LVSCC 0xc4 #define UVSCC 0xc8 #define ERASE_OPCODE_SHIFT 8 @@ -187,12 +191,16 @@ static void intel_spi_dump_regs(struct intel_spi *ispi) dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i, readl(ispi->pregs + PR(i))); - value = readl(ispi->sregs + SSFSTS_CTL); - dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); - dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", - readl(ispi->sregs + PREOP_OPTYPE)); - dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", readl(ispi->sregs + OPMENU0)); - dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", readl(ispi->sregs + OPMENU1)); + if (ispi->sregs) { + value = readl(ispi->sregs + SSFSTS_CTL); + dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); + dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", + readl(ispi->sregs + PREOP_OPTYPE)); + dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", + readl(ispi->sregs + OPMENU0)); + dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", + readl(ispi->sregs + OPMENU1)); + } if (ispi->info->type == INTEL_SPI_BYT) dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR)); @@ -340,6 +348,13 @@ static int intel_spi_init(struct intel_spi *ispi) ispi->erase_64k = true; break; + case INTEL_SPI_CNL: + ispi->sregs = NULL; + ispi->pregs = ispi->base + CNL_PR; + ispi->nregions = CNL_FREG_NUM; + ispi->pr_num = CNL_PR_NUM; + break; + default: return -EINVAL; } @@ -367,6 +382,11 @@ static int intel_spi_init(struct intel_spi *ispi) !(uvscc & ERASE_64K_OPCODE_MASK)) ispi->erase_64k = false; + if (ispi->sregs == NULL && (ispi->swseq_reg || ispi->swseq_erase)) { + dev_err(ispi->dev, "software sequencer not supported, but required\n"); + return -EINVAL; + } + /* * Some controllers can only do basic operations using hardware * sequencer. All other operations are supposed to be carried out @@ -383,7 +403,7 @@ static int intel_spi_init(struct intel_spi *ispi) val = readl(ispi->base + HSFSTS_CTL); ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN); - if (ispi->locked) { + if (ispi->locked && ispi->sregs) { /* * BIOS programs allowed opcodes and then locks down the * register. So read back what opcodes it decided to support. @@ -426,7 +446,7 @@ static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype) return 0; } -static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len) +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len) { u32 val, status; int ret; @@ -469,7 +489,7 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len) return 0; } -static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len, int optype) { u32 val = 0, status; @@ -535,7 +555,8 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, return 0; } -static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + size_t len) { struct intel_spi *ispi = nor->priv; int ret; @@ -555,7 +576,8 @@ static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return intel_spi_read_block(ispi, buf, len); } -static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, + size_t len) { struct intel_spi *ispi = nor->priv; int ret; @@ -621,6 +643,8 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len, switch (nor->read_opcode) { case SPINOR_OP_READ: case SPINOR_OP_READ_FAST: + case SPINOR_OP_READ_4B: + case SPINOR_OP_READ_FAST_4B: break; default: return -EINVAL; @@ -862,6 +886,14 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, } } +static const struct spi_nor_controller_ops intel_spi_controller_ops = { + .read_reg = intel_spi_read_reg, + .write_reg = intel_spi_write_reg, + .read = intel_spi_read, + .write = intel_spi_write, + .erase = intel_spi_erase, +}; + struct intel_spi *intel_spi_probe(struct device *dev, struct resource *mem, const struct intel_spi_boardinfo *info) { @@ -895,11 +927,7 @@ struct intel_spi *intel_spi_probe(struct device *dev, ispi->nor.dev = ispi->dev; ispi->nor.priv = ispi; - ispi->nor.read_reg = intel_spi_read_reg; - ispi->nor.write_reg = intel_spi_write_reg; - ispi->nor.read = intel_spi_read; - ispi->nor.write = intel_spi_write; - ispi->nor.erase = intel_spi_erase; + ispi->nor.controller_ops = &intel_spi_controller_ops; ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps); if (ret) { diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index 34db01ab6cab..b1691680d174 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -151,9 +151,9 @@ static int mtk_nor_execute_cmd(struct mtk_nor *mtk_nor, u8 cmdval) } static int mtk_nor_do_tx_rx(struct mtk_nor *mtk_nor, u8 op, - u8 *tx, int txlen, u8 *rx, int rxlen) + const u8 *tx, size_t txlen, u8 *rx, size_t rxlen) { - int len = 1 + txlen + rxlen; + size_t len = 1 + txlen + rxlen; int i, ret, idx; if (len > MTK_NOR_MAX_SHIFT) @@ -193,7 +193,7 @@ static int mtk_nor_do_tx_rx(struct mtk_nor *mtk_nor, u8 op, } /* Do a WRSR (Write Status Register) command */ -static int mtk_nor_wr_sr(struct mtk_nor *mtk_nor, u8 sr) +static int mtk_nor_wr_sr(struct mtk_nor *mtk_nor, const u8 sr) { writeb(sr, mtk_nor->base + MTK_NOR_PRGDATA5_REG); writeb(8, mtk_nor->base + MTK_NOR_CNT_REG); @@ -354,7 +354,7 @@ static ssize_t mtk_nor_write(struct spi_nor *nor, loff_t to, size_t len, return len; } -static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len) { int ret; struct mtk_nor *mtk_nor = nor->priv; @@ -376,8 +376,8 @@ static int mtk_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return ret; } -static int mtk_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, - int len) +static int mtk_nor_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, + size_t len) { int ret; struct mtk_nor *mtk_nor = nor->priv; @@ -419,6 +419,13 @@ static int mtk_nor_enable_clk(struct mtk_nor *mtk_nor) return 0; } +static const struct spi_nor_controller_ops mtk_controller_ops = { + .read_reg = mtk_nor_read_reg, + .write_reg = mtk_nor_write_reg, + .read = mtk_nor_read, + .write = mtk_nor_write, +}; + static int mtk_nor_init(struct mtk_nor *mtk_nor, struct device_node *flash_node) { @@ -438,12 +445,8 @@ static int mtk_nor_init(struct mtk_nor *mtk_nor, nor->dev = mtk_nor->dev; nor->priv = mtk_nor; spi_nor_set_flash_node(nor, flash_node); + nor->controller_ops = &mtk_controller_ops; - /* fill the hooks to spi nor */ - nor->read = mtk_nor_read; - nor->read_reg = mtk_nor_read_reg; - nor->write = mtk_nor_write; - nor->write_reg = mtk_nor_write_reg; nor->mtd.name = "mtk_nor"; /* initialized with NULL */ ret = spi_nor_scan(nor, NULL, &hwcaps); diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index 4a871587392b..9a5b1a7c636a 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -123,7 +123,8 @@ static int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi) return ret; } -static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + size_t len) { struct nxp_spifi *spifi = nor->priv; u32 cmd; @@ -145,7 +146,8 @@ static int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) return nxp_spifi_wait_for_cmd(spifi); } -static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +static int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, + size_t len) { struct nxp_spifi *spifi = nor->priv; u32 cmd; @@ -263,9 +265,18 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) static void nxp_spifi_dummy_id_read(struct spi_nor *nor) { u8 id[SPI_NOR_MAX_ID_LEN]; - nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, + SPI_NOR_MAX_ID_LEN); } +static const struct spi_nor_controller_ops nxp_spifi_controller_ops = { + .read_reg = nxp_spifi_read_reg, + .write_reg = nxp_spifi_write_reg, + .read = nxp_spifi_read, + .write = nxp_spifi_write, + .erase = nxp_spifi_erase, +}; + static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, struct device_node *np) { @@ -332,11 +343,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, spifi->nor.dev = spifi->dev; spi_nor_set_flash_node(&spifi->nor, np); spifi->nor.priv = spifi; - spifi->nor.read = nxp_spifi_read; - spifi->nor.write = nxp_spifi_write; - spifi->nor.erase = nxp_spifi_erase; - spifi->nor.read_reg = nxp_spifi_read_reg; - spifi->nor.write_reg = nxp_spifi_write_reg; + spifi->nor.controller_ops = &nxp_spifi_controller_ops; /* * The first read on a hard reset isn't reliable so do a diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0c2ec1c21434..f4afe123e9dc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -19,6 +19,7 @@ #include <linux/mtd/mtd.h> #include <linux/of_platform.h> +#include <linux/sched/task_stack.h> #include <linux/spi/flash.h> #include <linux/mtd/spi-nor.h> @@ -39,71 +40,6 @@ #define SPI_NOR_MAX_ID_LEN 6 #define SPI_NOR_MAX_ADDR_WIDTH 4 -struct spi_nor_read_command { - u8 num_mode_clocks; - u8 num_wait_states; - u8 opcode; - enum spi_nor_protocol proto; -}; - -struct spi_nor_pp_command { - u8 opcode; - enum spi_nor_protocol proto; -}; - -enum spi_nor_read_command_index { - SNOR_CMD_READ, - SNOR_CMD_READ_FAST, - SNOR_CMD_READ_1_1_1_DTR, - - /* Dual SPI */ - SNOR_CMD_READ_1_1_2, - SNOR_CMD_READ_1_2_2, - SNOR_CMD_READ_2_2_2, - SNOR_CMD_READ_1_2_2_DTR, - - /* Quad SPI */ - SNOR_CMD_READ_1_1_4, - SNOR_CMD_READ_1_4_4, - SNOR_CMD_READ_4_4_4, - SNOR_CMD_READ_1_4_4_DTR, - - /* Octal SPI */ - SNOR_CMD_READ_1_1_8, - SNOR_CMD_READ_1_8_8, - SNOR_CMD_READ_8_8_8, - SNOR_CMD_READ_1_8_8_DTR, - - SNOR_CMD_READ_MAX -}; - -enum spi_nor_pp_command_index { - SNOR_CMD_PP, - - /* Quad SPI */ - SNOR_CMD_PP_1_1_4, - SNOR_CMD_PP_1_4_4, - SNOR_CMD_PP_4_4_4, - - /* Octal SPI */ - SNOR_CMD_PP_1_1_8, - SNOR_CMD_PP_1_8_8, - SNOR_CMD_PP_8_8_8, - - SNOR_CMD_PP_MAX -}; - -struct spi_nor_flash_parameter { - u64 size; - u32 page_size; - - struct spi_nor_hwcaps hwcaps; - struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; - struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; - - int (*quad_enable)(struct spi_nor *nor); -}; - struct sfdp_parameter_header { u8 id_lsb; u8 minor; @@ -200,7 +136,7 @@ struct sfdp_header { * register does not modify status register 2. * - 101b: QE is bit 1 of status register 2. Status register 1 is read using * Read Status instruction 05h. Status register2 is read using - * instruction 35h. QE is set via Writ Status instruction 01h with + * instruction 35h. QE is set via Write Status instruction 01h with * two data bytes where bit 1 of the second byte is one. * [...] */ @@ -218,16 +154,26 @@ struct sfdp_bfpt { /** * struct spi_nor_fixups - SPI NOR fixup hooks + * @default_init: called after default flash parameters init. Used to tweak + * flash parameters when information provided by the flash_info + * table is incomplete or wrong. * @post_bfpt: called after the BFPT table has been parsed + * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs + * that do not support RDSFDP). Typically used to tweak various + * parameters that could not be extracted by other means (i.e. + * when information provided by the SFDP/flash_info tables are + * incomplete or wrong). * * Those hooks can be used to tweak the SPI NOR configuration when the SFDP * table is broken or not available. */ struct spi_nor_fixups { + void (*default_init)(struct spi_nor *nor); int (*post_bfpt)(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt, struct spi_nor_flash_parameter *params); + void (*post_sfdp)(struct spi_nor *nor); }; struct flash_info { @@ -265,6 +211,14 @@ struct flash_info { * bit. Must be used with * SPI_NOR_HAS_LOCK. */ +#define SPI_NOR_XSR_RDY BIT(10) /* + * S3AN flashes have specific opcode to + * read the status register. + * Flags SPI_NOR_XSR_RDY and SPI_S3AN + * use the same bit as one implies the + * other, but we will get rid of + * SPI_S3AN soon. + */ #define SPI_S3AN BIT(10) /* * Xilinx Spartan 3AN In-System Flash * (MFR cannot be used for probing @@ -282,293 +236,629 @@ struct flash_info { /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; - - int (*quad_enable)(struct spi_nor *nor); }; #define JEDEC_MFR(info) ((info)->id[0]) -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. +/** + * spi_nor_spimem_xfer_data() - helper function to read/write data to + * flash's memory region + * @nor: pointer to 'struct spi_nor' + * @op: pointer to 'struct spi_mem_op' template for transfer + * + * Return: number of bytes transferred on success, -errno otherwise */ -static int read_sr(struct spi_nor *nor) +static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor, + struct spi_mem_op *op) { + bool usebouncebuf = false; + void *rdbuf = NULL; + const void *buf; int ret; - u8 val; - ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); - if (ret < 0) { - pr_err("error %d reading SR\n", (int) ret); - return ret; + if (op->data.dir == SPI_MEM_DATA_IN) + buf = op->data.buf.in; + else + buf = op->data.buf.out; + + if (object_is_on_stack(buf) || !virt_addr_valid(buf)) + usebouncebuf = true; + + if (usebouncebuf) { + if (op->data.nbytes > nor->bouncebuf_size) + op->data.nbytes = nor->bouncebuf_size; + + if (op->data.dir == SPI_MEM_DATA_IN) { + rdbuf = op->data.buf.in; + op->data.buf.in = nor->bouncebuf; + } else { + op->data.buf.out = nor->bouncebuf; + memcpy(nor->bouncebuf, buf, + op->data.nbytes); + } } - return val; + ret = spi_mem_adjust_op_size(nor->spimem, op); + if (ret) + return ret; + + ret = spi_mem_exec_op(nor->spimem, op); + if (ret) + return ret; + + if (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN) + memcpy(rdbuf, nor->bouncebuf, op->data.nbytes); + + return op->data.nbytes; } -/* - * Read the flag status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. +/** + * spi_nor_spimem_read_data() - read data from flash's memory region via + * spi-mem + * @nor: pointer to 'struct spi_nor' + * @from: offset to read from + * @len: number of bytes to read + * @buf: pointer to dst buffer + * + * Return: number of bytes read successfully, -errno otherwise */ -static int read_fsr(struct spi_nor *nor) +static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, + size_t len, u8 *buf) { - int ret; - u8 val; + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, from, 1), + SPI_MEM_OP_DUMMY(nor->read_dummy, 1), + SPI_MEM_OP_DATA_IN(len, buf, 1)); + + /* get transfer protocols. */ + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); + op.dummy.buswidth = op.addr.buswidth; + op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + + return spi_nor_spimem_xfer_data(nor, &op); +} - ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1); - if (ret < 0) { - pr_err("error %d reading FSR\n", ret); - return ret; - } +/** + * spi_nor_read_data() - read data from flash memory + * @nor: pointer to 'struct spi_nor' + * @from: offset to read from + * @len: number of bytes to read + * @buf: pointer to dst buffer + * + * Return: number of bytes read successfully, -errno otherwise + */ +static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, + u8 *buf) +{ + if (nor->spimem) + return spi_nor_spimem_read_data(nor, from, len, buf); - return val; + return nor->controller_ops->read(nor, from, len, buf); } -/* - * Read configuration register, returning its value in the - * location. Return the configuration register value. - * Returns negative if error occurred. +/** + * spi_nor_spimem_write_data() - write data to flash memory via + * spi-mem + * @nor: pointer to 'struct spi_nor' + * @to: offset to write to + * @len: number of bytes to write + * @buf: pointer to src buffer + * + * Return: number of bytes written successfully, -errno otherwise */ -static int read_cr(struct spi_nor *nor) +static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, + size_t len, const u8 *buf) { - int ret; - u8 val; + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, to, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(len, buf, 1)); - ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); - if (ret < 0) { - dev_err(nor->dev, "error %d reading CR\n", ret); - return ret; - } + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - return val; + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + op.addr.nbytes = 0; + + return spi_nor_spimem_xfer_data(nor, &op); } -/* - * Write status register 1 byte - * Returns negative if error occurred. +/** + * spi_nor_write_data() - write data to flash memory + * @nor: pointer to 'struct spi_nor' + * @to: offset to write to + * @len: number of bytes to write + * @buf: pointer to src buffer + * + * Return: number of bytes written successfully, -errno otherwise */ -static int write_sr(struct spi_nor *nor, u8 val) +static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + const u8 *buf) { - nor->cmd_buf[0] = val; - return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); + if (nor->spimem) + return spi_nor_spimem_write_data(nor, to, len, buf); + + return nor->controller_ops->write(nor, to, len, buf); } -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. +/** + * spi_nor_write_enable() - Set write enable latch with Write Enable command. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. */ -static int write_enable(struct spi_nor *nor) +static int spi_nor_write_enable(struct spi_nor *nor) { - return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d on Write Enable\n", ret); + + return ret; } -/* - * Send write disable instruction to the chip. +/** + * spi_nor_write_disable() - Send Write Disable instruction to the chip. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. */ -static int write_disable(struct spi_nor *nor) +static int spi_nor_write_disable(struct spi_nor *nor) { - return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d on Write Disable\n", ret); + + return ret; } -static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +/** + * spi_nor_read_sr() - Read the Status Register. + * @nor: pointer to 'struct spi_nor'. + * @sr: pointer to a DMA-able buffer where the value of the + * Status Register will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) { - return mtd->priv; -} + int ret; + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, sr, 1)); -static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR, + sr, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading SR\n", ret); + + return ret; +} + +/** + * spi_nor_read_fsr() - Read the Flag Status Register. + * @nor: pointer to 'struct spi_nor' + * @fsr: pointer to a DMA-able buffer where the value of the + * Flag Status Register will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) { - size_t i; + int ret; - for (i = 0; i < size; i++) - if (table[i][0] == opcode) - return table[i][1]; + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, fsr, 1)); - /* No conversion found, keep input op code. */ - return opcode; + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR, + fsr, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading FSR\n", ret); + + return ret; } -static u8 spi_nor_convert_3to4_read(u8 opcode) +/** + * spi_nor_read_cr() - Read the Configuration Register using the + * SPINOR_OP_RDCR (35h) command. + * @nor: pointer to 'struct spi_nor' + * @cr: pointer to a DMA-able buffer where the value of the + * Configuration Register will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) { - static const u8 spi_nor_3to4_read[][2] = { - { SPINOR_OP_READ, SPINOR_OP_READ_4B }, - { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B }, - { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B }, - { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B }, - { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B }, - { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B }, - { SPINOR_OP_READ_1_1_8, SPINOR_OP_READ_1_1_8_4B }, - { SPINOR_OP_READ_1_8_8, SPINOR_OP_READ_1_8_8_4B }, + int ret; - { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B }, - { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B }, - { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B }, - }; + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, cr, 1)); - return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, - ARRAY_SIZE(spi_nor_3to4_read)); + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR, cr, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading CR\n", ret); + + return ret; } -static u8 spi_nor_convert_3to4_program(u8 opcode) +/** + * macronix_set_4byte() - Set 4-byte address mode for Macronix flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int macronix_set_4byte(struct spi_nor *nor, bool enable) { - static const u8 spi_nor_3to4_program[][2] = { - { SPINOR_OP_PP, SPINOR_OP_PP_4B }, - { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B }, - { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B }, - { SPINOR_OP_PP_1_1_8, SPINOR_OP_PP_1_1_8_4B }, - { SPINOR_OP_PP_1_8_8, SPINOR_OP_PP_1_8_8_4B }, - }; + int ret; - return spi_nor_convert_opcode(opcode, spi_nor_3to4_program, - ARRAY_SIZE(spi_nor_3to4_program)); + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? + SPINOR_OP_EN4B : + SPINOR_OP_EX4B, + 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, + enable ? SPINOR_OP_EN4B : + SPINOR_OP_EX4B, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret); + + return ret; } -static u8 spi_nor_convert_3to4_erase(u8 opcode) +/** + * st_micron_set_4byte() - Set 4-byte address mode for ST and Micron flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int st_micron_set_4byte(struct spi_nor *nor, bool enable) { - static const u8 spi_nor_3to4_erase[][2] = { - { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B }, - { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B }, - { SPINOR_OP_SE, SPINOR_OP_SE_4B }, - }; + int ret; - return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase, - ARRAY_SIZE(spi_nor_3to4_erase)); + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = macronix_set_4byte(nor, enable); + if (ret) + return ret; + + return spi_nor_write_disable(nor); } -static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) +/** + * spansion_set_4byte() - Set 4-byte address mode for Spansion flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int spansion_set_4byte(struct spi_nor *nor, bool enable) { - /* Do some manufacturer fixups first */ - switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_SPANSION: - /* No small sector erase for 4-byte command set */ - nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = nor->info->sector_size; - break; + int ret; - default: - break; + nor->bouncebuf[0] = enable << 7; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR, + nor->bouncebuf, 1); } - nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); - nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode); - nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); + if (ret) + dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret); - if (!spi_nor_has_uniform_erase(nor)) { - struct spi_nor_erase_map *map = &nor->erase_map; - struct spi_nor_erase_type *erase; - int i; + return ret; +} - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { - erase = &map->erase_type[i]; - erase->opcode = - spi_nor_convert_3to4_erase(erase->opcode); - } +/** + * spi_nor_write_ear() - Write Extended Address Register. + * @nor: pointer to 'struct spi_nor'. + * @ear: value to write to the Extended Address Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) +{ + int ret; + + nor->bouncebuf[0] = ear; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREAR, + nor->bouncebuf, 1); } + + if (ret) + dev_dbg(nor->dev, "error %d writing EAR\n", ret); + + return ret; } -/* Enable/disable 4-byte addressing mode. */ -static int set_4byte(struct spi_nor *nor, bool enable) +/** + * winbond_set_4byte() - Set 4-byte address mode for Winbond flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_set_4byte(struct spi_nor *nor, bool enable) { - int status; - bool need_wren = false; - u8 cmd; + int ret; - switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_ST: - case SNOR_MFR_MICRON: - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - /* fall through */ - case SNOR_MFR_MACRONIX: - case SNOR_MFR_WINBOND: - if (need_wren) - write_enable(nor); + ret = macronix_set_4byte(nor, enable); + if (ret || enable) + return ret; - cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; - status = nor->write_reg(nor, cmd, NULL, 0); - if (need_wren) - write_disable(nor); + /* + * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address + * Register to be set to 1, so all 3-byte-address reads come from the + * second 16M. We must clear the register to enable normal behavior. + */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; - if (!status && !enable && - JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { - /* - * On Winbond W25Q256FV, leaving 4byte mode causes - * the Extended Address Register to be set to 1, so all - * 3-byte-address reads come from the second 16M. - * We must clear the register to enable normal behavior. - */ - write_enable(nor); - nor->cmd_buf[0] = 0; - nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1); - write_disable(nor); - } + ret = spi_nor_write_ear(nor, 0); + if (ret) + return ret; - return status; - default: - /* Spansion style */ - nor->cmd_buf[0] = enable << 7; - return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); + return spi_nor_write_disable(nor); +} + +/** + * spi_nor_xread_sr() - Read the Status Register on S3AN flashes. + * @nor: pointer to 'struct spi_nor'. + * @sr: pointer to a DMA-able buffer where the value of the + * Status Register will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, sr, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_XRDSR, + sr, 1); } + + if (ret) + dev_dbg(nor->dev, "error %d reading XRDSR\n", ret); + + return ret; } +/** + * s3an_sr_ready() - Query the Status Register of the S3AN flash to see if the + * flash is ready for new commands. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ static int s3an_sr_ready(struct spi_nor *nor) { int ret; - u8 val; - ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); - if (ret < 0) { - dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + ret = spi_nor_xread_sr(nor, nor->bouncebuf); + if (ret) return ret; + + return !!(nor->bouncebuf[0] & XSR_RDY); +} + +/** + * spi_nor_clear_sr() - Clear the Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_sr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLSR, + NULL, 0); } - return !!(val & XSR_RDY); + if (ret) + dev_dbg(nor->dev, "error %d clearing SR\n", ret); } +/** + * spi_nor_sr_ready() - Query the Status Register to see if the flash is ready + * for new commands. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ static int spi_nor_sr_ready(struct spi_nor *nor) { - int sr = read_sr(nor); - if (sr < 0) - return sr; + int ret = spi_nor_read_sr(nor, nor->bouncebuf); + + if (ret) + return ret; - if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) { - if (sr & SR_E_ERR) + if (nor->flags & SNOR_F_USE_CLSR && + nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { + if (nor->bouncebuf[0] & SR_E_ERR) dev_err(nor->dev, "Erase Error occurred\n"); else dev_err(nor->dev, "Programming Error occurred\n"); - nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); + spi_nor_clear_sr(nor); return -EIO; } - return !(sr & SR_WIP); + return !(nor->bouncebuf[0] & SR_WIP); } +/** + * spi_nor_clear_fsr() - Clear the Flag Status Register. + * @nor: pointer to 'struct spi_nor'. + */ +static void spi_nor_clear_fsr(struct spi_nor *nor) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLFSR, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d clearing FSR\n", ret); +} + +/** + * spi_nor_fsr_ready() - Query the Flag Status Register to see if the flash is + * ready for new commands. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ static int spi_nor_fsr_ready(struct spi_nor *nor) { - int fsr = read_fsr(nor); - if (fsr < 0) - return fsr; + int ret = spi_nor_read_fsr(nor, nor->bouncebuf); + + if (ret) + return ret; - if (fsr & (FSR_E_ERR | FSR_P_ERR)) { - if (fsr & FSR_E_ERR) + if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { + if (nor->bouncebuf[0] & FSR_E_ERR) dev_err(nor->dev, "Erase operation failed.\n"); else dev_err(nor->dev, "Program operation failed.\n"); - if (fsr & FSR_PT_ERR) + if (nor->bouncebuf[0] & FSR_PT_ERR) dev_err(nor->dev, "Attempted to modify a protected sector.\n"); - nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0); + spi_nor_clear_fsr(nor); return -EIO; } - return fsr & FSR_READY; + return nor->bouncebuf[0] & FSR_READY; } +/** + * spi_nor_ready() - Query the flash to see if it is ready for new commands. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ static int spi_nor_ready(struct spi_nor *nor) { int sr, fsr; @@ -585,9 +875,13 @@ static int spi_nor_ready(struct spi_nor *nor) return sr && fsr; } -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. +/** + * spi_nor_wait_till_ready_with_timeout() - Service routine to read the + * Status Register until ready, or timeout occurs. + * @nor: pointer to "struct spi_nor". + * @timeout_jiffies: jiffies to wait until timeout. + * + * Return: 0 on success, -errno otherwise. */ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, unsigned long timeout_jiffies) @@ -610,27 +904,407 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, cond_resched(); } - dev_err(nor->dev, "flash operation timed out\n"); + dev_dbg(nor->dev, "flash operation timed out\n"); return -ETIMEDOUT; } +/** + * spi_nor_wait_till_ready() - Wait for a predefined amount of time for the + * flash to be ready, or timeout occurs. + * @nor: pointer to "struct spi_nor". + * + * Return: 0 on success, -errno otherwise. + */ static int spi_nor_wait_till_ready(struct spi_nor *nor) { return spi_nor_wait_till_ready_with_timeout(nor, DEFAULT_READY_WAIT_JIFFIES); } -/* - * Erase the whole flash memory +/** + * spi_nor_write_sr() - Write the Status Register. + * @nor: pointer to 'struct spi_nor'. + * @sr: pointer to DMA-able buffer to write to the Status Register. + * @len: number of bytes to write to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) +{ + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(len, sr, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR, + sr, len); + } + + if (ret) { + dev_dbg(nor->dev, "error %d writing SR\n", ret); + return ret; + } + + return spi_nor_wait_till_ready(nor); +} + +/** + * spi_nor_write_sr1_and_check() - Write one byte to the Status Register 1 and + * ensure that the byte written match the received value. + * @nor: pointer to a 'struct spi_nor'. + * @sr1: byte value to be written to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr1_and_check(struct spi_nor *nor, u8 sr1) +{ + int ret; + + nor->bouncebuf[0] = sr1; + + ret = spi_nor_write_sr(nor, nor->bouncebuf, 1); + if (ret) + return ret; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + if (nor->bouncebuf[0] != sr1) { + dev_dbg(nor->dev, "SR1: read back test failed\n"); + return -EIO; + } + + return 0; +} + +/** + * spi_nor_write_16bit_sr_and_check() - Write the Status Register 1 and the + * Status Register 2 in one shot. Ensure that the byte written in the Status + * Register 1 match the received value, and that the 16-bit Write did not + * affect what was already in the Status Register 2. + * @nor: pointer to a 'struct spi_nor'. + * @sr1: byte value to be written to the Status Register 1. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) +{ + int ret; + u8 *sr_cr = nor->bouncebuf; + u8 cr_written; + + /* Make sure we don't overwrite the contents of Status Register 2. */ + if (!(nor->flags & SNOR_F_NO_READ_CR)) { + ret = spi_nor_read_cr(nor, &sr_cr[1]); + if (ret) + return ret; + } else if (nor->params.quad_enable) { + /* + * If the Status Register 2 Read command (35h) is not + * supported, we should at least be sure we don't + * change the value of the SR2 Quad Enable bit. + * + * We can safely assume that when the Quad Enable method is + * set, the value of the QE bit is one, as a consequence of the + * nor->params.quad_enable() call. + * + * We can safely assume that the Quad Enable bit is present in + * the Status Register 2 at BIT(1). According to the JESD216 + * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit + * Write Status (01h) command is available just for the cases + * in which the QE bit is described in SR2 at BIT(1). + */ + sr_cr[1] = SR2_QUAD_EN_BIT1; + } else { + sr_cr[1] = 0; + } + + sr_cr[0] = sr1; + + ret = spi_nor_write_sr(nor, sr_cr, 2); + if (ret) + return ret; + + if (nor->flags & SNOR_F_NO_READ_CR) + return 0; + + cr_written = sr_cr[1]; + + ret = spi_nor_read_cr(nor, &sr_cr[1]); + if (ret) + return ret; + + if (cr_written != sr_cr[1]) { + dev_dbg(nor->dev, "CR: read back test failed\n"); + return -EIO; + } + + return 0; +} + +/** + * spi_nor_write_16bit_cr_and_check() - Write the Status Register 1 and the + * Configuration Register in one shot. Ensure that the byte written in the + * Configuration Register match the received value, and that the 16-bit Write + * did not affect what was already in the Status Register 1. + * @nor: pointer to a 'struct spi_nor'. + * @cr: byte value to be written to the Configuration Register. * - * Returns 0 if successful, non-zero otherwise. + * Return: 0 on success, -errno otherwise. */ -static int erase_chip(struct spi_nor *nor) +static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) { + int ret; + u8 *sr_cr = nor->bouncebuf; + u8 sr_written; + + /* Keep the current value of the Status Register 1. */ + ret = spi_nor_read_sr(nor, sr_cr); + if (ret) + return ret; + + sr_cr[1] = cr; + + ret = spi_nor_write_sr(nor, sr_cr, 2); + if (ret) + return ret; + + sr_written = sr_cr[0]; + + ret = spi_nor_read_sr(nor, sr_cr); + if (ret) + return ret; + + if (sr_written != sr_cr[0]) { + dev_dbg(nor->dev, "SR: Read back test failed\n"); + return -EIO; + } + + if (nor->flags & SNOR_F_NO_READ_CR) + return 0; + + ret = spi_nor_read_cr(nor, &sr_cr[1]); + if (ret) + return ret; + + if (cr != sr_cr[1]) { + dev_dbg(nor->dev, "CR: read back test failed\n"); + return -EIO; + } + + return 0; +} + +/** + * spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that + * the byte written match the received value without affecting other bits in the + * Status Register 1 and 2. + * @nor: pointer to a 'struct spi_nor'. + * @sr1: byte value to be written to the Status Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) +{ + if (nor->flags & SNOR_F_HAS_16BIT_SR) + return spi_nor_write_16bit_sr_and_check(nor, sr1); + + return spi_nor_write_sr1_and_check(nor, sr1); +} + +/** + * spi_nor_write_sr2() - Write the Status Register 2 using the + * SPINOR_OP_WRSR2 (3eh) command. + * @nor: pointer to 'struct spi_nor'. + * @sr2: pointer to DMA-able buffer to write to the Status Register 2. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) +{ + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, sr2, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR2, + sr2, 1); + } + + if (ret) { + dev_dbg(nor->dev, "error %d writing SR2\n", ret); + return ret; + } + + return spi_nor_wait_till_ready(nor); +} + +/** + * spi_nor_read_sr2() - Read the Status Register 2 using the + * SPINOR_OP_RDSR2 (3fh) command. + * @nor: pointer to 'struct spi_nor'. + * @sr2: pointer to DMA-able buffer where the value of the + * Status Register 2 will be written. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, sr2, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR2, + sr2, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d reading SR2\n", ret); + + return ret; +} + +/** + * spi_nor_erase_chip() - Erase the entire flash memory. + * @nor: pointer to 'struct spi_nor'. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_erase_chip(struct spi_nor *nor) +{ + int ret; + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); - return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE, + NULL, 0); + } + + if (ret) + dev_dbg(nor->dev, "error %d erasing chip\n", ret); + + return ret; +} + +static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + if (table[i][0] == opcode) + return table[i][1]; + + /* No conversion found, keep input op code. */ + return opcode; +} + +static u8 spi_nor_convert_3to4_read(u8 opcode) +{ + static const u8 spi_nor_3to4_read[][2] = { + { SPINOR_OP_READ, SPINOR_OP_READ_4B }, + { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B }, + { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B }, + { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B }, + { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B }, + { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B }, + { SPINOR_OP_READ_1_1_8, SPINOR_OP_READ_1_1_8_4B }, + { SPINOR_OP_READ_1_8_8, SPINOR_OP_READ_1_8_8_4B }, + + { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B }, + { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B }, + { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, + ARRAY_SIZE(spi_nor_3to4_read)); +} + +static u8 spi_nor_convert_3to4_program(u8 opcode) +{ + static const u8 spi_nor_3to4_program[][2] = { + { SPINOR_OP_PP, SPINOR_OP_PP_4B }, + { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B }, + { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B }, + { SPINOR_OP_PP_1_1_8, SPINOR_OP_PP_1_1_8_4B }, + { SPINOR_OP_PP_1_8_8, SPINOR_OP_PP_1_8_8_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_program, + ARRAY_SIZE(spi_nor_3to4_program)); +} + +static u8 spi_nor_convert_3to4_erase(u8 opcode) +{ + static const u8 spi_nor_3to4_erase[][2] = { + { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B }, + { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B }, + { SPINOR_OP_SE, SPINOR_OP_SE_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase, + ARRAY_SIZE(spi_nor_3to4_erase)); +} + +static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) +{ + nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); + nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode); + nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); + + if (!spi_nor_has_uniform_erase(nor)) { + struct spi_nor_erase_map *map = &nor->params.erase_map; + struct spi_nor_erase_type *erase; + int i; + + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + erase = &map->erase_type[i]; + erase->opcode = + spi_nor_convert_3to4_erase(erase->opcode); + } + } } static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) @@ -639,10 +1313,9 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) mutex_lock(&nor->lock); - if (nor->prepare) { - ret = nor->prepare(nor, ops); + if (nor->controller_ops && nor->controller_ops->prepare) { + ret = nor->controller_ops->prepare(nor, ops); if (ret) { - dev_err(nor->dev, "failed in the preparation.\n"); mutex_unlock(&nor->lock); return ret; } @@ -652,8 +1325,8 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) { - if (nor->unprepare) - nor->unprepare(nor, ops); + if (nor->controller_ops && nor->controller_ops->unprepare) + nor->controller_ops->unprepare(nor, ops); mutex_unlock(&nor->lock); } @@ -666,10 +1339,9 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) * Addr can safely be unsigned int, the biggest S3AN device is smaller than * 4 MiB. */ -static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) +static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr) { - unsigned int offset; - unsigned int page; + u32 offset, page; offset = addr % nor->page_size; page = addr / nor->page_size; @@ -678,30 +1350,46 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) return page | offset; } +static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr) +{ + if (!nor->params.convert_addr) + return addr; + + return nor->params.convert_addr(nor, addr); +} + /* * Initiate the erasure of a single sector */ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) { - u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; int i; - if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) - addr = spi_nor_s3an_addr_convert(nor, addr); + addr = spi_nor_convert_addr(nor, addr); - if (nor->erase) - return nor->erase(nor, addr); + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, addr, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + + return spi_mem_exec_op(nor->spimem, &op); + } else if (nor->controller_ops->erase) { + return nor->controller_ops->erase(nor, addr); + } /* * Default implementation, if driver doesn't have a specialized HW * control */ for (i = nor->addr_width - 1; i >= 0; i--) { - buf[i] = addr & 0xff; + nor->bouncebuf[i] = addr & 0xff; addr >>= 8; } - return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); + return nor->controller_ops->write_reg(nor, nor->erase_opcode, + nor->bouncebuf, nor->addr_width); } /** @@ -876,7 +1564,7 @@ static int spi_nor_init_erase_cmd_list(struct spi_nor *nor, struct list_head *erase_list, u64 addr, u32 len) { - const struct spi_nor_erase_map *map = &nor->erase_map; + const struct spi_nor_erase_map *map = &nor->params.erase_map; const struct spi_nor_erase_type *erase, *prev_erase = NULL; struct spi_nor_erase_region *region; struct spi_nor_erase_command *cmd = NULL; @@ -951,7 +1639,9 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len) list_for_each_entry_safe(cmd, next, &erase_list, list) { nor->erase_opcode = cmd->opcode; while (cmd->count) { - write_enable(nor); + ret = spi_nor_write_enable(nor); + if (ret) + goto destroy_erase_cmd_list; ret = spi_nor_erase_sector(nor, addr); if (ret) @@ -1006,12 +1696,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { unsigned long timeout; - write_enable(nor); + ret = spi_nor_write_enable(nor); + if (ret) + goto erase_err; - if (erase_chip(nor)) { - ret = -EIO; + ret = spi_nor_erase_chip(nor); + if (ret) goto erase_err; - } /* * Scale the timeout linearly with the size of the flash, with @@ -1034,7 +1725,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) /* "sector"-at-a-time erase */ } else if (spi_nor_has_uniform_erase(nor)) { while (len) { - write_enable(nor); + ret = spi_nor_write_enable(nor); + if (ret) + goto erase_err; ret = spi_nor_erase_sector(nor, addr); if (ret) @@ -1055,7 +1748,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) goto erase_err; } - write_disable(nor); + ret = spi_nor_write_disable(nor); erase_err: spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); @@ -1063,27 +1756,6 @@ erase_err: return ret; } -/* Write status register and ensure bits in mask match written values */ -static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) -{ - int ret; - - write_enable(nor); - ret = write_sr(nor, status_new); - if (ret) - return ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - - ret = read_sr(nor); - if (ret < 0) - return ret; - - return ((ret & mask) != (status_new & mask)) ? -EIO : 0; -} - static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, uint64_t *len) { @@ -1176,16 +1848,18 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; - int status_old, status_new; + int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 shift = ffs(mask) - 1, pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; - status_old = read_sr(nor); - if (status_old < 0) - return status_old; + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old = nor->bouncebuf[0]; /* If nothing in our range is unlocked, we don't need to do anything */ if (stm_is_locked_sr(nor, ofs, len, status_old)) @@ -1245,7 +1919,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) if ((status_new & mask) < (status_old & mask)) return -EINVAL; - return write_sr_and_check(nor, status_new, mask); + return spi_nor_write_sr_and_check(nor, status_new); } /* @@ -1256,16 +1930,18 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; - int status_old, status_new; + int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 shift = ffs(mask) - 1, pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; - status_old = read_sr(nor); - if (status_old < 0) - return status_old; + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old = nor->bouncebuf[0]; /* If nothing in our range is locked, we don't need to do anything */ if (stm_is_unlocked_sr(nor, ofs, len, status_old)) @@ -1328,7 +2004,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) if ((status_new & mask) > (status_old & mask)) return -EINVAL; - return write_sr_and_check(nor, status_new, mask); + return spi_nor_write_sr_and_check(nor, status_new); } /* @@ -1340,15 +2016,21 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) */ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) { - int status; + int ret; - status = read_sr(nor); - if (status < 0) - return status; + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; - return stm_is_locked_sr(nor, ofs, len, status); + return stm_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); } +static const struct spi_nor_locking_ops stm_locking_ops = { + .lock = stm_lock, + .unlock = stm_unlock, + .is_locked = stm_is_locked, +}; + static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); @@ -1358,7 +2040,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->flash_lock(nor, ofs, len); + ret = nor->params.locking_ops->lock(nor, ofs, len); spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); return ret; @@ -1373,7 +2055,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->flash_unlock(nor, ofs, len); + ret = nor->params.locking_ops->unlock(nor, ofs, len); spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); return ret; @@ -1388,205 +2070,65 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->flash_is_locked(nor, ofs, len); + ret = nor->params.locking_ops->is_locked(nor, ofs, len); spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); return ret; } -/* - * Write status Register and configuration register with 2 bytes - * The first byte will be written to the status register, while the - * second byte will be written to the configuration register. - * Return negative if error occurred. - */ -static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) -{ - int ret; - - write_enable(nor); - - ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); - if (ret < 0) { - dev_err(nor->dev, - "error while writing configuration register\n"); - return -EINVAL; - } - - ret = spi_nor_wait_till_ready(nor); - if (ret) { - dev_err(nor->dev, - "timeout while writing configuration register\n"); - return ret; - } - - return 0; -} - /** - * macronix_quad_enable() - set QE bit in Status Register. + * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status + * Register 1. * @nor: pointer to a 'struct spi_nor' * - * Set the Quad Enable (QE) bit in the Status Register. - * - * bit 6 of the Status Register is the QE bit for Macronix like QSPI memories. + * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -static int macronix_quad_enable(struct spi_nor *nor) +static int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) { - int ret, val; - - val = read_sr(nor); - if (val < 0) - return val; - if (val & SR_QUAD_EN_MX) - return 0; - - write_enable(nor); - - write_sr(nor, val | SR_QUAD_EN_MX); - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - - ret = read_sr(nor); - if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { - dev_err(nor->dev, "Macronix Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -/** - * spansion_quad_enable() - set QE bit in Configuraiton Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function is kept for legacy purpose because it has been used for a - * long time without anybody complaining but it should be considered as - * deprecated and maybe buggy. - * First, this function doesn't care about the previous values of the Status - * and Configuration Registers when it sets the QE bit (bit 1) in the - * Configuration Register: all other bits are cleared, which may have unwanted - * side effects like removing some block protections. - * Secondly, it uses the Read Configuration Register (35h) instruction though - * some very old and few memories don't support this instruction. If a pull-up - * resistor is present on the MISO/IO1 line, we might still be able to pass the - * "read back" test because the QSPI memory doesn't recognize the command, - * so leaves the MISO/IO1 line state unchanged, hence read_cr() returns 0xFF. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_quad_enable(struct spi_nor *nor) -{ - u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN}; int ret; - ret = write_sr_cr(nor, sr_cr); + ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) return ret; - /* read back and check it */ - ret = read_cr(nor); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(nor->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} - -/** - * spansion_no_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories not supporting the Read - * Configuration Register (35h) instruction. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. - * - * Return: 0 on success, -errno otherwise. - */ -static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) -{ - u8 sr_cr[2]; - int ret; + if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) + return 0; - /* Keep the current value of the Status Register. */ - ret = read_sr(nor); - if (ret < 0) { - dev_err(nor->dev, "error while reading status register\n"); - return -EINVAL; - } - sr_cr[0] = ret; - sr_cr[1] = CR_QUAD_EN_SPAN; + nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; - return write_sr_cr(nor, sr_cr); + return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]); } /** - * spansion_read_cr_quad_enable() - set QE bit in Configuration Register. - * @nor: pointer to a 'struct spi_nor' + * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status + * Register 2. + * @nor: pointer to a 'struct spi_nor'. * - * Set the Quad Enable (QE) bit in the Configuration Register. - * This function should be used with QSPI memories supporting the Read - * Configuration Register (35h) instruction. - * - * bit 1 of the Configuration Register is the QE bit for Spansion like QSPI - * memories. + * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -static int spansion_read_cr_quad_enable(struct spi_nor *nor) +static int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) { - struct device *dev = nor->dev; - u8 sr_cr[2]; int ret; - /* Check current Quad Enable bit value. */ - ret = read_cr(nor); - if (ret < 0) { - dev_err(dev, "error while reading configuration register\n"); - return -EINVAL; - } - - if (ret & CR_QUAD_EN_SPAN) - return 0; - - sr_cr[1] = ret | CR_QUAD_EN_SPAN; - - /* Keep the current value of the Status Register. */ - ret = read_sr(nor); - if (ret < 0) { - dev_err(dev, "error while reading status register\n"); - return -EINVAL; - } - sr_cr[0] = ret; + if (nor->flags & SNOR_F_NO_READ_CR) + return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); - ret = write_sr_cr(nor, sr_cr); + ret = spi_nor_read_cr(nor, nor->bouncebuf); if (ret) return ret; - /* Read back and check it. */ - ret = read_cr(nor); - if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { - dev_err(nor->dev, "Spansion Quad bit not set\n"); - return -EINVAL; - } + if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) + return 0; - return 0; + return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]); } /** - * sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2. * @nor: pointer to a 'struct spi_nor' * * Set the Quad Enable (QE) bit in the Status Register 2. @@ -1597,132 +2139,39 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int sr2_bit7_quad_enable(struct spi_nor *nor) +static int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) { - u8 sr2; + u8 *sr2 = nor->bouncebuf; int ret; + u8 sr2_written; /* Check current Quad Enable bit value. */ - ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); + ret = spi_nor_read_sr2(nor, sr2); if (ret) return ret; - if (sr2 & SR2_QUAD_EN_BIT7) + if (*sr2 & SR2_QUAD_EN_BIT7) return 0; /* Update the Quad Enable bit. */ - sr2 |= SR2_QUAD_EN_BIT7; - - write_enable(nor); - - ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1); - if (ret < 0) { - dev_err(nor->dev, "error while writing status register 2\n"); - return -EINVAL; - } - - ret = spi_nor_wait_till_ready(nor); - if (ret < 0) { - dev_err(nor->dev, "timeout while writing status register 2\n"); - return ret; - } - - /* Read back and check it. */ - ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); - if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) { - dev_err(nor->dev, "SR2 Quad bit not set\n"); - return -EINVAL; - } - - return 0; -} + *sr2 |= SR2_QUAD_EN_BIT7; -/** - * spi_nor_clear_sr_bp() - clear the Status Register Block Protection bits. - * @nor: pointer to a 'struct spi_nor' - * - * Read-modify-write function that clears the Block Protection bits from the - * Status Register without affecting other bits. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_clear_sr_bp(struct spi_nor *nor) -{ - int ret; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - - ret = read_sr(nor); - if (ret < 0) { - dev_err(nor->dev, "error while reading status register\n"); + ret = spi_nor_write_sr2(nor, sr2); + if (ret) return ret; - } - - write_enable(nor); - ret = write_sr(nor, ret & ~mask); - if (ret) { - dev_err(nor->dev, "write to status register failed\n"); - return ret; - } + sr2_written = *sr2; - ret = spi_nor_wait_till_ready(nor); + /* Read back and check it. */ + ret = spi_nor_read_sr2(nor, sr2); if (ret) - dev_err(nor->dev, "timeout while writing status register\n"); - return ret; -} - -/** - * spi_nor_spansion_clear_sr_bp() - clear the Status Register Block Protection - * bits on spansion flashes. - * @nor: pointer to a 'struct spi_nor' - * - * Read-modify-write function that clears the Block Protection bits from the - * Status Register without affecting other bits. The function is tightly - * coupled with the spansion_quad_enable() function. Both assume that the Write - * Register with 16 bits, together with the Read Configuration Register (35h) - * instructions are supported. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor) -{ - int ret; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 sr_cr[2] = {0}; - - /* Check current Quad Enable bit value. */ - ret = read_cr(nor); - if (ret < 0) { - dev_err(nor->dev, - "error while reading configuration register\n"); return ret; - } - - /* - * When the configuration register Quad Enable bit is one, only the - * Write Status (01h) command with two data bytes may be used. - */ - if (ret & CR_QUAD_EN_SPAN) { - sr_cr[1] = ret; - - ret = read_sr(nor); - if (ret < 0) { - dev_err(nor->dev, - "error while reading status register\n"); - return ret; - } - sr_cr[0] = ret & ~mask; - ret = write_sr_cr(nor, sr_cr); - if (ret) - dev_err(nor->dev, "16-bit write register failed\n"); - return ret; + if (*sr2 != sr2_written) { + dev_dbg(nor->dev, "SR2: Read back test failed\n"); + return -EIO; } - /* - * If the Quad Enable bit is zero, use the Write Status (01h) command - * with one data byte. - */ - return spi_nor_clear_sr_bp(nor); + return 0; } /* Used when the "_ext_id" is two bytes at most */ @@ -1776,6 +2225,28 @@ static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor) .flags = SPI_NOR_NO_FR | SPI_S3AN, static int +is25lp256_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * IS25LP256 supports 4B opcodes, but the BFPT advertises a + * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. + * Overwrite the address width advertised by the BFPT. + */ + if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == + BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) + nor->addr_width = 4; + + return 0; +} + +static struct spi_nor_fixups is25lp256_fixups = { + .post_bfpt = is25lp256_post_bfpt_fixups, +}; + +static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt, @@ -1800,6 +2271,21 @@ static struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, }; +static void gd25q256_default_init(struct spi_nor *nor) +{ + /* + * Some manufacturer like GigaDevice may use different + * bit to set QE on different memories, so the MFR can't + * indicate the quad_enable method for this case, we need + * to set it in the default_init fixup hook. + */ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; +} + +static struct spi_nor_fixups gd25q256_fixups = { + .default_init = gd25q256_default_init, +}; + /* NOTE: double check command sets and memory organization when you add * more nor chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. @@ -1836,6 +2322,8 @@ static const struct flash_info spi_nor_ids[] = { { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ) }, { "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) }, { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ) }, @@ -1892,7 +2380,7 @@ static const struct flash_info spi_nor_ids[] = { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - .quad_enable = macronix_quad_enable, + .fixups = &gd25q256_fixups, }, /* Intel/Numonyx -- xxxs33b */ @@ -1916,13 +2404,18 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ) }, { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES) }, + SPI_NOR_4B_OPCODES) + .fixups = &is25lp256_fixups }, { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) + .fixups = &is25lp256_fixups }, /* Macronix */ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, @@ -1965,10 +2458,16 @@ static const struct flash_info spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + NO_CHIP_ERASE) }, + { "mt25qu512a (n25q512a)", INFO(0x20bb20, 0, 64 * 1024, 1024, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, /* Micron */ @@ -1977,6 +2476,9 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, + { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048, + SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | + SPI_NOR_4B_OPCODES) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -1996,7 +2498,7 @@ static const struct flash_info spi_nor_ids[] = { { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | USE_CLSR) }, + SPI_NOR_HAS_LOCK | USE_CLSR) }, { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, @@ -2034,6 +2536,8 @@ static const struct flash_info spi_nor_ids[] = { { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K | + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, /* ST Microelectronics -- newer production may have feature updates */ @@ -2085,6 +2589,11 @@ static const struct flash_info spi_nor_ids[] = { SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { + "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) }, { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) }, { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) }, @@ -2120,6 +2629,10 @@ static const struct flash_info spi_nor_ids[] = { { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) }, @@ -2146,11 +2659,22 @@ static const struct flash_info spi_nor_ids[] = { static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) { int tmp; - u8 id[SPI_NOR_MAX_ID_LEN]; + u8 *id = nor->bouncebuf; const struct flash_info *info; - tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); - if (tmp < 0) { + if (nor->spimem) { + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1)); + + tmp = spi_mem_exec_op(nor->spimem, &op); + } else { + tmp = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, + SPI_NOR_MAX_ID_LEN); + } + if (tmp) { dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); return ERR_PTR(tmp); } @@ -2171,7 +2695,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; + ssize_t ret; dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); @@ -2182,10 +2706,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, while (len) { loff_t addr = from; - if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) - addr = spi_nor_s3an_addr_convert(nor, addr); + addr = spi_nor_convert_addr(nor, addr); - ret = nor->read(nor, addr, len, buf); + ret = spi_nor_read_data(nor, addr, len, buf); if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO; @@ -2211,7 +2734,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct spi_nor *nor = mtd_to_spi_nor(mtd); - size_t actual; + size_t actual = 0; int ret; dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); @@ -2220,67 +2743,74 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, if (ret) return ret; - write_enable(nor); + ret = spi_nor_write_enable(nor); + if (ret) + goto out; nor->sst_write_second = false; - actual = to % 2; /* Start write from odd address. */ - if (actual) { + if (to % 2) { nor->program_opcode = SPINOR_OP_BP; /* write one byte. */ - ret = nor->write(nor, to, 1, buf); + ret = spi_nor_write_data(nor, to, 1, buf); if (ret < 0) - goto sst_write_err; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", - (int)ret); + goto out; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); ret = spi_nor_wait_till_ready(nor); if (ret) - goto sst_write_err; + goto out; + + to++; + actual++; } - to += actual; /* Write out most of the data here. */ for (; actual < len - 1; actual += 2) { nor->program_opcode = SPINOR_OP_AAI_WP; /* write two bytes. */ - ret = nor->write(nor, to, 2, buf + actual); + ret = spi_nor_write_data(nor, to, 2, buf + actual); if (ret < 0) - goto sst_write_err; - WARN(ret != 2, "While writing 2 bytes written %i bytes\n", - (int)ret); + goto out; + WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); ret = spi_nor_wait_till_ready(nor); if (ret) - goto sst_write_err; + goto out; to += 2; nor->sst_write_second = true; } nor->sst_write_second = false; - write_disable(nor); + ret = spi_nor_write_disable(nor); + if (ret) + goto out; + ret = spi_nor_wait_till_ready(nor); if (ret) - goto sst_write_err; + goto out; /* Write out trailing byte if it exists. */ if (actual != len) { - write_enable(nor); + ret = spi_nor_write_enable(nor); + if (ret) + goto out; nor->program_opcode = SPINOR_OP_BP; - ret = nor->write(nor, to, 1, buf + actual); + ret = spi_nor_write_data(nor, to, 1, buf + actual); if (ret < 0) - goto sst_write_err; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", - (int)ret); + goto out; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); ret = spi_nor_wait_till_ready(nor); if (ret) - goto sst_write_err; - write_disable(nor); + goto out; + actual += 1; + + ret = spi_nor_write_disable(nor); } -sst_write_err: +out: *retlen += actual; spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); return ret; @@ -2327,11 +2857,13 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, page_remain = min_t(size_t, nor->page_size - page_offset, len - i); - if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) - addr = spi_nor_s3an_addr_convert(nor, addr); + addr = spi_nor_convert_addr(nor, addr); + + ret = spi_nor_write_enable(nor); + if (ret) + goto write_err; - write_enable(nor); - ret = nor->write(nor, addr, page_remain, buf + i); + ret = spi_nor_write_data(nor, addr, page_remain, buf + i); if (ret < 0) goto write_err; written = ret; @@ -2350,25 +2882,33 @@ write_err: static int spi_nor_check(struct spi_nor *nor) { - if (!nor->dev || !nor->read || !nor->write || - !nor->read_reg || !nor->write_reg) { + if (!nor->dev || + (!nor->spimem && !nor->controller_ops) || + (!nor->spimem && nor->controller_ops && + (!nor->controller_ops->read || + !nor->controller_ops->write || + !nor->controller_ops->read_reg || + !nor->controller_ops->write_reg))) { pr_err("spi-nor: please fill all the necessary fields!\n"); return -EINVAL; } + if (nor->spimem && nor->controller_ops) { + dev_err(nor->dev, "nor->spimem and nor->controller_ops are mutually exclusive, please set just one of them.\n"); + return -EINVAL; + } + return 0; } -static int s3an_nor_scan(struct spi_nor *nor) +static int s3an_nor_setup(struct spi_nor *nor, + const struct spi_nor_hwcaps *hwcaps) { int ret; - u8 val; - ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); - if (ret < 0) { - dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + ret = spi_nor_xread_sr(nor, nor->bouncebuf); + if (ret) return ret; - } nor->erase_opcode = SPINOR_OP_XSE; nor->program_opcode = SPINOR_OP_XPP; @@ -2386,7 +2926,7 @@ static int s3an_nor_scan(struct spi_nor *nor) * The current addressing mode can be read from the XRDSR register * and should not be changed, because is a destructive operation. */ - if (val & XSR_PAGESIZE) { + if (nor->bouncebuf[0] & XSR_PAGESIZE) { /* Flash in Power of 2 mode */ nor->page_size = (nor->page_size == 264) ? 256 : 512; nor->mtd.writebufsize = nor->page_size; @@ -2394,7 +2934,8 @@ static int s3an_nor_scan(struct spi_nor *nor) nor->mtd.erasesize = 8 * nor->page_size; } else { /* Flash in Default addressing mode */ - nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT; + nor->params.convert_addr = s3an_convert_addr; + nor->mtd.erasesize = nor->info->sector_size; } return 0; @@ -2491,14 +3032,14 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) */ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) { - int ret; + ssize_t ret; while (len) { - ret = nor->read(nor, addr, len, buf); - if (!ret || ret > len) - return -EIO; + ret = spi_nor_read_data(nor, addr, len, buf); if (ret < 0) return ret; + if (!ret || ret > len) + return -EIO; buf += ret; addr += ret; @@ -2544,6 +3085,126 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, } /** + * spi_nor_spimem_check_op - check if the operation is supported + * by controller + *@nor: pointer to a 'struct spi_nor' + *@op: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_spimem_check_op(struct spi_nor *nor, + struct spi_mem_op *op) +{ + /* + * First test with 4 address bytes. The opcode itself might + * be a 3B addressing opcode but we don't care, because + * SPI controller implementation should not check the opcode, + * but just the sequence. + */ + op->addr.nbytes = 4; + if (!spi_mem_supports_op(nor->spimem, op)) { + if (nor->mtd.size > SZ_16M) + return -ENOTSUPP; + + /* If flash size <= 16MB, 3 address bytes are sufficient */ + op->addr.nbytes = 3; + if (!spi_mem_supports_op(nor->spimem, op)) + return -ENOTSUPP; + } + + return 0; +} + +/** + * spi_nor_spimem_check_readop - check if the read op is supported + * by controller + *@nor: pointer to a 'struct spi_nor' + *@read: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_spimem_check_readop(struct spi_nor *nor, + const struct spi_nor_read_command *read) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_DUMMY(0, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto); + op.dummy.buswidth = op.addr.buswidth; + op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * + op.dummy.buswidth / 8; + + return spi_nor_spimem_check_op(nor, &op); +} + +/** + * spi_nor_spimem_check_pp - check if the page program op is supported + * by controller + *@nor: pointer to a 'struct spi_nor' + *@pp: pointer to op template to be checked + * + * Returns 0 if operation is supported, -ENOTSUPP otherwise. + */ +static int spi_nor_spimem_check_pp(struct spi_nor *nor, + const struct spi_nor_pp_command *pp) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1), + SPI_MEM_OP_ADDR(3, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + + op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto); + op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto); + op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto); + + return spi_nor_spimem_check_op(nor, &op); +} + +/** + * spi_nor_spimem_adjust_hwcaps - Find optimal Read/Write protocol + * based on SPI controller capabilities + * @nor: pointer to a 'struct spi_nor' + * @hwcaps: pointer to resulting capabilities after adjusting + * according to controller and flash's capability + */ +static void +spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) +{ + struct spi_nor_flash_parameter *params = &nor->params; + unsigned int cap; + + /* DTR modes are not supported yet, mask them all. */ + *hwcaps &= ~SNOR_HWCAPS_DTR; + + /* X-X-X modes are not supported yet, mask them all. */ + *hwcaps &= ~SNOR_HWCAPS_X_X_X; + + for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { + int rdidx, ppidx; + + if (!(*hwcaps & BIT(cap))) + continue; + + rdidx = spi_nor_hwcaps_read2cmd(BIT(cap)); + if (rdidx >= 0 && + spi_nor_spimem_check_readop(nor, ¶ms->reads[rdidx])) + *hwcaps &= ~BIT(cap); + + ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap)); + if (ppidx < 0) + continue; + + if (spi_nor_spimem_check_pp(nor, + ¶ms->page_programs[ppidx])) + *hwcaps &= ~BIT(cap); + } +} + +/** * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' * @addr: offset in the SFDP area to start reading data from @@ -2861,7 +3522,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, struct spi_nor_flash_parameter *params) { - struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_map *map = ¶ms->erase_map; struct spi_nor_erase_type *erase_type = map->erase_type; struct sfdp_bfpt bfpt; size_t len; @@ -2942,7 +3603,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, * Erase Types defined in the bfpt table. */ erase_mask = 0; - memset(&nor->erase_map, 0, sizeof(nor->erase_map)); + memset(¶ms->erase_map, 0, sizeof(params->erase_map)); for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; u32 erasesize; @@ -2995,20 +3656,39 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, break; case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + /* + * Writing only one byte to the Status Register has the + * side-effect of clearing Status Register 2. + */ case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: - params->quad_enable = spansion_no_read_cr_quad_enable; + /* + * Read Configuration Register (35h) instruction is not + * supported. + */ + nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; + params->quad_enable = spi_nor_sr2_bit1_quad_enable; break; case BFPT_DWORD15_QER_SR1_BIT6: - params->quad_enable = macronix_quad_enable; + nor->flags &= ~SNOR_F_HAS_16BIT_SR; + params->quad_enable = spi_nor_sr1_bit6_quad_enable; break; case BFPT_DWORD15_QER_SR2_BIT7: - params->quad_enable = sr2_bit7_quad_enable; + nor->flags &= ~SNOR_F_HAS_16BIT_SR; + params->quad_enable = spi_nor_sr2_bit7_quad_enable; break; case BFPT_DWORD15_QER_SR2_BIT1: - params->quad_enable = spansion_read_cr_quad_enable; + /* + * JESD216 rev B or later does not specify if writing only one + * byte to the Status Register clears or not the Status + * Register 2, so let's be cautious and keep the default + * assumption of a 16-bit Write Status (01h) command. + */ + nor->flags |= SNOR_F_HAS_16BIT_SR; + + params->quad_enable = spi_nor_sr2_bit1_quad_enable; break; default: @@ -3217,14 +3897,18 @@ spi_nor_region_check_overlay(struct spi_nor_erase_region *region, /** * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map * @nor: pointer to a 'struct spi_nor' + * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' that is + * used for storing SFDP parsed data * @smpt: pointer to the sector map parameter table * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, - const u32 *smpt) +static int +spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, + struct spi_nor_flash_parameter *params, + const u32 *smpt) { - struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_map *map = ¶ms->erase_map; struct spi_nor_erase_type *erase = map->erase_type; struct spi_nor_erase_region *region; u64 offset; @@ -3303,6 +3987,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, * spi_nor_parse_smpt() - parse Sector Map Parameter Table * @nor: pointer to a 'struct spi_nor' * @smpt_header: sector map parameter table header + * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' + * that is used for storing SFDP parsed data * * This table is optional, but when available, we parse it to identify the * location and size of sectors within the main data array of the flash memory @@ -3311,7 +3997,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, * Return: 0 on success, -errno otherwise. */ static int spi_nor_parse_smpt(struct spi_nor *nor, - const struct sfdp_parameter_header *smpt_header) + const struct sfdp_parameter_header *smpt_header, + struct spi_nor_flash_parameter *params) { const u32 *sector_map; u32 *smpt; @@ -3340,11 +4027,11 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, goto out; } - ret = spi_nor_init_non_uniform_erase_map(nor, sector_map); + ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map); if (ret) goto out; - spi_nor_regions_sort_erase_types(&nor->erase_map); + spi_nor_regions_sort_erase_types(¶ms->erase_map); /* fall through */ out: kfree(smpt); @@ -3400,7 +4087,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, { 0u /* not used */, BIT(12) }, }; struct spi_nor_pp_command *params_pp = params->page_programs; - struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_map *map = ¶ms->erase_map; struct spi_nor_erase_type *erase_type = map->erase_type; u32 *dwords; size_t len; @@ -3422,7 +4109,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, addr = SFDP_PARAM_HEADER_PTP(param_header); ret = spi_nor_read_sfdp(nor, addr, len, dwords); if (ret) - return ret; + goto out; /* Fix endianness of the 4BAIT DWORDs. */ for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++) @@ -3600,7 +4287,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_read_sfdp(nor, sizeof(header), psize, param_headers); if (err < 0) { - dev_err(dev, "failed to read SFDP parameter headers\n"); + dev_dbg(dev, "failed to read SFDP parameter headers\n"); goto exit; } } @@ -3630,7 +4317,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, switch (SFDP_PARAM_HEADER_ID(param_header)) { case SFDP_SECTOR_MAP_ID: - err = spi_nor_parse_smpt(nor, param_header); + err = spi_nor_parse_smpt(nor, param_header, params); break; case SFDP_4BAIT_ID: @@ -3659,135 +4346,7 @@ exit: return err; } -static int spi_nor_init_params(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - struct spi_nor_erase_map *map = &nor->erase_map; - const struct flash_info *info = nor->info; - u8 i, erase_mask; - - /* Set legacy flash parameters as default. */ - memset(params, 0, sizeof(*params)); - - /* Set SPI NOR sizes. */ - params->size = (u64)info->sector_size * info->n_sectors; - params->page_size = info->page_size; - - /* (Fast) Read settings. */ - params->hwcaps.mask |= SNOR_HWCAPS_READ; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], - 0, 0, SPINOR_OP_READ, - SNOR_PROTO_1_1_1); - - if (!(info->flags & SPI_NOR_NO_FR)) { - params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST], - 0, 8, SPINOR_OP_READ_FAST, - SNOR_PROTO_1_1_1); - } - - if (info->flags & SPI_NOR_DUAL_READ) { - params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2], - 0, 8, SPINOR_OP_READ_1_1_2, - SNOR_PROTO_1_1_2); - } - - if (info->flags & SPI_NOR_QUAD_READ) { - params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], - 0, 8, SPINOR_OP_READ_1_1_4, - SNOR_PROTO_1_1_4); - } - - if (info->flags & SPI_NOR_OCTAL_READ) { - params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_8], - 0, 8, SPINOR_OP_READ_1_1_8, - SNOR_PROTO_1_1_8); - } - - /* Page Program settings. */ - params->hwcaps.mask |= SNOR_HWCAPS_PP; - spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], - SPINOR_OP_PP, SNOR_PROTO_1_1_1); - - /* - * Sector Erase settings. Sort Erase Types in ascending order, with the - * smallest erase size starting at BIT(0). - */ - erase_mask = 0; - i = 0; - if (info->flags & SECT_4K_PMC) { - erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], 4096u, - SPINOR_OP_BE_4K_PMC); - i++; - } else if (info->flags & SECT_4K) { - erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], 4096u, - SPINOR_OP_BE_4K); - i++; - } - erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], info->sector_size, - SPINOR_OP_SE); - spi_nor_init_uniform_erase_map(map, erase_mask, params->size); - - /* Select the procedure to set the Quad Enable bit. */ - if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD | - SNOR_HWCAPS_PP_QUAD)) { - switch (JEDEC_MFR(info)) { - case SNOR_MFR_MACRONIX: - params->quad_enable = macronix_quad_enable; - break; - - case SNOR_MFR_ST: - case SNOR_MFR_MICRON: - break; - - default: - /* Kept only for backward compatibility purpose. */ - params->quad_enable = spansion_quad_enable; - if (nor->clear_sr_bp) - nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp; - break; - } - - /* - * Some manufacturer like GigaDevice may use different - * bit to set QE on different memories, so the MFR can't - * indicate the quad_enable method for this case, we need - * set it in flash info list. - */ - if (info->quad_enable) - params->quad_enable = info->quad_enable; - } - - if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && - !(info->flags & SPI_NOR_SKIP_SFDP)) { - struct spi_nor_flash_parameter sfdp_params; - struct spi_nor_erase_map prev_map; - - memcpy(&sfdp_params, params, sizeof(sfdp_params)); - memcpy(&prev_map, &nor->erase_map, sizeof(prev_map)); - - if (spi_nor_parse_sfdp(nor, &sfdp_params)) { - nor->addr_width = 0; - nor->flags &= ~SNOR_F_4B_OPCODES; - /* restore previous erase map */ - memcpy(&nor->erase_map, &prev_map, - sizeof(nor->erase_map)); - } else { - memcpy(params, &sfdp_params, sizeof(*params)); - } - } - - return 0; -} - static int spi_nor_select_read(struct spi_nor *nor, - const struct spi_nor_flash_parameter *params, u32 shared_hwcaps) { int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1; @@ -3800,7 +4359,7 @@ static int spi_nor_select_read(struct spi_nor *nor, if (cmd < 0) return -EINVAL; - read = ¶ms->reads[cmd]; + read = &nor->params.reads[cmd]; nor->read_opcode = read->opcode; nor->read_proto = read->proto; @@ -3819,7 +4378,6 @@ static int spi_nor_select_read(struct spi_nor *nor, } static int spi_nor_select_pp(struct spi_nor *nor, - const struct spi_nor_flash_parameter *params, u32 shared_hwcaps) { int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1; @@ -3832,7 +4390,7 @@ static int spi_nor_select_pp(struct spi_nor *nor, if (cmd < 0) return -EINVAL; - pp = ¶ms->page_programs[cmd]; + pp = &nor->params.page_programs[cmd]; nor->program_opcode = pp->opcode; nor->write_proto = pp->proto; return 0; @@ -3891,11 +4449,12 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map, return erase; } -static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) +static int spi_nor_select_erase(struct spi_nor *nor) { - struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_map *map = &nor->params.erase_map; const struct spi_nor_erase_type *erase = NULL; struct mtd_info *mtd = &nor->mtd; + u32 wanted_size = nor->info->sector_size; int i; /* @@ -3938,12 +4497,11 @@ static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size) return 0; } -static int spi_nor_setup(struct spi_nor *nor, - const struct spi_nor_flash_parameter *params, - const struct spi_nor_hwcaps *hwcaps) +static int spi_nor_default_setup(struct spi_nor *nor, + const struct spi_nor_hwcaps *hwcaps) { + struct spi_nor_flash_parameter *params = &nor->params; u32 ignored_mask, shared_mask; - bool enable_quad_io; int err; /* @@ -3952,49 +4510,408 @@ static int spi_nor_setup(struct spi_nor *nor, */ shared_mask = hwcaps->mask & params->hwcaps.mask; - /* SPI n-n-n protocols are not supported yet. */ - ignored_mask = (SNOR_HWCAPS_READ_2_2_2 | - SNOR_HWCAPS_READ_4_4_4 | - SNOR_HWCAPS_READ_8_8_8 | - SNOR_HWCAPS_PP_4_4_4 | - SNOR_HWCAPS_PP_8_8_8); - if (shared_mask & ignored_mask) { - dev_dbg(nor->dev, - "SPI n-n-n protocols are not supported yet.\n"); - shared_mask &= ~ignored_mask; + if (nor->spimem) { + /* + * When called from spi_nor_probe(), all caps are set and we + * need to discard some of them based on what the SPI + * controller actually supports (using spi_mem_supports_op()). + */ + spi_nor_spimem_adjust_hwcaps(nor, &shared_mask); + } else { + /* + * SPI n-n-n protocols are not supported when the SPI + * controller directly implements the spi_nor interface. + * Yet another reason to switch to spi-mem. + */ + ignored_mask = SNOR_HWCAPS_X_X_X; + if (shared_mask & ignored_mask) { + dev_dbg(nor->dev, + "SPI n-n-n protocols are not supported.\n"); + shared_mask &= ~ignored_mask; + } } /* Select the (Fast) Read command. */ - err = spi_nor_select_read(nor, params, shared_mask); + err = spi_nor_select_read(nor, shared_mask); if (err) { - dev_err(nor->dev, + dev_dbg(nor->dev, "can't select read settings supported by both the SPI controller and memory.\n"); return err; } /* Select the Page Program command. */ - err = spi_nor_select_pp(nor, params, shared_mask); + err = spi_nor_select_pp(nor, shared_mask); if (err) { - dev_err(nor->dev, + dev_dbg(nor->dev, "can't select write settings supported by both the SPI controller and memory.\n"); return err; } /* Select the Sector Erase command. */ - err = spi_nor_select_erase(nor, nor->info->sector_size); + err = spi_nor_select_erase(nor); if (err) { - dev_err(nor->dev, + dev_dbg(nor->dev, "can't select erase settings supported by both the SPI controller and memory.\n"); return err; } - /* Enable Quad I/O if needed. */ - enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 || - spi_nor_get_protocol_width(nor->write_proto) == 4); - if (enable_quad_io && params->quad_enable) - nor->quad_enable = params->quad_enable; - else - nor->quad_enable = NULL; + return 0; +} + +static int spi_nor_setup(struct spi_nor *nor, + const struct spi_nor_hwcaps *hwcaps) +{ + if (!nor->params.setup) + return 0; + + return nor->params.setup(nor, hwcaps); +} + +static void atmel_set_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static void intel_set_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static void issi_set_default_init(struct spi_nor *nor) +{ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; +} + +static void macronix_set_default_init(struct spi_nor *nor) +{ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params.set_4byte = macronix_set_4byte; +} + +static void sst_set_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static void st_micron_set_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; + nor->params.quad_enable = NULL; + nor->params.set_4byte = st_micron_set_4byte; +} + +static void winbond_set_default_init(struct spi_nor *nor) +{ + nor->params.set_4byte = winbond_set_4byte; +} + +/** + * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and + * settings based on MFR register and ->default_init() hook. + * @nor: pointer to a 'struct spi-nor'. + */ +static void spi_nor_manufacturer_init_params(struct spi_nor *nor) +{ + /* Init flash parameters based on MFR */ + switch (JEDEC_MFR(nor->info)) { + case SNOR_MFR_ATMEL: + atmel_set_default_init(nor); + break; + + case SNOR_MFR_INTEL: + intel_set_default_init(nor); + break; + + case SNOR_MFR_ISSI: + issi_set_default_init(nor); + break; + + case SNOR_MFR_MACRONIX: + macronix_set_default_init(nor); + break; + + case SNOR_MFR_ST: + case SNOR_MFR_MICRON: + st_micron_set_default_init(nor); + break; + + case SNOR_MFR_SST: + sst_set_default_init(nor); + break; + + case SNOR_MFR_WINBOND: + winbond_set_default_init(nor); + break; + + default: + break; + } + + if (nor->info->fixups && nor->info->fixups->default_init) + nor->info->fixups->default_init(nor); +} + +/** + * spi_nor_sfdp_init_params() - Initialize the flash's parameters and settings + * based on JESD216 SFDP standard. + * @nor: pointer to a 'struct spi-nor'. + * + * The method has a roll-back mechanism: in case the SFDP parsing fails, the + * legacy flash parameters and settings will be restored. + */ +static void spi_nor_sfdp_init_params(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter sfdp_params; + + memcpy(&sfdp_params, &nor->params, sizeof(sfdp_params)); + + if (spi_nor_parse_sfdp(nor, &sfdp_params)) { + nor->addr_width = 0; + nor->flags &= ~SNOR_F_4B_OPCODES; + } else { + memcpy(&nor->params, &sfdp_params, sizeof(nor->params)); + } +} + +/** + * spi_nor_info_init_params() - Initialize the flash's parameters and settings + * based on nor->info data. + * @nor: pointer to a 'struct spi-nor'. + */ +static void spi_nor_info_init_params(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = &nor->params; + struct spi_nor_erase_map *map = ¶ms->erase_map; + const struct flash_info *info = nor->info; + struct device_node *np = spi_nor_get_flash_node(nor); + u8 i, erase_mask; + + /* Initialize legacy flash parameters and settings. */ + params->quad_enable = spi_nor_sr2_bit1_quad_enable; + params->set_4byte = spansion_set_4byte; + params->setup = spi_nor_default_setup; + /* Default to 16-bit Write Status (01h) Command */ + nor->flags |= SNOR_F_HAS_16BIT_SR; + + /* Set SPI NOR sizes. */ + params->size = (u64)info->sector_size * info->n_sectors; + params->page_size = info->page_size; + + if (!(info->flags & SPI_NOR_NO_FR)) { + /* Default to Fast Read for DT and non-DT platform devices. */ + params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST; + + /* Mask out Fast Read if not requested at DT instantiation. */ + if (np && !of_property_read_bool(np, "m25p,fast-read")) + params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; + } + + /* (Fast) Read settings. */ + params->hwcaps.mask |= SNOR_HWCAPS_READ; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], + 0, 0, SPINOR_OP_READ, + SNOR_PROTO_1_1_1); + + if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST) + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST], + 0, 8, SPINOR_OP_READ_FAST, + SNOR_PROTO_1_1_1); + + if (info->flags & SPI_NOR_DUAL_READ) { + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2], + 0, 8, SPINOR_OP_READ_1_1_2, + SNOR_PROTO_1_1_2); + } + + if (info->flags & SPI_NOR_QUAD_READ) { + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], + 0, 8, SPINOR_OP_READ_1_1_4, + SNOR_PROTO_1_1_4); + } + + if (info->flags & SPI_NOR_OCTAL_READ) { + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_8], + 0, 8, SPINOR_OP_READ_1_1_8, + SNOR_PROTO_1_1_8); + } + + /* Page Program settings. */ + params->hwcaps.mask |= SNOR_HWCAPS_PP; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], + SPINOR_OP_PP, SNOR_PROTO_1_1_1); + + /* + * Sector Erase settings. Sort Erase Types in ascending order, with the + * smallest erase size starting at BIT(0). + */ + erase_mask = 0; + i = 0; + if (info->flags & SECT_4K_PMC) { + erase_mask |= BIT(i); + spi_nor_set_erase_type(&map->erase_type[i], 4096u, + SPINOR_OP_BE_4K_PMC); + i++; + } else if (info->flags & SECT_4K) { + erase_mask |= BIT(i); + spi_nor_set_erase_type(&map->erase_type[i], 4096u, + SPINOR_OP_BE_4K); + i++; + } + erase_mask |= BIT(i); + spi_nor_set_erase_type(&map->erase_type[i], info->sector_size, + SPINOR_OP_SE); + spi_nor_init_uniform_erase_map(map, erase_mask, params->size); +} + +static void spansion_post_sfdp_fixups(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + + if (mtd->size <= SZ_16M) + return; + + nor->flags |= SNOR_F_4B_OPCODES; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = SPINOR_OP_SE; + nor->mtd.erasesize = nor->info->sector_size; +} + +static void s3an_post_sfdp_fixups(struct spi_nor *nor) +{ + nor->params.setup = s3an_nor_setup; +} + +/** + * spi_nor_post_sfdp_fixups() - Updates the flash's parameters and settings + * after SFDP has been parsed (is also called for SPI NORs that do not + * support RDSFDP). + * @nor: pointer to a 'struct spi_nor' + * + * Typically used to tweak various parameters that could not be extracted by + * other means (i.e. when information provided by the SFDP/flash_info tables + * are incomplete or wrong). + */ +static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) +{ + switch (JEDEC_MFR(nor->info)) { + case SNOR_MFR_SPANSION: + spansion_post_sfdp_fixups(nor); + break; + + default: + break; + } + + if (nor->info->flags & SPI_S3AN) + s3an_post_sfdp_fixups(nor); + + if (nor->info->fixups && nor->info->fixups->post_sfdp) + nor->info->fixups->post_sfdp(nor); +} + +/** + * spi_nor_late_init_params() - Late initialization of default flash parameters. + * @nor: pointer to a 'struct spi_nor' + * + * Used to set default flash parameters and settings when the ->default_init() + * hook or the SFDP parser let voids. + */ +static void spi_nor_late_init_params(struct spi_nor *nor) +{ + /* + * NOR protection support. When locking_ops are not provided, we pick + * the default ones. + */ + if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops) + nor->params.locking_ops = &stm_locking_ops; +} + +/** + * spi_nor_init_params() - Initialize the flash's parameters and settings. + * @nor: pointer to a 'struct spi-nor'. + * + * The flash parameters and settings are initialized based on a sequence of + * calls that are ordered by priority: + * + * 1/ Default flash parameters initialization. The initializations are done + * based on nor->info data: + * spi_nor_info_init_params() + * + * which can be overwritten by: + * 2/ Manufacturer flash parameters initialization. The initializations are + * done based on MFR register, or when the decisions can not be done solely + * based on MFR, by using specific flash_info tweeks, ->default_init(): + * spi_nor_manufacturer_init_params() + * + * which can be overwritten by: + * 3/ SFDP flash parameters initialization. JESD216 SFDP is a standard and + * should be more accurate that the above. + * spi_nor_sfdp_init_params() + * + * Please note that there is a ->post_bfpt() fixup hook that can overwrite + * the flash parameters and settings immediately after parsing the Basic + * Flash Parameter Table. + * + * which can be overwritten by: + * 4/ Post SFDP flash parameters initialization. Used to tweak various + * parameters that could not be extracted by other means (i.e. when + * information provided by the SFDP/flash_info tables are incomplete or + * wrong). + * spi_nor_post_sfdp_fixups() + * + * 5/ Late default flash parameters initialization, used when the + * ->default_init() hook or the SFDP parser do not set specific params. + * spi_nor_late_init_params() + */ +static void spi_nor_init_params(struct spi_nor *nor) +{ + spi_nor_info_init_params(nor); + + spi_nor_manufacturer_init_params(nor); + + if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && + !(nor->info->flags & SPI_NOR_SKIP_SFDP)) + spi_nor_sfdp_init_params(nor); + + spi_nor_post_sfdp_fixups(nor); + + spi_nor_late_init_params(nor); +} + +/** + * spi_nor_quad_enable() - enable Quad I/O if needed. + * @nor: pointer to a 'struct spi_nor' + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_quad_enable(struct spi_nor *nor) +{ + if (!nor->params.quad_enable) + return 0; + + if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4)) + return 0; + + return nor->params.quad_enable(nor); +} + +/** + * spi_nor_unlock_all() - Unlocks the entire flash memory array. + * @nor: pointer to a 'struct spi_nor'. + * + * Some SPI NOR flashes are write protected by default after a power-on reset + * cycle, in order to avoid inadvertent writes during power-up. Backward + * compatibility imposes to unlock the entire flash memory array at power-up + * by default. + */ +static int spi_nor_unlock_all(struct spi_nor *nor) +{ + if (nor->flags & SNOR_F_HAS_LOCK) + return spi_nor_unlock(&nor->mtd, 0, nor->params.size); return 0; } @@ -4003,21 +4920,16 @@ static int spi_nor_init(struct spi_nor *nor) { int err; - if (nor->clear_sr_bp) { - err = nor->clear_sr_bp(nor); - if (err) { - dev_err(nor->dev, - "fail to clear block protection bits\n"); - return err; - } + err = spi_nor_quad_enable(nor); + if (err) { + dev_dbg(nor->dev, "quad mode not supported\n"); + return err; } - if (nor->quad_enable) { - err = nor->quad_enable(nor); - if (err) { - dev_err(nor->dev, "quad mode not supported\n"); - return err; - } + err = spi_nor_unlock_all(nor); + if (err) { + dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); + return err; } if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { @@ -4030,7 +4942,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - set_4byte(nor, true); + nor->params.set_4byte(nor, true); } return 0; @@ -4054,7 +4966,7 @@ void spi_nor_restore(struct spi_nor *nor) /* restore the addressing mode */ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) - set_4byte(nor, false); + nor->params.set_4byte(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); @@ -4070,25 +4982,47 @@ static const struct flash_info *spi_nor_match_id(const char *name) return NULL; } -int spi_nor_scan(struct spi_nor *nor, const char *name, - const struct spi_nor_hwcaps *hwcaps) +static int spi_nor_set_addr_width(struct spi_nor *nor) +{ + if (nor->addr_width) { + /* already configured from SFDP */ + } else if (nor->info->addr_width) { + nor->addr_width = nor->info->addr_width; + } else if (nor->mtd.size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + } else { + nor->addr_width = 3; + } + + if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { + dev_dbg(nor->dev, "address width is too large: %u\n", + nor->addr_width); + return -EINVAL; + } + + /* Set 4byte opcodes when possible. */ + if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + !(nor->flags & SNOR_F_HAS_4BAIT)) + spi_nor_set_4byte_opcodes(nor); + + return 0; +} + +static void spi_nor_debugfs_init(struct spi_nor *nor, + const struct flash_info *info) { - struct spi_nor_flash_parameter params; - const struct flash_info *info = NULL; - struct device *dev = nor->dev; struct mtd_info *mtd = &nor->mtd; - struct device_node *np = spi_nor_get_flash_node(nor); - int ret; - int i; - ret = spi_nor_check(nor); - if (ret) - return ret; + mtd->dbg.partname = info->name; + mtd->dbg.partid = devm_kasprintf(nor->dev, GFP_KERNEL, "spi-nor:%*phN", + info->id_len, info->id); +} - /* Reset SPI protocol for all commands. */ - nor->reg_proto = SNOR_PROTO_1_1_1; - nor->read_proto = SNOR_PROTO_1_1_1; - nor->write_proto = SNOR_PROTO_1_1_1; +static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, + const char *name) +{ + const struct flash_info *info = NULL; if (name) info = spi_nor_match_id(name); @@ -4096,7 +5030,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (!info) info = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(info)) - return -ENOENT; + return ERR_PTR(-ENOENT); /* * If caller has specified name of flash model that can normally be @@ -4107,7 +5041,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, jinfo = spi_nor_read_id(nor); if (IS_ERR(jinfo)) { - return PTR_ERR(jinfo); + return jinfo; } else if (jinfo != info) { /* * JEDEC knows better, so overwrite platform ID. We @@ -4116,14 +5050,57 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * marked read-only, and we don't want to lose that * information, even if it's not 100% accurate. */ - dev_warn(dev, "found %s, expected %s\n", + dev_warn(nor->dev, "found %s, expected %s\n", jinfo->name, info->name); info = jinfo; } } + return info; +} + +int spi_nor_scan(struct spi_nor *nor, const char *name, + const struct spi_nor_hwcaps *hwcaps) +{ + const struct flash_info *info; + struct device *dev = nor->dev; + struct mtd_info *mtd = &nor->mtd; + struct device_node *np = spi_nor_get_flash_node(nor); + struct spi_nor_flash_parameter *params = &nor->params; + int ret; + int i; + + ret = spi_nor_check(nor); + if (ret) + return ret; + + /* Reset SPI protocol for all commands. */ + nor->reg_proto = SNOR_PROTO_1_1_1; + nor->read_proto = SNOR_PROTO_1_1_1; + nor->write_proto = SNOR_PROTO_1_1_1; + + /* + * We need the bounce buffer early to read/write registers when going + * through the spi-mem layer (buffers have to be DMA-able). + * For spi-mem drivers, we'll reallocate a new buffer if + * nor->page_size turns out to be greater than PAGE_SIZE (which + * shouldn't happen before long since NOR pages are usually less + * than 1KB) after spi_nor_scan() returns. + */ + nor->bouncebuf_size = PAGE_SIZE; + nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size, + GFP_KERNEL); + if (!nor->bouncebuf) + return -ENOMEM; + + info = spi_nor_get_flash_info(nor, name); + if (IS_ERR(info)) + return PTR_ERR(info); + nor->info = info; + spi_nor_debugfs_init(nor, info); + mutex_init(&nor->lock); /* @@ -4131,23 +5108,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * spi_nor_wait_till_ready(). Xilinx S3AN share MFR * with Atmel spi-nor */ - if (info->flags & SPI_S3AN) + if (info->flags & SPI_NOR_XSR_RDY) nor->flags |= SNOR_F_READY_XSR_RDY; - /* - * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up - * with the software protection bits set. - */ - if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || - JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || - JEDEC_MFR(nor->info) == SNOR_MFR_SST || - nor->info->flags & SPI_NOR_HAS_LOCK) - nor->clear_sr_bp = spi_nor_clear_sr_bp; - - /* Parse the Serial Flash Discoverable Parameters table. */ - ret = spi_nor_init_params(nor, ¶ms); - if (ret) - return ret; + if (info->flags & SPI_NOR_HAS_LOCK) + nor->flags |= SNOR_F_HAS_LOCK; + + /* Init flash parameters based on flash_info struct and SFDP */ + spi_nor_init_params(nor); if (!mtd->name) mtd->name = dev_name(dev); @@ -4155,21 +5123,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->type = MTD_NORFLASH; mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; - mtd->size = params.size; + mtd->size = params->size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; mtd->_resume = spi_nor_resume; - /* NOR protection support for STmicro/Micron chips and similar */ - if (JEDEC_MFR(info) == SNOR_MFR_ST || - JEDEC_MFR(info) == SNOR_MFR_MICRON || - info->flags & SPI_NOR_HAS_LOCK) { - nor->flash_lock = stm_lock; - nor->flash_unlock = stm_unlock; - nor->flash_is_locked = stm_is_locked; - } - - if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { + if (nor->params.locking_ops) { mtd->_lock = spi_nor_lock; mtd->_unlock = spi_nor_unlock; mtd->_is_locked = spi_nor_is_locked; @@ -4194,68 +5153,28 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->flags |= MTD_NO_ERASE; mtd->dev.parent = dev; - nor->page_size = params.page_size; + nor->page_size = params->page_size; mtd->writebufsize = nor->page_size; - if (np) { - /* If we were instantiated by DT, use it */ - if (of_property_read_bool(np, "m25p,fast-read")) - params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; - else - params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; - } else { - /* If we weren't instantiated by DT, default to fast-read */ - params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; - } - if (of_property_read_bool(np, "broken-flash-reset")) nor->flags |= SNOR_F_BROKEN_RESET; - /* Some devices cannot do fast-read, no matter what DT tells us */ - if (info->flags & SPI_NOR_NO_FR) - params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; - /* * Configure the SPI memory: * - select op codes for (Fast) Read, Page Program and Sector Erase. * - set the number of dummy cycles (mode cycles + wait states). * - set the SPI protocols for register and memory accesses. - * - set the Quad Enable bit if needed (required by SPI x-y-4 protos). */ - ret = spi_nor_setup(nor, ¶ms, hwcaps); + ret = spi_nor_setup(nor, hwcaps); if (ret) return ret; - if (nor->addr_width) { - /* already configured from SFDP */ - } else if (info->addr_width) { - nor->addr_width = info->addr_width; - } else if (mtd->size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - nor->addr_width = 4; - } else { - nor->addr_width = 3; - } - - if (info->flags & SPI_NOR_4B_OPCODES || - (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M)) + if (info->flags & SPI_NOR_4B_OPCODES) nor->flags |= SNOR_F_4B_OPCODES; - if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && - !(nor->flags & SNOR_F_HAS_4BAIT)) - spi_nor_set_4byte_opcodes(nor); - - if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { - dev_err(dev, "address width is too large: %u\n", - nor->addr_width); - return -EINVAL; - } - - if (info->flags & SPI_S3AN) { - ret = s3an_nor_scan(nor); - if (ret) - return ret; - } + ret = spi_nor_set_addr_width(nor); + if (ret) + return ret; /* Send all the required SPI flash commands to initialize device */ ret = spi_nor_init(nor); @@ -4285,6 +5204,174 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } EXPORT_SYMBOL_GPL(spi_nor_scan); +static int spi_nor_probe(struct spi_mem *spimem) +{ + struct spi_device *spi = spimem->spi; + struct flash_platform_data *data = dev_get_platdata(&spi->dev); + struct spi_nor *nor; + /* + * Enable all caps by default. The core will mask them after + * checking what's really supported using spi_mem_supports_op(). + */ + const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL }; + char *flash_name; + int ret; + + nor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL); + if (!nor) + return -ENOMEM; + + nor->spimem = spimem; + nor->dev = &spi->dev; + spi_nor_set_flash_node(nor, spi->dev.of_node); + + spi_mem_set_drvdata(spimem, nor); + + if (data && data->name) + nor->mtd.name = data->name; + + if (!nor->mtd.name) + nor->mtd.name = spi_mem_get_name(spimem); + + /* + * For some (historical?) reason many platforms provide two different + * names in flash_platform_data: "name" and "type". Quite often name is + * set to "m25p80" and then "type" provides a real chip name. + * If that's the case, respect "type" and ignore a "name". + */ + if (data && data->type) + flash_name = data->type; + else if (!strcmp(spi->modalias, "spi-nor")) + flash_name = NULL; /* auto-detect */ + else + flash_name = spi->modalias; + + ret = spi_nor_scan(nor, flash_name, &hwcaps); + if (ret) + return ret; + + /* + * None of the existing parts have > 512B pages, but let's play safe + * and add this logic so that if anyone ever adds support for such + * a NOR we don't end up with buffer overflows. + */ + if (nor->page_size > PAGE_SIZE) { + nor->bouncebuf_size = nor->page_size; + devm_kfree(nor->dev, nor->bouncebuf); + nor->bouncebuf = devm_kmalloc(nor->dev, + nor->bouncebuf_size, + GFP_KERNEL); + if (!nor->bouncebuf) + return -ENOMEM; + } + + return mtd_device_register(&nor->mtd, data ? data->parts : NULL, + data ? data->nr_parts : 0); +} + +static int spi_nor_remove(struct spi_mem *spimem) +{ + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + spi_nor_restore(nor); + + /* Clean up MTD stuff. */ + return mtd_device_unregister(&nor->mtd); +} + +static void spi_nor_shutdown(struct spi_mem *spimem) +{ + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + spi_nor_restore(nor); +} + +/* + * Do NOT add to this array without reading the following: + * + * Historically, many flash devices are bound to this driver by their name. But + * since most of these flash are compatible to some extent, and their + * differences can often be differentiated by the JEDEC read-ID command, we + * encourage new users to add support to the spi-nor library, and simply bind + * against a generic string here (e.g., "jedec,spi-nor"). + * + * Many flash names are kept here in this list (as well as in spi-nor.c) to + * keep them available as module aliases for existing platforms. + */ +static const struct spi_device_id spi_nor_dev_ids[] = { + /* + * Allow non-DT platform devices to bind to the "spi-nor" modalias, and + * hack around the fact that the SPI core does not provide uevent + * matching for .of_match_table + */ + {"spi-nor"}, + + /* + * Entries not used in DTs that should be safe to drop after replacing + * them with "spi-nor" in platform data. + */ + {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"}, + + /* + * Entries that were used in DTs without "jedec,spi-nor" fallback and + * should be kept for backward compatibility. + */ + {"at25df321a"}, {"at25df641"}, {"at26df081a"}, + {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"}, + {"mx25l25635e"},{"mx66l51235l"}, + {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"}, + {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"}, + {"s25fl064k"}, + {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"}, + {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"}, + {"m25p64"}, {"m25p128"}, + {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"}, + {"w25q80bl"}, {"w25q128"}, {"w25q256"}, + + /* Flashes that can't be detected using JEDEC */ + {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"}, + {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, + {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, + + /* Everspin MRAMs (non-JEDEC) */ + { "mr25h128" }, /* 128 Kib, 40 MHz */ + { "mr25h256" }, /* 256 Kib, 40 MHz */ + { "mr25h10" }, /* 1 Mib, 40 MHz */ + { "mr25h40" }, /* 4 Mib, 40 MHz */ + + { }, +}; +MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids); + +static const struct of_device_id spi_nor_of_table[] = { + /* + * Generic compatibility for SPI NOR that can be identified by the + * JEDEC READ ID opcode (0x9F). Use this, if possible. + */ + { .compatible = "jedec,spi-nor" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, spi_nor_of_table); + +/* + * REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ +static struct spi_mem_driver spi_nor_driver = { + .spidrv = { + .driver = { + .name = "spi-nor", + .of_match_table = spi_nor_of_table, + }, + .id_table = spi_nor_dev_ids, + }, + .probe = spi_nor_probe, + .remove = spi_nor_remove, + .shutdown = spi_nor_shutdown, +}; +module_spi_mem_driver(spi_nor_driver); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>"); MODULE_AUTHOR("Mike Lavender"); diff --git a/drivers/mtd/spi-nor/stm32-quadspi.c b/drivers/mtd/spi-nor/stm32-quadspi.c deleted file mode 100644 index 33534f9e296b..000000000000 --- a/drivers/mtd/spi-nor/stm32-quadspi.c +++ /dev/null @@ -1,707 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for stm32 quadspi controller - * - * Copyright (C) 2017, STMicroelectronics - All Rights Reserved - * Author(s): Ludovic Barre author <ludovic.barre@st.com>. - */ -#include <linux/clk.h> -#include <linux/errno.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/spi-nor.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/reset.h> -#include <linux/sizes.h> - -#define QUADSPI_CR 0x00 -#define CR_EN BIT(0) -#define CR_ABORT BIT(1) -#define CR_DMAEN BIT(2) -#define CR_TCEN BIT(3) -#define CR_SSHIFT BIT(4) -#define CR_DFM BIT(6) -#define CR_FSEL BIT(7) -#define CR_FTHRES_SHIFT 8 -#define CR_FTHRES_MASK GENMASK(12, 8) -#define CR_FTHRES(n) (((n) << CR_FTHRES_SHIFT) & CR_FTHRES_MASK) -#define CR_TEIE BIT(16) -#define CR_TCIE BIT(17) -#define CR_FTIE BIT(18) -#define CR_SMIE BIT(19) -#define CR_TOIE BIT(20) -#define CR_PRESC_SHIFT 24 -#define CR_PRESC_MASK GENMASK(31, 24) -#define CR_PRESC(n) (((n) << CR_PRESC_SHIFT) & CR_PRESC_MASK) - -#define QUADSPI_DCR 0x04 -#define DCR_CSHT_SHIFT 8 -#define DCR_CSHT_MASK GENMASK(10, 8) -#define DCR_CSHT(n) (((n) << DCR_CSHT_SHIFT) & DCR_CSHT_MASK) -#define DCR_FSIZE_SHIFT 16 -#define DCR_FSIZE_MASK GENMASK(20, 16) -#define DCR_FSIZE(n) (((n) << DCR_FSIZE_SHIFT) & DCR_FSIZE_MASK) - -#define QUADSPI_SR 0x08 -#define SR_TEF BIT(0) -#define SR_TCF BIT(1) -#define SR_FTF BIT(2) -#define SR_SMF BIT(3) -#define SR_TOF BIT(4) -#define SR_BUSY BIT(5) -#define SR_FLEVEL_SHIFT 8 -#define SR_FLEVEL_MASK GENMASK(13, 8) - -#define QUADSPI_FCR 0x0c -#define FCR_CTCF BIT(1) - -#define QUADSPI_DLR 0x10 - -#define QUADSPI_CCR 0x14 -#define CCR_INST_SHIFT 0 -#define CCR_INST_MASK GENMASK(7, 0) -#define CCR_INST(n) (((n) << CCR_INST_SHIFT) & CCR_INST_MASK) -#define CCR_IMODE_NONE (0U << 8) -#define CCR_IMODE_1 (1U << 8) -#define CCR_IMODE_2 (2U << 8) -#define CCR_IMODE_4 (3U << 8) -#define CCR_ADMODE_NONE (0U << 10) -#define CCR_ADMODE_1 (1U << 10) -#define CCR_ADMODE_2 (2U << 10) -#define CCR_ADMODE_4 (3U << 10) -#define CCR_ADSIZE_SHIFT 12 -#define CCR_ADSIZE_MASK GENMASK(13, 12) -#define CCR_ADSIZE(n) (((n) << CCR_ADSIZE_SHIFT) & CCR_ADSIZE_MASK) -#define CCR_ABMODE_NONE (0U << 14) -#define CCR_ABMODE_1 (1U << 14) -#define CCR_ABMODE_2 (2U << 14) -#define CCR_ABMODE_4 (3U << 14) -#define CCR_ABSIZE_8 (0U << 16) -#define CCR_ABSIZE_16 (1U << 16) -#define CCR_ABSIZE_24 (2U << 16) -#define CCR_ABSIZE_32 (3U << 16) -#define CCR_DCYC_SHIFT 18 -#define CCR_DCYC_MASK GENMASK(22, 18) -#define CCR_DCYC(n) (((n) << CCR_DCYC_SHIFT) & CCR_DCYC_MASK) -#define CCR_DMODE_NONE (0U << 24) -#define CCR_DMODE_1 (1U << 24) -#define CCR_DMODE_2 (2U << 24) -#define CCR_DMODE_4 (3U << 24) -#define CCR_FMODE_INDW (0U << 26) -#define CCR_FMODE_INDR (1U << 26) -#define CCR_FMODE_APM (2U << 26) -#define CCR_FMODE_MM (3U << 26) - -#define QUADSPI_AR 0x18 -#define QUADSPI_ABR 0x1c -#define QUADSPI_DR 0x20 -#define QUADSPI_PSMKR 0x24 -#define QUADSPI_PSMAR 0x28 -#define QUADSPI_PIR 0x2c -#define QUADSPI_LPTR 0x30 -#define LPTR_DFT_TIMEOUT 0x10 - -#define FSIZE_VAL(size) (__fls(size) - 1) - -#define STM32_MAX_MMAP_SZ SZ_256M -#define STM32_MAX_NORCHIP 2 - -#define STM32_QSPI_FIFO_SZ 32 -#define STM32_QSPI_FIFO_TIMEOUT_US 30000 -#define STM32_QSPI_BUSY_TIMEOUT_US 100000 - -struct stm32_qspi_flash { - struct spi_nor nor; - struct stm32_qspi *qspi; - u32 cs; - u32 fsize; - u32 presc; - u32 read_mode; - bool registered; - u32 prefetch_limit; -}; - -struct stm32_qspi { - struct device *dev; - void __iomem *io_base; - void __iomem *mm_base; - resource_size_t mm_size; - u32 nor_num; - struct clk *clk; - u32 clk_rate; - struct stm32_qspi_flash flash[STM32_MAX_NORCHIP]; - struct completion cmd_completion; - - /* - * to protect device configuration, could be different between - * 2 flash access (bk1, bk2) - */ - struct mutex lock; -}; - -struct stm32_qspi_cmd { - u8 addr_width; - u8 dummy; - bool tx_data; - u8 opcode; - u32 framemode; - u32 qspimode; - u32 addr; - size_t len; - void *buf; -}; - -static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi) -{ - u32 cr; - int err = 0; - - if (readl_relaxed(qspi->io_base + QUADSPI_SR) & SR_TCF) - return 0; - - reinit_completion(&qspi->cmd_completion); - cr = readl_relaxed(qspi->io_base + QUADSPI_CR); - writel_relaxed(cr | CR_TCIE, qspi->io_base + QUADSPI_CR); - - if (!wait_for_completion_interruptible_timeout(&qspi->cmd_completion, - msecs_to_jiffies(1000))) - err = -ETIMEDOUT; - - writel_relaxed(cr, qspi->io_base + QUADSPI_CR); - return err; -} - -static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi) -{ - u32 sr; - - return readl_relaxed_poll_timeout(qspi->io_base + QUADSPI_SR, sr, - !(sr & SR_BUSY), 10, - STM32_QSPI_BUSY_TIMEOUT_US); -} - -static void stm32_qspi_set_framemode(struct spi_nor *nor, - struct stm32_qspi_cmd *cmd, bool read) -{ - u32 dmode = CCR_DMODE_1; - - cmd->framemode = CCR_IMODE_1; - - if (read) { - switch (nor->read_proto) { - default: - case SNOR_PROTO_1_1_1: - dmode = CCR_DMODE_1; - break; - case SNOR_PROTO_1_1_2: - dmode = CCR_DMODE_2; - break; - case SNOR_PROTO_1_1_4: - dmode = CCR_DMODE_4; - break; - } - } - - cmd->framemode |= cmd->tx_data ? dmode : 0; - cmd->framemode |= cmd->addr_width ? CCR_ADMODE_1 : 0; -} - -static void stm32_qspi_read_fifo(u8 *val, void __iomem *addr) -{ - *val = readb_relaxed(addr); -} - -static void stm32_qspi_write_fifo(u8 *val, void __iomem *addr) -{ - writeb_relaxed(*val, addr); -} - -static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, - const struct stm32_qspi_cmd *cmd) -{ - void (*tx_fifo)(u8 *, void __iomem *); - u32 len = cmd->len, sr; - u8 *buf = cmd->buf; - int ret; - - if (cmd->qspimode == CCR_FMODE_INDW) - tx_fifo = stm32_qspi_write_fifo; - else - tx_fifo = stm32_qspi_read_fifo; - - while (len--) { - ret = readl_relaxed_poll_timeout(qspi->io_base + QUADSPI_SR, - sr, (sr & SR_FTF), 10, - STM32_QSPI_FIFO_TIMEOUT_US); - if (ret) { - dev_err(qspi->dev, "fifo timeout (stat:%#x)\n", sr); - return ret; - } - tx_fifo(buf++, qspi->io_base + QUADSPI_DR); - } - - return 0; -} - -static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, - const struct stm32_qspi_cmd *cmd) -{ - memcpy_fromio(cmd->buf, qspi->mm_base + cmd->addr, cmd->len); - return 0; -} - -static int stm32_qspi_tx(struct stm32_qspi *qspi, - const struct stm32_qspi_cmd *cmd) -{ - if (!cmd->tx_data) - return 0; - - if (cmd->qspimode == CCR_FMODE_MM) - return stm32_qspi_tx_mm(qspi, cmd); - - return stm32_qspi_tx_poll(qspi, cmd); -} - -static int stm32_qspi_send(struct stm32_qspi_flash *flash, - const struct stm32_qspi_cmd *cmd) -{ - struct stm32_qspi *qspi = flash->qspi; - u32 ccr, dcr, cr; - u32 last_byte; - int err; - - err = stm32_qspi_wait_nobusy(qspi); - if (err) - goto abort; - - dcr = readl_relaxed(qspi->io_base + QUADSPI_DCR) & ~DCR_FSIZE_MASK; - dcr |= DCR_FSIZE(flash->fsize); - writel_relaxed(dcr, qspi->io_base + QUADSPI_DCR); - - cr = readl_relaxed(qspi->io_base + QUADSPI_CR); - cr &= ~CR_PRESC_MASK & ~CR_FSEL; - cr |= CR_PRESC(flash->presc); - cr |= flash->cs ? CR_FSEL : 0; - writel_relaxed(cr, qspi->io_base + QUADSPI_CR); - - if (cmd->tx_data) - writel_relaxed(cmd->len - 1, qspi->io_base + QUADSPI_DLR); - - ccr = cmd->framemode | cmd->qspimode; - - if (cmd->dummy) - ccr |= CCR_DCYC(cmd->dummy); - - if (cmd->addr_width) - ccr |= CCR_ADSIZE(cmd->addr_width - 1); - - ccr |= CCR_INST(cmd->opcode); - writel_relaxed(ccr, qspi->io_base + QUADSPI_CCR); - - if (cmd->addr_width && cmd->qspimode != CCR_FMODE_MM) - writel_relaxed(cmd->addr, qspi->io_base + QUADSPI_AR); - - err = stm32_qspi_tx(qspi, cmd); - if (err) - goto abort; - - if (cmd->qspimode != CCR_FMODE_MM) { - err = stm32_qspi_wait_cmd(qspi); - if (err) - goto abort; - writel_relaxed(FCR_CTCF, qspi->io_base + QUADSPI_FCR); - } else { - last_byte = cmd->addr + cmd->len; - if (last_byte > flash->prefetch_limit) - goto abort; - } - - return err; - -abort: - cr = readl_relaxed(qspi->io_base + QUADSPI_CR) | CR_ABORT; - writel_relaxed(cr, qspi->io_base + QUADSPI_CR); - - if (err) - dev_err(qspi->dev, "%s abort err:%d\n", __func__, err); - - return err; -} - -static int stm32_qspi_read_reg(struct spi_nor *nor, - u8 opcode, u8 *buf, int len) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct device *dev = flash->qspi->dev; - struct stm32_qspi_cmd cmd; - - dev_dbg(dev, "read_reg: cmd:%#.2x buf:%pK len:%#x\n", opcode, buf, len); - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = opcode; - cmd.tx_data = true; - cmd.len = len; - cmd.buf = buf; - cmd.qspimode = CCR_FMODE_INDR; - - stm32_qspi_set_framemode(nor, &cmd, false); - - return stm32_qspi_send(flash, &cmd); -} - -static int stm32_qspi_write_reg(struct spi_nor *nor, u8 opcode, - u8 *buf, int len) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct device *dev = flash->qspi->dev; - struct stm32_qspi_cmd cmd; - - dev_dbg(dev, "write_reg: cmd:%#.2x buf:%pK len:%#x\n", opcode, buf, len); - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = opcode; - cmd.tx_data = !!(buf && len > 0); - cmd.len = len; - cmd.buf = buf; - cmd.qspimode = CCR_FMODE_INDW; - - stm32_qspi_set_framemode(nor, &cmd, false); - - return stm32_qspi_send(flash, &cmd); -} - -static ssize_t stm32_qspi_read(struct spi_nor *nor, loff_t from, size_t len, - u_char *buf) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct stm32_qspi *qspi = flash->qspi; - struct stm32_qspi_cmd cmd; - int err; - - dev_dbg(qspi->dev, "read(%#.2x): buf:%pK from:%#.8x len:%#zx\n", - nor->read_opcode, buf, (u32)from, len); - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = nor->read_opcode; - cmd.addr_width = nor->addr_width; - cmd.addr = (u32)from; - cmd.tx_data = true; - cmd.dummy = nor->read_dummy; - cmd.len = len; - cmd.buf = buf; - cmd.qspimode = flash->read_mode; - - stm32_qspi_set_framemode(nor, &cmd, true); - err = stm32_qspi_send(flash, &cmd); - - return err ? err : len; -} - -static ssize_t stm32_qspi_write(struct spi_nor *nor, loff_t to, size_t len, - const u_char *buf) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct device *dev = flash->qspi->dev; - struct stm32_qspi_cmd cmd; - int err; - - dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n", - nor->program_opcode, buf, (u32)to, len); - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = nor->program_opcode; - cmd.addr_width = nor->addr_width; - cmd.addr = (u32)to; - cmd.tx_data = true; - cmd.len = len; - cmd.buf = (void *)buf; - cmd.qspimode = CCR_FMODE_INDW; - - stm32_qspi_set_framemode(nor, &cmd, false); - err = stm32_qspi_send(flash, &cmd); - - return err ? err : len; -} - -static int stm32_qspi_erase(struct spi_nor *nor, loff_t offs) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct device *dev = flash->qspi->dev; - struct stm32_qspi_cmd cmd; - - dev_dbg(dev, "erase(%#.2x):offs:%#x\n", nor->erase_opcode, (u32)offs); - - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = nor->erase_opcode; - cmd.addr_width = nor->addr_width; - cmd.addr = (u32)offs; - cmd.qspimode = CCR_FMODE_INDW; - - stm32_qspi_set_framemode(nor, &cmd, false); - - return stm32_qspi_send(flash, &cmd); -} - -static irqreturn_t stm32_qspi_irq(int irq, void *dev_id) -{ - struct stm32_qspi *qspi = (struct stm32_qspi *)dev_id; - u32 cr, sr, fcr = 0; - - cr = readl_relaxed(qspi->io_base + QUADSPI_CR); - sr = readl_relaxed(qspi->io_base + QUADSPI_SR); - - if ((cr & CR_TCIE) && (sr & SR_TCF)) { - /* tx complete */ - fcr |= FCR_CTCF; - complete(&qspi->cmd_completion); - } else { - dev_info_ratelimited(qspi->dev, "spurious interrupt\n"); - } - - writel_relaxed(fcr, qspi->io_base + QUADSPI_FCR); - - return IRQ_HANDLED; -} - -static int stm32_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct stm32_qspi *qspi = flash->qspi; - - mutex_lock(&qspi->lock); - return 0; -} - -static void stm32_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) -{ - struct stm32_qspi_flash *flash = nor->priv; - struct stm32_qspi *qspi = flash->qspi; - - mutex_unlock(&qspi->lock); -} - -static int stm32_qspi_flash_setup(struct stm32_qspi *qspi, - struct device_node *np) -{ - struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; - u32 width, presc, cs_num, max_rate = 0; - struct stm32_qspi_flash *flash; - struct mtd_info *mtd; - int ret; - - of_property_read_u32(np, "reg", &cs_num); - if (cs_num >= STM32_MAX_NORCHIP) - return -EINVAL; - - of_property_read_u32(np, "spi-max-frequency", &max_rate); - if (!max_rate) - return -EINVAL; - - presc = DIV_ROUND_UP(qspi->clk_rate, max_rate) - 1; - - if (of_property_read_u32(np, "spi-rx-bus-width", &width)) - width = 1; - - if (width == 4) - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; - else if (width == 2) - hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; - else if (width != 1) - return -EINVAL; - - flash = &qspi->flash[cs_num]; - flash->qspi = qspi; - flash->cs = cs_num; - flash->presc = presc; - - flash->nor.dev = qspi->dev; - spi_nor_set_flash_node(&flash->nor, np); - flash->nor.priv = flash; - mtd = &flash->nor.mtd; - - flash->nor.read = stm32_qspi_read; - flash->nor.write = stm32_qspi_write; - flash->nor.erase = stm32_qspi_erase; - flash->nor.read_reg = stm32_qspi_read_reg; - flash->nor.write_reg = stm32_qspi_write_reg; - flash->nor.prepare = stm32_qspi_prep; - flash->nor.unprepare = stm32_qspi_unprep; - - writel_relaxed(LPTR_DFT_TIMEOUT, qspi->io_base + QUADSPI_LPTR); - - writel_relaxed(CR_PRESC(presc) | CR_FTHRES(3) | CR_TCEN | CR_SSHIFT - | CR_EN, qspi->io_base + QUADSPI_CR); - - /* - * in stm32 qspi controller, QUADSPI_DCR register has a fsize field - * which define the size of nor flash. - * if fsize is NULL, the controller can't sent spi-nor command. - * set a temporary value just to discover the nor flash with - * "spi_nor_scan". After, the right value (mtd->size) can be set. - */ - flash->fsize = FSIZE_VAL(SZ_1K); - - ret = spi_nor_scan(&flash->nor, NULL, &hwcaps); - if (ret) { - dev_err(qspi->dev, "device scan failed\n"); - return ret; - } - - flash->fsize = FSIZE_VAL(mtd->size); - flash->prefetch_limit = mtd->size - STM32_QSPI_FIFO_SZ; - - flash->read_mode = CCR_FMODE_MM; - if (mtd->size > qspi->mm_size) - flash->read_mode = CCR_FMODE_INDR; - - writel_relaxed(DCR_CSHT(1), qspi->io_base + QUADSPI_DCR); - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(qspi->dev, "mtd device parse failed\n"); - return ret; - } - - flash->registered = true; - - dev_dbg(qspi->dev, "read mm:%s cs:%d bus:%d\n", - flash->read_mode == CCR_FMODE_MM ? "yes" : "no", cs_num, width); - - return 0; -} - -static void stm32_qspi_mtd_free(struct stm32_qspi *qspi) -{ - int i; - - for (i = 0; i < STM32_MAX_NORCHIP; i++) - if (qspi->flash[i].registered) - mtd_device_unregister(&qspi->flash[i].nor.mtd); -} - -static int stm32_qspi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *flash_np; - struct reset_control *rstc; - struct stm32_qspi *qspi; - struct resource *res; - int ret, irq; - - qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); - if (!qspi) - return -ENOMEM; - - qspi->nor_num = of_get_child_count(dev->of_node); - if (!qspi->nor_num || qspi->nor_num > STM32_MAX_NORCHIP) - return -ENODEV; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); - qspi->io_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qspi->io_base)) - return PTR_ERR(qspi->io_base); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); - qspi->mm_base = devm_ioremap_resource(dev, res); - if (IS_ERR(qspi->mm_base)) - return PTR_ERR(qspi->mm_base); - - qspi->mm_size = resource_size(res); - - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0, - dev_name(dev), qspi); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return ret; - } - - init_completion(&qspi->cmd_completion); - - qspi->clk = devm_clk_get(dev, NULL); - if (IS_ERR(qspi->clk)) - return PTR_ERR(qspi->clk); - - qspi->clk_rate = clk_get_rate(qspi->clk); - if (!qspi->clk_rate) - return -EINVAL; - - ret = clk_prepare_enable(qspi->clk); - if (ret) { - dev_err(dev, "can not enable the clock\n"); - return ret; - } - - rstc = devm_reset_control_get_exclusive(dev, NULL); - if (!IS_ERR(rstc)) { - reset_control_assert(rstc); - udelay(2); - reset_control_deassert(rstc); - } - - qspi->dev = dev; - platform_set_drvdata(pdev, qspi); - mutex_init(&qspi->lock); - - for_each_available_child_of_node(dev->of_node, flash_np) { - ret = stm32_qspi_flash_setup(qspi, flash_np); - if (ret) { - dev_err(dev, "unable to setup flash chip\n"); - goto err_flash; - } - } - - return 0; - -err_flash: - mutex_destroy(&qspi->lock); - stm32_qspi_mtd_free(qspi); - - clk_disable_unprepare(qspi->clk); - return ret; -} - -static int stm32_qspi_remove(struct platform_device *pdev) -{ - struct stm32_qspi *qspi = platform_get_drvdata(pdev); - - /* disable qspi */ - writel_relaxed(0, qspi->io_base + QUADSPI_CR); - - stm32_qspi_mtd_free(qspi); - mutex_destroy(&qspi->lock); - - clk_disable_unprepare(qspi->clk); - return 0; -} - -static const struct of_device_id stm32_qspi_match[] = { - {.compatible = "st,stm32f469-qspi"}, - {} -}; -MODULE_DEVICE_TABLE(of, stm32_qspi_match); - -static struct platform_driver stm32_qspi_driver = { - .probe = stm32_qspi_probe, - .remove = stm32_qspi_remove, - .driver = { - .name = "stm32-quadspi", - .of_match_table = stm32_qspi_match, - }, -}; -module_platform_driver(stm32_qspi_driver); - -MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics STM32 quad spi driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index 6025398955a2..e1a2ae21dfd3 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -345,15 +345,36 @@ static const struct blk_mq_ops ubiblock_mq_ops = { .init_request = ubiblock_init_request, }; +static int calc_disk_capacity(struct ubi_volume_info *vi, u64 *disk_capacity) +{ + u64 size = vi->used_bytes >> 9; + + if (vi->used_bytes % 512) { + pr_warn("UBI: block: volume size is not a multiple of 512, " + "last %llu bytes are ignored!\n", + vi->used_bytes - (size << 9)); + } + + if ((sector_t)size != size) + return -EFBIG; + + *disk_capacity = size; + + return 0; +} + int ubiblock_create(struct ubi_volume_info *vi) { struct ubiblock *dev; struct gendisk *gd; - u64 disk_capacity = vi->used_bytes >> 9; + u64 disk_capacity; int ret; - if ((sector_t)disk_capacity != disk_capacity) - return -EFBIG; + ret = calc_disk_capacity(vi, &disk_capacity); + if (ret) { + return ret; + } + /* Check that the volume isn't already handled */ mutex_lock(&devices_mutex); if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { @@ -507,7 +528,8 @@ out_unlock: static int ubiblock_resize(struct ubi_volume_info *vi) { struct ubiblock *dev; - u64 disk_capacity = vi->used_bytes >> 9; + u64 disk_capacity; + int ret; /* * Need to lock the device list until we stop using the device, @@ -520,11 +542,16 @@ static int ubiblock_resize(struct ubi_volume_info *vi) mutex_unlock(&devices_mutex); return -ENODEV; } - if ((sector_t)disk_capacity != disk_capacity) { + + ret = calc_disk_capacity(vi, &disk_capacity); + if (ret) { mutex_unlock(&devices_mutex); - dev_warn(disk_to_dev(dev->gd), "the volume is too big (%d LEBs), cannot resize", - vi->size); - return -EFBIG; + if (ret == -EFBIG) { + dev_warn(disk_to_dev(dev->gd), + "the volume is too big (%d LEBs), cannot resize", + vi->size); + } + return ret; } mutex_lock(&dev->dev_mutex); diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index a1dff92ceedf..0f847d510950 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -509,11 +509,9 @@ static const struct file_operations eraseblk_count_fops = { */ int ubi_debugfs_init_dev(struct ubi_device *ubi) { - int err, n; unsigned long ubi_num = ubi->ubi_num; - const char *fname; - struct dentry *dent; struct ubi_debug_info *d = &ubi->dbg; + int n; if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; @@ -522,95 +520,52 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi) ubi->ubi_num); if (n == UBI_DFS_DIR_LEN) { /* The array size is too small */ - fname = UBI_DFS_DIR_NAME; - dent = ERR_PTR(-EINVAL); - goto out; + return -EINVAL; } - fname = d->dfs_dir_name; - dent = debugfs_create_dir(fname, dfs_rootdir); - if (IS_ERR_OR_NULL(dent)) - goto out; - d->dfs_dir = dent; - - fname = "chk_gen"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_chk_gen = dent; - - fname = "chk_io"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_chk_io = dent; - - fname = "chk_fastmap"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_chk_fastmap = dent; - - fname = "tst_disable_bgt"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_disable_bgt = dent; - - fname = "tst_emulate_bitflips"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_emulate_bitflips = dent; - - fname = "tst_emulate_io_failures"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_emulate_io_failures = dent; - - fname = "tst_emulate_power_cut"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_emulate_power_cut = dent; - - fname = "tst_emulate_power_cut_min"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_power_cut_min = dent; - - fname = "tst_emulate_power_cut_max"; - dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, - &dfs_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; - d->dfs_power_cut_max = dent; - - fname = "detailed_erase_block_info"; - dent = debugfs_create_file(fname, S_IRUSR, d->dfs_dir, (void *)ubi_num, - &eraseblk_count_fops); - if (IS_ERR_OR_NULL(dent)) - goto out_remove; + d->dfs_dir = debugfs_create_dir(d->dfs_dir_name, dfs_rootdir); - return 0; + d->dfs_chk_gen = debugfs_create_file("chk_gen", S_IWUSR, d->dfs_dir, + (void *)ubi_num, &dfs_fops); -out_remove: - debugfs_remove_recursive(d->dfs_dir); -out: - err = dent ? PTR_ERR(dent) : -ENODEV; - ubi_err(ubi, "cannot create \"%s\" debugfs file or directory, error %d\n", - fname, err); - return err; + d->dfs_chk_io = debugfs_create_file("chk_io", S_IWUSR, d->dfs_dir, + (void *)ubi_num, &dfs_fops); + + d->dfs_chk_fastmap = debugfs_create_file("chk_fastmap", S_IWUSR, + d->dfs_dir, (void *)ubi_num, + &dfs_fops); + + d->dfs_disable_bgt = debugfs_create_file("tst_disable_bgt", S_IWUSR, + d->dfs_dir, (void *)ubi_num, + &dfs_fops); + + d->dfs_emulate_bitflips = debugfs_create_file("tst_emulate_bitflips", + S_IWUSR, d->dfs_dir, + (void *)ubi_num, + &dfs_fops); + + d->dfs_emulate_io_failures = debugfs_create_file("tst_emulate_io_failures", + S_IWUSR, d->dfs_dir, + (void *)ubi_num, + &dfs_fops); + + d->dfs_emulate_power_cut = debugfs_create_file("tst_emulate_power_cut", + S_IWUSR, d->dfs_dir, + (void *)ubi_num, + &dfs_fops); + + d->dfs_power_cut_min = debugfs_create_file("tst_emulate_power_cut_min", + S_IWUSR, d->dfs_dir, + (void *)ubi_num, &dfs_fops); + + d->dfs_power_cut_max = debugfs_create_file("tst_emulate_power_cut_max", + S_IWUSR, d->dfs_dir, + (void *)ubi_num, &dfs_fops); + + debugfs_create_file("detailed_erase_block_info", S_IRUSR, d->dfs_dir, + (void *)ubi_num, &eraseblk_count_fops); + + return 0; } /** diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c index d9e2e3a6e105..c44c8470247e 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -196,7 +196,7 @@ static int produce_free_peb(struct ubi_device *ubi) */ int ubi_wl_get_peb(struct ubi_device *ubi) { - int ret, retried = 0; + int ret, attempts = 0; struct ubi_fm_pool *pool = &ubi->fm_pool; struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool; @@ -221,12 +221,12 @@ again: if (pool->used == pool->size) { spin_unlock(&ubi->wl_lock); - if (retried) { + attempts++; + if (attempts == 10) { ubi_err(ubi, "Unable to get a free PEB from user WL pool"); ret = -ENOSPC; goto out; } - retried = 1; up_read(&ubi->fm_eba_sem); ret = produce_free_peb(ubi); if (ret < 0) { diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 949700a9bb4b..3fcdefe2714d 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -710,6 +710,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (!e2) goto out_cancel; + /* + * Anchor move within the anchor area is useless. + */ + if (e2->pnum < UBI_FM_MAX_START) + goto out_cancel; + self_check_in_wl_tree(ubi, e1, &ubi->used); rb_erase(&e1->u.rb, &ubi->used); dbg_wl("anchor-move PEB %d to PEB %d", e1->pnum, e2->pnum); |