Skip to content

Commit 00360eb

Browse files
committed
spi: mxic: Add support for pipelined ECC operations
Some SPI-NAND chips do not have a proper on-die ECC engine providing error correction/detection. This is particularly an issue on embedded devices with limited resources because all the computations must happen in software, unless an external hardware engine is provided. These external engines are new and can be of two categories: external or pipelined. Macronix is providing both, the former being already supported. The second, however, is very SoC implementation dependent and must be instantiated by the SPI host controller directly. An entire subsystem has been contributed to support these engines which makes the insertion into another subsystem such as SPI quite straightforward without the need for a lot of specific functions. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Reviewed-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/linux-mtd/20220202144536.393792-1-miquel.raynal@bootlin.com
1 parent 33fce1d commit 00360eb

4 files changed

Lines changed: 128 additions & 3 deletions

File tree

drivers/spi/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,7 @@ config SPI_SYNQUACER
879879
config SPI_MXIC
880880
tristate "Macronix MX25F0A SPI controller"
881881
depends on SPI_MASTER
882+
imply MTD_NAND_ECC_MXIC
882883
help
883884
This selects the Macronix MX25F0A SPI controller driver.
884885

drivers/spi/spi-mxic.c

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/io.h>
1313
#include <linux/iopoll.h>
1414
#include <linux/module.h>
15+
#include <linux/mtd/nand.h>
16+
#include <linux/mtd/nand-ecc-mxic.h>
1517
#include <linux/platform_device.h>
1618
#include <linux/pm_runtime.h>
1719
#include <linux/spi/spi.h>
@@ -167,6 +169,7 @@
167169
#define HW_TEST(x) (0xe0 + ((x) * 4))
168170

169171
struct mxic_spi {
172+
struct device *dev;
170173
struct clk *ps_clk;
171174
struct clk *send_clk;
172175
struct clk *send_dly_clk;
@@ -177,6 +180,12 @@ struct mxic_spi {
177180
dma_addr_t dma;
178181
size_t size;
179182
} linear;
183+
184+
struct {
185+
bool use_pipelined_conf;
186+
struct nand_ecc_engine *pipelined_engine;
187+
void *ctx;
188+
} ecc;
180189
};
181190

182191
static int mxic_spi_clk_enable(struct mxic_spi *mxic)
@@ -400,7 +409,15 @@ static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
400409
LMODE_EN,
401410
mxic->regs + LRD_CTRL);
402411

403-
memcpy_fromio(buf, mxic->linear.map, len);
412+
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
413+
ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
414+
NAND_PAGE_READ,
415+
mxic->linear.dma + offs);
416+
if (ret)
417+
return ret;
418+
} else {
419+
memcpy_fromio(buf, mxic->linear.map, len);
420+
}
404421

405422
writel(INT_LRD_DIS, mxic->regs + INT_STS);
406423
writel(0, mxic->regs + LRD_CTRL);
@@ -436,7 +453,15 @@ static ssize_t mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
436453
LMODE_EN,
437454
mxic->regs + LWR_CTRL);
438455

439-
memcpy_toio(mxic->linear.map, buf, len);
456+
if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) {
457+
ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine,
458+
NAND_PAGE_WRITE,
459+
mxic->linear.dma + offs);
460+
if (ret)
461+
return ret;
462+
} else {
463+
memcpy_toio(mxic->linear.map, buf, len);
464+
}
440465

441466
writel(INT_LWR_DIS, mxic->regs + INT_STS);
442467
writel(0, mxic->regs + LWR_CTRL);
@@ -547,6 +572,7 @@ static const struct spi_controller_mem_ops mxic_spi_mem_ops = {
547572

548573
static const struct spi_controller_mem_caps mxic_spi_mem_caps = {
549574
.dtr = true,
575+
.ecc = true,
550576
};
551577

552578
static void mxic_spi_set_cs(struct spi_device *spi, bool lvl)
@@ -611,6 +637,80 @@ static int mxic_spi_transfer_one(struct spi_master *master,
611637
return 0;
612638
}
613639

640+
/* ECC wrapper */
641+
static int mxic_spi_mem_ecc_init_ctx(struct nand_device *nand)
642+
{
643+
struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
644+
struct mxic_spi *mxic = nand->ecc.engine->priv;
645+
646+
mxic->ecc.use_pipelined_conf = true;
647+
648+
return ops->init_ctx(nand);
649+
}
650+
651+
static void mxic_spi_mem_ecc_cleanup_ctx(struct nand_device *nand)
652+
{
653+
struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
654+
struct mxic_spi *mxic = nand->ecc.engine->priv;
655+
656+
mxic->ecc.use_pipelined_conf = false;
657+
658+
ops->cleanup_ctx(nand);
659+
}
660+
661+
static int mxic_spi_mem_ecc_prepare_io_req(struct nand_device *nand,
662+
struct nand_page_io_req *req)
663+
{
664+
struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
665+
666+
return ops->prepare_io_req(nand, req);
667+
}
668+
669+
static int mxic_spi_mem_ecc_finish_io_req(struct nand_device *nand,
670+
struct nand_page_io_req *req)
671+
{
672+
struct nand_ecc_engine_ops *ops = mxic_ecc_get_pipelined_ops();
673+
674+
return ops->finish_io_req(nand, req);
675+
}
676+
677+
static struct nand_ecc_engine_ops mxic_spi_mem_ecc_engine_pipelined_ops = {
678+
.init_ctx = mxic_spi_mem_ecc_init_ctx,
679+
.cleanup_ctx = mxic_spi_mem_ecc_cleanup_ctx,
680+
.prepare_io_req = mxic_spi_mem_ecc_prepare_io_req,
681+
.finish_io_req = mxic_spi_mem_ecc_finish_io_req,
682+
};
683+
684+
static void mxic_spi_mem_ecc_remove(struct mxic_spi *mxic)
685+
{
686+
if (mxic->ecc.pipelined_engine) {
687+
mxic_ecc_put_pipelined_engine(mxic->ecc.pipelined_engine);
688+
nand_ecc_unregister_on_host_hw_engine(mxic->ecc.pipelined_engine);
689+
}
690+
}
691+
692+
static int mxic_spi_mem_ecc_probe(struct platform_device *pdev,
693+
struct mxic_spi *mxic)
694+
{
695+
struct nand_ecc_engine *eng;
696+
697+
if (!mxic_ecc_get_pipelined_ops())
698+
return -EOPNOTSUPP;
699+
700+
eng = mxic_ecc_get_pipelined_engine(pdev);
701+
if (IS_ERR(eng))
702+
return PTR_ERR(eng);
703+
704+
eng->dev = &pdev->dev;
705+
eng->integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
706+
eng->ops = &mxic_spi_mem_ecc_engine_pipelined_ops;
707+
eng->priv = mxic;
708+
mxic->ecc.pipelined_engine = eng;
709+
nand_ecc_register_on_host_hw_engine(eng);
710+
711+
return 0;
712+
}
713+
614714
static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
615715
{
616716
struct spi_master *master = dev_get_drvdata(dev);
@@ -656,6 +756,7 @@ static int mxic_spi_probe(struct platform_device *pdev)
656756
platform_set_drvdata(pdev, master);
657757

658758
mxic = spi_master_get_devdata(master);
759+
mxic->dev = &pdev->dev;
659760

660761
master->dev.of_node = pdev->dev.of_node;
661762

@@ -702,6 +803,12 @@ static int mxic_spi_probe(struct platform_device *pdev)
702803

703804
mxic_spi_hw_init(mxic);
704805

806+
ret = mxic_spi_mem_ecc_probe(pdev, mxic);
807+
if (ret == -EPROBE_DEFER) {
808+
pm_runtime_disable(&pdev->dev);
809+
return ret;
810+
}
811+
705812
ret = spi_register_master(master);
706813
if (ret) {
707814
dev_err(&pdev->dev, "spi_register_master failed\n");
@@ -714,8 +821,10 @@ static int mxic_spi_probe(struct platform_device *pdev)
714821
static int mxic_spi_remove(struct platform_device *pdev)
715822
{
716823
struct spi_master *master = platform_get_drvdata(pdev);
824+
struct mxic_spi *mxic = spi_master_get_devdata(master);
717825

718826
pm_runtime_disable(&pdev->dev);
827+
mxic_spi_mem_ecc_remove(mxic);
719828
spi_unregister_master(master);
720829

721830
return 0;

include/linux/mtd/nand-ecc-mxic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
struct mxic_ecc_engine;
1616

17-
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_MXIC)
17+
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_MXIC) && IS_REACHABLE(CONFIG_MTD_NAND_CORE)
1818

1919
struct nand_ecc_engine_ops *mxic_ecc_get_pipelined_ops(void);
2020
struct nand_ecc_engine *mxic_ecc_get_pipelined_engine(struct platform_device *spi_pdev);

include/linux/mtd/nand.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,23 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
303303
int nand_ecc_finish_io_req(struct nand_device *nand,
304304
struct nand_page_io_req *req);
305305
bool nand_ecc_is_strong_enough(struct nand_device *nand);
306+
307+
#if IS_REACHABLE(CONFIG_MTD_NAND_CORE)
306308
int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine);
307309
int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine);
310+
#else
311+
static inline int
312+
nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
313+
{
314+
return -ENOTSUPP;
315+
}
316+
static inline int
317+
nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
318+
{
319+
return -ENOTSUPP;
320+
}
321+
#endif
322+
308323
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
309324
struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand);
310325
struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand);

0 commit comments

Comments
 (0)