configs: Migrate CONFIG_SYS_MAX_FLASH_BANKS to Kconfig
[platform/kernel/u-boot.git] / drivers / mtd / spi / spi-nor-core.c
index 7f1ed1b..a70fbda 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <common.h>
+#include <flash.h>
 #include <log.h>
 #include <watchdog.h>
 #include <dm.h>
@@ -26,6 +27,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/spi-nor.h>
+#include <mtd/cfi_flash.h>
 #include <spi-mem.h>
 #include <spi.h>
 
@@ -662,6 +664,9 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                }
 
                return status;
+       case SNOR_MFR_CYPRESS:
+               cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B_CYPRESS;
+               return nor->write_reg(nor, cmd, NULL, 0);
        default:
                /* Spansion style */
                nor->cmd_buf[0] = enable << 7;
@@ -903,30 +908,40 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       bool addr_known = false;
        u32 addr, len, rem;
-       int ret;
+       int ret, err;
 
        dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
                (long long)instr->len);
 
-       if (!instr->len)
-               return 0;
-
        div_u64_rem(instr->len, mtd->erasesize, &rem);
-       if (rem)
-               return -EINVAL;
+       if (rem) {
+               ret = -EINVAL;
+               goto err;
+       }
 
        addr = instr->addr;
        len = instr->len;
 
+       instr->state = MTD_ERASING;
+       addr_known = true;
+
        while (len) {
                WATCHDOG_RESET();
+               if (ctrlc()) {
+                       addr_known = false;
+                       ret = -EINTR;
+                       goto erase_err;
+               }
 #ifdef CONFIG_SPI_FLASH_BAR
                ret = write_bar(nor, addr);
                if (ret < 0)
-                       return ret;
+                       goto erase_err;
 #endif
-               write_enable(nor);
+               ret = write_enable(nor);
+               if (ret < 0)
+                       goto erase_err;
 
                ret = spi_nor_erase_sector(nor, addr);
                if (ret < 0)
@@ -940,16 +955,29 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
                        goto erase_err;
        }
 
+       addr_known = false;
 erase_err:
 #ifdef CONFIG_SPI_FLASH_BAR
-       ret = clean_bar(nor);
+       err = clean_bar(nor);
+       if (!ret)
+               ret = err;
 #endif
-       write_disable(nor);
+       err = write_disable(nor);
+       if (!ret)
+               ret = err;
+
+err:
+       if (ret) {
+               instr->fail_addr = addr_known ? addr : MTD_FAIL_ADDR_UNKNOWN;
+               instr->state = MTD_ERASE_FAILED;
+       } else {
+               instr->state = MTD_ERASE_DONE;
+       }
 
        return ret;
 }
 
-#ifdef CONFIG_SPI_FLASH_S28HS512T
+#ifdef CONFIG_SPI_FLASH_SPANSION
 /**
  * spansion_erase_non_uniform() - erase non-uniform sectors for Spansion/Cypress
  *                                chips
@@ -1660,9 +1688,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-       if (!len)
-               return 0;
-
        for (i = 0; i < len; ) {
                ssize_t written;
                loff_t addr = to + i;
@@ -2601,18 +2626,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
        params->size = 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 CONFIG_IS_ENABLED(DM_SPI)
+               if (!ofnode_read_bool(dev_ofnode(nor->spi->dev),
+                                     "m25p,fast-read"))
+                       params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
+#endif
+       }
+
        /* (Fast) Read settings. */
        params->hwcaps.mask |= SNOR_HWCAPS_READ;
        spi_nor_set_read_settings(&params->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;
+       if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST)
                spi_nor_set_read_settings(&params->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;
@@ -2855,10 +2890,11 @@ spi_nor_adjust_hwcaps(struct spi_nor *nor,
        unsigned int cap;
 
        /*
-        * Enable all caps by default. We will mask them after checking what's
-        * really supported using spi_mem_supports_op().
+        * Start by assuming the controller supports every capability.
+        * We will mask them after checking what's really supported
+        * using spi_mem_supports_op().
         */
-       *hwcaps = SNOR_HWCAPS_ALL;
+       *hwcaps = SNOR_HWCAPS_ALL & params->hwcaps.mask;
 
        /* X-X-X modes are not supported yet, mask them all. */
        *hwcaps &= ~SNOR_HWCAPS_X_X_X;
@@ -3082,6 +3118,149 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
        return nor->setup(nor, info, params);
 }
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+static int s25hx_t_mdp_ready(struct spi_nor *nor)
+{
+       u32 addr;
+       int ret;
+
+       for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+               ret = spansion_sr_ready(nor, addr, 0);
+               if (!ret)
+                       return ret;
+       }
+
+       return 1;
+}
+
+static int s25hx_t_quad_enable(struct spi_nor *nor)
+{
+       u32 addr;
+       int ret;
+
+       for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+               ret = spansion_quad_enable_volatile(nor, addr, 0);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int s25hx_t_erase_non_uniform(struct spi_nor *nor, loff_t addr)
+{
+       /* Support 32 x 4KB sectors at bottom */
+       return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0,
+                                         SZ_128K);
+}
+
+static int s25hx_t_setup(struct spi_nor *nor, const struct flash_info *info,
+                        const struct spi_nor_flash_parameter *params)
+{
+       int ret;
+       u8 cfr3v;
+
+#ifdef CONFIG_SPI_FLASH_BAR
+       return -ENOTSUPP; /* Bank Address Register is not supported */
+#endif
+       /*
+        * Read CFR3V to check if uniform sector is selected. If not, assign an
+        * erase hook that supports non-uniform erase.
+        */
+       ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V, 0, &cfr3v);
+       if (ret)
+               return ret;
+       if (!(cfr3v & CFR3V_UNHYSA))
+               nor->erase = s25hx_t_erase_non_uniform;
+
+       /*
+        * For the multi-die package parts, the ready() hook is needed to check
+        * all dies' status via read any register.
+        */
+       if (nor->mtd.size > SZ_128M)
+               nor->ready = s25hx_t_mdp_ready;
+
+       return spi_nor_default_setup(nor, info, params);
+}
+
+static void s25hx_t_default_init(struct spi_nor *nor)
+{
+       nor->setup = s25hx_t_setup;
+}
+
+static int s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
+                                  const struct sfdp_parameter_header *header,
+                                  const struct sfdp_bfpt *bfpt,
+                                  struct spi_nor_flash_parameter *params)
+{
+       int ret;
+       u32 addr;
+       u8 cfr3v;
+
+       /* erase size in case it is set to 4K from BFPT */
+       nor->erase_opcode = SPINOR_OP_SE_4B;
+       nor->mtd.erasesize = nor->info->sector_size;
+
+       ret = set_4byte(nor, nor->info, 1);
+       if (ret)
+               return ret;
+       nor->addr_width = 4;
+
+       /*
+        * The page_size is set to 512B from BFPT, but it actually depends on
+        * the configuration register. Look up the CFR3V and determine the
+        * page_size. For multi-die package parts, use 512B only when the all
+        * dies are configured to 512B buffer.
+        */
+       for (addr = 0; addr < params->size; addr += SZ_128M) {
+               ret = spansion_read_any_reg(nor, addr + SPINOR_REG_ADDR_CFR3V,
+                                           0, &cfr3v);
+               if (ret)
+                       return ret;
+
+               if (!(cfr3v & CFR3V_PGMBUF)) {
+                       params->page_size = 256;
+                       return 0;
+               }
+       }
+       params->page_size = 512;
+
+       return 0;
+}
+
+static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor,
+                                   struct spi_nor_flash_parameter *params)
+{
+       /* READ_FAST_4B (0Ch) requires mode cycles*/
+       params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
+       /* PP_1_1_4 is not supported */
+       params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
+       /* Use volatile register to enable quad */
+       params->quad_enable = s25hx_t_quad_enable;
+}
+
+static struct spi_nor_fixups s25hx_t_fixups = {
+       .default_init = s25hx_t_default_init,
+       .post_bfpt = s25hx_t_post_bfpt_fixup,
+       .post_sfdp = s25hx_t_post_sfdp_fixup,
+};
+
+static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info,
+                          const struct spi_nor_flash_parameter *params)
+{
+       return -ENOTSUPP; /* Bank Address Register is not supported */
+}
+
+static void s25fl256l_default_init(struct spi_nor *nor)
+{
+       nor->setup = s25fl256l_setup;
+}
+
+static struct spi_nor_fixups s25fl256l_fixups = {
+       .default_init = s25fl256l_default_init,
+};
+#endif
+
 #ifdef CONFIG_SPI_FLASH_S28HS512T
 /**
  * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
@@ -3490,6 +3669,24 @@ int spi_nor_remove(struct spi_nor *nor)
 
 void spi_nor_set_fixups(struct spi_nor *nor)
 {
+#ifdef CONFIG_SPI_FLASH_SPANSION
+       if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) {
+               switch (nor->info->id[1]) {
+               case 0x2a: /* S25HL (QSPI, 3.3V) */
+               case 0x2b: /* S25HS (QSPI, 1.8V) */
+                       nor->fixups = &s25hx_t_fixups;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
+           !strcmp(nor->info->name, "s25fl256l"))
+               nor->fixups = &s25fl256l_fixups;
+#endif
+
 #ifdef CONFIG_SPI_FLASH_S28HS512T
        if (!strcmp(nor->info->name, "s28hs512t"))
                nor->fixups = &s28hs512t_fixups;
@@ -3508,6 +3705,11 @@ int spi_nor_scan(struct spi_nor *nor)
        struct mtd_info *mtd = &nor->mtd;
        struct spi_slave *spi = nor->spi;
        int ret;
+       int cfi_mtd_nb = 0;
+
+#ifdef CONFIG_FLASH_CFI_MTD
+       cfi_mtd_nb = CFI_FLASH_BANKS;
+#endif
 
        /* Reset SPI protocol for all commands. */
        nor->reg_proto = SNOR_PROTO_1_1_1;
@@ -3559,8 +3761,12 @@ int spi_nor_scan(struct spi_nor *nor)
        if (ret)
                return ret;
 
-       if (!mtd->name)
-               mtd->name = info->name;
+       if (!mtd->name) {
+               sprintf(nor->mtd_name, "%s%d",
+                       MTD_DEV_TYPE(MTD_DEV_TYPE_NOR),
+                       cfi_mtd_nb + dev_seq(nor->dev));
+               mtd->name = nor->mtd_name;
+       }
        mtd->dev = nor->dev;
        mtd->priv = nor;
        mtd->type = MTD_NORFLASH;
@@ -3665,7 +3871,7 @@ int spi_nor_scan(struct spi_nor *nor)
 
        nor->rdsr_dummy = params.rdsr_dummy;
        nor->rdsr_addr_nbytes = params.rdsr_addr_nbytes;
-       nor->name = mtd->name;
+       nor->name = info->name;
        nor->size = mtd->size;
        nor->erase_size = mtd->erasesize;
        nor->sector_size = mtd->erasesize;