mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
Aside from preparation changes in the SPI NAND core, the changes carried
here focus on the shared spi-mem layer which is enhanced in order to bring two new features: - The possibility to fill a primary and a secondary operation template in the direct mapping structure in order to support continuous reads in SPI NAND, which may require two different read operations. - SPI controllers may indicate possible CS instabilities over long transfers by setting a boolean. This capability is related to the previous one, the need for it has arised while testing SPI NAND continuous reads with the Cadence QSPI controller which cannot, under certain conditions, keep the CS asserted for the length of an eraseblock-large transfer. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmn4m50ACgkQJWrqGEe9 VoSUzwf8D7an5inOVMcX2IsbXSkA6vKgrzjc1jG3rGiLrFks/bZyo0R8atp4pUsy 6WDkJfFyHg6IzU92N/37Kha8rnFp7um9OhfB/z2O/ztyqr9d1yM6QDtenknYr+WH BwcuJ8zenyWuIY526Ar04REJ07s8MZpMUhcnUIw/KpvQiS12UR6NHgEaUrmIjtLD GCKKFTGOY++9dgObo8ZrkrsLlVUhV5GyvCqUtBNNbADTNAt7j4m6YuyztS7rrmwb b33RoZiWmG0szkUsTJJBDH15cMB/4wV2SoGm3wzsACHqJhCy0n9FvSVAXwcRm5l7 qOLedtMoBIIIIwBbHPsf0SySToy2ag== =3qpz -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmosAyEACgkQJNaLcl1U h9BOcgf9EtmCuxpcX2n0PQlE6j88fr/zFDArtOXFZNFKdhL46u2kUWhdrvDH8i0J 1FBbxRWsQqfrLesveqHqzVSg0ub6USe80Cbzjt2Vo1Ri+RF2i0b+5KNd1VrHC/iF DG+8A2c4VmjdiuCkaNI1gixMqdRJHSSWEjGD61+rv4M/VBIr1YtAV5kIJ+tFLPoY lYrvNMGcUGUTHUK2tJx4uaMuo7dd3C+hIWnnMu6OZ+TB/VbX77z4jrhqTONzxdYy uV+Zxh1PPLBvysPsqwSXpdW2FjfUi2SSGYrNn+kw0pdb58Th3/x7Tocb3t7awfWU Ah5Yw6zUF2vJMBFAOhHNJ9r3hvtoBw== =WwE4 -----END PGP SIGNATURE----- spi: spi-mem: Add a no_cs_assertion capability Merge tag 'mtd/spi-mem-cont-read-for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into spi-7.2 Miquel Raynal <miquel.raynal@bootlin.com> says: Aside from preparation changes in the SPI NAND core, the changes carried here focus on the shared spi-mem layer which is enhanced in order to bring two new features: - The possibility to fill a primary and a secondary operation template in the direct mapping structure in order to support continuous reads in SPI NAND, which may require two different read operations. - SPI controllers may indicate possible CS instabilities over long transfers by setting a boolean. This capability is related to the previous one, the need for it has arised while testing SPI NAND continuous reads with the Cadence QSPI controller which cannot, under certain conditions, keep the CS asserted for the length of an eraseblock-large transfer.
This commit is contained in:
commit
b63c2c3199
|
|
@ -501,10 +501,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
|
|||
}
|
||||
}
|
||||
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
rdesc = spinand->dirmaps[req->pos.plane].rdesc;
|
||||
rdesc = spinand->dirmaps[req->pos.plane].rdesc;
|
||||
|
||||
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
|
||||
req->mode != MTD_OPS_RAW)
|
||||
rdesc->info.op_tmpl->data.ecc = true;
|
||||
else
|
||||
rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
|
||||
rdesc->info.op_tmpl->data.ecc = false;
|
||||
|
||||
if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT)
|
||||
column |= req->pos.plane << fls(nanddev_page_size(nand));
|
||||
|
|
@ -593,10 +596,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
|
|||
req->ooblen);
|
||||
}
|
||||
|
||||
if (req->mode == MTD_OPS_RAW)
|
||||
wdesc = spinand->dirmaps[req->pos.plane].wdesc;
|
||||
wdesc = spinand->dirmaps[req->pos.plane].wdesc;
|
||||
|
||||
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
|
||||
req->mode != MTD_OPS_RAW)
|
||||
wdesc->info.op_tmpl->data.ecc = true;
|
||||
else
|
||||
wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
|
||||
wdesc->info.op_tmpl->data.ecc = false;
|
||||
|
||||
if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT)
|
||||
column |= req->pos.plane << fls(nanddev_page_size(nand));
|
||||
|
|
@ -892,6 +898,12 @@ static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from,
|
|||
* Each data read must be a multiple of 4-bytes and full pages should be read;
|
||||
* otherwise, the data output might get out of sequence from one read command
|
||||
* to another.
|
||||
*
|
||||
* Continuous reads never cross LUN boundaries. Some devices don't
|
||||
* support crossing planes boundaries. Some devices don't even support
|
||||
* crossing blocks boundaries. The common case being to read through UBI,
|
||||
* we will very rarely read two consequent blocks or more, so let's only enable
|
||||
* continuous reads when reading within the same erase block.
|
||||
*/
|
||||
nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) {
|
||||
ret = spinand_select_target(spinand, iter.req.pos.target);
|
||||
|
|
@ -982,19 +994,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from,
|
|||
nanddev_offs_to_pos(nand, from, &start_pos);
|
||||
nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos);
|
||||
|
||||
/*
|
||||
* Continuous reads never cross LUN boundaries. Some devices don't
|
||||
* support crossing planes boundaries. Some devices don't even support
|
||||
* crossing blocks boundaries. The common case being to read through UBI,
|
||||
* we will very rarely read two consequent blocks or more, so it is safer
|
||||
* and easier (can be improved) to only enable continuous reads when
|
||||
* reading within the same erase block.
|
||||
*/
|
||||
if (start_pos.target != end_pos.target ||
|
||||
start_pos.plane != end_pos.plane ||
|
||||
start_pos.eraseblock != end_pos.eraseblock)
|
||||
return false;
|
||||
|
||||
return start_pos.page < end_pos.page;
|
||||
}
|
||||
|
||||
|
|
@ -1252,12 +1251,18 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
|
|||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
struct spi_mem_dirmap_info info = { 0 };
|
||||
struct spi_mem_dirmap_desc *desc;
|
||||
bool enable_ecc = false;
|
||||
|
||||
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
|
||||
enable_ecc = true;
|
||||
|
||||
/* The plane number is passed in MSB just above the column address */
|
||||
info.offset = plane << fls(nand->memorg.pagesize);
|
||||
|
||||
/* Write descriptor */
|
||||
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
|
||||
info.op_tmpl = *spinand->op_templates->update_cache;
|
||||
info.primary_op_tmpl = *spinand->op_templates->update_cache;
|
||||
info.primary_op_tmpl.data.ecc = enable_ecc;
|
||||
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
|
||||
spinand->spimem, &info);
|
||||
if (IS_ERR(desc))
|
||||
|
|
@ -1265,38 +1270,15 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
|
|||
|
||||
spinand->dirmaps[plane].wdesc = desc;
|
||||
|
||||
info.op_tmpl = *spinand->op_templates->read_cache;
|
||||
/* Read descriptor */
|
||||
info.primary_op_tmpl = *spinand->op_templates->read_cache;
|
||||
info.primary_op_tmpl.data.ecc = enable_ecc;
|
||||
desc = spinand_create_rdesc(spinand, &info);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
spinand->dirmaps[plane].rdesc = desc;
|
||||
|
||||
if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) {
|
||||
spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
|
||||
spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
|
||||
info.op_tmpl = *spinand->op_templates->update_cache;
|
||||
info.op_tmpl.data.ecc = true;
|
||||
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
|
||||
spinand->spimem, &info);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
spinand->dirmaps[plane].wdesc_ecc = desc;
|
||||
|
||||
info.op_tmpl = *spinand->op_templates->read_cache;
|
||||
info.op_tmpl.data.ecc = true;
|
||||
desc = spinand_create_rdesc(spinand, &info);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
spinand->dirmaps[plane].rdesc_ecc = desc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1421,7 +1403,7 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
|
|||
return spinand->manufacturer->ops->cleanup(spinand);
|
||||
}
|
||||
|
||||
static bool spinand_op_is_odtr(const struct spi_mem_op *op)
|
||||
bool spinand_op_is_odtr(const struct spi_mem_op *op)
|
||||
{
|
||||
return op->cmd.dtr && op->cmd.buswidth == 8;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3641,14 +3641,15 @@ EXPORT_SYMBOL_GPL(spi_nor_scan);
|
|||
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_nbytes, 0, 0),
|
||||
SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
|
||||
SPI_MEM_OP_DATA_IN(0, NULL, 0)),
|
||||
.op_tmpl = &info.primary_op_tmpl,
|
||||
.primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
|
||||
SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
|
||||
SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
|
||||
SPI_MEM_OP_DATA_IN(0, NULL, 0)),
|
||||
.offset = 0,
|
||||
.length = nor->params->size,
|
||||
};
|
||||
struct spi_mem_op *op = &info.op_tmpl;
|
||||
struct spi_mem_op *op = info.op_tmpl;
|
||||
|
||||
spi_nor_spimem_setup_op(nor, op, nor->read_proto);
|
||||
|
||||
|
|
@ -3672,14 +3673,15 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor)
|
|||
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_nbytes, 0, 0),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
|
||||
.op_tmpl = &info.primary_op_tmpl,
|
||||
.primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
|
||||
SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
|
||||
.offset = 0,
|
||||
.length = nor->params->size,
|
||||
};
|
||||
struct spi_mem_op *op = &info.op_tmpl;
|
||||
struct spi_mem_op *op = info.op_tmpl;
|
||||
|
||||
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
|
||||
op->addr.nbytes = 0;
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
if (desc->info.length > SPI_NAND_CACHE_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
if (!airoha_snand_supports_op(desc->mem, desc->info.op_tmpl))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
|
|
@ -572,7 +572,7 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
* DUALIO and QUADIO opcodes are not supported by the spi controller,
|
||||
* replace them with supported opcodes.
|
||||
*/
|
||||
opcode = desc->info.op_tmpl.cmd.opcode;
|
||||
opcode = desc->info.op_tmpl->cmd.opcode;
|
||||
switch (opcode) {
|
||||
case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
|
||||
case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
|
||||
|
|
@ -761,7 +761,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
|||
/* minimum oob size is 64 */
|
||||
bytes = round_up(offs + len, 64);
|
||||
|
||||
opcode = desc->info.op_tmpl.cmd.opcode;
|
||||
opcode = desc->info.op_tmpl->cmd.opcode;
|
||||
switch (opcode) {
|
||||
case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
|
||||
case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
|
||||
|
|
|
|||
|
|
@ -697,7 +697,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
{
|
||||
struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)];
|
||||
struct spi_mem_op *op = &desc->info.op_tmpl;
|
||||
struct spi_mem_op *op = desc->info.op_tmpl;
|
||||
u32 ctl_val;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -769,7 +769,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
if (chip->ahb_window_size < offset + len || chip->force_user_mode) {
|
||||
int ret;
|
||||
|
||||
ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf);
|
||||
ret = aspeed_spi_read_user(chip, desc->info.op_tmpl, offset, len, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -814,7 +814,7 @@ static int intel_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
const struct intel_spi_mem_op *iop;
|
||||
|
||||
iop = intel_spi_match_mem_op(ispi, &desc->info.op_tmpl);
|
||||
iop = intel_spi_match_mem_op(ispi, desc->info.op_tmpl);
|
||||
if (!iop)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
|
@ -827,7 +827,7 @@ static ssize_t intel_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs,
|
|||
{
|
||||
struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
const struct intel_spi_mem_op *iop = desc->priv;
|
||||
struct spi_mem_op op = desc->info.op_tmpl;
|
||||
struct spi_mem_op op = *desc->info.op_tmpl;
|
||||
int ret;
|
||||
|
||||
/* Fill in the gaps */
|
||||
|
|
@ -844,7 +844,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs
|
|||
{
|
||||
struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
const struct intel_spi_mem_op *iop = desc->priv;
|
||||
struct spi_mem_op op = desc->info.op_tmpl;
|
||||
struct spi_mem_op op = *desc->info.op_tmpl;
|
||||
int ret;
|
||||
|
||||
op.addr.val = offs;
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
|
|||
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
struct spi_mem_op op = desc->info.op_tmpl;
|
||||
struct spi_mem_op op = *desc->info.op_tmpl;
|
||||
int ret;
|
||||
|
||||
op.addr.val = desc->info.offset + offs;
|
||||
|
|
@ -674,7 +674,7 @@ static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, const void *buf)
|
||||
{
|
||||
struct spi_mem_op op = desc->info.op_tmpl;
|
||||
struct spi_mem_op op = *desc->info.op_tmpl;
|
||||
int ret;
|
||||
|
||||
op.addr.val = desc->info.offset + offs;
|
||||
|
|
@ -713,19 +713,37 @@ spi_mem_dirmap_create(struct spi_mem *mem,
|
|||
int ret = -ENOTSUPP;
|
||||
|
||||
/* Make sure the number of address cycles is between 1 and 8 bytes. */
|
||||
if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
|
||||
if (!info->primary_op_tmpl.addr.nbytes || info->primary_op_tmpl.addr.nbytes > 8)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
|
||||
if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
|
||||
if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* Apply similar constraints to the secondary template */
|
||||
if (info->secondary_op_tmpl.cmd.opcode) {
|
||||
if (!info->secondary_op_tmpl.addr.nbytes ||
|
||||
info->secondary_op_tmpl.addr.nbytes > 8)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (info->secondary_op_tmpl.data.dir == SPI_MEM_NO_DATA)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!spi_mem_supports_op(mem, &info->secondary_op_tmpl))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create &&
|
||||
!spi_mem_controller_is_capable(ctlr, secondary_op_tmpl))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
desc = kzalloc_obj(*desc);
|
||||
if (!desc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
desc->mem = mem;
|
||||
desc->info = *info;
|
||||
desc->info.op_tmpl = &desc->info.primary_op_tmpl;
|
||||
if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) {
|
||||
ret = spi_mem_access_start(mem);
|
||||
if (ret) {
|
||||
|
|
@ -740,7 +758,7 @@ spi_mem_dirmap_create(struct spi_mem *mem,
|
|||
|
||||
if (ret) {
|
||||
desc->nodirmap = true;
|
||||
if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
if (!spi_mem_supports_op(desc->mem, &desc->info.primary_op_tmpl))
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
ret = 0;
|
||||
|
|
@ -864,7 +882,7 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
struct spi_controller *ctlr = desc->mem->spi->controller;
|
||||
ssize_t ret;
|
||||
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN)
|
||||
return -EINVAL;
|
||||
|
||||
if (!len)
|
||||
|
|
@ -910,7 +928,7 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
|||
struct spi_controller *ctlr = desc->mem->spi->controller;
|
||||
ssize_t ret;
|
||||
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
|
||||
if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_OUT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!len)
|
||||
|
|
|
|||
|
|
@ -403,20 +403,20 @@ static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
if (WARN_ON(offs + desc->info.offset + len > U32_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16),
|
||||
writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16),
|
||||
mxic->regs + HC_CFG);
|
||||
|
||||
writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len),
|
||||
writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len),
|
||||
mxic->regs + LRD_CFG);
|
||||
writel(desc->info.offset + offs, mxic->regs + LRD_ADDR);
|
||||
len = min_t(size_t, len, mxic->linear.size);
|
||||
writel(len, mxic->regs + LRD_RANGE);
|
||||
writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) |
|
||||
writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) |
|
||||
LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) |
|
||||
LMODE_EN,
|
||||
mxic->regs + LRD_CTRL);
|
||||
|
||||
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
|
||||
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) {
|
||||
ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
|
||||
NAND_PAGE_READ,
|
||||
mxic->linear.dma + offs);
|
||||
|
|
@ -448,20 +448,20 @@ static ssize_t mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
|||
if (WARN_ON(offs + desc->info.offset + len > U32_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16),
|
||||
writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16),
|
||||
mxic->regs + HC_CFG);
|
||||
|
||||
writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len),
|
||||
writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len),
|
||||
mxic->regs + LWR_CFG);
|
||||
writel(desc->info.offset + offs, mxic->regs + LWR_ADDR);
|
||||
len = min_t(size_t, len, mxic->linear.size);
|
||||
writel(len, mxic->regs + LWR_RANGE);
|
||||
writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) |
|
||||
writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) |
|
||||
LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) |
|
||||
LMODE_EN,
|
||||
mxic->regs + LWR_CTRL);
|
||||
|
||||
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
|
||||
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) {
|
||||
ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
|
||||
NAND_PAGE_WRITE,
|
||||
mxic->linear.dma + offs);
|
||||
|
|
@ -509,7 +509,7 @@ static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mxic_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
if (!mxic_spi_mem_supports_op(desc->mem, desc->info.op_tmpl))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -299,11 +299,11 @@ static ssize_t npcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc,
|
|||
for (i = 0 ; i < len ; i++)
|
||||
*(buf_rx + i) = ioread8(src + i);
|
||||
} else {
|
||||
if (desc->info.op_tmpl.addr.buswidth != fiu->drd_op.addr.buswidth ||
|
||||
desc->info.op_tmpl.dummy.nbytes != fiu->drd_op.dummy.nbytes ||
|
||||
desc->info.op_tmpl.cmd.opcode != fiu->drd_op.cmd.opcode ||
|
||||
desc->info.op_tmpl.addr.nbytes != fiu->drd_op.addr.nbytes)
|
||||
npcm_fiu_set_drd(fiu, &desc->info.op_tmpl);
|
||||
if (desc->info.op_tmpl->addr.buswidth != fiu->drd_op.addr.buswidth ||
|
||||
desc->info.op_tmpl->dummy.nbytes != fiu->drd_op.dummy.nbytes ||
|
||||
desc->info.op_tmpl->cmd.opcode != fiu->drd_op.cmd.opcode ||
|
||||
desc->info.op_tmpl->addr.nbytes != fiu->drd_op.addr.nbytes)
|
||||
npcm_fiu_set_drd(fiu, desc->info.op_tmpl);
|
||||
|
||||
memcpy_fromio(buf_rx, src, len);
|
||||
}
|
||||
|
|
@ -609,7 +609,7 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
}
|
||||
|
||||
if (!fiu->spix_mode &&
|
||||
desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
|
||||
desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) {
|
||||
desc->nodirmap = true;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -644,9 +644,9 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
NPCM_FIU_CFG_FIU_FIX);
|
||||
}
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) {
|
||||
if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) {
|
||||
if (!fiu->spix_mode)
|
||||
npcm_fiu_set_drd(fiu, &desc->info.op_tmpl);
|
||||
npcm_fiu_set_drd(fiu, desc->info.op_tmpl);
|
||||
else
|
||||
npcm_fiux_set_direct_rd(fiu);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ static ssize_t xspi_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
|
|||
if (offs + desc->info.offset + len > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len);
|
||||
rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len);
|
||||
|
||||
return xspi_dirmap_write(rpc->dev, offs, len, buf);
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ static ssize_t rpcif_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
if (offs + desc->info.offset + len > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len);
|
||||
rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len);
|
||||
|
||||
return rpcif_dirmap_read(rpc->dev, offs, len, buf);
|
||||
}
|
||||
|
|
@ -110,13 +110,13 @@ static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rpcif_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
if (!rpcif_spi_mem_supports_op(desc->mem, desc->info.op_tmpl))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rpc->dirmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rpc->xspi && desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
if (!rpc->xspi && desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -601,11 +601,11 @@ static int stm32_ospi_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
{
|
||||
struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
|
||||
if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Should never happen, as mm_base == null is an error probe exit condition */
|
||||
if (!ospi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
|
||||
if (!ospi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!ospi->mm_size)
|
||||
|
|
@ -632,7 +632,7 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
* spi_mem_op template with offs, len and *buf in order to get
|
||||
* all needed transfer information into struct spi_mem_op
|
||||
*/
|
||||
memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op));
|
||||
memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op));
|
||||
dev_dbg(ospi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf);
|
||||
|
||||
op.data.nbytes = len;
|
||||
|
|
|
|||
|
|
@ -505,11 +505,11 @@ static int stm32_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
|
||||
if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* should never happen, as mm_base == null is an error probe exit condition */
|
||||
if (!qspi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
|
||||
if (!qspi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!qspi->mm_size)
|
||||
|
|
@ -535,7 +535,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
|||
* spi_mem_op template with offs, len and *buf in order to get
|
||||
* all needed transfer information into struct spi_mem_op
|
||||
*/
|
||||
memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op));
|
||||
memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op));
|
||||
dev_dbg(qspi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf);
|
||||
|
||||
op.data.nbytes = len;
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
|||
struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
int cs = spi_get_chipselect(desc->mem->spi, 0);
|
||||
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -691,8 +691,6 @@ struct spinand_info {
|
|||
struct spinand_dirmap {
|
||||
struct spi_mem_dirmap_desc *wdesc;
|
||||
struct spi_mem_dirmap_desc *rdesc;
|
||||
struct spi_mem_dirmap_desc *wdesc_ecc;
|
||||
struct spi_mem_dirmap_desc *rdesc_ecc;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -869,6 +867,8 @@ static inline void spinand_set_of_node(struct spinand_device *spinand,
|
|||
nanddev_set_of_node(&spinand->base, np);
|
||||
}
|
||||
|
||||
bool spinand_op_is_odtr(const struct spi_mem_op *op);
|
||||
|
||||
int spinand_match_and_init(struct spinand_device *spinand,
|
||||
const struct spinand_info *table,
|
||||
unsigned int table_size,
|
||||
|
|
|
|||
|
|
@ -227,6 +227,8 @@ struct spi_mem_op {
|
|||
* struct spi_mem_dirmap_info - Direct mapping information
|
||||
* @op_tmpl: operation template that should be used by the direct mapping when
|
||||
* the memory device is accessed
|
||||
* @secondary_op_tmpl: secondary template, may be used as an alternative to the
|
||||
* primary template (decided by the upper layer)
|
||||
* @offset: absolute offset this direct mapping is pointing to
|
||||
* @length: length in byte of this direct mapping
|
||||
*
|
||||
|
|
@ -237,7 +239,9 @@ struct spi_mem_op {
|
|||
* direction is directly encoded in the ->op_tmpl.data.dir field.
|
||||
*/
|
||||
struct spi_mem_dirmap_info {
|
||||
struct spi_mem_op op_tmpl;
|
||||
struct spi_mem_op *op_tmpl;
|
||||
struct spi_mem_op primary_op_tmpl;
|
||||
struct spi_mem_op secondary_op_tmpl;
|
||||
u64 offset;
|
||||
u64 length;
|
||||
};
|
||||
|
|
@ -381,12 +385,18 @@ struct spi_controller_mem_ops {
|
|||
* @swap16: Supports swapping bytes on a 16 bit boundary when configured in
|
||||
* Octal DTR
|
||||
* @per_op_freq: Supports per operation frequency switching
|
||||
* @no_cs_assertion: The controller may automatically deassert the CS if there
|
||||
* is a pause in the transfer (eg. internal bus contention or
|
||||
* DMA arbitration on an interconnect). Features such as NAND
|
||||
* continuous reads shall not be leveraged.
|
||||
*/
|
||||
struct spi_controller_mem_caps {
|
||||
bool dtr;
|
||||
bool ecc;
|
||||
bool swap16;
|
||||
bool per_op_freq;
|
||||
bool secondary_op_tmpl;
|
||||
bool no_cs_assertion;
|
||||
};
|
||||
|
||||
#define spi_mem_controller_is_capable(ctlr, cap) \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user