Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / drivers / mtd / spi / spi-nor-core.c
index 5a8c084..e840c60 100644 (file)
@@ -10,6 +10,8 @@
  */
 
 #include <common.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/log2.h>
@@ -251,6 +253,8 @@ static u8 spi_nor_convert_3to4_read(u8 opcode)
                { 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 },
@@ -267,6 +271,8 @@ static u8 spi_nor_convert_3to4_program(u8 opcode)
                { 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,
@@ -319,6 +325,7 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
        case SNOR_MFR_MICRON:
                /* Some Micron need WREN command; all will accept it */
                need_wren = true;
+       case SNOR_MFR_ISSI:
        case SNOR_MFR_MACRONIX:
        case SNOR_MFR_WINBOND:
                if (need_wren)
@@ -546,6 +553,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
        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;
@@ -1226,6 +1236,9 @@ 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;
@@ -1234,11 +1247,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                 * If page_size is a power of two, the offset can be quickly
                 * calculated with an AND operation. On the other cases we
                 * need to do a modulus operation (more expensive).
-                * Power of two numbers have only one bit set and we can use
-                * the instruction hweight32 to detect if we need to do a
-                * modulus (do_div()) or not.
                 */
-               if (hweight32(nor->page_size) == 1) {
+               if (is_power_of_2(nor->page_size)) {
                        page_offset = addr & (nor->page_size - 1);
                } else {
                        u64 aux = addr;
@@ -1588,6 +1598,7 @@ struct sfdp_parameter_header {
 
 #define SFDP_BFPT_ID           0xff00  /* Basic Flash Parameter Table */
 #define SFDP_SECTOR_MAP_ID     0xff81  /* Sector Map Table */
+#define SFDP_SST_ID            0x01bf  /* Manufacturer specific Table */
 
 #define SFDP_SIGNATURE         0x50444653U
 #define SFDP_JESD216_MAJOR     1
@@ -1968,6 +1979,34 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
 }
 
 /**
+ * spi_nor_parse_microchip_sfdp() - parse the Microchip manufacturer specific
+ * SFDP table.
+ * @nor:               pointer to a 'struct spi_nor'.
+ * @param_header:      pointer to the SFDP parameter header.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+spi_nor_parse_microchip_sfdp(struct spi_nor *nor,
+                            const struct sfdp_parameter_header *param_header)
+{
+       size_t size;
+       u32 addr;
+       int ret;
+
+       size = param_header->length * sizeof(u32);
+       addr = SFDP_PARAM_HEADER_PTP(param_header);
+
+       nor->manufacturer_sfdp = devm_kmalloc(nor->dev, size, GFP_KERNEL);
+       if (!nor->manufacturer_sfdp)
+               return -ENOMEM;
+
+       ret = spi_nor_read_sfdp(nor, addr, size, nor->manufacturer_sfdp);
+
+       return ret;
+}
+
+/**
  * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
  * @nor:               pointer to a 'struct spi_nor'
  * @params:            pointer to the 'struct spi_nor_flash_parameter' to be
@@ -2063,12 +2102,25 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
                        dev_info(dev, "non-uniform erase sector maps are not supported yet.\n");
                        break;
 
+               case SFDP_SST_ID:
+                       err = spi_nor_parse_microchip_sfdp(nor, param_header);
+                       break;
+
                default:
                        break;
                }
 
-               if (err)
-                       goto exit;
+               if (err) {
+                       dev_warn(dev, "Failed to parse optional parameter table: %04x\n",
+                                SFDP_PARAM_HEADER_ID(param_header));
+                       /*
+                        * Let's not drop all information we extracted so far
+                        * if optional table parsers fail. In case of failing,
+                        * each optional parser is responsible to roll back to
+                        * the previously known spi_nor data.
+                        */
+                       err = 0;
+               }
        }
 
 exit:
@@ -2121,6 +2173,13 @@ static int spi_nor_init_params(struct spi_nor *nor,
                                          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(&params->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(&params->page_programs[SNOR_CMD_PP],
@@ -2428,7 +2487,14 @@ int spi_nor_scan(struct spi_nor *nor)
        nor->read_reg = spi_nor_read_reg;
        nor->write_reg = spi_nor_write_reg;
 
-       if (spi->mode & SPI_RX_QUAD) {
+       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)