Message ID | 20220414112350.3994901-8-chin-ting_kuo@aspeedtech.com |
---|---|
State | Changes Requested |
Delegated to: | Jagannadha Sutradharudu Teki |
Headers | show |
Series | Add ASPEED SPI controller driver | expand |
On 14/04/22 07:23PM, Chin-Ting Kuo wrote: > This adds support for the dirmap API to the spi-nor subsystem, as > introduced in Linux commit df5c210 ("mtd: spi-nor: use spi-mem > dirmap API"). > > This patch is synchronize from the following patch > https://patchwork.ozlabs.org/project/uboot/patch/20210205043924.149504-4-seanga2@gmail.com/ > > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com> > Signed-off-by: Sean Anderson <seanga2@gmail.com> > --- > drivers/mtd/spi/sf_probe.c | 82 ++++++++++++++++++++++++++++++++++ > drivers/mtd/spi/spi-nor-core.c | 55 ++++++++++++++++------- > include/linux/mtd/spi-nor.h | 18 ++++++++ > 3 files changed, 139 insertions(+), 16 deletions(-) > > diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c > index f461082e03..a3b38b6a29 100644 > --- a/drivers/mtd/spi/sf_probe.c > +++ b/drivers/mtd/spi/sf_probe.c > @@ -10,13 +10,81 @@ > #include <common.h> > #include <dm.h> > #include <errno.h> > +#include <linux/mtd/spi-nor.h> > #include <log.h> > #include <malloc.h> > #include <spi.h> > #include <spi_flash.h> > +#include <spi-mem.h> > > #include "sf_internal.h" > > +#if CONFIG_IS_ENABLED(SPI_DIRMAP) > +static int spi_nor_create_read_dirmap(struct spi_nor *nor) > +{ > + struct spi_mem_dirmap_info info = { > + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), > + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), > + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), > + SPI_MEM_OP_DATA_IN(0, NULL, 0)), > + .offset = 0, > + .length = nor->mtd.size, > + }; > + struct spi_mem_op *op = &info.op_tmpl; > + > + /* get transfer protocols. */ > + spi_nor_setup_op(nor, op, nor->read_proto); > + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); > + > + /* convert the dummy cycles to the number of bytes */ > + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; > + if (spi_nor_protocol_is_dtr(nor->read_proto)) > + op->dummy.nbytes *= 2; > + > + nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info); > + if (IS_ERR(nor->dirmap.rdesc)) > + return PTR_ERR(nor->dirmap.rdesc); > + > + return 0; > +} > + > +static int spi_nor_create_write_dirmap(struct spi_nor *nor) > +{ > + struct spi_mem_dirmap_info info = { > + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), > + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), > + SPI_MEM_OP_NO_DUMMY, > + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), > + .offset = 0, > + .length = nor->mtd.size, > + }; > + struct spi_mem_op *op = &info.op_tmpl; > + > + /* get transfer protocols. */ > + spi_nor_setup_op(nor, op, nor->write_proto); > + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); > + > + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) > + op->addr.nbytes = 0; > + > + nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info); > + if (IS_ERR(nor->dirmap.wdesc)) > + return PTR_ERR(nor->dirmap.wdesc); > + > + return 0; > +} > +#else > +static int spi_nor_create_read_dirmap(struct spi_nor *nor) > +{ > + return 0; > +} > + > +static int spi_nor_create_write_dirmap(struct spi_nor *nor) > +{ > + return 0; > +} > +#endif /* CONFIG_SPI_DIRMAP */ > + Instead of wrapping these in #ifdefs... > /** > * spi_flash_probe_slave() - Probe for a SPI flash device on a bus > * > @@ -45,6 +113,14 @@ static int spi_flash_probe_slave(struct spi_flash *flash) > if (ret) > goto err_read_id; > > + ret = spi_nor_create_read_dirmap(flash); > + if (ret) > + return ret; > + > + ret = spi_nor_create_write_dirmap(flash); > + if (ret) > + return ret; > + ... wrap these in a if (CONFIG_IS_ENABLED(SPI_DIRMAP)) { // Create read and write dirmap } Then at compile time if the config is not enabled, this is a dead branch and the compiler should not look at spi_nor_create_{read,write}_dirmap() at all. > if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) > ret = spi_flash_mtd_register(flash); > > @@ -83,6 +159,9 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs, > > void spi_flash_free(struct spi_flash *flash) > { > + spi_mem_dirmap_destroy(flash->dirmap.wdesc); > + spi_mem_dirmap_destroy(flash->dirmap.rdesc); > + > if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) > spi_flash_mtd_unregister(flash); > > @@ -153,6 +232,9 @@ static int spi_flash_std_remove(struct udevice *dev) > struct spi_flash *flash = dev_get_uclass_priv(dev); > int ret; > > + spi_mem_dirmap_destroy(flash->dirmap.wdesc); > + spi_mem_dirmap_destroy(flash->dirmap.rdesc); > + > ret = spi_nor_remove(flash); > if (ret) > return ret; > diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c > index 3b7c817c02..0c6262b7fd 100644 > --- a/drivers/mtd/spi/spi-nor-core.c > +++ b/drivers/mtd/spi/spi-nor-core.c > @@ -239,9 +239,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, > * need to be initialized. > * @proto: the protocol from which the properties need to be set. > */ > -static void spi_nor_setup_op(const struct spi_nor *nor, > - struct spi_mem_op *op, > - const enum spi_nor_protocol proto) > +void spi_nor_setup_op(const struct spi_nor *nor, > + struct spi_mem_op *op, > + const enum spi_nor_protocol proto) > { > u8 ext; > > @@ -362,13 +362,29 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, > > while (remaining) { > op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; > - ret = spi_mem_adjust_op_size(nor->spi, &op); > - if (ret) > - return ret; > > - ret = spi_mem_exec_op(nor->spi, &op); > - if (ret) > - return ret; > + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) { > + /* > + * Record current operation information which may be used > + * when the address or data length exceeds address mapping. > + */ > + memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op, > + sizeof(struct spi_mem_op)); > + ret = spi_mem_dirmap_read(nor->dirmap.rdesc, > + op.addr.val, op.data.nbytes, > + op.data.buf.in); > + if (ret < 0) > + return ret; > + op.data.nbytes = ret; > + } else { > + ret = spi_mem_adjust_op_size(nor->spi, &op); > + if (ret) > + return ret; > + > + ret = spi_mem_exec_op(nor->spi, &op); > + if (ret) > + return ret; > + } > > op.addr.val += op.data.nbytes; > remaining -= op.data.nbytes; > @@ -393,14 +409,21 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, > > spi_nor_setup_op(nor, &op, nor->write_proto); > > - ret = spi_mem_adjust_op_size(nor->spi, &op); > - if (ret) > - return ret; > - op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; > + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) { > + memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op, > + sizeof(struct spi_mem_op)); > + op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val, > + op.data.nbytes, op.data.buf.out); > + } else { > + ret = spi_mem_adjust_op_size(nor->spi, &op); > + if (ret) > + return ret; > + op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; > > - ret = spi_mem_exec_op(nor->spi, &op); > - if (ret) > - return ret; > + ret = spi_mem_exec_op(nor->spi, &op); > + if (ret) > + return ret; > + } > > return op.data.nbytes; > } > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index 4ceeae623d..2a5ad09625 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -11,6 +11,7 @@ > #include <linux/bitops.h> > #include <linux/mtd/cfi.h> > #include <linux/mtd/mtd.h> > +#include <spi-mem.h> > > /* > * Manufacturer IDs > @@ -511,6 +512,7 @@ struct spi_flash { > * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode > * @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode. > * @ready: [FLASH-SPECIFIC] check if the flash is ready > + * @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes. > * @priv: the private data > */ > struct spi_nor { > @@ -561,6 +563,11 @@ struct spi_nor { > int (*octal_dtr_enable)(struct spi_nor *nor); > int (*ready)(struct spi_nor *nor); > > + struct { > + struct spi_mem_dirmap_desc *rdesc; > + struct spi_mem_dirmap_desc *wdesc; > + } dirmap; > + > void *priv; > char mtd_name[MTD_NAME_SIZE(MTD_DEV_TYPE_NOR)]; > /* Compatibility for spi_flash, remove once sf layer is merged with mtd */ > @@ -584,6 +591,17 @@ device_node *spi_nor_get_flash_node(struct spi_nor *nor) > } > #endif /* __UBOOT__ */ > > +/** > + * spi_nor_setup_op() - Set up common properties of a spi-mem op. > + * @nor: pointer to a 'struct spi_nor' > + * @op: pointer to the 'struct spi_mem_op' whose properties > + * need to be initialized. > + * @proto: the protocol from which the properties need to be set. > + */ > +void spi_nor_setup_op(const struct spi_nor *nor, > + struct spi_mem_op *op, > + const enum spi_nor_protocol proto); > + > /** > * spi_nor_scan() - scan the SPI NOR > * @nor: the spi_nor structure > -- > 2.25.1 >
Hi Pratyush, Thanks for your review. > -----Original Message----- > From: Pratyush Yadav <p.yadav@ti.com> > Sent: Wednesday, April 20, 2022 4:21 PM > To: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com> > Subject: Re: [PATCH 7/8] mtd: spi-nor: use spi-mem dirmap API > > On 14/04/22 07:23PM, Chin-Ting Kuo wrote: > > This adds support for the dirmap API to the spi-nor subsystem, as > > introduced in Linux commit df5c210 ("mtd: spi-nor: use spi-mem dirmap > > API"). > > > > This patch is synchronize from the following patch > > https://patchwork.ozlabs.org/project/uboot/patch/20210205043924.149504 > > -4-seanga2@gmail.com/ > > > > Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com> > > Signed-off-by: Sean Anderson <seanga2@gmail.com> > > --- > > drivers/mtd/spi/sf_probe.c | 82 > ++++++++++++++++++++++++++++++++++ > > drivers/mtd/spi/spi-nor-core.c | 55 ++++++++++++++++------- > > include/linux/mtd/spi-nor.h | 18 ++++++++ > > 3 files changed, 139 insertions(+), 16 deletions(-) > > > > diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c > > index f461082e03..a3b38b6a29 100644 > > --- a/drivers/mtd/spi/sf_probe.c > > +++ b/drivers/mtd/spi/sf_probe.c > > @@ -10,13 +10,81 @@ > > #include <common.h> > > #include <dm.h> > > #include <errno.h> > > +#include <linux/mtd/spi-nor.h> > > #include <log.h> > > #include <malloc.h> > > #include <spi.h> > > #include <spi_flash.h> > > +#include <spi-mem.h> > > > > #include "sf_internal.h" > > > > +#if CONFIG_IS_ENABLED(SPI_DIRMAP) > > +static int spi_nor_create_read_dirmap(struct spi_nor *nor) { > > + struct spi_mem_dirmap_info info = { > > + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), > > + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), > > + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), > > + SPI_MEM_OP_DATA_IN(0, NULL, 0)), > > + .offset = 0, > > + .length = nor->mtd.size, > > + }; > > + struct spi_mem_op *op = &info.op_tmpl; > > + > > + /* get transfer protocols. */ > > + spi_nor_setup_op(nor, op, nor->read_proto); > > + op->data.buswidth = > > +spi_nor_get_protocol_data_nbits(nor->read_proto); > > + > > + /* convert the dummy cycles to the number of bytes */ > > + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; > > + if (spi_nor_protocol_is_dtr(nor->read_proto)) > > + op->dummy.nbytes *= 2; > > + > > + nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info); > > + if (IS_ERR(nor->dirmap.rdesc)) > > + return PTR_ERR(nor->dirmap.rdesc); > > + > > + return 0; > > +} > > + > > +static int spi_nor_create_write_dirmap(struct spi_nor *nor) { > > + struct spi_mem_dirmap_info info = { > > + .op_tmpl = > SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), > > + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), > > + SPI_MEM_OP_NO_DUMMY, > > + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), > > + .offset = 0, > > + .length = nor->mtd.size, > > + }; > > + struct spi_mem_op *op = &info.op_tmpl; > > + > > + /* get transfer protocols. */ > > + spi_nor_setup_op(nor, op, nor->write_proto); > > + op->data.buswidth = > > +spi_nor_get_protocol_data_nbits(nor->write_proto); > > + > > + if (nor->program_opcode == SPINOR_OP_AAI_WP && > nor->sst_write_second) > > + op->addr.nbytes = 0; > > + > > + nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info); > > + if (IS_ERR(nor->dirmap.wdesc)) > > + return PTR_ERR(nor->dirmap.wdesc); > > + > > + return 0; > > +} > > +#else > > +static int spi_nor_create_read_dirmap(struct spi_nor *nor) { > > + return 0; > > +} > > + > > +static int spi_nor_create_write_dirmap(struct spi_nor *nor) { > > + return 0; > > +} > > +#endif /* CONFIG_SPI_DIRMAP */ > > + > > Instead of wrapping these in #ifdefs... > > > /** > > * spi_flash_probe_slave() - Probe for a SPI flash device on a bus > > * > > @@ -45,6 +113,14 @@ static int spi_flash_probe_slave(struct spi_flash > *flash) > > if (ret) > > goto err_read_id; > > > > + ret = spi_nor_create_read_dirmap(flash); > > + if (ret) > > + return ret; > > + > > + ret = spi_nor_create_write_dirmap(flash); > > + if (ret) > > + return ret; > > + > > ... wrap these in a > > if (CONFIG_IS_ENABLED(SPI_DIRMAP)) { > // Create read and write dirmap > } > > Then at compile time if the config is not enabled, this is a dead branch and the > compiler should not look at spi_nor_create_{read,write}_dirmap() > at all. Okay, this will be updated in the next version. Best Wishes, Chin-Ting > > > if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) > > ret = spi_flash_mtd_register(flash); > > > > @@ -83,6 +159,9 @@ struct spi_flash *spi_flash_probe(unsigned int > > busnum, unsigned int cs, > > > > void spi_flash_free(struct spi_flash *flash) { > > + spi_mem_dirmap_destroy(flash->dirmap.wdesc); > > + spi_mem_dirmap_destroy(flash->dirmap.rdesc); > > + > > if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) > > spi_flash_mtd_unregister(flash); > > > > @@ -153,6 +232,9 @@ static int spi_flash_std_remove(struct udevice *dev) > > struct spi_flash *flash = dev_get_uclass_priv(dev); > > int ret; > > > > + spi_mem_dirmap_destroy(flash->dirmap.wdesc); > > + spi_mem_dirmap_destroy(flash->dirmap.rdesc); > > + > > ret = spi_nor_remove(flash); > > if (ret) > > return ret; > > diff --git a/drivers/mtd/spi/spi-nor-core.c > > b/drivers/mtd/spi/spi-nor-core.c index 3b7c817c02..0c6262b7fd 100644 > > --- a/drivers/mtd/spi/spi-nor-core.c > > +++ b/drivers/mtd/spi/spi-nor-core.c > > @@ -239,9 +239,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor > *nor, > > * need to be initialized. > > * @proto: the protocol from which the properties need to be set. > > */ > > -static void spi_nor_setup_op(const struct spi_nor *nor, > > - struct spi_mem_op *op, > > - const enum spi_nor_protocol proto) > > +void spi_nor_setup_op(const struct spi_nor *nor, > > + struct spi_mem_op *op, > > + const enum spi_nor_protocol proto) > > { > > u8 ext; > > > > @@ -362,13 +362,29 @@ static ssize_t spi_nor_read_data(struct spi_nor > > *nor, loff_t from, size_t len, > > > > while (remaining) { > > op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; > > - ret = spi_mem_adjust_op_size(nor->spi, &op); > > - if (ret) > > - return ret; > > > > - ret = spi_mem_exec_op(nor->spi, &op); > > - if (ret) > > - return ret; > > + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) { > > + /* > > + * Record current operation information which may be used > > + * when the address or data length exceeds address mapping. > > + */ > > + memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op, > > + sizeof(struct spi_mem_op)); > > + ret = spi_mem_dirmap_read(nor->dirmap.rdesc, > > + op.addr.val, op.data.nbytes, > > + op.data.buf.in); > > + if (ret < 0) > > + return ret; > > + op.data.nbytes = ret; > > + } else { > > + ret = spi_mem_adjust_op_size(nor->spi, &op); > > + if (ret) > > + return ret; > > + > > + ret = spi_mem_exec_op(nor->spi, &op); > > + if (ret) > > + return ret; > > + } > > > > op.addr.val += op.data.nbytes; > > remaining -= op.data.nbytes; > > @@ -393,14 +409,21 @@ static ssize_t spi_nor_write_data(struct spi_nor > > *nor, loff_t to, size_t len, > > > > spi_nor_setup_op(nor, &op, nor->write_proto); > > > > - ret = spi_mem_adjust_op_size(nor->spi, &op); > > - if (ret) > > - return ret; > > - op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; > > + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) { > > + memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op, > > + sizeof(struct spi_mem_op)); > > + op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, > op.addr.val, > > + op.data.nbytes, op.data.buf.out); > > + } else { > > + ret = spi_mem_adjust_op_size(nor->spi, &op); > > + if (ret) > > + return ret; > > + op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; > > > > - ret = spi_mem_exec_op(nor->spi, &op); > > - if (ret) > > - return ret; > > + ret = spi_mem_exec_op(nor->spi, &op); > > + if (ret) > > + return ret; > > + } > > > > return op.data.nbytes; > > } > > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > > index 4ceeae623d..2a5ad09625 100644 > > --- a/include/linux/mtd/spi-nor.h > > +++ b/include/linux/mtd/spi-nor.h > > @@ -11,6 +11,7 @@ > > #include <linux/bitops.h> > > #include <linux/mtd/cfi.h> > > #include <linux/mtd/mtd.h> > > +#include <spi-mem.h> > > > > /* > > * Manufacturer IDs > > @@ -511,6 +512,7 @@ struct spi_flash { > > * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode > > * @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR > mode. > > * @ready: [FLASH-SPECIFIC] check if the flash is ready > > + * @dirmap: pointers to struct spi_mem_dirmap_desc for > reads/writes. > > * @priv: the private data > > */ > > struct spi_nor { > > @@ -561,6 +563,11 @@ struct spi_nor { > > int (*octal_dtr_enable)(struct spi_nor *nor); > > int (*ready)(struct spi_nor *nor); > > > > + struct { > > + struct spi_mem_dirmap_desc *rdesc; > > + struct spi_mem_dirmap_desc *wdesc; > > + } dirmap; > > + > > void *priv; > > char mtd_name[MTD_NAME_SIZE(MTD_DEV_TYPE_NOR)]; > > /* Compatibility for spi_flash, remove once sf layer is merged with > > mtd */ @@ -584,6 +591,17 @@ device_node > *spi_nor_get_flash_node(struct > > spi_nor *nor) } #endif /* __UBOOT__ */ > > > > +/** > > + * spi_nor_setup_op() - Set up common properties of a spi-mem op. > > + * @nor: pointer to a 'struct spi_nor' > > + * @op: pointer to the 'struct spi_mem_op' whose properties > > + * need to be initialized. > > + * @proto: the protocol from which the properties need to be set. > > + */ > > +void spi_nor_setup_op(const struct spi_nor *nor, > > + struct spi_mem_op *op, > > + const enum spi_nor_protocol proto); > > + > > /** > > * spi_nor_scan() - scan the SPI NOR > > * @nor: the spi_nor structure > > -- > > 2.25.1 > > > > -- > Regards, > Pratyush Yadav > Texas Instruments Inc.
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index f461082e03..a3b38b6a29 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -10,13 +10,81 @@ #include <common.h> #include <dm.h> #include <errno.h> +#include <linux/mtd/spi-nor.h> #include <log.h> #include <malloc.h> #include <spi.h> #include <spi_flash.h> +#include <spi-mem.h> #include "sf_internal.h" +#if CONFIG_IS_ENABLED(SPI_DIRMAP) +static int spi_nor_create_read_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(0, NULL, 0)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + spi_nor_setup_op(nor, op, nor->read_proto); + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op->dummy.nbytes *= 2; + + nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info); + if (IS_ERR(nor->dirmap.rdesc)) + return PTR_ERR(nor->dirmap.rdesc); + + return 0; +} + +static int spi_nor_create_write_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + spi_nor_setup_op(nor, op, nor->write_proto); + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + op->addr.nbytes = 0; + + nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info); + if (IS_ERR(nor->dirmap.wdesc)) + return PTR_ERR(nor->dirmap.wdesc); + + return 0; +} +#else +static int spi_nor_create_read_dirmap(struct spi_nor *nor) +{ + return 0; +} + +static int spi_nor_create_write_dirmap(struct spi_nor *nor) +{ + return 0; +} +#endif /* CONFIG_SPI_DIRMAP */ + /** * spi_flash_probe_slave() - Probe for a SPI flash device on a bus * @@ -45,6 +113,14 @@ static int spi_flash_probe_slave(struct spi_flash *flash) if (ret) goto err_read_id; + ret = spi_nor_create_read_dirmap(flash); + if (ret) + return ret; + + ret = spi_nor_create_write_dirmap(flash); + if (ret) + return ret; + if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) ret = spi_flash_mtd_register(flash); @@ -83,6 +159,9 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs, void spi_flash_free(struct spi_flash *flash) { + spi_mem_dirmap_destroy(flash->dirmap.wdesc); + spi_mem_dirmap_destroy(flash->dirmap.rdesc); + if (CONFIG_IS_ENABLED(SPI_FLASH_MTD)) spi_flash_mtd_unregister(flash); @@ -153,6 +232,9 @@ static int spi_flash_std_remove(struct udevice *dev) struct spi_flash *flash = dev_get_uclass_priv(dev); int ret; + spi_mem_dirmap_destroy(flash->dirmap.wdesc); + spi_mem_dirmap_destroy(flash->dirmap.rdesc); + ret = spi_nor_remove(flash); if (ret) return ret; diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 3b7c817c02..0c6262b7fd 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -239,9 +239,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, * need to be initialized. * @proto: the protocol from which the properties need to be set. */ -static void spi_nor_setup_op(const struct spi_nor *nor, - struct spi_mem_op *op, - const enum spi_nor_protocol proto) +void spi_nor_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto) { u8 ext; @@ -362,13 +362,29 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, while (remaining) { op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; - ret = spi_mem_adjust_op_size(nor->spi, &op); - if (ret) - return ret; - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) { + /* + * Record current operation information which may be used + * when the address or data length exceeds address mapping. + */ + memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op, + sizeof(struct spi_mem_op)); + ret = spi_mem_dirmap_read(nor->dirmap.rdesc, + op.addr.val, op.data.nbytes, + op.data.buf.in); + if (ret < 0) + return ret; + op.data.nbytes = ret; + } else { + ret = spi_mem_adjust_op_size(nor->spi, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; + } op.addr.val += op.data.nbytes; remaining -= op.data.nbytes; @@ -393,14 +409,21 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, spi_nor_setup_op(nor, &op, nor->write_proto); - ret = spi_mem_adjust_op_size(nor->spi, &op); - if (ret) - return ret; - op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; + if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) { + memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op, + sizeof(struct spi_mem_op)); + op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val, + op.data.nbytes, op.data.buf.out); + } else { + ret = spi_mem_adjust_op_size(nor->spi, &op); + if (ret) + return ret; + op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; - ret = spi_mem_exec_op(nor->spi, &op); - if (ret) - return ret; + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) + return ret; + } return op.data.nbytes; } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4ceeae623d..2a5ad09625 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,6 +11,7 @@ #include <linux/bitops.h> #include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> +#include <spi-mem.h> /* * Manufacturer IDs @@ -511,6 +512,7 @@ struct spi_flash { * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode * @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode. * @ready: [FLASH-SPECIFIC] check if the flash is ready + * @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes. * @priv: the private data */ struct spi_nor { @@ -561,6 +563,11 @@ struct spi_nor { int (*octal_dtr_enable)(struct spi_nor *nor); int (*ready)(struct spi_nor *nor); + struct { + struct spi_mem_dirmap_desc *rdesc; + struct spi_mem_dirmap_desc *wdesc; + } dirmap; + void *priv; char mtd_name[MTD_NAME_SIZE(MTD_DEV_TYPE_NOR)]; /* Compatibility for spi_flash, remove once sf layer is merged with mtd */ @@ -584,6 +591,17 @@ device_node *spi_nor_get_flash_node(struct spi_nor *nor) } #endif /* __UBOOT__ */ +/** + * spi_nor_setup_op() - Set up common properties of a spi-mem op. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * @proto: the protocol from which the properties need to be set. + */ +void spi_nor_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto); + /** * spi_nor_scan() - scan the SPI NOR * @nor: the spi_nor structure