lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <b1739d32-24e6-7cdc-e053-6fc4db234c3f@wedev4u.fr>
Date:   Fri, 24 Mar 2017 12:39:25 +0100
From:   Cyrille Pitchen <cyrille.pitchen@...ev4u.fr>
To:     Cédric Le Goater <clg@...d.org>,
        Cyrille Pitchen <cyrille.pitchen@...el.com>,
        marek.vasut@...il.com, linux-mtd@...ts.infradead.org,
        jartur@...ence.com, kdasu.kdev@...il.com, mar.krzeminski@...il.com
Cc:     boris.brezillon@...e-electrons.com, richard@....at,
        nicolas.ferre@...rochip.com, linux-kernel@...r.kernel.org,
        computersforpeace@...il.com, dwmw2@...radead.org
Subject: Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the
 Dual Transfer Mode

Le 24/03/2017 à 11:03, Cédric Le Goater a écrit :
> On 03/23/2017 08:10 PM, Cyrille Pitchen wrote:
>> Hi Cédic,
>>
>> Le 23/03/2017 à 16:13, Cédric Le Goater a écrit :
>>> 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_quad_io() was renamed into .quad_enable()
>>
>>>> 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.
>>>
>>
>> No, nor->flash_quad_enable() [ = params->quad_enable() ] is a flash
>> specific handler as described in spi-nor.h and like other flash specific
>> handlers such as flash_lock(), flash_unlock() or flash_is_locked(), is
>> only used internally in spi-nor.c: spi-nor controller drivers like
>> aspeed-smc.c should not use those handlers at all.
> 
> I was talking about adding a spi-nor handler to set up the chip for 
> dual I/O because today we only have one for quad I/O, and some
> controllers (like the Aspeed) do not support Quad. So the only 
> fast I/O we can do today for these controllers is dual I/O data 
> (SPI_NOR_DUAL)
>

OK but as I've explained below, there is nothing to do at the memory
side before using the SPI 1-1-2 or SPI 1-2-2 protocols: there is no need
to reassign pins IO0 and IO1 to other functions ;)

Hence, the JESD216B specification describes all the vendor specific
procedures to be done before using SPI 1-1-4 or SPI 1-4-4 but no
procedure is provided for the Dual SPI case simply because there is
nothing to do!

There are also dedicated procedures for SPI 2-2-2 and 4-4-4 protocols
but those protocols are not supported yet because they come with many
associated issues but no real benefit.
They are state-full and the spi-nor framework would have to guess in
which state the SPI NOR memory is when running spi_nor_scan(). Currently
spi_nor_scan() relies on the READ JEDEC ID (9Fh) command to probe the
memory but for instance Micron memories don't support this command once
in 2-2-2 or 4-4-4 modes; only the READ JEDEC ID Multiple I/O (AFh) is
supported. However this command is not supported at all by many other
vendors. Winbond memories expect the 9Fh command even in 4-4-4 mode but
I've found out that sending 9Fh with the SPI 4-4-4 protocol to many
Micron QSPI memories has a bad side effect: it seems that they return in
their reset state. Also, we don't know the right command to send before
we actually know the memory vendor...

So it's a real mess to support the 2-2-2 and 4-4-4 modes. Besides, the
bandwidth improvement would be so small that it is not worth.

So except for the 2-2-2 mode, there is no SPI command to send to the SPI
flash memory before using Dual SPI protocol.

So everything should already be ready if you want to add support to the
SPI 1-1-2 or SPI 1-2-2 protocols in the Aspeed driver.
If you need support or have more questions, I can help you of course :)

>> the choice of the nor->flash_quad_enable() handler depends only on the
>> memory manufacturer (and the memory part):
>> - (none): Micron
>> - macronix_quad_enable: Macronix
>> - spansion_quad_enable: Spansion, Winbond, ...
>> - spansion_new_quad_enable: Spansion (latest memories), ...
>> - sr2_bit7_quad_enable: ??? (defined in the JESD216B specification)
> 
> yes. Some Micron have a VCONF register to set multiple I/O also.
>  
>> The purpose of those functions is to implement the vendor specific
>> procedure to set the so called "Quad Enable" (QE) bit in some Status
>> Register of the SPI NOR memory.
>>
>> Indeed, most QSPI memories are pin to pin compatible with legacy SPI
>> memories. 2 pins of those memories were dedicated to the Write Protect
>> (WP) and Reset/Hold (RST) functions. Hence before using any Quad SPI
>> commands, almost all QSPI memories (Micron being the only exception I
>> know) require us to set their QE bit so the WP and RST pins are
>> reassigned to functions IO2 and IO3, the 3rd and 4th IO lines needed by
>> SPI 1-1-4 and SPI 1-4-4 protocols. Then the Write Protect and Hold/Reset
>> functions are disabled.
>>
>> From a software point of view, there is nothing to do before using the
>> SPI 1-1-2 or SPI 1-2-2 protocols. As a matter of fact, with those
>> protocols, MISO and MOSI pins are simply reassigned to functions IO0 and
>> IO1.
>>
>>
>> Back to the SPI flash controller, if the hardware needs to be configured
>> in some way to use any Dual or Quad SPI protocols, it has to be done
>> before calling spi_nor_scan() or more likely directly inside the
>> controller driver specific implementation of nor->read(), nor->write().
> 
> yes.
> 
>> With nor->read(), the driver has to check the value of nor->read_proto
>> to know the actual number of I/O lines used during Instruction (x),
>> Address/Dummy (y) and Data (z) clock cycles: nor->read_proto provides
>> the driver with the SPI x-y-z protocol to be used.
>>
>> Also nor->write() has to check the value of nor->write_proto.
>>
>> [ nor->erase() has to check the value of nor->reg_proto: I removed
>> nor->erase_proto since it always had the same value as nor->reg_proto. ]
>>
>> Both nor->write() and nor->read are controller driver specific: they
>> must be implemented by the controller driver and set before calling
>> spi_nor_scan().
>>
>> Then nor->read_proto and nor->write_proto are chosen from spi_nor_scan()
>> based on the actual hardware capabilities shared by both the SPI memory
>> and controller. The SPI controller driver tells the spi-nor framework
>> which SPI protocols it supports or wants to use by setting the new
>> 'struct spi_nor_hwcaps' argument of spi_nor_scan() accordingly.
>>
>>
>> You can have a look at the atmel-quadspi.c driver to have an example of
>> what to do to support the SPI 1-2-2 or SPI 1-4-4 protocols at the
>> controller side.
>>
>> I've updated the Atmel Quad SPI driver since I'm the maintainer of this
>> driver so I know the exact hardware capabilities of this controller but
>> I don't have such a knowledge for Quad SPI controllers of other vendors.
>>
>> Please note that patch is conservative: if a controller driver like the
>> Aspeed one currently doesn't support the Quad or Dual SPI protocols, the
>> patch doesn't enable them in hwcaps.mask.
>>
>> Maintainers of the controller drivers will have to, if they want, extend
>> their driver to add the support of new SPI protocols, otherwise those
>> drivers will keep on working exactly as they used to do before this
>> series. For instance, a controller driver which used SPI_NOR_QUAD before
>> still uses the SPI 1-1-4 protocol.
> 
> Yes. I have some patches adding DUAL support to Aspeed but I will rebase 
> on your patchset when it is merged because they currently conflict.
> 
> Thanks for the detailed explanations !
> 
> C. 
> 
> 
>> Best regards,
>>
>> Cyrille
>>
>>
>>> 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@...el.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
>>>>
>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>>
>>
> 
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ