mtd: spi: spi-nor-core: Add back U-Boot specific features
authorVignesh R <vigneshr@ti.com>
Tue, 5 Feb 2019 05:59:21 +0000 (11:29 +0530)
committerJagan Teki <jagan@amarulasolutions.com>
Thu, 7 Feb 2019 10:03:21 +0000 (15:33 +0530)
For legacy reasons, we will have to keep around U-Boot specific
SPI_FLASH_BAR and SPI_TX_BYTE. Add them back to the new framework

Signed-off-by: Vignesh R <vigneshr@ti.com>
Reviewed-by: Jagan Teki <jagan@openedev.com>
Tested-by: Jagan Teki <jagan@amarulasolutions.com> #zynq-microzed
drivers/mtd/spi/spi-nor-core.c
include/linux/mtd/spi-nor.h

index 2b39aaedead4424122aee3e3907fba8ed9a07a16..13c6236d623abb150fc9e8ecacd3cde6b9691bb2 100644 (file)
@@ -291,6 +291,7 @@ static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return mtd->priv;
 }
 
+#ifndef CONFIG_SPI_FLASH_BAR
 static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
 {
        size_t i;
@@ -365,6 +366,7 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
        nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
        nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
 }
+#endif /* !CONFIG_SPI_FLASH_BAR */
 
 /* Enable/disable 4-byte addressing mode. */
 static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
@@ -499,6 +501,79 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
                                                    DEFAULT_READY_WAIT_JIFFIES);
 }
 
+#ifdef CONFIG_SPI_FLASH_BAR
+/*
+ * This "clean_bar" is necessary in a situation when one was accessing
+ * spi flash memory > 16 MiB by using Bank Address Register's BA24 bit.
+ *
+ * After it the BA24 bit shall be cleared to allow access to correct
+ * memory region after SW reset (by calling "reset" command).
+ *
+ * Otherwise, the BA24 bit may be left set and then after reset, the
+ * ROM would read/write/erase SPL from 16 MiB * bank_sel address.
+ */
+static int clean_bar(struct spi_nor *nor)
+{
+       u8 cmd, bank_sel = 0;
+
+       if (nor->bank_curr == 0)
+               return 0;
+       cmd = nor->bank_write_cmd;
+       nor->bank_curr = 0;
+       write_enable(nor);
+
+       return nor->write_reg(nor, cmd, &bank_sel, 1);
+}
+
+static int write_bar(struct spi_nor *nor, u32 offset)
+{
+       u8 cmd, bank_sel;
+       int ret;
+
+       bank_sel = offset / SZ_16M;
+       if (bank_sel == nor->bank_curr)
+               goto bar_end;
+
+       cmd = nor->bank_write_cmd;
+       write_enable(nor);
+       ret = nor->write_reg(nor, cmd, &bank_sel, 1);
+       if (ret < 0) {
+               debug("SF: fail to write bank register\n");
+               return ret;
+       }
+
+bar_end:
+       nor->bank_curr = bank_sel;
+       return nor->bank_curr;
+}
+
+static int read_bar(struct spi_nor *nor, const struct flash_info *info)
+{
+       u8 curr_bank = 0;
+       int ret;
+
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               nor->bank_read_cmd = SPINOR_OP_BRRD;
+               nor->bank_write_cmd = SPINOR_OP_BRWR;
+               break;
+       default:
+               nor->bank_read_cmd = SPINOR_OP_RDEAR;
+               nor->bank_write_cmd = SPINOR_OP_WREAR;
+       }
+
+       ret = nor->read_reg(nor, nor->bank_read_cmd,
+                                   &curr_bank, 1);
+       if (ret) {
+               debug("SF: fail to read bank addr register\n");
+               return ret;
+       }
+       nor->bank_curr = curr_bank;
+
+       return 0;
+}
+#endif
+
 /*
  * Initiate the erasure of a single sector
  */
@@ -543,6 +618,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
        len = instr->len;
 
        while (len) {
+#ifdef CONFIG_SPI_FLASH_BAR
+               ret = write_bar(nor, addr);
+               if (ret < 0)
+                       return ret;
+#endif
                write_enable(nor);
 
                ret = spi_nor_erase_sector(nor, addr);
@@ -557,9 +637,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
                        goto erase_err;
        }
 
+erase_err:
+#ifdef CONFIG_SPI_FLASH_BAR
+       ret = clean_bar(nor);
+#endif
        write_disable(nor);
 
-erase_err:
        return ret;
 }
 
@@ -1144,8 +1227,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 
        while (len) {
                loff_t addr = from;
+               size_t read_len = len;
 
-               ret = nor->read(nor, addr, len, buf);
+#ifdef CONFIG_SPI_FLASH_BAR
+               u32 remain_len;
+
+               ret = write_bar(nor, addr);
+               if (ret < 0)
+                       return log_ret(ret);
+               remain_len = (SZ_16M * (nor->bank_curr + 1)) - addr;
+
+               if (len < remain_len)
+                       read_len = len;
+               else
+                       read_len = remain_len;
+#endif
+
+               ret = nor->read(nor, addr, read_len, buf);
                if (ret == 0) {
                        /* We shouldn't see 0-length reads */
                        ret = -EIO;
@@ -1162,18 +1260,49 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
        ret = 0;
 
 read_err:
+#ifdef CONFIG_SPI_FLASH_BAR
+       ret = clean_bar(nor);
+#endif
        return ret;
 }
 
 #ifdef CONFIG_SPI_FLASH_SST
+static int sst_write_byteprogram(struct spi_nor *nor, loff_t to, size_t len,
+                                size_t *retlen, const u_char *buf)
+{
+       size_t actual;
+       int ret = 0;
+
+       for (actual = 0; actual < len; actual++) {
+               nor->program_opcode = SPINOR_OP_BP;
+
+               write_enable(nor);
+               /* write one byte. */
+               ret = nor->write(nor, to, 1, buf + actual);
+               if (ret < 0)
+                       goto sst_write_err;
+               ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto sst_write_err;
+               to++;
+       }
+
+sst_write_err:
+       write_disable(nor);
+       return ret;
+}
+
 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);
+       struct spi_slave *spi = nor->spi;
        size_t actual;
        int ret;
 
        dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+       if (spi->mode & SPI_TX_BYTE)
+               return sst_write_byteprogram(nor, to, len, retlen, buf);
 
        write_enable(nor);
 
@@ -1271,6 +1400,11 @@ 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);
 
+#ifdef CONFIG_SPI_FLASH_BAR
+               ret = write_bar(nor, addr);
+               if (ret < 0)
+                       return ret;
+#endif
                write_enable(nor);
                ret = nor->write(nor, addr, page_remain, buf + i);
                if (ret < 0)
@@ -1289,6 +1423,9 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
        }
 
 write_err:
+#ifdef CONFIG_SPI_FLASH_BAR
+       ret = clean_bar(nor);
+#endif
        return ret;
 }
 
@@ -2532,12 +2669,20 @@ int spi_nor_scan(struct spi_nor *nor)
                /* already configured from SFDP */
        } else if (info->addr_width) {
                nor->addr_width = info->addr_width;
-       } else if (mtd->size > 0x1000000) {
+       } else if (mtd->size > SZ_16M) {
+#ifndef CONFIG_SPI_FLASH_BAR
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
                if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
                    info->flags & SPI_NOR_4B_OPCODES)
                        spi_nor_set_4byte_opcodes(nor, info);
+#else
+       /* Configure the BAR - discover bank cmds and read current bank */
+       nor->addr_width = 3;
+       ret = read_bar(nor, info);
+       if (ret < 0)
+               return ret;
+#endif
        } else {
                nor->addr_width = 3;
        }
@@ -2569,3 +2714,14 @@ int spi_nor_scan(struct spi_nor *nor)
 
        return 0;
 }
+
+/* U-Boot specific functions, need to extend MTD to support these */
+int spi_flash_cmd_get_sw_write_prot(struct spi_nor *nor)
+{
+       int sr = read_sr(nor);
+
+       if (sr < 0)
+               return sr;
+
+       return (sr >> 2) & 7;
+}
index 507458a76076f63a3110cca43633e9f68687eb72..88e80af57941556474c87276bbaa11e5364caf82 100644 (file)
 
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR         0x17    /* Bank register write */
+#define SPINOR_OP_BRRD         0x16    /* Bank register read */
 #define SPINOR_OP_CLSR         0x30    /* Clear status register 1 */
 
 /* Used for Micron flashes only. */
@@ -256,6 +257,9 @@ struct flash_info;
  * @read_opcode:       the read opcode
  * @read_dummy:                the dummy needed by the read operation
  * @program_opcode:    the program opcode
+ * @bank_read_cmd:     Bank read cmd
+ * @bank_write_cmd:    Bank write cmd
+ * @bank_curr:         Current flash bank
  * @sst_write_second:  used by the SST write operation
  * @flags:             flag options for the current SPI-NOR (SNOR_F_*)
  * @read_proto:                the SPI protocol for read operations
@@ -291,6 +295,11 @@ struct spi_nor {
        u8                      read_opcode;
        u8                      read_dummy;
        u8                      program_opcode;
+#ifdef CONFIG_SPI_FLASH_BAR
+       u8                      bank_read_cmd;
+       u8                      bank_write_cmd;
+       u8                      bank_curr;
+#endif
        enum spi_nor_protocol   read_proto;
        enum spi_nor_protocol   write_proto;
        enum spi_nor_protocol   reg_proto;