diff options
| -rw-r--r-- | Documentation/devicetree/bindings/mtd/cadence-quadspi.txt | 7 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/mtd/mtk-quadspi.txt | 15 | ||||
| -rw-r--r-- | drivers/mtd/devices/m25p80.c | 1 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/cadence-quadspi.c | 55 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/intel-spi-pci.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/intel-spi.c | 209 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/mtk-quadspi.c | 70 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/spi-nor.c | 105 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/stm32-quadspi.c | 35 | ||||
| -rw-r--r-- | include/linux/mtd/spi-nor.h | 10 | 
12 files changed, 404 insertions, 113 deletions
| diff --git a/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt index f248056da24c..bb2075df9b38 100644 --- a/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt @@ -1,7 +1,9 @@  * Cadence Quad SPI controller  Required properties: -- compatible : Should be "cdns,qspi-nor". +- compatible : should be one of the following: +	Generic default - "cdns,qspi-nor". +	For TI 66AK2G SoC - "ti,k2g-qspi", "cdns,qspi-nor".  - reg : Contains two entries, each of which is a tuple consisting of a  	physical address and length. The first entry is the address and  	length of the controller register set. The second entry is the @@ -14,6 +16,9 @@ Required properties:  Optional properties:  - cdns,is-decoded-cs : Flag to indicate whether decoder is used or not. +- cdns,rclk-en : Flag to indicate that QSPI return clock is used to latch +  the read data rather than the QSPI clock. Make sure that QSPI return +  clock is populated on the board before using this property.  Optional subnodes:  Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt index 9ce35af8507c..956bb046e599 100644 --- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt +++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt @@ -13,6 +13,7 @@ Required properties:                   at25df321a                   at25df641                   at26df081a +                 mr25h128                   mr25h256                   mr25h10                   mr25h40 diff --git a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt index 840f9405dcf0..56d3668e2c50 100644 --- a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt +++ b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt @@ -1,13 +1,16 @@  * Serial NOR flash controller for MTK MT81xx (and similar)  Required properties: -- compatible: 	  The possible values are: -		  "mediatek,mt2701-nor" -		  "mediatek,mt7623-nor" +- compatible: 	  For mt8173, compatible should be "mediatek,mt8173-nor", +		  and it's the fallback compatible for other Soc. +		  For every other SoC, should contain both the SoC-specific compatible +		  string and "mediatek,mt8173-nor". +		  The possible values are: +		  "mediatek,mt2701-nor", "mediatek,mt8173-nor" +		  "mediatek,mt2712-nor", "mediatek,mt8173-nor" +		  "mediatek,mt7622-nor", "mediatek,mt8173-nor" +		  "mediatek,mt7623-nor", "mediatek,mt8173-nor"  		  "mediatek,mt8173-nor" -		  For mt8173, compatible should be "mediatek,mt8173-nor". -		  For every other SoC, should contain both the SoC-specific compatible string -		  and "mediatek,mt8173-nor".  - reg: 		  physical base address and length of the controller's register  - clocks: 	  the phandle of the clocks needed by the nor controller  - clock-names: 	  the names of the clocks diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 00eea6fd379c..dbe6a1de2bb8 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -359,6 +359,7 @@ static const struct spi_device_id m25p_ids[] = {  	{"m25p32-nonjedec"},	{"m25p64-nonjedec"},	{"m25p128-nonjedec"},  	/* Everspin MRAMs (non-JEDEC) */ +	{ "mr25h128" }, /* 128 Kib, 40 MHz */  	{ "mr25h256" }, /* 256 Kib, 40 MHz */  	{ "mr25h10" },  /*   1 Mib, 40 MHz */  	{ "mr25h40" },  /*   4 Mib, 40 MHz */ diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 69c638dd0484..89da88e59121 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -50,7 +50,7 @@ config SPI_ATMEL_QUADSPI  config SPI_CADENCE_QUADSPI  	tristate "Cadence Quad SPI controller" -	depends on OF && (ARM || COMPILE_TEST) +	depends on OF && (ARM || ARM64 || COMPILE_TEST)  	help  	  Enable support for the Cadence Quad SPI Flash controller. @@ -90,7 +90,7 @@ config SPI_INTEL_SPI  	tristate  config SPI_INTEL_SPI_PCI -	tristate "Intel PCH/PCU SPI flash PCI driver" if EXPERT +	tristate "Intel PCH/PCU SPI flash PCI driver"  	depends on X86 && PCI  	select SPI_INTEL_SPI  	help @@ -106,7 +106,7 @@ config SPI_INTEL_SPI_PCI  	  will be called intel-spi-pci.  config SPI_INTEL_SPI_PLATFORM -	tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT +	tristate "Intel PCH/PCU SPI flash platform driver"  	depends on X86  	select SPI_INTEL_SPI  	help diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 53c7d8e0327a..75a2bc447a99 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -31,6 +31,7 @@  #include <linux/of_device.h>  #include <linux/of.h>  #include <linux/platform_device.h> +#include <linux/pm_runtime.h>  #include <linux/sched.h>  #include <linux/spi/spi.h>  #include <linux/timer.h> @@ -38,6 +39,9 @@  #define CQSPI_NAME			"cadence-qspi"  #define CQSPI_MAX_CHIPSELECT		16 +/* Quirks */ +#define CQSPI_NEEDS_WR_DELAY		BIT(0) +  struct cqspi_st;  struct cqspi_flash_pdata { @@ -75,7 +79,9 @@ struct cqspi_st {  	bool			is_decoded_cs;  	u32			fifo_depth;  	u32			fifo_width; +	bool			rclk_en;  	u32			trigger_address; +	u32			wr_delay;  	struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT];  }; @@ -608,6 +614,15 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor,  	reinit_completion(&cqspi->transfer_complete);  	writel(CQSPI_REG_INDIRECTWR_START_MASK,  	       reg_base + CQSPI_REG_INDIRECTWR); +	/* +	 * As per 66AK2G02 TRM SPRUHY8F section 11.15.5.3 Indirect Access +	 * Controller programming sequence, couple of cycles of +	 * QSPI_REF_CLK delay is required for the above bit to +	 * be internally synchronized by the QSPI module. Provide 5 +	 * cycles of delay. +	 */ +	if (cqspi->wr_delay) +		ndelay(cqspi->wr_delay);  	while (remaining > 0) {  		write_bytes = remaining > page_size ? page_size : remaining; @@ -775,7 +790,7 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi)  }  static void cqspi_readdata_capture(struct cqspi_st *cqspi, -				   const unsigned int bypass, +				   const bool bypass,  				   const unsigned int delay)  {  	void __iomem *reg_base = cqspi->iobase; @@ -839,7 +854,8 @@ static void cqspi_configure(struct spi_nor *nor)  		cqspi->sclk = sclk;  		cqspi_config_baudrate_div(cqspi);  		cqspi_delay(nor); -		cqspi_readdata_capture(cqspi, 1, f_pdata->read_delay); +		cqspi_readdata_capture(cqspi, !cqspi->rclk_en, +				       f_pdata->read_delay);  	}  	if (switch_cs || switch_ck) @@ -1036,6 +1052,8 @@ static int cqspi_of_get_pdata(struct platform_device *pdev)  		return -ENXIO;  	} +	cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en"); +  	return 0;  } @@ -1156,6 +1174,7 @@ static int cqspi_probe(struct platform_device *pdev)  	struct cqspi_st *cqspi;  	struct resource *res;  	struct resource *res_ahb; +	unsigned long data;  	int ret;  	int irq; @@ -1206,13 +1225,24 @@ static int cqspi_probe(struct platform_device *pdev)  		return -ENXIO;  	} +	pm_runtime_enable(dev); +	ret = pm_runtime_get_sync(dev); +	if (ret < 0) { +		pm_runtime_put_noidle(dev); +		return ret; +	} +  	ret = clk_prepare_enable(cqspi->clk);  	if (ret) {  		dev_err(dev, "Cannot enable QSPI clock.\n"); -		return ret; +		goto probe_clk_failed;  	}  	cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); +	data  = (unsigned long)of_device_get_match_data(dev); +	if (data & CQSPI_NEEDS_WR_DELAY) +		cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, +						   cqspi->master_ref_clk_hz);  	ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,  			       pdev->name, cqspi); @@ -1233,10 +1263,13 @@ static int cqspi_probe(struct platform_device *pdev)  	}  	return ret; -probe_irq_failed: -	cqspi_controller_enable(cqspi, 0);  probe_setup_failed: +	cqspi_controller_enable(cqspi, 0); +probe_irq_failed:  	clk_disable_unprepare(cqspi->clk); +probe_clk_failed: +	pm_runtime_put_sync(dev); +	pm_runtime_disable(dev);  	return ret;  } @@ -1253,6 +1286,9 @@ static int cqspi_remove(struct platform_device *pdev)  	clk_disable_unprepare(cqspi->clk); +	pm_runtime_put_sync(&pdev->dev); +	pm_runtime_disable(&pdev->dev); +  	return 0;  } @@ -1284,7 +1320,14 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = {  #endif  static const struct of_device_id cqspi_dt_ids[] = { -	{.compatible = "cdns,qspi-nor",}, +	{ +		.compatible = "cdns,qspi-nor", +		.data = (void *)0, +	}, +	{ +		.compatible = "ti,k2g-qspi", +		.data = (void *)CQSPI_NEEDS_WR_DELAY, +	},  	{ /* end of table */ }  }; diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/intel-spi-pci.c index e82652335ede..c0976f2e3dd1 100644 --- a/drivers/mtd/spi-nor/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/intel-spi-pci.c @@ -63,7 +63,10 @@ static void intel_spi_pci_remove(struct pci_dev *pdev)  }  static const struct pci_device_id intel_spi_pci_ids[] = { +	{ PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info },  	{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, +	{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, +	{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },  	{ },  };  MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c index 8a596bfeddff..ef034d898a23 100644 --- a/drivers/mtd/spi-nor/intel-spi.c +++ b/drivers/mtd/spi-nor/intel-spi.c @@ -67,8 +67,6 @@  #define PR_LIMIT_MASK			(0x3fff << PR_LIMIT_SHIFT)  #define PR_RPE				BIT(15)  #define PR_BASE_MASK			0x3fff -/* Last PR is GPR0 */ -#define PR_NUM				(5 + 1)  /* Offsets are from @ispi->sregs */  #define SSFSTS_CTL			0x00 @@ -90,20 +88,35 @@  #define OPMENU0				0x08  #define OPMENU1				0x0c +#define OPTYPE_READ_NO_ADDR		0 +#define OPTYPE_WRITE_NO_ADDR		1 +#define OPTYPE_READ_WITH_ADDR		2 +#define OPTYPE_WRITE_WITH_ADDR		3 +  /* CPU specifics */  #define BYT_PR				0x74  #define BYT_SSFSTS_CTL			0x90  #define BYT_BCR				0xfc  #define BYT_BCR_WPD			BIT(0)  #define BYT_FREG_NUM			5 +#define BYT_PR_NUM			5  #define LPT_PR				0x74  #define LPT_SSFSTS_CTL			0x90  #define LPT_FREG_NUM			5 +#define LPT_PR_NUM			5  #define BXT_PR				0x84  #define BXT_SSFSTS_CTL			0xa0  #define BXT_FREG_NUM			12 +#define BXT_PR_NUM			6 + +#define LVSCC				0xc4 +#define UVSCC				0xc8 +#define ERASE_OPCODE_SHIFT		8 +#define ERASE_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT) +#define ERASE_64K_OPCODE_SHIFT		16 +#define ERASE_64K_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT)  #define INTEL_SPI_TIMEOUT		5000 /* ms */  #define INTEL_SPI_FIFO_SZ		64 @@ -117,8 +130,11 @@   * @pregs: Start of protection registers   * @sregs: Start of software sequencer registers   * @nregions: Maximum number of regions + * @pr_num: Maximum number of protected range registers   * @writeable: Is the chip writeable - * @swseq: Use SW sequencer in register reads/writes + * @locked: Is SPI setting locked + * @swseq_reg: Use SW sequencer in register reads/writes + * @swseq_erase: Use SW sequencer in erase operation   * @erase_64k: 64k erase supported   * @opcodes: Opcodes which are supported. This are programmed by BIOS   *           before it locks down the controller. @@ -132,8 +148,11 @@ struct intel_spi {  	void __iomem *pregs;  	void __iomem *sregs;  	size_t nregions; +	size_t pr_num;  	bool writeable; -	bool swseq; +	bool locked; +	bool swseq_reg; +	bool swseq_erase;  	bool erase_64k;  	u8 opcodes[8];  	u8 preopcodes[2]; @@ -167,7 +186,7 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)  	for (i = 0; i < ispi->nregions; i++)  		dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,  			readl(ispi->base + FREG(i))); -	for (i = 0; i < PR_NUM; i++) +	for (i = 0; i < ispi->pr_num; i++)  		dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,  			readl(ispi->pregs + PR(i))); @@ -181,8 +200,11 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)  	if (ispi->info->type == INTEL_SPI_BYT)  		dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR)); +	dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC)); +	dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC)); +  	dev_dbg(ispi->dev, "Protected regions:\n"); -	for (i = 0; i < PR_NUM; i++) { +	for (i = 0; i < ispi->pr_num; i++) {  		u32 base, limit;  		value = readl(ispi->pregs + PR(i)); @@ -214,7 +236,9 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)  	}  	dev_dbg(ispi->dev, "Using %cW sequencer for register access\n", -		ispi->swseq ? 'S' : 'H'); +		ispi->swseq_reg ? 'S' : 'H'); +	dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n", +		ispi->swseq_erase ? 'S' : 'H');  }  /* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */ @@ -278,7 +302,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi)  static int intel_spi_init(struct intel_spi *ispi)  { -	u32 opmenu0, opmenu1, val; +	u32 opmenu0, opmenu1, lvscc, uvscc, val;  	int i;  	switch (ispi->info->type) { @@ -286,6 +310,8 @@ static int intel_spi_init(struct intel_spi *ispi)  		ispi->sregs = ispi->base + BYT_SSFSTS_CTL;  		ispi->pregs = ispi->base + BYT_PR;  		ispi->nregions = BYT_FREG_NUM; +		ispi->pr_num = BYT_PR_NUM; +		ispi->swseq_reg = true;  		if (writeable) {  			/* Disable write protection */ @@ -305,12 +331,15 @@ static int intel_spi_init(struct intel_spi *ispi)  		ispi->sregs = ispi->base + LPT_SSFSTS_CTL;  		ispi->pregs = ispi->base + LPT_PR;  		ispi->nregions = LPT_FREG_NUM; +		ispi->pr_num = LPT_PR_NUM; +		ispi->swseq_reg = true;  		break;  	case INTEL_SPI_BXT:  		ispi->sregs = ispi->base + BXT_SSFSTS_CTL;  		ispi->pregs = ispi->base + BXT_PR;  		ispi->nregions = BXT_FREG_NUM; +		ispi->pr_num = BXT_PR_NUM;  		ispi->erase_64k = true;  		break; @@ -318,42 +347,64 @@ static int intel_spi_init(struct intel_spi *ispi)  		return -EINVAL;  	} -	/* Disable #SMI generation */ +	/* Disable #SMI generation from HW sequencer */  	val = readl(ispi->base + HSFSTS_CTL);  	val &= ~HSFSTS_CTL_FSMIE;  	writel(val, ispi->base + HSFSTS_CTL);  	/* -	 * BIOS programs allowed opcodes and then locks down the register. -	 * So read back what opcodes it decided to support. That's the set -	 * we are going to support as well. +	 * Determine whether erase operation should use HW or SW sequencer. +	 * +	 * The HW sequencer has a predefined list of opcodes, with only the +	 * erase opcode being programmable in LVSCC and UVSCC registers. +	 * If these registers don't contain a valid erase opcode, erase +	 * cannot be done using HW sequencer.  	 */ -	opmenu0 = readl(ispi->sregs + OPMENU0); -	opmenu1 = readl(ispi->sregs + OPMENU1); +	lvscc = readl(ispi->base + LVSCC); +	uvscc = readl(ispi->base + UVSCC); +	if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK)) +		ispi->swseq_erase = true; +	/* SPI controller on Intel BXT supports 64K erase opcode */ +	if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase) +		if (!(lvscc & ERASE_64K_OPCODE_MASK) || +		    !(uvscc & ERASE_64K_OPCODE_MASK)) +			ispi->erase_64k = false;  	/*  	 * Some controllers can only do basic operations using hardware  	 * sequencer. All other operations are supposed to be carried out -	 * using software sequencer. If we find that BIOS has programmed -	 * opcodes for the software sequencer we use that over the hardware -	 * sequencer. +	 * using software sequencer.  	 */ -	if (opmenu0 && opmenu1) { -		for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { -			ispi->opcodes[i] = opmenu0 >> i * 8; -			ispi->opcodes[i + 4] = opmenu1 >> i * 8; -		} - -		val = readl(ispi->sregs + PREOP_OPTYPE); -		ispi->preopcodes[0] = val; -		ispi->preopcodes[1] = val >> 8; - +	if (ispi->swseq_reg) {  		/* Disable #SMI generation from SW sequencer */  		val = readl(ispi->sregs + SSFSTS_CTL);  		val &= ~SSFSTS_CTL_FSMIE;  		writel(val, ispi->sregs + SSFSTS_CTL); +	} + +	/* Check controller's lock status */ +	val = readl(ispi->base + HSFSTS_CTL); +	ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN); + +	if (ispi->locked) { +		/* +		 * BIOS programs allowed opcodes and then locks down the +		 * register. So read back what opcodes it decided to support. +		 * That's the set we are going to support as well. +		 */ +		opmenu0 = readl(ispi->sregs + OPMENU0); +		opmenu1 = readl(ispi->sregs + OPMENU1); -		ispi->swseq = true; +		if (opmenu0 && opmenu1) { +			for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { +				ispi->opcodes[i] = opmenu0 >> i * 8; +				ispi->opcodes[i + 4] = opmenu1 >> i * 8; +			} + +			val = readl(ispi->sregs + PREOP_OPTYPE); +			ispi->preopcodes[0] = val; +			ispi->preopcodes[1] = val >> 8; +		}  	}  	intel_spi_dump_regs(ispi); @@ -361,18 +412,28 @@ static int intel_spi_init(struct intel_spi *ispi)  	return 0;  } -static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode) +static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)  {  	int i; +	int preop; -	for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) -		if (ispi->opcodes[i] == opcode) -			return i; -	return -EINVAL; +	if (ispi->locked) { +		for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) +			if (ispi->opcodes[i] == opcode) +				return i; + +		return -EINVAL; +	} + +	/* The lock is off, so just use index 0 */ +	writel(opcode, ispi->sregs + OPMENU0); +	preop = readw(ispi->sregs + PREOP_OPTYPE); +	writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE); + +	return 0;  } -static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, -			      int len) +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, int len)  {  	u32 val, status;  	int ret; @@ -394,6 +455,9 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,  		return -EINVAL;  	} +	if (len > INTEL_SPI_FIFO_SZ) +		return -EINVAL; +  	val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;  	val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;  	val |= HSFSTS_CTL_FGO; @@ -412,27 +476,39 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,  	return 0;  } -static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf, -			      int len) +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, int len, +			      int optype)  { -	u32 val, status; +	u32 val = 0, status; +	u16 preop;  	int ret; -	ret = intel_spi_opcode_index(ispi, opcode); +	ret = intel_spi_opcode_index(ispi, opcode, optype);  	if (ret < 0)  		return ret; -	val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; +	if (len > INTEL_SPI_FIFO_SZ) +		return -EINVAL; + +	/* Only mark 'Data Cycle' bit when there is data to be transferred */ +	if (len > 0) +		val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;  	val |= ret << SSFSTS_CTL_COP_SHIFT;  	val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;  	val |= SSFSTS_CTL_SCGO; +	preop = readw(ispi->sregs + PREOP_OPTYPE); +	if (preop) { +		val |= SSFSTS_CTL_ACS; +		if (preop >> 8) +			val |= SSFSTS_CTL_SPOP; +	}  	writel(val, ispi->sregs + SSFSTS_CTL);  	ret = intel_spi_wait_sw_busy(ispi);  	if (ret)  		return ret; -	status = readl(ispi->base + SSFSTS_CTL); +	status = readl(ispi->sregs + SSFSTS_CTL);  	if (status & SSFSTS_CTL_FCERR)  		return -EIO;  	else if (status & SSFSTS_CTL_AEL) @@ -449,10 +525,11 @@ static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	/* Address of the first chip */  	writel(0, ispi->base + FADDR); -	if (ispi->swseq) -		ret = intel_spi_sw_cycle(ispi, opcode, buf, len); +	if (ispi->swseq_reg) +		ret = intel_spi_sw_cycle(ispi, opcode, len, +					 OPTYPE_READ_NO_ADDR);  	else -		ret = intel_spi_hw_cycle(ispi, opcode, buf, len); +		ret = intel_spi_hw_cycle(ispi, opcode, len);  	if (ret)  		return ret; @@ -467,10 +544,15 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	/*  	 * This is handled with atomic operation and preop code in Intel -	 * controller so skip it here now. +	 * controller so skip it here now. If the controller is not locked, +	 * program the opcode to the PREOP register for later use.  	 */ -	if (opcode == SPINOR_OP_WREN) +	if (opcode == SPINOR_OP_WREN) { +		if (!ispi->locked) +			writel(opcode, ispi->sregs + PREOP_OPTYPE); +  		return 0; +	}  	writel(0, ispi->base + FADDR); @@ -479,9 +561,10 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)  	if (ret)  		return ret; -	if (ispi->swseq) -		return intel_spi_sw_cycle(ispi, opcode, buf, len); -	return intel_spi_hw_cycle(ispi, opcode, buf, len); +	if (ispi->swseq_reg) +		return intel_spi_sw_cycle(ispi, opcode, len, +					  OPTYPE_WRITE_NO_ADDR); +	return intel_spi_hw_cycle(ispi, opcode, len);  }  static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len, @@ -561,12 +644,6 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,  		val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;  		val |= HSFSTS_CTL_FCYCLE_WRITE; -		/* Write enable */ -		if (ispi->preopcodes[1] == SPINOR_OP_WREN) -			val |= SSFSTS_CTL_SPOP; -		val |= SSFSTS_CTL_ACS; -		writel(val, ispi->base + HSFSTS_CTL); -  		ret = intel_spi_write_block(ispi, write_buf, block_size);  		if (ret) {  			dev_err(ispi->dev, "failed to write block\n"); @@ -574,8 +651,8 @@ static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,  		}  		/* Start the write now */ -		val = readl(ispi->base + HSFSTS_CTL); -		writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL); +		val |= HSFSTS_CTL_FGO; +		writel(val, ispi->base + HSFSTS_CTL);  		ret = intel_spi_wait_hw_busy(ispi);  		if (ret) { @@ -620,6 +697,22 @@ static int intel_spi_erase(struct spi_nor *nor, loff_t offs)  		erase_size = SZ_4K;  	} +	if (ispi->swseq_erase) { +		while (len > 0) { +			writel(offs, ispi->base + FADDR); + +			ret = intel_spi_sw_cycle(ispi, nor->erase_opcode, +						 0, OPTYPE_WRITE_WITH_ADDR); +			if (ret) +				return ret; + +			offs += erase_size; +			len -= erase_size; +		} + +		return 0; +	} +  	while (len > 0) {  		writel(offs, ispi->base + FADDR); @@ -652,7 +745,7 @@ static bool intel_spi_is_protected(const struct intel_spi *ispi,  {  	int i; -	for (i = 0; i < PR_NUM; i++) { +	for (i = 0; i < ispi->pr_num; i++) {  		u32 pr_base, pr_limit, pr_value;  		pr_value = readl(ispi->pregs + PR(i)); diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index c258c7adf1c5..abe455ccd68b 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -404,6 +404,29 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,  	return ret;  } +static void mt8173_nor_disable_clk(struct mt8173_nor *mt8173_nor) +{ +	clk_disable_unprepare(mt8173_nor->spi_clk); +	clk_disable_unprepare(mt8173_nor->nor_clk); +} + +static int mt8173_nor_enable_clk(struct mt8173_nor *mt8173_nor) +{ +	int ret; + +	ret = clk_prepare_enable(mt8173_nor->spi_clk); +	if (ret) +		return ret; + +	ret = clk_prepare_enable(mt8173_nor->nor_clk); +	if (ret) { +		clk_disable_unprepare(mt8173_nor->spi_clk); +		return ret; +	} + +	return 0; +} +  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,  			struct device_node *flash_node)  { @@ -468,15 +491,11 @@ static int mtk_nor_drv_probe(struct platform_device *pdev)  		return PTR_ERR(mt8173_nor->nor_clk);  	mt8173_nor->dev = &pdev->dev; -	ret = clk_prepare_enable(mt8173_nor->spi_clk); + +	ret = mt8173_nor_enable_clk(mt8173_nor);  	if (ret)  		return ret; -	ret = clk_prepare_enable(mt8173_nor->nor_clk); -	if (ret) { -		clk_disable_unprepare(mt8173_nor->spi_clk); -		return ret; -	}  	/* only support one attached flash */  	flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);  	if (!flash_np) { @@ -487,10 +506,9 @@ static int mtk_nor_drv_probe(struct platform_device *pdev)  	ret = mtk_nor_init(mt8173_nor, flash_np);  nor_free: -	if (ret) { -		clk_disable_unprepare(mt8173_nor->spi_clk); -		clk_disable_unprepare(mt8173_nor->nor_clk); -	} +	if (ret) +		mt8173_nor_disable_clk(mt8173_nor); +  	return ret;  } @@ -498,11 +516,38 @@ static int mtk_nor_drv_remove(struct platform_device *pdev)  {  	struct mt8173_nor *mt8173_nor = platform_get_drvdata(pdev); -	clk_disable_unprepare(mt8173_nor->spi_clk); -	clk_disable_unprepare(mt8173_nor->nor_clk); +	mt8173_nor_disable_clk(mt8173_nor); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_nor_suspend(struct device *dev) +{ +	struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev); + +	mt8173_nor_disable_clk(mt8173_nor); +  	return 0;  } +static int mtk_nor_resume(struct device *dev) +{ +	struct mt8173_nor *mt8173_nor = dev_get_drvdata(dev); + +	return mt8173_nor_enable_clk(mt8173_nor); +} + +static const struct dev_pm_ops mtk_nor_dev_pm_ops = { +	.suspend = mtk_nor_suspend, +	.resume = mtk_nor_resume, +}; + +#define MTK_NOR_DEV_PM_OPS	(&mtk_nor_dev_pm_ops) +#else +#define MTK_NOR_DEV_PM_OPS	NULL +#endif +  static const struct of_device_id mtk_nor_of_ids[] = {  	{ .compatible = "mediatek,mt8173-nor"},  	{ /* sentinel */ } @@ -514,6 +559,7 @@ static struct platform_driver mtk_nor_driver = {  	.remove = mtk_nor_drv_remove,  	.driver = {  		.name = "mtk-nor", +		.pm = MTK_NOR_DEV_PM_OPS,  		.of_match_table = mtk_nor_of_ids,  	},  }; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 19c000722cbc..bc266f70a15b 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -89,6 +89,8 @@ struct flash_info {  #define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */  #define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */  #define USE_CLSR		BIT(14)	/* use CLSR command */ + +	int	(*quad_enable)(struct spi_nor *nor);  };  #define JEDEC_MFR(info)	((info)->id[0]) @@ -870,6 +872,8 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)  	return ret;  } +static int macronix_quad_enable(struct spi_nor *nor); +  /* Used when the "_ext_id" is two bytes at most */  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\  		.id = {							\ @@ -964,6 +968,7 @@ static const struct flash_info spi_nor_ids[] = {  	{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },  	/* Everspin */ +	{ "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },  	{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },  	{ "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },  	{ "mr25h40",  CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, @@ -983,6 +988,11 @@ static const struct flash_info spi_nor_ids[] = {  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)  	},  	{ +		"gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64, +			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | +			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +	}, +	{  		"gd25q64", INFO(0xc84017, 0, 64 * 1024, 128,  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) @@ -997,6 +1007,12 @@ static const struct flash_info spi_nor_ids[] = {  			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |  			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)  	}, +	{ +		"gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, +			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | +			SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +			.quad_enable = macronix_quad_enable, +	},  	/* Intel/Numonyx -- xxxs33b */  	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) }, @@ -1024,7 +1040,7 @@ static const struct flash_info spi_nor_ids[] = {  	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },  	{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },  	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, -	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },  	{ "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },  	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },  	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, @@ -1137,6 +1153,11 @@ static const struct flash_info spi_nor_ids[] = {  	{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },  	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },  	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) }, +	{ +		"w25q16dw", INFO(0xef6015, 0, 64 * 1024,  32, +			SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | +			SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +	},  	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },  	{ "w25q20cl", INFO(0xef4012, 0, 64 * 1024,  4, SECT_4K) },  	{ "w25q20bw", INFO(0xef5012, 0, 64 * 1024,  4, SECT_4K) }, @@ -2288,8 +2309,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,  	/* Check the SFDP header version. */  	if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || -	    header.major != SFDP_JESD216_MAJOR || -	    header.minor < SFDP_JESD216_MINOR) +	    header.major != SFDP_JESD216_MAJOR)  		return -EINVAL;  	/* @@ -2427,6 +2447,15 @@ static int spi_nor_init_params(struct spi_nor *nor,  			params->quad_enable = spansion_quad_enable;  			break;  		} + +		/* +		 * Some manufacturer like GigaDevice may use different +		 * bit to set QE on different memories, so the MFR can't +		 * indicate the quad_enable method for this case, we need +		 * set it in flash info list. +		 */ +		if (info->quad_enable) +			params->quad_enable = info->quad_enable;  	}  	/* Override the parameters with data read from SFDP tables. */ @@ -2630,17 +2659,60 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,  	/* Enable Quad I/O if needed. */  	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||  			  spi_nor_get_protocol_width(nor->write_proto) == 4); -	if (enable_quad_io && params->quad_enable) { -		err = params->quad_enable(nor); +	if (enable_quad_io && params->quad_enable) +		nor->quad_enable = params->quad_enable; +	else +		nor->quad_enable = NULL; + +	return 0; +} + +static int spi_nor_init(struct spi_nor *nor) +{ +	int err; + +	/* +	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up +	 * with the software protection bits set +	 */ +	if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || +	    JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || +	    JEDEC_MFR(nor->info) == SNOR_MFR_SST || +	    nor->info->flags & SPI_NOR_HAS_LOCK) { +		write_enable(nor); +		write_sr(nor, 0); +		spi_nor_wait_till_ready(nor); +	} + +	if (nor->quad_enable) { +		err = nor->quad_enable(nor);  		if (err) {  			dev_err(nor->dev, "quad mode not supported\n");  			return err;  		}  	} +	if ((nor->addr_width == 4) && +	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && +	    !(nor->info->flags & SPI_NOR_4B_OPCODES)) +		set_4byte(nor, nor->info, 1); +  	return 0;  } +/* mtd resume handler */ +static void spi_nor_resume(struct mtd_info *mtd) +{ +	struct spi_nor *nor = mtd_to_spi_nor(mtd); +	struct device *dev = nor->dev; +	int ret; + +	/* re-initialize the nor chip */ +	ret = spi_nor_init(nor); +	if (ret) +		dev_err(dev, "resume() failed\n"); +} +  int spi_nor_scan(struct spi_nor *nor, const char *name,  		 const struct spi_nor_hwcaps *hwcaps)  { @@ -2708,20 +2780,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,  	if (ret)  		return ret; -	/* -	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up -	 * with the software protection bits set -	 */ - -	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || -	    JEDEC_MFR(info) == SNOR_MFR_INTEL || -	    JEDEC_MFR(info) == SNOR_MFR_SST || -	    info->flags & SPI_NOR_HAS_LOCK) { -		write_enable(nor); -		write_sr(nor, 0); -		spi_nor_wait_till_ready(nor); -	} -  	if (!mtd->name)  		mtd->name = dev_name(dev);  	mtd->priv = nor; @@ -2731,6 +2789,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,  	mtd->size = params.size;  	mtd->_erase = spi_nor_erase;  	mtd->_read = spi_nor_read; +	mtd->_resume = spi_nor_resume;  	/* NOR protection support for STmicro/Micron chips and similar */  	if (JEDEC_MFR(info) == SNOR_MFR_MICRON || @@ -2804,8 +2863,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,  		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||  		    info->flags & SPI_NOR_4B_OPCODES)  			spi_nor_set_4byte_opcodes(nor, info); -		else -			set_4byte(nor, info, 1);  	} else {  		nor->addr_width = 3;  	} @@ -2822,6 +2879,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,  			return ret;  	} +	/* Send all the required SPI flash commands to initialize device */ +	nor->info = info; +	ret = spi_nor_init(nor); +	if (ret) +		return ret; +  	dev_info(dev, "%s (%lld Kbytes)\n", info->name,  			(long long)mtd->size >> 10); diff --git a/drivers/mtd/spi-nor/stm32-quadspi.c b/drivers/mtd/spi-nor/stm32-quadspi.c index 86c0931543c5..b3c7f6addba7 100644 --- a/drivers/mtd/spi-nor/stm32-quadspi.c +++ b/drivers/mtd/spi-nor/stm32-quadspi.c @@ -1,9 +1,22 @@  /* - * stm32_quadspi.c + * Driver for stm32 quadspi controller   * - * Copyright (C) 2017, Ludovic Barre + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Ludovic Barre author <ludovic.barre@st.com>.   * - * License terms: GNU General Public License (GPL), version 2 + * License terms: GPL V2.0. + * + * 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/>.   */  #include <linux/clk.h>  #include <linux/errno.h> @@ -113,6 +126,7 @@  #define STM32_MAX_MMAP_SZ	SZ_256M  #define STM32_MAX_NORCHIP	2 +#define STM32_QSPI_FIFO_SZ	32  #define STM32_QSPI_FIFO_TIMEOUT_US 30000  #define STM32_QSPI_BUSY_TIMEOUT_US 100000 @@ -124,6 +138,7 @@ struct stm32_qspi_flash {  	u32 presc;  	u32 read_mode;  	bool registered; +	u32 prefetch_limit;  };  struct stm32_qspi { @@ -240,12 +255,12 @@ static int stm32_qspi_tx_poll(struct stm32_qspi *qspi,  						 STM32_QSPI_FIFO_TIMEOUT_US);  		if (ret) {  			dev_err(qspi->dev, "fifo timeout (stat:%#x)\n", sr); -			break; +			return ret;  		}  		tx_fifo(buf++, qspi->io_base + QUADSPI_DR);  	} -	return ret; +	return 0;  }  static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, @@ -272,6 +287,7 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash,  {  	struct stm32_qspi *qspi = flash->qspi;  	u32 ccr, dcr, cr; +	u32 last_byte;  	int err;  	err = stm32_qspi_wait_nobusy(qspi); @@ -314,6 +330,10 @@ static int stm32_qspi_send(struct stm32_qspi_flash *flash,  		if (err)  			goto abort;  		writel_relaxed(FCR_CTCF, qspi->io_base + QUADSPI_FCR); +	} else { +		last_byte = cmd->addr + cmd->len; +		if (last_byte > flash->prefetch_limit) +			goto abort;  	}  	return err; @@ -322,7 +342,9 @@ abort:  	cr = readl_relaxed(qspi->io_base + QUADSPI_CR) | CR_ABORT;  	writel_relaxed(cr, qspi->io_base + QUADSPI_CR); -	dev_err(qspi->dev, "%s abort err:%d\n", __func__, err); +	if (err) +		dev_err(qspi->dev, "%s abort err:%d\n", __func__, err); +  	return err;  } @@ -550,6 +572,7 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,  	}  	flash->fsize = FSIZE_VAL(mtd->size); +	flash->prefetch_limit = mtd->size - STM32_QSPI_FIFO_SZ;  	flash->read_mode = CCR_FMODE_MM;  	if (mtd->size > qspi->mm_size) diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 1f0a7fc7772f..d0c66a0975cf 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -232,10 +232,17 @@ enum spi_nor_option_flags {  };  /** + * struct flash_info - Forward declaration of a structure used internally by + *		       spi_nor_scan() + */ +struct flash_info; + +/**   * struct spi_nor - Structure for defining a the SPI NOR layer   * @mtd:		point to a mtd_info structure   * @lock:		the lock for the read/write/erase/lock/unlock operations   * @dev:		point to a spi device, or a spi nor controller device. + * @info:		spi-nor part JDEC MFR id and other info   * @page_size:		the page size of the SPI NOR   * @addr_width:		number of address bytes   * @erase_opcode:	the opcode for erasing a sector @@ -262,6 +269,7 @@ enum spi_nor_option_flags {   * @flash_lock:		[FLASH-SPECIFIC] lock a region of the SPI NOR   * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR   * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is + * @quad_enable:	[FLASH-SPECIFIC] enables SPI NOR quad mode   *			completely locked   * @priv:		the private data   */ @@ -269,6 +277,7 @@ struct spi_nor {  	struct mtd_info		mtd;  	struct mutex		lock;  	struct device		*dev; +	const struct flash_info	*info;  	u32			page_size;  	u8			addr_width;  	u8			erase_opcode; @@ -296,6 +305,7 @@ struct spi_nor {  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);  	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); +	int (*quad_enable)(struct spi_nor *nor);  	void *priv;  }; | 
