Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfc5604c authored by Cyrille Pitchen's avatar Cyrille Pitchen Committed by Cyrille Pitchen
Browse files

mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols



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.

Then, to improve the readability of spi_nor_scan(), the discovery of the
memory settings and the memory initialization are now split into two
dedicated functions.

1 - spi_nor_init_params()

The spi_nor_init_params() function is responsible for initializing the
'struct spi_nor_flash_parameter'. Currently this structure is filled with
legacy values but further patches will allow to override some parameter
values dynamically, for instance by reading the JESD216 Serial Flash
Discoverable Parameter (SFDP) tables from the SPI memory.
The spi_nor_init_params() function only deals with the hardware
capabilities of the SPI flash memory: especially it doesn't care about
the hardware capabilities supported by the SPI controller.

2 - spi_nor_setup()

The second function is called once the 'struct spi_nor_flash_parameter'
has been initialized by spi_nor_init_params().
With both 'struct spi_nor_flash_parameter' and 'struct spi_nor_hwcaps',
the new argument of spi_nor_scan(), spi_nor_setup() computes the best
match between hardware caps supported by both the (Q)SPI memory and
controller hence selecting the relevant settings for (Fast) Read and Page
Program operations.

Signed-off-by: default avatarCyrille Pitchen <cyrille.pitchen@atmel.com>
Reviewed-by: default avatarMarek Vasut <marek.vasut@gmail.com>
parent 2ea659a9
Loading
Loading
Loading
Loading
+9 −12
Original line number Original line Diff line number Diff line
@@ -111,14 +111,7 @@ 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)
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
{
{
	switch (nor->flash_read) {
	return spi_nor_get_protocol_data_nbits(nor->read_proto);
	case SPI_NOR_DUAL:
		return 2;
	case SPI_NOR_QUAD:
		return 4;
	default:
		return 0;
	}
}
}


/*
/*
@@ -196,7 +189,11 @@ static int m25p_probe(struct spi_device *spi)
	struct flash_platform_data	*data;
	struct flash_platform_data	*data;
	struct m25p *flash;
	struct m25p *flash;
	struct spi_nor *nor;
	struct spi_nor *nor;
	enum read_mode mode = SPI_NOR_NORMAL;
	struct spi_nor_hwcaps hwcaps = {
		.mask = SNOR_HWCAPS_READ |
			SNOR_HWCAPS_READ_FAST |
			SNOR_HWCAPS_PP,
	};
	char *flash_name;
	char *flash_name;
	int ret;
	int ret;


@@ -222,9 +219,9 @@ static int m25p_probe(struct spi_device *spi)
	flash->spi = spi;
	flash->spi = spi;


	if (spi->mode & SPI_RX_QUAD)
	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)
	else if (spi->mode & SPI_RX_DUAL)
		mode = SPI_NOR_DUAL;
		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;


	if (data && data->name)
	if (data && data->name)
		nor->mtd.name = data->name;
		nor->mtd.name = data->name;
@@ -241,7 +238,7 @@ static int m25p_probe(struct spi_device *spi)
	else
	else
		flash_name = spi->modalias;
		flash_name = spi->modalias;


	ret = spi_nor_scan(nor, flash_name, mode);
	ret = spi_nor_scan(nor, flash_name, &hwcaps);
	if (ret)
	if (ret)
		return ret;
		return ret;


+13 −10
Original line number Original line Diff line number Diff line
@@ -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
	 * TODO: Adjust clocks if fast read is supported and interpret
	 * SPI-NOR flags to adjust controller settings.
	 * SPI-NOR flags to adjust controller settings.
	 */
	 */
	switch (chip->nor.flash_read) {
	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
	case SPI_NOR_NORMAL:
		if (chip->nor.read_dummy == 0)
			cmd = CONTROL_COMMAND_MODE_NORMAL;
			cmd = CONTROL_COMMAND_MODE_NORMAL;
		break;
		else
	case SPI_NOR_FAST:
			cmd = CONTROL_COMMAND_MODE_FREAD;
			cmd = CONTROL_COMMAND_MODE_FREAD;
		break;
	} else {
	default:
		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
		return -EINVAL;
		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,
static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
				  struct device_node *np, struct resource *r)
				  struct device_node *np, struct resource *r)
{
{
	const struct spi_nor_hwcaps hwcaps = {
		.mask = SNOR_HWCAPS_READ |
			SNOR_HWCAPS_READ_FAST |
			SNOR_HWCAPS_PP,
	};
	const struct aspeed_smc_info *info = controller->info;
	const struct aspeed_smc_info *info = controller->info;
	struct device *dev = controller->dev;
	struct device *dev = controller->dev;
	struct device_node *child;
	struct device_node *child;
@@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
			break;
			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
		 * attach when board support is present as determined
		 * by of property.
		 * by of property.
		 */
		 */
		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
		ret = spi_nor_scan(nor, NULL, &hwcaps);
		if (ret)
		if (ret)
			break;
			break;


+56 −27
Original line number Original line Diff line number Diff line
@@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,


static int atmel_qspi_run_command(struct atmel_qspi *aq,
static int atmel_qspi_run_command(struct atmel_qspi *aq,
				  const struct atmel_qspi_command *cmd,
				  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;
	u32 iar, icr, ifr, sr;
	int err = 0;
	int err = 0;


	iar = 0;
	iar = 0;
	icr = 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 */
	/* Compute instruction parameters */
	if (cmd->enable.bits.instruction) {
	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.rx_buf = buf;
	cmd.buf_len = len;
	cmd.buf_len = len;
	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
	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,
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.tx_buf = buf;
	cmd.buf_len = len;
	cmd.buf_len = len;
	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
	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,
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.tx_buf = write_buf;
	cmd.buf_len = len;
	cmd.buf_len = len;
	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
	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;
	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.instruction = nor->erase_opcode;
	cmd.address = (u32)offs;
	cmd.address = (u32)offs;
	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
	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,
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 *aq = nor->priv;
	struct atmel_qspi_command cmd;
	struct atmel_qspi_command cmd;
	u8 num_mode_cycles, num_dummy_cycles;
	u8 num_mode_cycles, num_dummy_cycles;
	u32 ifr_width;
	ssize_t ret;
	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) {
	if (nor->read_dummy >= 2) {
		num_mode_cycles = 2;
		num_mode_cycles = 2;
		num_dummy_cycles = nor->read_dummy - 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.rx_buf = read_buf;
	cmd.buf_len = len;
	cmd.buf_len = len;
	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
				     ifr_width);
				     nor->read_proto);
	return (ret < 0) ? ret : len;
	return (ret < 0) ? ret : len;
}
}


@@ -590,6 +605,20 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)


static int atmel_qspi_probe(struct platform_device *pdev)
static int atmel_qspi_probe(struct platform_device *pdev)
{
{
	const struct spi_nor_hwcaps hwcaps = {
		.mask = SNOR_HWCAPS_READ |
			SNOR_HWCAPS_READ_FAST |
			SNOR_HWCAPS_READ_1_1_2 |
			SNOR_HWCAPS_READ_1_2_2 |
			SNOR_HWCAPS_READ_2_2_2 |
			SNOR_HWCAPS_READ_1_1_4 |
			SNOR_HWCAPS_READ_1_4_4 |
			SNOR_HWCAPS_READ_4_4_4 |
			SNOR_HWCAPS_PP |
			SNOR_HWCAPS_PP_1_1_4 |
			SNOR_HWCAPS_PP_1_4_4 |
			SNOR_HWCAPS_PP_4_4_4,
	};
	struct device_node *child, *np = pdev->dev.of_node;
	struct device_node *child, *np = pdev->dev.of_node;
	struct atmel_qspi *aq;
	struct atmel_qspi *aq;
	struct resource *res;
	struct resource *res;
@@ -679,7 +708,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
	if (err)
	if (err)
		goto disable_clk;
		goto disable_clk;


	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
	err = spi_nor_scan(nor, NULL, &hwcaps);
	if (err)
	if (err)
		goto disable_clk;
		goto disable_clk;


+12 −6
Original line number Original line Diff line number Diff line
@@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;


	if (read) {
	if (read) {
		switch (nor->flash_read) {
		switch (nor->read_proto) {
		case SPI_NOR_NORMAL:
		case SNOR_PROTO_1_1_1:
		case SPI_NOR_FAST:
			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
			break;
			break;
		case SPI_NOR_DUAL:
		case SNOR_PROTO_1_1_2:
			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
			break;
			break;
		case SPI_NOR_QUAD:
		case SNOR_PROTO_1_1_4:
			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
			break;
			break;
		default:
		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)
static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
{
{
	const 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 platform_device *pdev = cqspi->pdev;
	struct device *dev = &pdev->dev;
	struct device *dev = &pdev->dev;
	struct cqspi_flash_pdata *f_pdata;
	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;
			goto err;
		}
		}


		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
		ret = spi_nor_scan(nor, NULL, &hwcaps);
		if (ret)
		if (ret)
			goto err;
			goto err;


+5 −1
Original line number Original line Diff line number Diff line
@@ -957,6 +957,10 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)


static int fsl_qspi_probe(struct platform_device *pdev)
static int fsl_qspi_probe(struct platform_device *pdev)
{
{
	const struct spi_nor_hwcaps hwcaps = {
		.mask = SNOR_HWCAPS_READ_1_1_4 |
			SNOR_HWCAPS_PP,
	};
	struct device_node *np = pdev->dev.of_node;
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	struct device *dev = &pdev->dev;
	struct fsl_qspi *q;
	struct fsl_qspi *q;
@@ -1065,7 +1069,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
		/* set the chip address for READID */
		/* set the chip address for READID */
		fsl_qspi_set_base_addr(q, nor);
		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)
		if (ret)
			goto mutex_failed;
			goto mutex_failed;


Loading