Browse Source

Merge tag 'spi-nor/for-4.19' of git://git.infradead.org/linux-mtd into mtd/next

Pull SPI NOR updates from Boris Brezillon:
"
 Core changes:
 - Apply reset hacks only when reset is explicitly marked as broken in
   the DT

 Driver changes:
 - Minor cleanup/fixes in the m25p80 driver
 - Release flash_np in the nxp-spifi driver
 - Add suspend/resume hooks to the atmel-quadspi driver
 - Include gpio/consumer.h instead of gpio.h in the atmel-quadspi driver
 - Use %pK instead of %p in the stm32-quadspi driver
 - Improve timeout handling in the cadence-quadspi driver
 - Use mtd_device_register() instead of mtd_device_parse_register() in
   the intel-spi driver
"
Boris Brezillon 7 years ago
parent
commit
d0eea5d8db

+ 9 - 0
Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt

@@ -69,6 +69,15 @@ Optional properties:
                    all chips and support for it can not be detected at runtime.
                    all chips and support for it can not be detected at runtime.
                    Refer to your chips' datasheet to check if this is supported
                    Refer to your chips' datasheet to check if this is supported
                    by your chip.
                    by your chip.
+- broken-flash-reset : Some flash devices utilize stateful addressing modes
+		   (e.g., for 32-bit addressing) which need to be managed
+		   carefully by a system. Because these sorts of flash don't
+		   have a standardized software reset command, and because some
+		   systems don't toggle the flash RESET# pin upon system reset
+		   (if the pin even exists at all), there are systems which
+		   cannot reboot properly if the flash is left in the "wrong"
+		   state. This boolean flag can be used on such systems, to
+		   denote the absence of a reliable reset mechanism.
 
 
 Example:
 Example:
 
 

+ 1 - 4
drivers/mtd/devices/m25p80.c

@@ -28,11 +28,9 @@
 #include <linux/spi/flash.h>
 #include <linux/spi/flash.h>
 #include <linux/mtd/spi-nor.h>
 #include <linux/mtd/spi-nor.h>
 
 
-#define	MAX_CMD_SIZE		6
 struct m25p {
 struct m25p {
 	struct spi_mem		*spimem;
 	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	struct spi_nor		spi_nor;
-	u8			command[MAX_CMD_SIZE];
 };
 };
 
 
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
@@ -70,7 +68,7 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 	struct spi_mem_op op =
 	struct spi_mem_op op =
 			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
 			SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
 				   SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
 				   SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
-				   SPI_MEM_OP_DUMMY(0, 1),
+				   SPI_MEM_OP_NO_DUMMY,
 				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
 				   SPI_MEM_OP_DATA_OUT(len, buf, 1));
 	size_t remaining = len;
 	size_t remaining = len;
 	int ret;
 	int ret;
@@ -78,7 +76,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 	/* get transfer protocols. */
 	/* get transfer protocols. */
 	op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
 	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.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-	op.dummy.buswidth = op.addr.buswidth;
 	op.data.buswidth = spi_nor_get_protocol_data_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)
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)

+ 22 - 1
drivers/mtd/spi-nor/atmel-quadspi.c

@@ -34,7 +34,7 @@
 #include <linux/of.h>
 #include <linux/of.h>
 
 
 #include <linux/io.h>
 #include <linux/io.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 
 
 /* QSPI register offsets */
 /* QSPI register offsets */
 #define QSPI_CR      0x0000  /* Control Register */
 #define QSPI_CR      0x0000  /* Control Register */
@@ -737,6 +737,26 @@ static int atmel_qspi_remove(struct platform_device *pdev)
 	return 0;
 	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[] = {
 static const struct of_device_id atmel_qspi_dt_ids[] = {
 	{ .compatible = "atmel,sama5d2-qspi" },
 	{ .compatible = "atmel,sama5d2-qspi" },
@@ -749,6 +769,7 @@ static struct platform_driver atmel_qspi_driver = {
 	.driver = {
 	.driver = {
 		.name	= "atmel_qspi",
 		.name	= "atmel_qspi",
 		.of_match_table	= atmel_qspi_dt_ids,
 		.of_match_table	= atmel_qspi_dt_ids,
+		.pm	= &atmel_qspi_pm_ops,
 	},
 	},
 	.probe		= atmel_qspi_probe,
 	.probe		= atmel_qspi_probe,
 	.remove		= atmel_qspi_remove,
 	.remove		= atmel_qspi_remove,

+ 8 - 12
drivers/mtd/spi-nor/cadence-quadspi.c

@@ -525,15 +525,14 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf,
 	       reg_base + CQSPI_REG_INDIRECTRD);
 	       reg_base + CQSPI_REG_INDIRECTRD);
 
 
 	while (remaining > 0) {
 	while (remaining > 0) {
-		ret = wait_for_completion_timeout(&cqspi->transfer_complete,
-						  msecs_to_jiffies
-						  (CQSPI_READ_TIMEOUT_MS));
+		if (!wait_for_completion_timeout(&cqspi->transfer_complete,
+				msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS)))
+			ret = -ETIMEDOUT;
 
 
 		bytes_to_read = cqspi_get_rd_sram_level(cqspi);
 		bytes_to_read = cqspi_get_rd_sram_level(cqspi);
 
 
-		if (!ret && bytes_to_read == 0) {
+		if (ret && bytes_to_read == 0) {
 			dev_err(nor->dev, "Indirect read timeout, no bytes\n");
 			dev_err(nor->dev, "Indirect read timeout, no bytes\n");
-			ret = -ETIMEDOUT;
 			goto failrd;
 			goto failrd;
 		}
 		}
 
 
@@ -649,10 +648,8 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr,
 		iowrite32_rep(cqspi->ahb_base, txbuf,
 		iowrite32_rep(cqspi->ahb_base, txbuf,
 			      DIV_ROUND_UP(write_bytes, 4));
 			      DIV_ROUND_UP(write_bytes, 4));
 
 
-		ret = wait_for_completion_timeout(&cqspi->transfer_complete,
-						  msecs_to_jiffies
-						  (CQSPI_TIMEOUT_MS));
-		if (!ret) {
+		if (!wait_for_completion_timeout(&cqspi->transfer_complete,
+					msecs_to_jiffies(CQSPI_TIMEOUT_MS))) {
 			dev_err(nor->dev, "Indirect write timeout\n");
 			dev_err(nor->dev, "Indirect write timeout\n");
 			ret = -ETIMEDOUT;
 			ret = -ETIMEDOUT;
 			goto failwr;
 			goto failwr;
@@ -986,9 +983,8 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf,
 	}
 	}
 
 
 	dma_async_issue_pending(cqspi->rx_chan);
 	dma_async_issue_pending(cqspi->rx_chan);
-	ret = wait_for_completion_timeout(&cqspi->rx_dma_complete,
-					  msecs_to_jiffies(len));
-	if (ret <= 0) {
+	if (!wait_for_completion_timeout(&cqspi->rx_dma_complete,
+					 msecs_to_jiffies(len))) {
 		dmaengine_terminate_sync(cqspi->rx_chan);
 		dmaengine_terminate_sync(cqspi->rx_chan);
 		dev_err(nor->dev, "DMA wait_for_completion_timeout\n");
 		dev_err(nor->dev, "DMA wait_for_completion_timeout\n");
 		ret = -ETIMEDOUT;
 		ret = -ETIMEDOUT;

+ 1 - 1
drivers/mtd/spi-nor/intel-spi.c

@@ -908,7 +908,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
 	if (!ispi->writeable || !writeable)
 	if (!ispi->writeable || !writeable)
 		ispi->nor.mtd.flags &= ~MTD_WRITEABLE;
 		ispi->nor.mtd.flags &= ~MTD_WRITEABLE;
 
 
-	ret = mtd_device_parse_register(&ispi->nor.mtd, NULL, NULL, &part, 1);
+	ret = mtd_device_register(&ispi->nor.mtd, &part, 1);
 	if (ret)
 	if (ret)
 		return ERR_PTR(ret);
 		return ERR_PTR(ret);
 
 

+ 1 - 0
drivers/mtd/spi-nor/nxp-spifi.c

@@ -436,6 +436,7 @@ static int nxp_spifi_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	ret = nxp_spifi_setup_flash(spifi, flash_np);
 	ret = nxp_spifi_setup_flash(spifi, flash_np);
+	of_node_put(flash_np);
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "unable to setup flash chip\n");
 		dev_err(&pdev->dev, "unable to setup flash chip\n");
 		goto dis_clks;
 		goto dis_clks;

+ 16 - 2
drivers/mtd/spi-nor/spi-nor.c

@@ -2757,8 +2757,18 @@ static int spi_nor_init(struct spi_nor *nor)
 
 
 	if ((nor->addr_width == 4) &&
 	if ((nor->addr_width == 4) &&
 	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
 	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-	    !(nor->info->flags & SPI_NOR_4B_OPCODES))
+	    !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
+		/*
+		 * If the RESET# pin isn't hooked up properly, or the system
+		 * otherwise doesn't perform a reset command in the boot
+		 * sequence, it's impossible to 100% protect against unexpected
+		 * reboots (e.g., crashes). Warn the user (or hopefully, system
+		 * designer) that this is bad.
+		 */
+		WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
+			  "enabling reset hack; may not recover from unexpected reboots\n");
 		set_4byte(nor, nor->info, 1);
 		set_4byte(nor, nor->info, 1);
+	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -2781,7 +2791,8 @@ void spi_nor_restore(struct spi_nor *nor)
 	/* restore the addressing mode */
 	/* restore the addressing mode */
 	if ((nor->addr_width == 4) &&
 	if ((nor->addr_width == 4) &&
 	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
 	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-	    !(nor->info->flags & SPI_NOR_4B_OPCODES))
+	    !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
+	    (nor->flags & SNOR_F_BROKEN_RESET))
 		set_4byte(nor, nor->info, 0);
 		set_4byte(nor, nor->info, 0);
 }
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
 EXPORT_SYMBOL_GPL(spi_nor_restore);
@@ -2911,6 +2922,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
 		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 */
 	/* Some devices cannot do fast-read, no matter what DT tells us */
 	if (info->flags & SPI_NOR_NO_FR)
 	if (info->flags & SPI_NOR_NO_FR)
 		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
 		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;

+ 3 - 3
drivers/mtd/spi-nor/stm32-quadspi.c

@@ -355,7 +355,7 @@ static int stm32_qspi_read_reg(struct spi_nor *nor,
 	struct device *dev = flash->qspi->dev;
 	struct device *dev = flash->qspi->dev;
 	struct stm32_qspi_cmd cmd;
 	struct stm32_qspi_cmd cmd;
 
 
-	dev_dbg(dev, "read_reg: cmd:%#.2x buf:%p len:%#x\n", opcode, buf, len);
+	dev_dbg(dev, "read_reg: cmd:%#.2x buf:%pK len:%#x\n", opcode, buf, len);
 
 
 	memset(&cmd, 0, sizeof(cmd));
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.opcode = opcode;
 	cmd.opcode = opcode;
@@ -376,7 +376,7 @@ static int stm32_qspi_write_reg(struct spi_nor *nor, u8 opcode,
 	struct device *dev = flash->qspi->dev;
 	struct device *dev = flash->qspi->dev;
 	struct stm32_qspi_cmd cmd;
 	struct stm32_qspi_cmd cmd;
 
 
-	dev_dbg(dev, "write_reg: cmd:%#.2x buf:%p len:%#x\n", opcode, buf, len);
+	dev_dbg(dev, "write_reg: cmd:%#.2x buf:%pK len:%#x\n", opcode, buf, len);
 
 
 	memset(&cmd, 0, sizeof(cmd));
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.opcode = opcode;
 	cmd.opcode = opcode;
@@ -398,7 +398,7 @@ static ssize_t stm32_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
 	struct stm32_qspi_cmd cmd;
 	struct stm32_qspi_cmd cmd;
 	int err;
 	int err;
 
 
-	dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#zx\n",
+	dev_dbg(qspi->dev, "read(%#.2x): buf:%pK from:%#.8x len:%#zx\n",
 		nor->read_opcode, buf, (u32)from, len);
 		nor->read_opcode, buf, (u32)from, len);
 
 
 	memset(&cmd, 0, sizeof(cmd));
 	memset(&cmd, 0, sizeof(cmd));

+ 1 - 0
include/linux/mtd/spi-nor.h

@@ -235,6 +235,7 @@ enum spi_nor_option_flags {
 	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
 	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
 	SNOR_F_READY_XSR_RDY	= BIT(4),
 	SNOR_F_READY_XSR_RDY	= BIT(4),
 	SNOR_F_USE_CLSR		= BIT(5),
 	SNOR_F_USE_CLSR		= BIT(5),
+	SNOR_F_BROKEN_RESET	= BIT(6),
 };
 };
 
 
 /**
 /**