summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/Kconfig2
-rw-r--r--drivers/mtd/maps/sa1100-flash.c10
-rw-r--r--drivers/mtd/nand/bbt.c3
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c11
-rw-r--r--drivers/mtd/nand/raw/nand_base.c1
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c32
-rw-r--r--drivers/mtd/spi-nor/Kconfig9
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/atmel-quadspi.c781
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c21
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c165
11 files changed, 182 insertions, 854 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index e514d57a0419..aa983422aa97 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -207,7 +207,7 @@ comment "Disk-On-Chip Device Drivers"
config MTD_DOCG3
tristate "M-Systems Disk-On-Chip G3"
select BCH
- select BCH_CONST_PARAMS
+ select BCH_CONST_PARAMS if !MTD_NAND_BCH
select BITREVERSE
help
This provides an MTD device driver for the M-Systems DiskOnChip
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index 784c6e1a0391..fd5fe12d7461 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -221,7 +221,14 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
info->mtd = info->subdev[0].mtd;
ret = 0;
} else if (info->num_subdev > 1) {
- struct mtd_info *cdev[nr];
+ struct mtd_info **cdev;
+
+ cdev = kmalloc_array(nr, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
/*
* We detected multiple devices. Concatenate them together.
*/
@@ -230,6 +237,7 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
info->mtd = mtd_concat_create(cdev, info->num_subdev,
plat->name);
+ kfree(cdev);
if (info->mtd == NULL) {
ret = -ENXIO;
goto err;
diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
index 56cde38b92c0..044adf913854 100644
--- a/drivers/mtd/nand/bbt.c
+++ b/drivers/mtd/nand/bbt.c
@@ -27,7 +27,8 @@ int nanddev_bbt_init(struct nand_device *nand)
unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
BITS_PER_LONG);
- nand->bbt.cache = kzalloc(nwords, GFP_KERNEL);
+ nand->bbt.cache = kcalloc(nwords, sizeof(*nand->bbt.cache),
+ GFP_KERNEL);
if (!nand->bbt.cache)
return -ENOMEM;
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index fb33f6be7c4f..ad720494e8f7 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -2032,8 +2032,7 @@ atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
int ret;
nand_np = dev->of_node;
- nfc_np = of_find_compatible_node(dev->of_node, NULL,
- "atmel,sama5d3-nfc");
+ nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc");
if (!nfc_np) {
dev_err(dev, "Could not find device node for sama5d3-nfc\n");
return -ENODEV;
@@ -2447,15 +2446,19 @@ static int atmel_nand_controller_probe(struct platform_device *pdev)
}
if (caps->legacy_of_bindings) {
+ struct device_node *nfc_node;
u32 ale_offs = 21;
/*
* If we are parsing legacy DT props and the DT contains a
* valid NFC node, forward the request to the sama5 logic.
*/
- if (of_find_compatible_node(pdev->dev.of_node, NULL,
- "atmel,sama5d3-nfc"))
+ nfc_node = of_get_compatible_child(pdev->dev.of_node,
+ "atmel,sama5d3-nfc");
+ if (nfc_node) {
caps = &atmel_sama5_nand_caps;
+ of_node_put(nfc_node);
+ }
/*
* Even if the compatible says we are dealing with an
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 05bd0779fe9b..71050a0b31df 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -590,7 +590,6 @@ retry:
/**
* panic_nand_wait - [GENERIC] wait until the command is done
- * @mtd: MTD device structure
* @chip: NAND chip structure
* @timeo: timeout
*
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index ef75dfa62a4f..699d3cf49c6d 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -150,15 +150,15 @@
#define NAND_VERSION_MINOR_SHIFT 16
/* NAND OP_CMDs */
-#define PAGE_READ 0x2
-#define PAGE_READ_WITH_ECC 0x3
-#define PAGE_READ_WITH_ECC_SPARE 0x4
-#define PROGRAM_PAGE 0x6
-#define PAGE_PROGRAM_WITH_ECC 0x7
-#define PROGRAM_PAGE_SPARE 0x9
-#define BLOCK_ERASE 0xa
-#define FETCH_ID 0xb
-#define RESET_DEVICE 0xd
+#define OP_PAGE_READ 0x2
+#define OP_PAGE_READ_WITH_ECC 0x3
+#define OP_PAGE_READ_WITH_ECC_SPARE 0x4
+#define OP_PROGRAM_PAGE 0x6
+#define OP_PAGE_PROGRAM_WITH_ECC 0x7
+#define OP_PROGRAM_PAGE_SPARE 0x9
+#define OP_BLOCK_ERASE 0xa
+#define OP_FETCH_ID 0xb
+#define OP_RESET_DEVICE 0xd
/* Default Value for NAND_DEV_CMD_VLD */
#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
@@ -692,11 +692,11 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
if (read) {
if (host->use_ecc)
- cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
+ cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
else
- cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
+ cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE;
} else {
- cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
+ cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
}
if (host->use_ecc) {
@@ -1170,7 +1170,7 @@ static int nandc_param(struct qcom_nand_host *host)
* in use. we configure the controller to perform a raw read of 512
* bytes to read onfi params
*/
- nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE);
+ nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
nandc_set_reg(nandc, NAND_ADDR0, 0);
nandc_set_reg(nandc, NAND_ADDR1, 0);
nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
@@ -1224,7 +1224,7 @@ static int erase_block(struct qcom_nand_host *host, int page_addr)
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
nandc_set_reg(nandc, NAND_FLASH_CMD,
- BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
+ OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
nandc_set_reg(nandc, NAND_ADDR0, page_addr);
nandc_set_reg(nandc, NAND_ADDR1, 0);
nandc_set_reg(nandc, NAND_DEV0_CFG0,
@@ -1255,7 +1255,7 @@ static int read_id(struct qcom_nand_host *host, int column)
if (column == -1)
return 0;
- nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
+ nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID);
nandc_set_reg(nandc, NAND_ADDR0, column);
nandc_set_reg(nandc, NAND_ADDR1, 0);
nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
@@ -1276,7 +1276,7 @@ static int reset(struct qcom_nand_host *host)
struct nand_chip *chip = &host->chip;
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
+ nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 6cc9c929ff57..44fe8018733c 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -39,15 +39,6 @@ config SPI_ASPEED_SMC
and support for the SPI flash memory controller (SPI) for
the host firmware. The implementation only supports SPI NOR.
-config SPI_ATMEL_QUADSPI
- tristate "Atmel Quad SPI Controller"
- depends on ARCH_AT91 || (ARM && COMPILE_TEST)
- depends on OF && HAS_IOMEM
- help
- This enables support for the Quad SPI controller in master mode.
- This driver does not support generic SPI. The implementation only
- supports SPI NOR.
-
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
depends on OF && (ARM || ARM64 || COMPILE_TEST)
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index f4c61d282abd..a552efd22958 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
-obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
deleted file mode 100644
index 820048726b4f..000000000000
--- a/drivers/mtd/spi-nor/atmel-quadspi.c
+++ /dev/null
@@ -1,781 +0,0 @@
-/*
- * Driver for Atmel QSPI Controller
- *
- * Copyright (C) 2015 Atmel Corporation
- *
- * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * This driver is based on drivers/mtd/spi-nor/fsl-quadspi.c from Freescale.
- */
-
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/spi-nor.h>
-#include <linux/platform_data/atmel.h>
-#include <linux/of.h>
-
-#include <linux/io.h>
-#include <linux/gpio/consumer.h>
-
-/* QSPI register offsets */
-#define QSPI_CR 0x0000 /* Control Register */
-#define QSPI_MR 0x0004 /* Mode Register */
-#define QSPI_RD 0x0008 /* Receive Data Register */
-#define QSPI_TD 0x000c /* Transmit Data Register */
-#define QSPI_SR 0x0010 /* Status Register */
-#define QSPI_IER 0x0014 /* Interrupt Enable Register */
-#define QSPI_IDR 0x0018 /* Interrupt Disable Register */
-#define QSPI_IMR 0x001c /* Interrupt Mask Register */
-#define QSPI_SCR 0x0020 /* Serial Clock Register */
-
-#define QSPI_IAR 0x0030 /* Instruction Address Register */
-#define QSPI_ICR 0x0034 /* Instruction Code Register */
-#define QSPI_IFR 0x0038 /* Instruction Frame Register */
-
-#define QSPI_SMR 0x0040 /* Scrambling Mode Register */
-#define QSPI_SKR 0x0044 /* Scrambling Key Register */
-
-#define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */
-#define QSPI_WPSR 0x00E8 /* Write Protection Status Register */
-
-#define QSPI_VERSION 0x00FC /* Version Register */
-
-
-/* Bitfields in QSPI_CR (Control Register) */
-#define QSPI_CR_QSPIEN BIT(0)
-#define QSPI_CR_QSPIDIS BIT(1)
-#define QSPI_CR_SWRST BIT(7)
-#define QSPI_CR_LASTXFER BIT(24)
-
-/* Bitfields in QSPI_MR (Mode Register) */
-#define QSPI_MR_SSM BIT(0)
-#define QSPI_MR_LLB BIT(1)
-#define QSPI_MR_WDRBT BIT(2)
-#define QSPI_MR_SMRM BIT(3)
-#define QSPI_MR_CSMODE_MASK GENMASK(5, 4)
-#define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4)
-#define QSPI_MR_CSMODE_LASTXFER (1 << 4)
-#define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4)
-#define QSPI_MR_NBBITS_MASK GENMASK(11, 8)
-#define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK)
-#define QSPI_MR_DLYBCT_MASK GENMASK(23, 16)
-#define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK)
-#define QSPI_MR_DLYCS_MASK GENMASK(31, 24)
-#define QSPI_MR_DLYCS(n) (((n) << 24) & QSPI_MR_DLYCS_MASK)
-
-/* Bitfields in QSPI_SR/QSPI_IER/QSPI_IDR/QSPI_IMR */
-#define QSPI_SR_RDRF BIT(0)
-#define QSPI_SR_TDRE BIT(1)
-#define QSPI_SR_TXEMPTY BIT(2)
-#define QSPI_SR_OVRES BIT(3)
-#define QSPI_SR_CSR BIT(8)
-#define QSPI_SR_CSS BIT(9)
-#define QSPI_SR_INSTRE BIT(10)
-#define QSPI_SR_QSPIENS BIT(24)
-
-#define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR)
-
-/* Bitfields in QSPI_SCR (Serial Clock Register) */
-#define QSPI_SCR_CPOL BIT(0)
-#define QSPI_SCR_CPHA BIT(1)
-#define QSPI_SCR_SCBR_MASK GENMASK(15, 8)
-#define QSPI_SCR_SCBR(n) (((n) << 8) & QSPI_SCR_SCBR_MASK)
-#define QSPI_SCR_DLYBS_MASK GENMASK(23, 16)
-#define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK)
-
-/* Bitfields in QSPI_ICR (Instruction Code Register) */
-#define QSPI_ICR_INST_MASK GENMASK(7, 0)
-#define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK)
-#define QSPI_ICR_OPT_MASK GENMASK(23, 16)
-#define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK)
-
-/* Bitfields in QSPI_IFR (Instruction Frame Register) */
-#define QSPI_IFR_WIDTH_MASK GENMASK(2, 0)
-#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI (0 << 0)
-#define QSPI_IFR_WIDTH_DUAL_OUTPUT (1 << 0)
-#define QSPI_IFR_WIDTH_QUAD_OUTPUT (2 << 0)
-#define QSPI_IFR_WIDTH_DUAL_IO (3 << 0)
-#define QSPI_IFR_WIDTH_QUAD_IO (4 << 0)
-#define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0)
-#define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0)
-#define QSPI_IFR_INSTEN BIT(4)
-#define QSPI_IFR_ADDREN BIT(5)
-#define QSPI_IFR_OPTEN BIT(6)
-#define QSPI_IFR_DATAEN BIT(7)
-#define QSPI_IFR_OPTL_MASK GENMASK(9, 8)
-#define QSPI_IFR_OPTL_1BIT (0 << 8)
-#define QSPI_IFR_OPTL_2BIT (1 << 8)
-#define QSPI_IFR_OPTL_4BIT (2 << 8)
-#define QSPI_IFR_OPTL_8BIT (3 << 8)
-#define QSPI_IFR_ADDRL BIT(10)
-#define QSPI_IFR_TFRTYP_MASK GENMASK(13, 12)
-#define QSPI_IFR_TFRTYP_TRSFR_READ (0 << 12)
-#define QSPI_IFR_TFRTYP_TRSFR_READ_MEM (1 << 12)
-#define QSPI_IFR_TFRTYP_TRSFR_WRITE (2 << 12)
-#define QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM (3 << 13)
-#define QSPI_IFR_CRM BIT(14)
-#define QSPI_IFR_NBDUM_MASK GENMASK(20, 16)
-#define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK)
-
-/* Bitfields in QSPI_SMR (Scrambling Mode Register) */
-#define QSPI_SMR_SCREN BIT(0)
-#define QSPI_SMR_RVDIS BIT(1)
-
-/* Bitfields in QSPI_WPMR (Write Protection Mode Register) */
-#define QSPI_WPMR_WPEN BIT(0)
-#define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8)
-#define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK)
-
-/* Bitfields in QSPI_WPSR (Write Protection Status Register) */
-#define QSPI_WPSR_WPVS BIT(0)
-#define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8)
-#define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC)
-
-
-struct atmel_qspi {
- void __iomem *regs;
- void __iomem *mem;
- struct clk *clk;
- struct platform_device *pdev;
- u32 pending;
-
- struct spi_nor nor;
- u32 clk_rate;
- struct completion cmd_completion;
-};
-
-struct atmel_qspi_command {
- union {
- struct {
- u32 instruction:1;
- u32 address:3;
- u32 mode:1;
- u32 dummy:1;
- u32 data:1;
- u32 reserved:25;
- } bits;
- u32 word;
- } enable;
- u8 instruction;
- u8 mode;
- u8 num_mode_cycles;
- u8 num_dummy_cycles;
- u32 address;
-
- size_t buf_len;
- const void *tx_buf;
- void *rx_buf;
-};
-
-/* Register access functions */
-static inline u32 qspi_readl(struct atmel_qspi *aq, u32 reg)
-{
- return readl_relaxed(aq->regs + reg);
-}
-
-static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value)
-{
- writel_relaxed(value, aq->regs + reg);
-}
-
-static int atmel_qspi_run_transfer(struct atmel_qspi *aq,
- const struct atmel_qspi_command *cmd)
-{
- void __iomem *ahb_mem;
-
- /* Then fallback to a PIO transfer (memcpy() DOES NOT work!) */
- ahb_mem = aq->mem;
- if (cmd->enable.bits.address)
- ahb_mem += cmd->address;
- if (cmd->tx_buf)
- _memcpy_toio(ahb_mem, cmd->tx_buf, cmd->buf_len);
- else
- _memcpy_fromio(cmd->rx_buf, ahb_mem, cmd->buf_len);
-
- return 0;
-}
-
-#ifdef DEBUG
-static void atmel_qspi_debug_command(struct atmel_qspi *aq,
- const struct atmel_qspi_command *cmd,
- u32 ifr)
-{
- u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
- size_t len = 0;
- int i;
-
- if (cmd->enable.bits.instruction)
- cmd_buf[len++] = cmd->instruction;
-
- for (i = cmd->enable.bits.address-1; i >= 0; --i)
- cmd_buf[len++] = (cmd->address >> (i << 3)) & 0xff;
-
- if (cmd->enable.bits.mode)
- cmd_buf[len++] = cmd->mode;
-
- if (cmd->enable.bits.dummy) {
- int num = cmd->num_dummy_cycles;
-
- switch (ifr & QSPI_IFR_WIDTH_MASK) {
- case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
- case QSPI_IFR_WIDTH_DUAL_OUTPUT:
- case QSPI_IFR_WIDTH_QUAD_OUTPUT:
- num >>= 3;
- break;
- case QSPI_IFR_WIDTH_DUAL_IO:
- case QSPI_IFR_WIDTH_DUAL_CMD:
- num >>= 2;
- break;
- case QSPI_IFR_WIDTH_QUAD_IO:
- case QSPI_IFR_WIDTH_QUAD_CMD:
- num >>= 1;
- break;
- default:
- return;
- }
-
- for (i = 0; i < num; ++i)
- cmd_buf[len++] = 0;
- }
-
- /* Dump the SPI command */
- print_hex_dump(KERN_DEBUG, "qspi cmd: ", DUMP_PREFIX_NONE,
- 32, 1, cmd_buf, len, false);
-
-#ifdef VERBOSE_DEBUG
- /* If verbose debug is enabled, also dump the TX data */
- if (cmd->enable.bits.data && cmd->tx_buf)
- print_hex_dump(KERN_DEBUG, "qspi tx : ", DUMP_PREFIX_NONE,
- 32, 1, cmd->tx_buf, cmd->buf_len, false);
-#endif
-}
-#else
-#define atmel_qspi_debug_command(aq, cmd, ifr)
-#endif
-
-static int atmel_qspi_run_command(struct atmel_qspi *aq,
- const struct atmel_qspi_command *cmd,
- u32 ifr_tfrtyp, enum spi_nor_protocol proto)
-{
- u32 iar, icr, ifr, sr;
- int err = 0;
-
- iar = 0;
- icr = 0;
- ifr = ifr_tfrtyp;
-
- /* Set the SPI protocol */
- switch (proto) {
- case SNOR_PROTO_1_1_1:
- ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
- break;
-
- case SNOR_PROTO_1_1_2:
- ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
- break;
-
- case SNOR_PROTO_1_1_4:
- ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
- break;
-
- case SNOR_PROTO_1_2_2:
- ifr |= QSPI_IFR_WIDTH_DUAL_IO;
- break;
-
- case SNOR_PROTO_1_4_4:
- ifr |= QSPI_IFR_WIDTH_QUAD_IO;
- break;
-
- case SNOR_PROTO_2_2_2:
- ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
- break;
-
- case SNOR_PROTO_4_4_4:
- ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
- break;
-
- default:
- return -EINVAL;
- }
-
- /* Compute instruction parameters */
- if (cmd->enable.bits.instruction) {
- icr |= QSPI_ICR_INST(cmd->instruction);
- ifr |= QSPI_IFR_INSTEN;
- }
-
- /* Compute address parameters */
- switch (cmd->enable.bits.address) {
- case 4:
- ifr |= QSPI_IFR_ADDRL;
- /* fall through to the 24bit (3 byte) address case. */
- case 3:
- iar = (cmd->enable.bits.data) ? 0 : cmd->address;
- ifr |= QSPI_IFR_ADDREN;
- break;
- case 0:
- break;
- default:
- return -EINVAL;
- }
-
- /* Compute option parameters */
- if (cmd->enable.bits.mode && cmd->num_mode_cycles) {
- u32 mode_cycle_bits, mode_bits;
-
- icr |= QSPI_ICR_OPT(cmd->mode);
- ifr |= QSPI_IFR_OPTEN;
-
- switch (ifr & QSPI_IFR_WIDTH_MASK) {
- case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
- case QSPI_IFR_WIDTH_DUAL_OUTPUT:
- case QSPI_IFR_WIDTH_QUAD_OUTPUT:
- mode_cycle_bits = 1;
- break;
- case QSPI_IFR_WIDTH_DUAL_IO:
- case QSPI_IFR_WIDTH_DUAL_CMD:
- mode_cycle_bits = 2;
- break;
- case QSPI_IFR_WIDTH_QUAD_IO:
- case QSPI_IFR_WIDTH_QUAD_CMD:
- mode_cycle_bits = 4;
- break;
- default:
- return -EINVAL;
- }
-
- mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
- switch (mode_bits) {
- case 1:
- ifr |= QSPI_IFR_OPTL_1BIT;
- break;
-
- case 2:
- ifr |= QSPI_IFR_OPTL_2BIT;
- break;
-
- case 4:
- ifr |= QSPI_IFR_OPTL_4BIT;
- break;
-
- case 8:
- ifr |= QSPI_IFR_OPTL_8BIT;
- break;
-
- default:
- return -EINVAL;
- }
- }
-
- /* Set number of dummy cycles */
- if (cmd->enable.bits.dummy)
- ifr |= QSPI_IFR_NBDUM(cmd->num_dummy_cycles);
-
- /* Set data enable */
- if (cmd->enable.bits.data) {
- ifr |= QSPI_IFR_DATAEN;
-
- /* Special case for Continuous Read Mode */
- if (!cmd->tx_buf && !cmd->rx_buf)
- ifr |= QSPI_IFR_CRM;
- }
-
- /* Clear pending interrupts */
- (void)qspi_readl(aq, QSPI_SR);
-
- /* Set QSPI Instruction Frame registers */
- atmel_qspi_debug_command(aq, cmd, ifr);
- qspi_writel(aq, QSPI_IAR, iar);
- qspi_writel(aq, QSPI_ICR, icr);
- qspi_writel(aq, QSPI_IFR, ifr);
-
- /* Skip to the final steps if there is no data */
- if (!cmd->enable.bits.data)
- goto no_data;
-
- /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
- (void)qspi_readl(aq, QSPI_IFR);
-
- /* Stop here for continuous read */
- if (!cmd->tx_buf && !cmd->rx_buf)
- return 0;
- /* Send/Receive data */
- err = atmel_qspi_run_transfer(aq, cmd);
-
- /* Release the chip-select */
- qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
-
- if (err)
- return err;
-
-#if defined(DEBUG) && defined(VERBOSE_DEBUG)
- /*
- * If verbose debug is enabled, also dump the RX data in addition to
- * the SPI command previously dumped by atmel_qspi_debug_command()
- */
- if (cmd->rx_buf)
- print_hex_dump(KERN_DEBUG, "qspi rx : ", DUMP_PREFIX_NONE,
- 32, 1, cmd->rx_buf, cmd->buf_len, false);
-#endif
-no_data:
- /* Poll INSTRuction End status */
- sr = qspi_readl(aq, QSPI_SR);
- if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
- return err;
-
- /* Wait for INSTRuction End interrupt */
- reinit_completion(&aq->cmd_completion);
- aq->pending = sr & QSPI_SR_CMD_COMPLETED;
- qspi_writel(aq, QSPI_IER, QSPI_SR_CMD_COMPLETED);
- if (!wait_for_completion_timeout(&aq->cmd_completion,
- msecs_to_jiffies(1000)))
- err = -ETIMEDOUT;
- qspi_writel(aq, QSPI_IDR, QSPI_SR_CMD_COMPLETED);
-
- return err;
-}
-
-static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
- u8 *buf, int len)
-{
- struct atmel_qspi *aq = nor->priv;
- struct atmel_qspi_command cmd;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.enable.bits.instruction = 1;
- cmd.enable.bits.data = 1;
- cmd.instruction = opcode;
- cmd.rx_buf = buf;
- cmd.buf_len = len;
- return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
- nor->reg_proto);
-}
-
-static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
- u8 *buf, int len)
-{
- struct atmel_qspi *aq = nor->priv;
- struct atmel_qspi_command cmd;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.enable.bits.instruction = 1;
- cmd.enable.bits.data = (buf != NULL && len > 0);
- cmd.instruction = opcode;
- cmd.tx_buf = buf;
- cmd.buf_len = len;
- return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- nor->reg_proto);
-}
-
-static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
- const u_char *write_buf)
-{
- struct atmel_qspi *aq = nor->priv;
- struct atmel_qspi_command cmd;
- ssize_t ret;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.enable.bits.instruction = 1;
- cmd.enable.bits.address = nor->addr_width;
- cmd.enable.bits.data = 1;
- cmd.instruction = nor->program_opcode;
- cmd.address = (u32)to;
- cmd.tx_buf = write_buf;
- cmd.buf_len = len;
- ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
- nor->write_proto);
- return (ret < 0) ? ret : len;
-}
-
-static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
-{
- struct atmel_qspi *aq = nor->priv;
- struct atmel_qspi_command cmd;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.enable.bits.instruction = 1;
- cmd.enable.bits.address = nor->addr_width;
- cmd.instruction = nor->erase_opcode;
- cmd.address = (u32)offs;
- return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- nor->reg_proto);
-}
-
-static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
- u_char *read_buf)
-{
- struct atmel_qspi *aq = nor->priv;
- struct atmel_qspi_command cmd;
- u8 num_mode_cycles, num_dummy_cycles;
- ssize_t ret;
-
- if (nor->read_dummy >= 2) {
- num_mode_cycles = 2;
- num_dummy_cycles = nor->read_dummy - 2;
- } else {
- num_mode_cycles = nor->read_dummy;
- num_dummy_cycles = 0;
- }
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.enable.bits.instruction = 1;
- cmd.enable.bits.address = nor->addr_width;
- cmd.enable.bits.mode = (num_mode_cycles > 0);
- cmd.enable.bits.dummy = (num_dummy_cycles > 0);
- cmd.enable.bits.data = 1;
- cmd.instruction = nor->read_opcode;
- cmd.address = (u32)from;
- cmd.mode = 0xff; /* This value prevents from entering the 0-4-4 mode */
- cmd.num_mode_cycles = num_mode_cycles;
- cmd.num_dummy_cycles = num_dummy_cycles;
- cmd.rx_buf = read_buf;
- cmd.buf_len = len;
- ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
- nor->read_proto);
- return (ret < 0) ? ret : len;
-}
-
-static int atmel_qspi_init(struct atmel_qspi *aq)
-{
- unsigned long src_rate;
- u32 mr, scr, scbr;
-
- /* Reset the QSPI controller */
- qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
-
- /* Set the QSPI controller in Serial Memory Mode */
- mr = QSPI_MR_NBBITS(8) | QSPI_MR_SSM;
- qspi_writel(aq, QSPI_MR, mr);
-
- src_rate = clk_get_rate(aq->clk);
- if (!src_rate)
- return -EINVAL;
-
- /* Compute the QSPI baudrate */
- scbr = DIV_ROUND_UP(src_rate, aq->clk_rate);
- if (scbr > 0)
- scbr--;
- scr = QSPI_SCR_SCBR(scbr);
- qspi_writel(aq, QSPI_SCR, scr);
-
- /* Enable the QSPI controller */
- qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
-
- return 0;
-}
-
-static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
-{
- struct atmel_qspi *aq = (struct atmel_qspi *)dev_id;
- u32 status, mask, pending;
-
- status = qspi_readl(aq, QSPI_SR);
- mask = qspi_readl(aq, QSPI_IMR);
- pending = status & mask;
-
- if (!pending)
- return IRQ_NONE;
-
- aq->pending |= pending;
- if ((aq->pending & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
- complete(&aq->cmd_completion);
-
- return IRQ_HANDLED;
-}
-
-static int atmel_qspi_probe(struct platform_device *pdev)
-{
- const struct spi_nor_hwcaps hwcaps = {
- .mask = SNOR_HWCAPS_READ |
- SNOR_HWCAPS_READ_FAST |
- SNOR_HWCAPS_READ_1_1_2 |
- SNOR_HWCAPS_READ_1_2_2 |
- SNOR_HWCAPS_READ_2_2_2 |
- SNOR_HWCAPS_READ_1_1_4 |
- SNOR_HWCAPS_READ_1_4_4 |
- SNOR_HWCAPS_READ_4_4_4 |
- SNOR_HWCAPS_PP |
- SNOR_HWCAPS_PP_1_1_4 |
- SNOR_HWCAPS_PP_1_4_4 |
- SNOR_HWCAPS_PP_4_4_4,
- };
- struct device_node *child, *np = pdev->dev.of_node;
- struct atmel_qspi *aq;
- struct resource *res;
- struct spi_nor *nor;
- struct mtd_info *mtd;
- int irq, err = 0;
-
- if (of_get_child_count(np) != 1)
- return -ENODEV;
- child = of_get_next_child(np, NULL);
-
- aq = devm_kzalloc(&pdev->dev, sizeof(*aq), GFP_KERNEL);
- if (!aq) {
- err = -ENOMEM;
- goto exit;
- }
-
- platform_set_drvdata(pdev, aq);
- init_completion(&aq->cmd_completion);
- aq->pdev = pdev;
-
- /* Map the registers */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");
- aq->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(aq->regs)) {
- dev_err(&pdev->dev, "missing registers\n");
- err = PTR_ERR(aq->regs);
- goto exit;
- }
-
- /* Map the AHB memory */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mmap");
- aq->mem = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(aq->mem)) {
- dev_err(&pdev->dev, "missing AHB memory\n");
- err = PTR_ERR(aq->mem);
- goto exit;
- }
-
- /* Get the peripheral clock */
- aq->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(aq->clk)) {
- dev_err(&pdev->dev, "missing peripheral clock\n");
- err = PTR_ERR(aq->clk);
- goto exit;
- }
-
- /* Enable the peripheral clock */
- err = clk_prepare_enable(aq->clk);
- if (err) {
- dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
- goto exit;
- }
-
- /* Request the IRQ */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "missing IRQ\n");
- err = irq;
- goto disable_clk;
- }
- err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
- 0, dev_name(&pdev->dev), aq);
- if (err)
- goto disable_clk;
-
- /* Setup the spi-nor */
- nor = &aq->nor;
- mtd = &nor->mtd;
-
- nor->dev = &pdev->dev;
- spi_nor_set_flash_node(nor, child);
- nor->priv = aq;
- mtd->priv = nor;
-
- nor->read_reg = atmel_qspi_read_reg;
- nor->write_reg = atmel_qspi_write_reg;
- nor->read = atmel_qspi_read;
- nor->write = atmel_qspi_write;
- nor->erase = atmel_qspi_erase;
-
- err = of_property_read_u32(child, "spi-max-frequency", &aq->clk_rate);
- if (err < 0)
- goto disable_clk;
-
- err = atmel_qspi_init(aq);
- if (err)
- goto disable_clk;
-
- err = spi_nor_scan(nor, NULL, &hwcaps);
- if (err)
- goto disable_clk;
-
- err = mtd_device_register(mtd, NULL, 0);
- if (err)
- goto disable_clk;
-
- of_node_put(child);
-
- return 0;
-
-disable_clk:
- clk_disable_unprepare(aq->clk);
-exit:
- of_node_put(child);
-
- return err;
-}
-
-static int atmel_qspi_remove(struct platform_device *pdev)
-{
- struct atmel_qspi *aq = platform_get_drvdata(pdev);
-
- mtd_device_unregister(&aq->nor.mtd);
- qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS);
- clk_disable_unprepare(aq->clk);
- return 0;
-}
-
-static int __maybe_unused atmel_qspi_suspend(struct device *dev)
-{
- struct atmel_qspi *aq = dev_get_drvdata(dev);
-
- clk_disable_unprepare(aq->clk);
-
- return 0;
-}
-
-static int __maybe_unused atmel_qspi_resume(struct device *dev)
-{
- struct atmel_qspi *aq = dev_get_drvdata(dev);
-
- clk_prepare_enable(aq->clk);
-
- return atmel_qspi_init(aq);
-}
-
-static SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend,
- atmel_qspi_resume);
-
-static const struct of_device_id atmel_qspi_dt_ids[] = {
- { .compatible = "atmel,sama5d2-qspi" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
-
-static struct platform_driver atmel_qspi_driver = {
- .driver = {
- .name = "atmel_qspi",
- .of_match_table = atmel_qspi_dt_ids,
- .pm = &atmel_qspi_pm_ops,
- },
- .probe = atmel_qspi_probe,
- .remove = atmel_qspi_remove,
-};
-module_platform_driver(atmel_qspi_driver);
-
-MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
-MODULE_DESCRIPTION("Atmel QSPI Controller driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index e24db817154e..04cedd3a2bf6 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -644,9 +644,23 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
ndelay(cqspi->wr_delay);
while (remaining > 0) {
+ size_t write_words, mod_bytes;
+
write_bytes = remaining > page_size ? page_size : remaining;
- iowrite32_rep(cqspi->ahb_base, txbuf,
- DIV_ROUND_UP(write_bytes, 4));
+ write_words = write_bytes / 4;
+ mod_bytes = write_bytes % 4;
+ /* Write 4 bytes at a time then single bytes. */
+ if (write_words) {
+ iowrite32_rep(cqspi->ahb_base, txbuf, write_words);
+ txbuf += (write_words * 4);
+ }
+ if (mod_bytes) {
+ unsigned int temp = 0xFFFFFFFF;
+
+ memcpy(&temp, txbuf, mod_bytes);
+ iowrite32(temp, cqspi->ahb_base);
+ txbuf += mod_bytes;
+ }
if (!wait_for_completion_timeout(&cqspi->transfer_complete,
msecs_to_jiffies(CQSPI_TIMEOUT_MS))) {
@@ -655,7 +669,6 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
goto failwr;
}
- txbuf += write_bytes;
remaining -= write_bytes;
if (remaining > 0)
@@ -996,7 +1009,7 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf,
err_unmap:
dma_unmap_single(nor->dev, dma_dst, len, DMA_FROM_DEVICE);
- return 0;
+ return ret;
}
static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9407ca5f9443..1fdd2834fbcb 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -2156,7 +2156,7 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
* @nor: pointer to a 'struct spi_nor'
* @addr: offset in the serial flash memory
* @len: number of bytes to read
- * @buf: buffer where the data is copied into
+ * @buf: buffer where the data is copied into (dma-safe memory)
*
* Return: 0 on success, -errno otherwise.
*/
@@ -2522,6 +2522,34 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
}
/**
+ * spi_nor_sort_erase_mask() - sort erase mask
+ * @map: the erase map of the SPI NOR
+ * @erase_mask: the erase type mask to be sorted
+ *
+ * Replicate the sort done for the map's erase types in BFPT: sort the erase
+ * mask in ascending order with the smallest erase type size starting from
+ * BIT(0) in the sorted erase mask.
+ *
+ * Return: sorted erase mask.
+ */
+static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask)
+{
+ struct spi_nor_erase_type *erase_type = map->erase_type;
+ int i;
+ u8 sorted_erase_mask = 0;
+
+ if (!erase_mask)
+ return 0;
+
+ /* Replicate the sort done for the map's erase types. */
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx))
+ sorted_erase_mask |= BIT(i);
+
+ return sorted_erase_mask;
+}
+
+/**
* spi_nor_regions_sort_erase_types() - sort erase types in each region
* @map: the erase map of the SPI NOR
*
@@ -2536,19 +2564,13 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r)
static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
{
struct spi_nor_erase_region *region = map->regions;
- struct spi_nor_erase_type *erase_type = map->erase_type;
- int i;
u8 region_erase_mask, sorted_erase_mask;
while (region) {
region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
- /* Replicate the sort done for the map's erase types. */
- sorted_erase_mask = 0;
- for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
- if (erase_type[i].size &&
- region_erase_mask & BIT(erase_type[i].idx))
- sorted_erase_mask |= BIT(i);
+ sorted_erase_mask = spi_nor_sort_erase_mask(map,
+ region_erase_mask);
/* Overwrite erase mask. */
region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
@@ -2855,52 +2877,84 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
* spi_nor_get_map_in_use() - get the configuration map in use
* @nor: pointer to a 'struct spi_nor'
* @smpt: pointer to the sector map parameter table
+ * @smpt_len: sector map parameter table length
+ *
+ * Return: pointer to the map in use, ERR_PTR(-errno) otherwise.
*/
-static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt)
+static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
+ u8 smpt_len)
{
- const u32 *ret = NULL;
- u32 i, addr;
+ const u32 *ret;
+ u8 *buf;
+ u32 addr;
int err;
+ u8 i;
u8 addr_width, read_opcode, read_dummy;
- u8 read_data_mask, data_byte, map_id;
+ u8 read_data_mask, map_id;
+
+ /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
addr_width = nor->addr_width;
read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode;
map_id = 0;
- i = 0;
/* Determine if there are any optional Detection Command Descriptors */
- while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) {
+ for (i = 0; i < smpt_len; i += 2) {
+ if (smpt[i] & SMPT_DESC_TYPE_MAP)
+ break;
+
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]);
nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
addr = smpt[i + 1];
- err = spi_nor_read_raw(nor, addr, 1, &data_byte);
- if (err)
+ err = spi_nor_read_raw(nor, addr, 1, buf);
+ if (err) {
+ ret = ERR_PTR(err);
goto out;
+ }
/*
* Build an index value that is used to select the Sector Map
* Configuration that is currently in use.
*/
- map_id = map_id << 1 | !!(data_byte & read_data_mask);
- i = i + 2;
+ map_id = map_id << 1 | !!(*buf & read_data_mask);
}
- /* Find the matching configuration map */
- while (SMPT_MAP_ID(smpt[i]) != map_id) {
+ /*
+ * If command descriptors are provided, they always precede map
+ * descriptors in the table. There is no need to start the iteration
+ * over smpt array all over again.
+ *
+ * Find the matching configuration map.
+ */
+ ret = ERR_PTR(-EINVAL);
+ while (i < smpt_len) {
+ if (SMPT_MAP_ID(smpt[i]) == map_id) {
+ ret = smpt + i;
+ break;
+ }
+
+ /*
+ * If there are no more configuration map descriptors and no
+ * configuration ID matched the configuration identifier, the
+ * sector address map is unknown.
+ */
if (smpt[i] & SMPT_DESC_END)
- goto out;
+ break;
+
/* increment the table index to the next map */
i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1;
}
- ret = smpt + i;
/* fall through */
out:
+ kfree(buf);
nor->addr_width = addr_width;
nor->read_dummy = read_dummy;
nor->read_opcode = read_opcode;
@@ -2941,12 +2995,13 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
const u32 *smpt)
{
struct spi_nor_erase_map *map = &nor->erase_map;
- const struct spi_nor_erase_type *erase = map->erase_type;
+ struct spi_nor_erase_type *erase = map->erase_type;
struct spi_nor_erase_region *region;
u64 offset;
u32 region_count;
int i, j;
- u8 erase_type;
+ u8 uniform_erase_type, save_uniform_erase_type;
+ u8 erase_type, regions_erase_type;
region_count = SMPT_MAP_REGION_COUNT(*smpt);
/*
@@ -2959,7 +3014,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
return -ENOMEM;
map->regions = region;
- map->uniform_erase_type = 0xff;
+ uniform_erase_type = 0xff;
+ regions_erase_type = 0;
offset = 0;
/* Populate regions. */
for (i = 0; i < region_count; i++) {
@@ -2974,12 +3030,40 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
* Save the erase types that are supported in all regions and
* can erase the entire flash memory.
*/
- map->uniform_erase_type &= erase_type;
+ uniform_erase_type &= erase_type;
+
+ /*
+ * regions_erase_type mask will indicate all the erase types
+ * supported in this configuration map.
+ */
+ regions_erase_type |= erase_type;
offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
region[i].size;
}
+ save_uniform_erase_type = map->uniform_erase_type;
+ map->uniform_erase_type = spi_nor_sort_erase_mask(map,
+ uniform_erase_type);
+
+ if (!regions_erase_type) {
+ /*
+ * Roll back to the previous uniform_erase_type mask, SMPT is
+ * broken.
+ */
+ map->uniform_erase_type = save_uniform_erase_type;
+ return -EINVAL;
+ }
+
+ /*
+ * BFPT advertises all the erase types supported by all the possible
+ * map configurations. Mask out the erase types that are not supported
+ * by the current map configuration.
+ */
+ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
+ if (!(regions_erase_type & BIT(erase[i].idx)))
+ spi_nor_set_erase_type(&erase[i], 0, 0xFF);
+
spi_nor_region_mark_end(&region[i - 1]);
return 0;
@@ -3020,9 +3104,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
for (i = 0; i < smpt_header->length; i++)
smpt[i] = le32_to_cpu(smpt[i]);
- sector_map = spi_nor_get_map_in_use(nor, smpt);
- if (!sector_map) {
- ret = -EINVAL;
+ sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length);
+ if (IS_ERR(sector_map)) {
+ ret = PTR_ERR(sector_map);
goto out;
}
@@ -3125,7 +3209,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
if (err)
goto exit;
- /* Parse other parameter headers. */
+ /* Parse optional parameter tables. */
for (i = 0; i < header.nph; i++) {
param_header = &param_headers[i];
@@ -3138,8 +3222,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
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:
@@ -3250,12 +3343,14 @@ static int spi_nor_init_params(struct spi_nor *nor,
memcpy(&sfdp_params, params, sizeof(sfdp_params));
memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
- if (spi_nor_parse_sfdp(nor, &sfdp_params))
+ if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
+ nor->addr_width = 0;
/* restore previous erase map */
memcpy(&nor->erase_map, &prev_map,
sizeof(nor->erase_map));
- else
+ } else {
memcpy(params, &sfdp_params, sizeof(*params));
+ }
}
return 0;
OpenPOWER on IntegriCloud