[PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode

Cédric Le Goater clg at kaod.org
Thu Mar 23 08:13:48 PDT 2017


On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
> framework about the actual hardware capabilities supported by the SPI
> controller and its driver.
> 
> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
> telling the spi-nor framework about the hardware capabilities supported by
> the SPI flash memory and the associated settings required to use those
> hardware caps.
>
> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
> values but a later patch will allow to fill it dynamically by reading the
> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
> memory.
> 
> With both structures, the spi-nor framework can now compute the best
> match between hardware caps supported by both the (Q)SPI memory and
> controller hence selecting the relevant SPI protocols and op codes for
> (Fast) Read, Page Program and Sector Erase operations.
> 
> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
> with the number of dummy cycles to be used with each Fast Read commands
> and the erase block size associated to the erase block op codes.
> 
> Finally the 'struct spi_nor_flash_parameter', through the optional
> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.

The Aspeed controller only supports Dual I/O and a helper similar 
to the one for Quad I/O would be needed to setup the chip for 
multiple I/O: 

	int (*dual_enable)(struct spi_nor *nor);

Is the approach correct ? or maybe rename the current 'quad_enable()'
to 'multiple_enable()' and add an extra parameter.

I have a bunch of patches queued for Dual I/O data support but I 
think they will conflict with your patches. I will wait for this 
one to be merged. Then, I can look at Dual I/O address + data 
support.

Thanks,

C.     


> Signed-off-by: Cyrille Pitchen <cyrille.pitchen at atmel.com>
> ---
>  drivers/mtd/devices/m25p80.c          |  16 +-
>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>  11 files changed, 643 insertions(+), 177 deletions(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index c4df3b1bded0..68986a26c8fe 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  
>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>  {
> -	switch (nor->flash_read) {
> -	case SPI_NOR_DUAL:
> +	switch (nor->read_proto) {
> +	case SNOR_PROTO_1_1_2:
>  		return 2;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_4:
>  		return 4;
>  	default:
>  		return 0;
> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>  	struct flash_platform_data	*data;
>  	struct m25p *flash;
>  	struct spi_nor *nor;
> -	enum read_mode mode = SPI_NOR_NORMAL;
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
> +	};
>  	char *flash_name;
>  	int ret;
>  
> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>  	flash->spi = spi;
>  
>  	if (spi->mode & SPI_RX_QUAD)
> -		mode = SPI_NOR_QUAD;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>  	else if (spi->mode & SPI_RX_DUAL)
> -		mode = SPI_NOR_DUAL;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  
>  	if (data && data->name)
>  		nor->mtd.name = data->name;
> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>  	else
>  		flash_name = spi->modalias;
>  
> -	ret = spi_nor_scan(nor, flash_name, mode);
> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
> index 56051d30f000..723026d9cf0c 100644
> --- a/drivers/mtd/spi-nor/aspeed-smc.c
> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  	 * TODO: Adjust clocks if fast read is supported and interpret
>  	 * SPI-NOR flags to adjust controller settings.
>  	 */
> -	switch (chip->nor.flash_read) {
> -	case SPI_NOR_NORMAL:
> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
> -		break;
> -	case SPI_NOR_FAST:
> -		cmd = CONTROL_COMMAND_MODE_FREAD;
> -		break;
> -	default:
> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
> +		if (chip->nor.read_dummy == 0)
> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
> +		else
> +			cmd = CONTROL_COMMAND_MODE_FREAD;
> +	} else {
>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>  		return -EINVAL;
>  	}
> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  				  struct device_node *np, struct resource *r)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	const struct aspeed_smc_info *info = controller->info;
>  	struct device *dev = controller->dev;
>  	struct device_node *child;
> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  			break;
>  
>  		/*
> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
> +		 * TODO: Add support for Dual and Quad SPI protocols
>
>  		 * attach when board support is present as determined
>  		 * by of property.
>  		 */
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			break;
>  
> diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
> index 47937d9beec6..9f579f7c1733 100644
> --- a/drivers/mtd/spi-nor/atmel-quadspi.c
> +++ b/drivers/mtd/spi-nor/atmel-quadspi.c
> @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
>  
>  static int atmel_qspi_run_command(struct atmel_qspi *aq,
>  				  const struct atmel_qspi_command *cmd,
> -				  u32 ifr_tfrtyp, u32 ifr_width)
> +				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
>  {
>  	u32 iar, icr, ifr, sr;
>  	int err = 0;
>  
>  	iar = 0;
>  	icr = 0;
> -	ifr = ifr_tfrtyp | ifr_width;
> +	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) {
> @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
>  	cmd.rx_buf = buf;
>  	cmd.buf_len = len;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
> @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>  	cmd.tx_buf = buf;
>  	cmd.buf_len = len;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
> @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>  	cmd.tx_buf = write_buf;
>  	cmd.buf_len = len;
>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
> -				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				     nor->write_proto);
>  	return (ret < 0) ? ret : len;
>  }
>  
> @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
>  	cmd.instruction = nor->erase_opcode;
>  	cmd.address = (u32)offs;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
> @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>  	struct atmel_qspi *aq = nor->priv;
>  	struct atmel_qspi_command cmd;
>  	u8 num_mode_cycles, num_dummy_cycles;
> -	u32 ifr_width;
>  	ssize_t ret;
>  
> -	switch (nor->flash_read) {
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> -		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
> -		break;
> -
> -	case SPI_NOR_DUAL:
> -		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
> -		break;
> -
> -	case SPI_NOR_QUAD:
> -		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
> -		break;
> -
> -	default:
> -		return -EINVAL;
> -	}
> -
>  	if (nor->read_dummy >= 2) {
>  		num_mode_cycles = 2;
>  		num_dummy_cycles = nor->read_dummy - 2;
> @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>  	cmd.rx_buf = read_buf;
>  	cmd.buf_len = len;
>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
> -				     ifr_width);
> +				     nor->read_proto);
>  	return (ret < 0) ? ret : len;
>  }
>  
> @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
>  
>  static int atmel_qspi_probe(struct platform_device *pdev)
>  {
> +	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_1_1_4 |
> +			 SNOR_HWCAPS_READ_1_4_4 |
> +			 SNOR_HWCAPS_PP |
> +			 SNOR_HWCAPS_PP_1_1_4 |
> +			 SNOR_HWCAPS_PP_1_4_4),
> +	};
>  	struct device_node *child, *np = pdev->dev.of_node;
>  	struct atmel_qspi *aq;
>  	struct resource *res;
> @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
>  	if (err)
>  		goto disable_clk;
>  
> -	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +	err = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (err)
>  		goto disable_clk;
>  
> diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
> index 9f8102de1b16..3f91a3e97892 100644
> --- a/drivers/mtd/spi-nor/cadence-quadspi.c
> +++ b/drivers/mtd/spi-nor/cadence-quadspi.c
> @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>  	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>  
>  	if (read) {
> -		switch (nor->flash_read) {
> -		case SPI_NOR_NORMAL:
> -		case SPI_NOR_FAST:
> +		switch (nor->read_proto) {
> +		case SNOR_PROTO_1_1_1:
>  			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>  			break;
> -		case SPI_NOR_DUAL:
> +		case SNOR_PROTO_1_1_2:
>  			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
>  			break;
> -		case SPI_NOR_QUAD:
> +		case SNOR_PROTO_1_1_4:
>  			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
>  			break;
>  		default:
> @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
>  
>  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct platform_device *pdev = cqspi->pdev;
>  	struct device *dev = &pdev->dev;
>  	struct cqspi_flash_pdata *f_pdata;
> @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>  			goto err;
>  		}
>  
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			goto err;
>  
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 1476135e0d50..ec9c8e960fd2 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
>  
>  static int fsl_qspi_probe(struct platform_device *pdev)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct device_node *np = pdev->dev.of_node;
>  	struct device *dev = &pdev->dev;
>  	struct fsl_qspi *q;
> @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
>  		/* set the chip address for READID */
>  		fsl_qspi_set_base_addr(q, nor);
>  
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			goto mutex_failed;
>  
> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
> index a286350627a6..80e2d173abdd 100644
> --- a/drivers/mtd/spi-nor/hisi-sfc.c
> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
> @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
>  		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
>  }
>  
> -static int get_if_type(enum read_mode flash_read)
> +static int get_if_type(enum spi_nor_protocol proto)
>  {
>  	enum hifmc_iftype if_type;
>  
> -	switch (flash_read) {
> -	case SPI_NOR_DUAL:
> +	switch (proto) {
> +	case SNOR_PROTO_1_1_2:
>  		if_type = IF_TYPE_DUAL;
>  		break;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_2_2:
> +		if_type = IF_TYPE_DIO;
> +		break;
> +	case SNOR_PROTO_1_1_4:
>  		if_type = IF_TYPE_QUAD;
>  		break;
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> +	case SNOR_PROTO_1_4_4:
> +		if_type = IF_TYPE_QIO;
> +		break;
> +	case SNOR_PROTO_1_1_1:
>  	default:
>  		if_type = IF_TYPE_STD;
>  		break;
> @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>  	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
>  
>  	reg = OP_CFG_FM_CS(priv->chipselect);
> -	if_type = get_if_type(nor->flash_read);
> +	if (op_type == FMC_OP_READ)
> +		if_type = get_if_type(nor->read_proto);
> +	else
> +		if_type = get_if_type(nor->write_proto);
>  	reg |= OP_CFG_MEM_IF_TYPE(if_type);
>  	if (op_type == FMC_OP_READ)
>  		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
> @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>  static int hisi_spi_nor_register(struct device_node *np,
>  				struct hifmc_host *host)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct device *dev = host->dev;
>  	struct spi_nor *nor;
>  	struct hifmc_priv *priv;
> @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
>  	nor->read = hisi_spi_nor_read;
>  	nor->write = hisi_spi_nor_write;
>  	nor->erase = NULL;
> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
> index 986a3d020a3a..515aa1f7f4f1 100644
> --- a/drivers/mtd/spi-nor/intel-spi.c
> +++ b/drivers/mtd/spi-nor/intel-spi.c
> @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
>  struct intel_spi *intel_spi_probe(struct device *dev,
>  	struct resource *mem, const struct intel_spi_boardinfo *info)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct mtd_partition part;
>  	struct intel_spi *ispi;
>  	int ret;
> @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
>  	ispi->nor.write = intel_spi_write;
>  	ispi->nor.erase = intel_spi_erase;
>  
> -	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
> +	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
>  	if (ret) {
>  		dev_info(dev, "failed to locate the chip\n");
>  		return ERR_PTR(ret);
> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
> index e661877c23de..615e258866f7 100644
> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
> @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
>  {
>  	struct spi_nor *nor = &mt8173_nor->nor;
>  
> -	switch (nor->flash_read) {
> -	case SPI_NOR_FAST:
> +	switch (nor->read_proto) {
> +	case SNOR_PROTO_1_1_1:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA3_REG);
>  		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
>  		       MTK_NOR_CFG1_REG);
>  		break;
> -	case SPI_NOR_DUAL:
> +	case SNOR_PROTO_1_1_2:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA3_REG);
>  		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
>  		       MTK_NOR_DUAL_REG);
>  		break;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_4:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA4_REG);
>  		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
> @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
>  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>  			struct device_node *flash_node)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	int ret;
>  	struct spi_nor *nor;
>  
> @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>  	nor->write_reg = mt8173_nor_write_reg;
>  	nor->mtd.name = "mtk_nor";
>  	/* initialized with NULL */
> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
> index 73a14f40928b..c5992e099542 100644
> --- a/drivers/mtd/spi-nor/nxp-spifi.c
> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
> @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
>  
>  static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
>  {
> -	switch (spifi->nor.flash_read) {
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> +	switch (spifi->nor.read_proto) {
> +	case SNOR_PROTO_1_1_1:
>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
>  		break;
> -	case SPI_NOR_DUAL:
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_2:
> +	case SNOR_PROTO_1_1_4:
>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
>  		break;
>  	default:
> @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
>  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  				 struct device_node *np)
>  {
> -	enum read_mode flash_read;
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	u32 ctrl, property;
>  	u16 mode = 0;
>  	int ret;
> @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  
>  	if (mode & SPI_RX_DUAL) {
>  		ctrl |= SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_DUAL;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  	} else if (mode & SPI_RX_QUAD) {
>  		ctrl &= ~SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_QUAD;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>  	} else {
>  		ctrl |= SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_NORMAL;
>  	}
>  
>  	switch (mode & (SPI_CPHA | SPI_CPOL)) {
> @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  	 */
>  	nxp_spifi_dummy_id_read(&spifi->nor);
>  
> -	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
> +	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
>  	if (ret) {
>  		dev_err(spifi->dev, "device scan failed\n");
>  		return ret;
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index d3cb44b28490..cc443c6cbae8 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
>  }
>  
>  /*
> - * Dummy Cycle calculation for different type of read.
> - * It can be used to support more commands with
> - * different dummy cycle requirements.
> - */
> -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
> -{
> -	switch (nor->flash_read) {
> -	case SPI_NOR_FAST:
> -	case SPI_NOR_DUAL:
> -	case SPI_NOR_QUAD:
> -		return 8;
> -	case SPI_NOR_NORMAL:
> -		return 0;
> -	}
> -	return 0;
> -}
> -
> -/*
>   * Write status register 1 byte
>   * Returns negative if error occurred.
>   */
> @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
>  		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
>  		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
>  		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
> +
> +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
> +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
> +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
>  	};
>  
>  	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
> @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
>  	return 0;
>  }
>  
> -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
> -{
> -	int status;
> -
> -	switch (JEDEC_MFR(info)) {
> -	case SNOR_MFR_MACRONIX:
> -		status = macronix_quad_enable(nor);
> -		if (status) {
> -			dev_err(nor->dev, "Macronix quad-read not enabled\n");
> -			return -EINVAL;
> -		}
> -		return status;
> -	case SNOR_MFR_MICRON:
> -		return 0;
> -	default:
> -		status = spansion_quad_enable(nor);
> -		if (status) {
> -			dev_err(nor->dev, "Spansion quad-read not enabled\n");
> -			return -EINVAL;
> -		}
> -		return status;
> -	}
> -}
> -
>  static int spi_nor_check(struct spi_nor *nor)
>  {
>  	if (!nor->dev || !nor->read || !nor->write ||
> @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
>  	return 0;
>  }
>  
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> +
> +struct spi_nor_read_command {
> +	u8			num_mode_clocks;
> +	u8			num_wait_states;
> +	u8			opcode;
> +	enum spi_nor_protocol	proto;
> +};
> +
> +struct spi_nor_pp_command {
> +	u8			opcode;
> +	enum spi_nor_protocol	proto;
> +};
> +
> +enum spi_nor_read_command_index {
> +	SNOR_CMD_READ,
> +	SNOR_CMD_READ_FAST,
> +	SNOR_CMD_READ_1_1_1_DTR,
> +
> +	/* Dual SPI */
> +	SNOR_CMD_READ_1_1_2,
> +	SNOR_CMD_READ_1_2_2,
> +	SNOR_CMD_READ_2_2_2,
> +	SNOR_CMD_READ_1_2_2_DTR,
> +
> +	/* Quad SPI */
> +	SNOR_CMD_READ_1_1_4,
> +	SNOR_CMD_READ_1_4_4,
> +	SNOR_CMD_READ_4_4_4,
> +	SNOR_CMD_READ_1_4_4_DTR,
> +
> +	/* Octo SPI */
> +	SNOR_CMD_READ_1_1_8,
> +	SNOR_CMD_READ_1_8_8,
> +	SNOR_CMD_READ_8_8_8,
> +	SNOR_CMD_READ_1_8_8_DTR,
> +
> +	SNOR_CMD_READ_MAX
> +};
> +
> +enum spi_nor_pp_command_index {
> +	SNOR_CMD_PP,
> +
> +	/* Quad SPI */
> +	SNOR_CMD_PP_1_1_4,
> +	SNOR_CMD_PP_1_4_4,
> +	SNOR_CMD_PP_4_4_4,
> +
> +	/* Octo SPI */
> +	SNOR_CMD_PP_1_1_8,
> +	SNOR_CMD_PP_1_8_8,
> +	SNOR_CMD_PP_8_8_8,
> +
> +	SNOR_CMD_PP_MAX
> +};
> +
> +struct spi_nor_flash_parameter {
> +	u64				size;
> +	u32				page_size;
> +
> +	struct spi_nor_hwcaps		hwcaps;
> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
> +
> +	int (*quad_enable)(struct spi_nor *nor);
> +};
> +
> +
> +static inline void
> +spi_nor_set_read_settings(struct spi_nor_read_command *read,
> +			  u8 num_mode_clocks,
> +			  u8 num_wait_states,
> +			  u8 opcode,
> +			  enum spi_nor_protocol proto)
> +{
> +	read->num_mode_clocks = num_mode_clocks;
> +	read->num_wait_states = num_wait_states;
> +	read->opcode = opcode;
> +	read->proto = proto;
> +}
> +
> +static inline void
> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
> +			u8 opcode,
> +			enum spi_nor_protocol proto)
> +{
> +	pp->opcode = opcode;
> +	pp->proto = proto;
> +}
> +
> +static int spi_nor_init_params(struct spi_nor *nor,
> +			       const struct flash_info *info,
> +			       struct spi_nor_flash_parameter *params)
> +{
> +	/* Set legacy flash parameters as default. */
> +	memset(params, 0, sizeof(*params));
> +
> +	/* Set SPI NOR sizes. */
> +	params->size = info->sector_size * info->n_sectors;
> +	params->page_size = info->page_size;
> +
> +	/* (Fast) Read settings. */
> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
> +				  0, 0, SPINOR_OP_READ,
> +				  SNOR_PROTO_1_1_1);
> +	if (!(info->flags & SPI_NOR_NO_FR)) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
> +					  0, 8, SPINOR_OP_READ_FAST,
> +					  SNOR_PROTO_1_1_1);
> +	}
> +	if (info->flags & SPI_NOR_DUAL_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
> +					  0, 8, SPINOR_OP_READ_1_1_2,
> +					  SNOR_PROTO_1_1_2);
> +	}
> +	if (info->flags & SPI_NOR_QUAD_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
> +					  0, 8, SPINOR_OP_READ_1_1_4,
> +					  SNOR_PROTO_1_1_4);
> +	}
> +
> +	/* Page Program settings. */
> +	params->hwcaps.mask |= SNOR_HWCAPS_PP;
> +	spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
> +				SPINOR_OP_PP, SNOR_PROTO_1_1_1);
> +
> +	/* Select the procedure to set the Quad Enable bit. */
> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> +				   SNOR_HWCAPS_PP_QUAD)) {
> +		switch (JEDEC_MFR(info)) {
> +		case SNOR_MFR_MACRONIX:
> +			params->quad_enable = macronix_quad_enable;
> +			break;
> +
> +		case SNOR_MFR_MICRON:
> +			break;
> +
> +		default:
> +			params->quad_enable = spansion_quad_enable;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>  {
> +	switch (hwcaps) {
> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
> +
> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int spi_nor_select_read(struct spi_nor *nor,
> +			       const struct spi_nor_flash_parameter *params,
> +			       u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
> +	const struct spi_nor_read_command *read;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	read = &params->reads[cmd];
> +	nor->read_opcode = read->opcode;
> +	nor->read_proto = read->proto;
> +
> +	/*
> +	 * In the spi-nor framework, we don't need to make the difference
> +	 * between mode clock cycles and wait state clock cycles.
> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
> +	 * flash memory to know whether it should enter or leave its 0-4-4
> +	 * (Continuous Read / XIP) mode.
> +	 * eXecution In Place is out of the scope of the mtd sub-system.
> +	 * Hence we choose to merge both mode and wait state clock cycles
> +	 * into the so called dummy clock cycles.
> +	 */
> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
> +	return 0;
> +}
> +
> +static int spi_nor_select_pp(struct spi_nor *nor,
> +			     const struct spi_nor_flash_parameter *params,
> +			     u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
> +	const struct spi_nor_pp_command *pp;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	pp = &params->page_programs[cmd];
> +	nor->program_opcode = pp->opcode;
> +	nor->write_proto = pp->proto;
> +	return 0;
> +}
> +
> +static int spi_nor_select_erase(struct spi_nor *nor,
> +				const struct flash_info *info)
> +{
> +	struct mtd_info *mtd = &nor->mtd;
> +
> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> +	/* prefer "small sector" erase if possible */
> +	if (info->flags & SECT_4K) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K;
> +		mtd->erasesize = 4096;
> +	} else if (info->flags & SECT_4K_PMC) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
> +		mtd->erasesize = 4096;
> +	} else
> +#endif
> +	{
> +		nor->erase_opcode = SPINOR_OP_SE;
> +		mtd->erasesize = info->sector_size;
> +	}
> +	return 0;
> +}
> +
> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
> +			 const struct spi_nor_flash_parameter *params,
> +			 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	u32 ignored_mask, shared_mask;
> +	bool enable_quad_io;
> +	int err;
> +
> +	/*
> +	 * Keep only the hardware capabilities supported by both the SPI
> +	 * controller and the SPI flash memory.
> +	 */
> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
> +
> +	/* SPI protocol classes N-N-N are not supported yet. */
> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
> +			SNOR_HWCAPS_READ_4_4_4 |
> +			SNOR_HWCAPS_READ_8_8_8 |
> +			SNOR_HWCAPS_PP_4_4_4 |
> +			SNOR_HWCAPS_PP_8_8_8);
> +	if (shared_mask & ignored_mask) {
> +		dev_dbg(nor->dev,
> +			"SPI protocol classes N-N-N are not supported yet.\n");
> +		shared_mask &= ~ignored_mask;
> +	}
> +
> +	/* Select the (Fast) Read command. */
> +	err = spi_nor_select_read(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid (fast) read\n");
> +		return err;
> +	}
> +
> +	/* Select the Page Program command. */
> +	err = spi_nor_select_pp(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid page program\n");
> +		return err;
> +	}
> +
> +	/* Select the Sector Erase command. */
> +	err = spi_nor_select_erase(nor, info);
> +	if (err) {
> +		dev_err(nor->dev, "invalid sector/block erase\n");
> +		return err;
> +	}
> +
> +	/* 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)
> +		nor->flash_quad_enable = params->quad_enable;
> +	else
> +		nor->flash_quad_enable = NULL;
> +
> +	return 0;
> +}
> +
> +int spi_nor_scan(struct spi_nor *nor, const char *name,
> +		 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	struct spi_nor_flash_parameter params;
>  	const struct flash_info *info = NULL;
>  	struct device *dev = nor->dev;
>  	struct mtd_info *mtd = &nor->mtd;
> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	if (ret)
>  		return ret;
>  
> +	/* Reset SPI protocol for all commands */
> +	nor->reg_proto = SNOR_PROTO_1_1_1;
> +	nor->read_proto = SNOR_PROTO_1_1_1;
> +	nor->write_proto = SNOR_PROTO_1_1_1;
> +
>  	if (name)
>  		info = spi_nor_match_id(name);
>  	/* Try to auto-detect if chip name wasn't specified or not found */
> @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  		}
>  	}
>  
> +	/* Parse the Serial Flash Discoverable Parameters table */
> +	ret = spi_nor_init_params(nor, info, &params);
> +	if (ret)
> +		return ret;
> +
>  	mutex_init(&nor->lock);
>  
>  	/*
> @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	mtd->type = MTD_NORFLASH;
>  	mtd->writesize = 1;
>  	mtd->flags = MTD_CAP_NORFLASH;
> -	mtd->size = info->sector_size * info->n_sectors;
> +	mtd->size = params.size;
>  	mtd->_erase = spi_nor_erase;
>  	mtd->_read = spi_nor_read;
>  
> @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	if (info->flags & NO_CHIP_ERASE)
>  		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
>  
> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> -	/* prefer "small sector" erase if possible */
> -	if (info->flags & SECT_4K) {
> -		nor->erase_opcode = SPINOR_OP_BE_4K;
> -		mtd->erasesize = 4096;
> -	} else if (info->flags & SECT_4K_PMC) {
> -		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
> -		mtd->erasesize = 4096;
> -	} else
> -#endif
> -	{
> -		nor->erase_opcode = SPINOR_OP_SE;
> -		mtd->erasesize = info->sector_size;
> -	}
> -
>  	if (info->flags & SPI_NOR_NO_ERASE)
>  		mtd->flags |= MTD_NO_ERASE;
>  
>  	mtd->dev.parent = dev;
> -	nor->page_size = info->page_size;
> +	nor->page_size = params.page_size;
>  	mtd->writebufsize = nor->page_size;
>  
>  	if (np) {
>  		/* If we were instantiated by DT, use it */
>  		if (of_property_read_bool(np, "m25p,fast-read"))
> -			nor->flash_read = SPI_NOR_FAST;
> +			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>  		else
> -			nor->flash_read = SPI_NOR_NORMAL;
> +			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>  	} else {
>  		/* If we weren't instantiated by DT, default to fast-read */
> -		nor->flash_read = SPI_NOR_FAST;
> +		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>  	}
>  
>  	/* Some devices cannot do fast-read, no matter what DT tells us */
>  	if (info->flags & SPI_NOR_NO_FR)
> -		nor->flash_read = SPI_NOR_NORMAL;
> +		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
> +
> +	/*
> +	 * Configure the SPI memory:
> +	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
> +	 * - set the number of dummy cycles (mode cycles + wait states).
> +	 * - set the SPI protocols for register and memory accesses.
> +	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
> +	 */
> +	ret = spi_nor_setup(nor, info, &params, hwcaps);
> +	if (ret)
> +		return ret;
>  
> -	/* Quad/Dual-read mode takes precedence over fast/normal */
> -	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
> -		ret = set_quad_mode(nor, info);
> +	if (nor->flash_quad_enable) {
> +		ret = nor->flash_quad_enable(nor);
>  		if (ret) {
>  			dev_err(dev, "quad mode not supported\n");
>  			return ret;
>  		}
> -		nor->flash_read = SPI_NOR_QUAD;
> -	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
> -		nor->flash_read = SPI_NOR_DUAL;
>  	}
>  
> -	/* Default commands */
> -	switch (nor->flash_read) {
> -	case SPI_NOR_QUAD:
> -		nor->read_opcode = SPINOR_OP_READ_1_1_4;
> -		break;
> -	case SPI_NOR_DUAL:
> -		nor->read_opcode = SPINOR_OP_READ_1_1_2;
> -		break;
> -	case SPI_NOR_FAST:
> -		nor->read_opcode = SPINOR_OP_READ_FAST;
> -		break;
> -	case SPI_NOR_NORMAL:
> -		nor->read_opcode = SPINOR_OP_READ;
> -		break;
> -	default:
> -		dev_err(dev, "No Read opcode defined\n");
> -		return -EINVAL;
> -	}
> -
> -	nor->program_opcode = SPINOR_OP_PP;
> -
>  	if (info->addr_width)
>  		nor->addr_width = info->addr_width;
>  	else if (mtd->size > 0x1000000) {
> @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  		return -EINVAL;
>  	}
>  
> -	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
> -
>  	if (info->flags & SPI_S3AN) {
>  		ret = s3an_nor_scan(info, nor);
>  		if (ret)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index f2a718030476..732ee6cd5330 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -73,6 +73,15 @@
>  #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
>  #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
>  
> +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
> +#define SPINOR_OP_READ_1_1_1_DTR	0x0d
> +#define SPINOR_OP_READ_1_2_2_DTR	0xbd
> +#define SPINOR_OP_READ_1_4_4_DTR	0xed
> +
> +#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
> +#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
> +#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
> +
>  /* Used for SST flashes only. */
>  #define SPINOR_OP_BP		0x02	/* Byte program */
>  #define SPINOR_OP_WRDI		0x04	/* Write disable */
> @@ -119,13 +128,75 @@
>  /* Configuration Register bits. */
>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>  
> -enum read_mode {
> -	SPI_NOR_NORMAL = 0,
> -	SPI_NOR_FAST,
> -	SPI_NOR_DUAL,
> -	SPI_NOR_QUAD,
> +
> +/* Supported SPI protocols */
> +#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
> +
> +#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
> +#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
> +#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
> +#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
> +
> +#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
> +
> +#define SNOR_PROTO_STR(_pclass, _pwidth) \
> +	((_pclass) | (_pwidth))
> +#define SNOR_PROTO_DTR(_pclass, _pwidth) \
> +	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
> +
> +enum spi_nor_protocol {
> +	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
> +	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
> +	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
> +	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
> +	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
> +	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
> +	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
> +	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
> +	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
> +	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
> +
> +	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
> +	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
> +	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
> +	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
>  };
>  
> +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
> +{
> +	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
> +}
> +
> +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
> +{
> +	return proto & SNOR_PROTO_CLASS_MASK;
> +}
> +
> +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
> +{
> +	return proto & SNOR_PROTO_WIDTH_MASK;
> +}
> +
> +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
> +{
> +	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
> +		spi_nor_get_protocol_width(proto) :
> +		1u;
> +}
> +
> +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
> +{
> +	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
> +		spi_nor_get_protocol_width(proto) :
> +		1u;
> +}
> +
> +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
> +{
> +	return spi_nor_get_protocol_width(proto);
> +}
> +
> +
>  #define SPI_NOR_MAX_CMD_SIZE	8
>  enum spi_nor_ops {
>  	SPI_NOR_OPS_READ = 0,
> @@ -154,9 +225,11 @@ enum spi_nor_option_flags {
>   * @read_opcode:	the read opcode
>   * @read_dummy:		the dummy needed by the read operation
>   * @program_opcode:	the program opcode
> - * @flash_read:		the mode of the read
>   * @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
> + * @write_proto:	the SPI protocol for write operations
> + * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
>   * @cmd_buf:		used by the write_reg
>   * @prepare:		[OPTIONAL] do some preparations for the
>   *			read/write/erase/lock/unlock operations
> @@ -173,6 +246,7 @@ enum spi_nor_option_flags {
>   * @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
>   *			completely locked
> + * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
>   * @priv:		the private data
>   */
>  struct spi_nor {
> @@ -185,7 +259,9 @@ struct spi_nor {
>  	u8			read_opcode;
>  	u8			read_dummy;
>  	u8			program_opcode;
> -	enum read_mode		flash_read;
> +	enum spi_nor_protocol	read_proto;
> +	enum spi_nor_protocol	write_proto;
> +	enum spi_nor_protocol	reg_proto;
>  	bool			sst_write_second;
>  	u32			flags;
>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> @@ -204,6 +280,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 (*flash_quad_enable)(struct spi_nor *nor);
>  
>  	void *priv;
>  };
> @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>  	return mtd_get_of_node(&nor->mtd);
>  }
>  
> +
> +/**
> + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
> + * supported by the SPI controller (bus master).
> + * @mask:		the bitmask listing all the supported hw capabilies
> + */
> +struct spi_nor_hwcaps {
> +	u32	mask;
> +};
> +
> +/*
> + *(Fast) Read capabilities.
> + * MUST be ordered by priority: the higher bit position, the higher priority.
> + * As a matter of performances, it is relevant to use Octo SPI protocols first,
> + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
> + * (Slow) Read.
> + */
> +#define SNOR_HWCAPS_READ_MASK		GENMASK(15, 0)
> +#define SNOR_HWCAPS_READ		BIT(0)
> +#define SNOR_HWCAPS_READ_FAST		BIT(1)
> +#define SNOR_HWCAPS_READ_1_1_1_DTR	BIT(2)
> +
> +#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
> +#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
> +#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
> +#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
> +#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
> +
> +#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
> +#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
> +#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
> +#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
> +#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
> +
> +#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
> +#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
> +#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
> +#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
> +#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
> +
> +/*
> + * Page Program capabilities.
> + * MUST be ordered by priority: the higher bit position, the higher priority.
> + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
> + * legacy SPI 1-1-1 protocol.
> + * Note that Dual Page Programs are not supported because there is no existing
> + * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
> + * implements such commands.
> + */
> +#define SNOR_HWCAPS_PP_MASK	GENMASK(22, 16)
> +#define SNOR_HWCAPS_PP		BIT(16)
> +
> +#define SNOR_HWCAPS_PP_QUAD	GENMASK(19, 17)
> +#define SNOR_HWCAPS_PP_1_1_4	BIT(17)
> +#define SNOR_HWCAPS_PP_1_4_4	BIT(18)
> +#define SNOR_HWCAPS_PP_4_4_4	BIT(19)
> +
> +#define SNOR_HWCAPS_PP_OCTO	GENMASK(22, 20)
> +#define SNOR_HWCAPS_PP_1_1_8	BIT(20)
> +#define SNOR_HWCAPS_PP_1_8_8	BIT(21)
> +#define SNOR_HWCAPS_PP_8_8_8	BIT(22)
> +
>  /**
>   * spi_nor_scan() - scan the SPI NOR
>   * @nor:	the spi_nor structure
>   * @name:	the chip type name
> - * @mode:	the read mode supported by the driver
> + * @hwcaps:	the hardware capabilities supported by the controller driver
>   *
>   * The drivers can use this fuction to scan the SPI NOR.
>   * In the scanning, it will try to get all the necessary information to
> @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>   *
>   * Return: 0 for success, others for failure.
>   */
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
> +int spi_nor_scan(struct spi_nor *nor, const char *name,
> +		 const struct spi_nor_hwcaps *hwcaps);
>  
>  #endif
> 




More information about the linux-mtd mailing list