From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Paul Cercueil <paul@crapouillou.net>
Cc: Mark Rutland <mark.rutland@arm.com>,
devicetree@vger.kernel.org,
Boris Brezillon <bbrezillon@kernel.org>,
Mathieu Malaterre <malat@debian.org>,
Richard Weinberger <richard@nod.at>,
linux-kernel@vger.kernel.org, Marek Vasut <marek.vasut@gmail.com>,
Rob Herring <robh+dt@kernel.org>,
linux-mtd@lists.infradead.org,
Harvey Hunt <harveyhuntnexus@gmail.com>,
Brian Norris <computersforpeace@gmail.com>,
David Woodhouse <dwmw2@infradead.org>
Subject: Re: [PATCH v3 8/9] mtd: rawnand: ingenic: Add support for the JZ4725B
Date: Tue, 5 Feb 2019 15:12:56 +0100 [thread overview]
Message-ID: <20190205151256.1bb3dab0@xps13> (raw)
In-Reply-To: <20190204190426.11618-8-paul@crapouillou.net>
Hi Paul,
Paul Cercueil <paul@crapouillou.net> wrote on Mon, 4 Feb 2019 16:04:25
-0300:
> The boot ROM of the JZ4725B SoC expects a specific OOB layout on the
> NAND, so we use it unconditionally in the ingenic-nand driver.
>
> Also add the jz4725b-bch driver to support the JZ4725B-specific BCH
> hardware.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
>
> Changes:
>
> v2: Instead of forcing the OOB layout, leave it to the board code or
> devicetree to decide if the jz4725b-specific layout should be used
> or not.
>
> v3: - Revert the change in v2, as the previous behaviour was correct.
> - Also add support for the hardware BCH of the JZ4725B in this
> patch.
>
> drivers/mtd/nand/raw/ingenic/Kconfig | 10 ++
> drivers/mtd/nand/raw/ingenic/Makefile | 1 +
> drivers/mtd/nand/raw/ingenic/ingenic_nand.c | 48 +++++-
> drivers/mtd/nand/raw/ingenic/jz4725b_bch.c | 251 ++++++++++++++++++++++++++++
> 4 files changed, 309 insertions(+), 1 deletion(-)
> create mode 100644 drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
>
> diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig
> index cc663cc15119..497b46b144f2 100644
> --- a/drivers/mtd/nand/raw/ingenic/Kconfig
> +++ b/drivers/mtd/nand/raw/ingenic/Kconfig
> @@ -27,6 +27,16 @@ config MTD_NAND_JZ4740_ECC
> This driver can also be built as a module. If so, the module
> will be called jz4740-ecc.
>
> +config MTD_NAND_JZ4725B_BCH
> + tristate "Hardware BCH support for JZ4725B SoC"
> + select MTD_NAND_INGENIC_ECC
> + help
> + Enable this driver to support the BCH error-correction hardware
> + present on the JZ4725B SoC from Ingenic.
> +
> + This driver can also be built as a module. If so, the module
> + will be called jz4725b-bch.
> +
> config MTD_NAND_JZ4780_BCH
> tristate "Hardware BCH support for JZ4780 SoC"
> select MTD_NAND_INGENIC_ECC
> diff --git a/drivers/mtd/nand/raw/ingenic/Makefile b/drivers/mtd/nand/raw/ingenic/Makefile
> index 563b7effcf59..ab2c5f47e5b7 100644
> --- a/drivers/mtd/nand/raw/ingenic/Makefile
> +++ b/drivers/mtd/nand/raw/ingenic/Makefile
> @@ -3,4 +3,5 @@ obj-$(CONFIG_MTD_NAND_JZ4780) += ingenic_nand.o
>
> obj-$(CONFIG_MTD_NAND_INGENIC_ECC) += ingenic_ecc.o
> obj-$(CONFIG_MTD_NAND_JZ4740_ECC) += jz4740_ecc.o
> +obj-$(CONFIG_MTD_NAND_JZ4725B_BCH) += jz4725b_bch.o
> obj-$(CONFIG_MTD_NAND_JZ4780_BCH) += jz4780_bch.o
> diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> index 3fd078920b17..0e6d79993ff3 100644
> --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> @@ -34,6 +34,7 @@ struct jz_soc_info {
> unsigned long data_offset;
> unsigned long addr_offset;
> unsigned long cmd_offset;
> + const struct mtd_ooblayout_ops *oob_layout;
> };
>
> struct ingenic_nand_cs {
> @@ -71,6 +72,41 @@ static inline struct ingenic_nfc *to_ingenic_nfc(struct nand_controller *ctrl)
> return container_of(ctrl, struct ingenic_nfc, controller);
> }
>
> +static int jz4725b_ooblayout_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> + if (section || !ecc->total)
> + return -ERANGE;
> +
> + oobregion->length = ecc->total;
> + oobregion->offset = 3;
> +
> + return 0;
> +}
> +
> +static int jz4725b_ooblayout_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> + if (section)
> + return -ERANGE;
> +
> + oobregion->length = mtd->oobsize - ecc->total - 3;
> + oobregion->offset = 3 + ecc->total;
> +
> + return 0;
> +}
> +
> +const struct mtd_ooblayout_ops jz4725b_ooblayout_ops = {
> + .ecc = jz4725b_ooblayout_ecc,
> + .free = jz4725b_ooblayout_free,
> +};
> +
> static void ingenic_nand_select_chip(struct nand_chip *chip, int chipnr)
> {
> struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
> @@ -211,7 +247,7 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip)
> return -EINVAL;
> }
>
> - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
> + mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout);
>
> return 0;
> }
> @@ -407,16 +443,26 @@ static const struct jz_soc_info jz4740_soc_info = {
> .data_offset = 0x00000000,
> .cmd_offset = 0x00008000,
> .addr_offset = 0x00010000,
> + .oob_layout = &nand_ooblayout_lp_ops,
> +};
> +
> +static const struct jz_soc_info jz4725b_soc_info = {
> + .data_offset = 0x00000000,
> + .cmd_offset = 0x00008000,
> + .addr_offset = 0x00010000,
> + .oob_layout = &jz4725b_ooblayout_ops,
> };
>
> static const struct jz_soc_info jz4780_soc_info = {
> .data_offset = 0x00000000,
> .cmd_offset = 0x00400000,
> .addr_offset = 0x00800000,
> + .oob_layout = &nand_ooblayout_lp_ops,
> };
>
> static const struct of_device_id ingenic_nand_dt_match[] = {
> { .compatible = "ingenic,jz4740-nand", .data = &jz4740_soc_info },
> + { .compatible = "ingenic,jz4725b-nand", .data = &jz4725b_soc_info },
> { .compatible = "ingenic,jz4780-nand", .data = &jz4780_soc_info },
> {},
> };
> diff --git a/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
> new file mode 100644
> index 000000000000..66be454be383
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * JZ4725B BCH controller driver
> + *
> + * Copyright (C) 2018 Paul Cercueil <paul@crapouillou.net>
> + *
> + * Based on jz4780_bch.c
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#include "ingenic_ecc.h"
> +
> +#define BCH_BHCR 0x0
> +#define BCH_BHCSR 0x4
> +#define BCH_BHCCR 0x8
> +#define BCH_BHCNT 0xc
> +#define BCH_BHDR 0x10
> +#define BCH_BHPAR0 0x14
> +#define BCH_BHERR0 0x28
> +#define BCH_BHINT 0x24
> +#define BCH_BHINTES 0x3c
> +#define BCH_BHINTEC 0x40
> +#define BCH_BHINTE 0x38
> +
> +#define BCH_BHCR_BSEL_SHIFT 2
> +#define BCH_BHCR_BSEL_MASK (0x1 << BCH_BHCR_BSEL_SHIFT)
> +#define BCH_BHCR_ENCE BIT(3)
> +#define BCH_BHCR_INIT BIT(1)
> +#define BCH_BHCR_BCHE BIT(0)
> +
> +#define BCH_BHCNT_DEC_COUNT_SHIFT 16
> +#define BCH_BHCNT_DEC_COUNT_MASK (0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT)
> +#define BCH_BHCNT_ENC_COUNT_SHIFT 0
> +#define BCH_BHCNT_ENC_COUNT_MASK (0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT)
> +
> +#define BCH_BHERR_INDEX0_SHIFT 0
> +#define BCH_BHERR_INDEX0_MASK (0x1fff << BCH_BHERR_INDEX0_SHIFT)
> +#define BCH_BHERR_INDEX1_SHIFT 16
> +#define BCH_BHERR_INDEX1_MASK (0x1fff << BCH_BHERR_INDEX1_SHIFT)
> +
> +#define BCH_BHINT_ERRC_SHIFT 28
> +#define BCH_BHINT_ERRC_MASK (0xf << BCH_BHINT_ERRC_SHIFT)
> +#define BCH_BHINT_TERRC_SHIFT 16
> +#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT)
> +#define BCH_BHINT_ALL_0 BIT(5)
> +#define BCH_BHINT_ALL_F BIT(4)
> +#define BCH_BHINT_DECF BIT(3)
> +#define BCH_BHINT_ENCF BIT(2)
> +#define BCH_BHINT_UNCOR BIT(1)
> +#define BCH_BHINT_ERR BIT(0)
> +
> +/* Timeout for BCH calculation/correction. */
> +#define BCH_TIMEOUT_US 100000
> +
> +static void jz4725b_bch_init(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params, bool encode)
I don't know the IP but 'encode' looks strange, what is it supposed to
mean?
> +{
> + u32 reg;
> +
> + /* Clear interrupt status. */
> + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
> +
> + /* Initialise and enable BCH. */
> + writel(0x1f, bch->base + BCH_BHCCR);
> + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCSR);
> +
> + if (params->strength == 8)
> + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCSR);
> + else
> + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCCR);
Here you write to BCH_BHCSR or BCH_BHCCR depending on
params->strength...
> +
> + if (encode)
> + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCSR);
> + else
> + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCCR);
...and here depending on encode.
Can you explain a bit?
> +
> + writel(BCH_BHCR_INIT, bch->base + BCH_BHCSR);
> +
> + /* Set up BCH count register. */
> + reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT;
> + reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT;
Maybe you want to check params->size to fit the bitfield length?
> + writel(reg, bch->base + BCH_BHCNT);
> +}
> +
> +static void jz4725b_bch_disable(struct ingenic_ecc *bch)
> +{
> + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
> + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR);
I would add comments, one to say you clear the interrupts, one when you
disable the engine.
> +}
> +
> +static void jz4725b_bch_write_data(struct ingenic_ecc *bch, const u8 *buf,
> + size_t size)
> +{
> + while (size--)
> + writeb(*buf++, bch->base + BCH_BHDR);
> +}
> +
> +static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf,
> + size_t size)
> +{
> + size_t size32 = size / sizeof(u32);
> + size_t size8 = size % sizeof(u32);
> + u32 *dest32;
> + u8 *dest8;
> + u32 val, offset = 0;
> +
> + dest32 = (u32 *)buf;
> + while (size32--) {
> + *dest32++ = readl(bch->base + BCH_BHPAR0 + offset);
> + offset += sizeof(u32);
> + }
> +
> + dest8 = (u8 *)dest32;
> + val = readl(bch->base + BCH_BHPAR0 + offset);
> + switch (size8) {
> + case 3:
> + dest8[2] = (val >> 16) & 0xff;
> + case 2:
> + dest8[1] = (val >> 8) & 0xff;
> + case 1:
> + dest8[0] = val & 0xff;
> + break;
> + }
> +}
Have you tried with _relaxed operators?
> +
> +static bool jz4725b_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
> + u32 *status)
> +{
> + u32 reg;
> + int ret;
> +
> + /*
> + * While we could use interrupts here and sleep until the operation
> + * completes, the controller works fairly quickly (usually a few
> + * microseconds) and so the overhead of sleeping until we get an
> + * interrupt quite noticeably decreases performance.
> + */
> + ret = readl_poll_timeout(bch->base + BCH_BHINT, reg,
> + (reg & irq) == irq, 0, BCH_TIMEOUT_US);
I suppose (reg & irq) is enough?
> + if (ret)
> + return false;
> +
> + if (status)
> + *status = reg;
> +
> + writel(reg, bch->base + BCH_BHINT);
space
> + return true;
> +}
> +
> +static int jz4725b_calculate(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params,
> + const u8 *buf, u8 *ecc_code)
> +{
> + int ret = 0;
> +
> + mutex_lock(&bch->lock);
> + jz4725b_bch_init(bch, params, true);
> + jz4725b_bch_write_data(bch, buf, params->size);
> +
> + if (jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) {
I would prefer bch_wait_complete() to return an usual negative error
instead of a boolean.
And maybe a goto could be used in case of error to disable BCH, unlock
the mutex and return.
> + jz4725b_bch_read_parity(bch, ecc_code, params->bytes);
> + } else {
> + dev_err(bch->dev, "timed out while calculating ECC\n");
> + ret = -ETIMEDOUT;
> + }
> +
> + jz4725b_bch_disable(bch);
> + mutex_unlock(&bch->lock);
Space
> + return ret;
> +}
> +
> +static int jz4725b_correct(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params,
> + u8 *buf, u8 *ecc_code)
> +{
> + u32 reg, errors, bit;
> + unsigned int i;
> + int ret = 0;
> +
> + mutex_lock(&bch->lock);
> +
> + jz4725b_bch_init(bch, params, false);
> + jz4725b_bch_write_data(bch, buf, params->size);
> + jz4725b_bch_write_data(bch, ecc_code, params->bytes);
> +
> + if (!jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) {
> + dev_err(bch->dev, "timed out while correcting data\n");
> + ret = -ETIMEDOUT;
> + goto out;
> + }
> +
> + if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) {
> + /* Data and ECC is all 0xff or 0x00 - nothing to correct */
> + ret = 0;
> + goto out;
> + }
> +
> + if (reg & BCH_BHINT_UNCOR) {
> + /* Uncorrectable ECC error */
> + ret = -EBADMSG;
> + goto out;
> + }
> +
> + errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
> +
> + /* Correct any detected errors. */
> + for (i = 0; i < errors; i++) {
> + if (i & 1) {
> + bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT;
> + } else {
> + reg = readl(bch->base + BCH_BHERR0 + (i * 4));
> + bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT;
> + }
> +
> + buf[(bit >> 3)] ^= BIT(bit & 0x7);
> + }
> +
> +out:
> + jz4725b_bch_disable(bch);
> + mutex_unlock(&bch->lock);
> + return ret;
> +}
> +
> +static const struct ingenic_ecc_ops jz4725b_bch_ops = {
> + .disable = jz4725b_bch_disable,
> + .calculate = jz4725b_calculate,
> + .correct = jz4725b_correct,
> +};
> +
> +static const struct of_device_id jz4725b_bch_dt_match[] = {
> + { .compatible = "ingenic,jz4725b-bch", .data = &jz4725b_bch_ops },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, jz4725b_bch_dt_match);
> +
> +static struct platform_driver jz4725b_bch_driver = {
> + .probe = ingenic_ecc_probe,
> + .driver = {
> + .name = "jz4725b-bch",
> + .of_match_table = jz4725b_bch_dt_match,
> + },
> +};
> +module_platform_driver(jz4725b_bch_driver);
Missing MODULE_ macros? (author, license)
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
WARNING: multiple messages have this Message-ID (diff)
From: Miquel Raynal <miquel.raynal@bootlin.com>
To: Paul Cercueil <paul@crapouillou.net>
Cc: David Woodhouse <dwmw2@infradead.org>,
Brian Norris <computersforpeace@gmail.com>,
Boris Brezillon <bbrezillon@kernel.org>,
Marek Vasut <marek.vasut@gmail.com>,
Richard Weinberger <richard@nod.at>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Harvey Hunt <harveyhuntnexus@gmail.com>,
Mathieu Malaterre <malat@debian.org>,
linux-mtd@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 8/9] mtd: rawnand: ingenic: Add support for the JZ4725B
Date: Tue, 5 Feb 2019 15:12:56 +0100 [thread overview]
Message-ID: <20190205151256.1bb3dab0@xps13> (raw)
In-Reply-To: <20190204190426.11618-8-paul@crapouillou.net>
Hi Paul,
Paul Cercueil <paul@crapouillou.net> wrote on Mon, 4 Feb 2019 16:04:25
-0300:
> The boot ROM of the JZ4725B SoC expects a specific OOB layout on the
> NAND, so we use it unconditionally in the ingenic-nand driver.
>
> Also add the jz4725b-bch driver to support the JZ4725B-specific BCH
> hardware.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
>
> Changes:
>
> v2: Instead of forcing the OOB layout, leave it to the board code or
> devicetree to decide if the jz4725b-specific layout should be used
> or not.
>
> v3: - Revert the change in v2, as the previous behaviour was correct.
> - Also add support for the hardware BCH of the JZ4725B in this
> patch.
>
> drivers/mtd/nand/raw/ingenic/Kconfig | 10 ++
> drivers/mtd/nand/raw/ingenic/Makefile | 1 +
> drivers/mtd/nand/raw/ingenic/ingenic_nand.c | 48 +++++-
> drivers/mtd/nand/raw/ingenic/jz4725b_bch.c | 251 ++++++++++++++++++++++++++++
> 4 files changed, 309 insertions(+), 1 deletion(-)
> create mode 100644 drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
>
> diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig
> index cc663cc15119..497b46b144f2 100644
> --- a/drivers/mtd/nand/raw/ingenic/Kconfig
> +++ b/drivers/mtd/nand/raw/ingenic/Kconfig
> @@ -27,6 +27,16 @@ config MTD_NAND_JZ4740_ECC
> This driver can also be built as a module. If so, the module
> will be called jz4740-ecc.
>
> +config MTD_NAND_JZ4725B_BCH
> + tristate "Hardware BCH support for JZ4725B SoC"
> + select MTD_NAND_INGENIC_ECC
> + help
> + Enable this driver to support the BCH error-correction hardware
> + present on the JZ4725B SoC from Ingenic.
> +
> + This driver can also be built as a module. If so, the module
> + will be called jz4725b-bch.
> +
> config MTD_NAND_JZ4780_BCH
> tristate "Hardware BCH support for JZ4780 SoC"
> select MTD_NAND_INGENIC_ECC
> diff --git a/drivers/mtd/nand/raw/ingenic/Makefile b/drivers/mtd/nand/raw/ingenic/Makefile
> index 563b7effcf59..ab2c5f47e5b7 100644
> --- a/drivers/mtd/nand/raw/ingenic/Makefile
> +++ b/drivers/mtd/nand/raw/ingenic/Makefile
> @@ -3,4 +3,5 @@ obj-$(CONFIG_MTD_NAND_JZ4780) += ingenic_nand.o
>
> obj-$(CONFIG_MTD_NAND_INGENIC_ECC) += ingenic_ecc.o
> obj-$(CONFIG_MTD_NAND_JZ4740_ECC) += jz4740_ecc.o
> +obj-$(CONFIG_MTD_NAND_JZ4725B_BCH) += jz4725b_bch.o
> obj-$(CONFIG_MTD_NAND_JZ4780_BCH) += jz4780_bch.o
> diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> index 3fd078920b17..0e6d79993ff3 100644
> --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand.c
> @@ -34,6 +34,7 @@ struct jz_soc_info {
> unsigned long data_offset;
> unsigned long addr_offset;
> unsigned long cmd_offset;
> + const struct mtd_ooblayout_ops *oob_layout;
> };
>
> struct ingenic_nand_cs {
> @@ -71,6 +72,41 @@ static inline struct ingenic_nfc *to_ingenic_nfc(struct nand_controller *ctrl)
> return container_of(ctrl, struct ingenic_nfc, controller);
> }
>
> +static int jz4725b_ooblayout_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> + if (section || !ecc->total)
> + return -ERANGE;
> +
> + oobregion->length = ecc->total;
> + oobregion->offset = 3;
> +
> + return 0;
> +}
> +
> +static int jz4725b_ooblayout_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> + if (section)
> + return -ERANGE;
> +
> + oobregion->length = mtd->oobsize - ecc->total - 3;
> + oobregion->offset = 3 + ecc->total;
> +
> + return 0;
> +}
> +
> +const struct mtd_ooblayout_ops jz4725b_ooblayout_ops = {
> + .ecc = jz4725b_ooblayout_ecc,
> + .free = jz4725b_ooblayout_free,
> +};
> +
> static void ingenic_nand_select_chip(struct nand_chip *chip, int chipnr)
> {
> struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip));
> @@ -211,7 +247,7 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip)
> return -EINVAL;
> }
>
> - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
> + mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout);
>
> return 0;
> }
> @@ -407,16 +443,26 @@ static const struct jz_soc_info jz4740_soc_info = {
> .data_offset = 0x00000000,
> .cmd_offset = 0x00008000,
> .addr_offset = 0x00010000,
> + .oob_layout = &nand_ooblayout_lp_ops,
> +};
> +
> +static const struct jz_soc_info jz4725b_soc_info = {
> + .data_offset = 0x00000000,
> + .cmd_offset = 0x00008000,
> + .addr_offset = 0x00010000,
> + .oob_layout = &jz4725b_ooblayout_ops,
> };
>
> static const struct jz_soc_info jz4780_soc_info = {
> .data_offset = 0x00000000,
> .cmd_offset = 0x00400000,
> .addr_offset = 0x00800000,
> + .oob_layout = &nand_ooblayout_lp_ops,
> };
>
> static const struct of_device_id ingenic_nand_dt_match[] = {
> { .compatible = "ingenic,jz4740-nand", .data = &jz4740_soc_info },
> + { .compatible = "ingenic,jz4725b-nand", .data = &jz4725b_soc_info },
> { .compatible = "ingenic,jz4780-nand", .data = &jz4780_soc_info },
> {},
> };
> diff --git a/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
> new file mode 100644
> index 000000000000..66be454be383
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c
> @@ -0,0 +1,251 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * JZ4725B BCH controller driver
> + *
> + * Copyright (C) 2018 Paul Cercueil <paul@crapouillou.net>
> + *
> + * Based on jz4780_bch.c
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#include "ingenic_ecc.h"
> +
> +#define BCH_BHCR 0x0
> +#define BCH_BHCSR 0x4
> +#define BCH_BHCCR 0x8
> +#define BCH_BHCNT 0xc
> +#define BCH_BHDR 0x10
> +#define BCH_BHPAR0 0x14
> +#define BCH_BHERR0 0x28
> +#define BCH_BHINT 0x24
> +#define BCH_BHINTES 0x3c
> +#define BCH_BHINTEC 0x40
> +#define BCH_BHINTE 0x38
> +
> +#define BCH_BHCR_BSEL_SHIFT 2
> +#define BCH_BHCR_BSEL_MASK (0x1 << BCH_BHCR_BSEL_SHIFT)
> +#define BCH_BHCR_ENCE BIT(3)
> +#define BCH_BHCR_INIT BIT(1)
> +#define BCH_BHCR_BCHE BIT(0)
> +
> +#define BCH_BHCNT_DEC_COUNT_SHIFT 16
> +#define BCH_BHCNT_DEC_COUNT_MASK (0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT)
> +#define BCH_BHCNT_ENC_COUNT_SHIFT 0
> +#define BCH_BHCNT_ENC_COUNT_MASK (0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT)
> +
> +#define BCH_BHERR_INDEX0_SHIFT 0
> +#define BCH_BHERR_INDEX0_MASK (0x1fff << BCH_BHERR_INDEX0_SHIFT)
> +#define BCH_BHERR_INDEX1_SHIFT 16
> +#define BCH_BHERR_INDEX1_MASK (0x1fff << BCH_BHERR_INDEX1_SHIFT)
> +
> +#define BCH_BHINT_ERRC_SHIFT 28
> +#define BCH_BHINT_ERRC_MASK (0xf << BCH_BHINT_ERRC_SHIFT)
> +#define BCH_BHINT_TERRC_SHIFT 16
> +#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT)
> +#define BCH_BHINT_ALL_0 BIT(5)
> +#define BCH_BHINT_ALL_F BIT(4)
> +#define BCH_BHINT_DECF BIT(3)
> +#define BCH_BHINT_ENCF BIT(2)
> +#define BCH_BHINT_UNCOR BIT(1)
> +#define BCH_BHINT_ERR BIT(0)
> +
> +/* Timeout for BCH calculation/correction. */
> +#define BCH_TIMEOUT_US 100000
> +
> +static void jz4725b_bch_init(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params, bool encode)
I don't know the IP but 'encode' looks strange, what is it supposed to
mean?
> +{
> + u32 reg;
> +
> + /* Clear interrupt status. */
> + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
> +
> + /* Initialise and enable BCH. */
> + writel(0x1f, bch->base + BCH_BHCCR);
> + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCSR);
> +
> + if (params->strength == 8)
> + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCSR);
> + else
> + writel(BCH_BHCR_BSEL_MASK, bch->base + BCH_BHCCR);
Here you write to BCH_BHCSR or BCH_BHCCR depending on
params->strength...
> +
> + if (encode)
> + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCSR);
> + else
> + writel(BCH_BHCR_ENCE, bch->base + BCH_BHCCR);
...and here depending on encode.
Can you explain a bit?
> +
> + writel(BCH_BHCR_INIT, bch->base + BCH_BHCSR);
> +
> + /* Set up BCH count register. */
> + reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT;
> + reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT;
Maybe you want to check params->size to fit the bitfield length?
> + writel(reg, bch->base + BCH_BHCNT);
> +}
> +
> +static void jz4725b_bch_disable(struct ingenic_ecc *bch)
> +{
> + writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
> + writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR);
I would add comments, one to say you clear the interrupts, one when you
disable the engine.
> +}
> +
> +static void jz4725b_bch_write_data(struct ingenic_ecc *bch, const u8 *buf,
> + size_t size)
> +{
> + while (size--)
> + writeb(*buf++, bch->base + BCH_BHDR);
> +}
> +
> +static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf,
> + size_t size)
> +{
> + size_t size32 = size / sizeof(u32);
> + size_t size8 = size % sizeof(u32);
> + u32 *dest32;
> + u8 *dest8;
> + u32 val, offset = 0;
> +
> + dest32 = (u32 *)buf;
> + while (size32--) {
> + *dest32++ = readl(bch->base + BCH_BHPAR0 + offset);
> + offset += sizeof(u32);
> + }
> +
> + dest8 = (u8 *)dest32;
> + val = readl(bch->base + BCH_BHPAR0 + offset);
> + switch (size8) {
> + case 3:
> + dest8[2] = (val >> 16) & 0xff;
> + case 2:
> + dest8[1] = (val >> 8) & 0xff;
> + case 1:
> + dest8[0] = val & 0xff;
> + break;
> + }
> +}
Have you tried with _relaxed operators?
> +
> +static bool jz4725b_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
> + u32 *status)
> +{
> + u32 reg;
> + int ret;
> +
> + /*
> + * While we could use interrupts here and sleep until the operation
> + * completes, the controller works fairly quickly (usually a few
> + * microseconds) and so the overhead of sleeping until we get an
> + * interrupt quite noticeably decreases performance.
> + */
> + ret = readl_poll_timeout(bch->base + BCH_BHINT, reg,
> + (reg & irq) == irq, 0, BCH_TIMEOUT_US);
I suppose (reg & irq) is enough?
> + if (ret)
> + return false;
> +
> + if (status)
> + *status = reg;
> +
> + writel(reg, bch->base + BCH_BHINT);
space
> + return true;
> +}
> +
> +static int jz4725b_calculate(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params,
> + const u8 *buf, u8 *ecc_code)
> +{
> + int ret = 0;
> +
> + mutex_lock(&bch->lock);
> + jz4725b_bch_init(bch, params, true);
> + jz4725b_bch_write_data(bch, buf, params->size);
> +
> + if (jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) {
I would prefer bch_wait_complete() to return an usual negative error
instead of a boolean.
And maybe a goto could be used in case of error to disable BCH, unlock
the mutex and return.
> + jz4725b_bch_read_parity(bch, ecc_code, params->bytes);
> + } else {
> + dev_err(bch->dev, "timed out while calculating ECC\n");
> + ret = -ETIMEDOUT;
> + }
> +
> + jz4725b_bch_disable(bch);
> + mutex_unlock(&bch->lock);
Space
> + return ret;
> +}
> +
> +static int jz4725b_correct(struct ingenic_ecc *bch,
> + struct ingenic_ecc_params *params,
> + u8 *buf, u8 *ecc_code)
> +{
> + u32 reg, errors, bit;
> + unsigned int i;
> + int ret = 0;
> +
> + mutex_lock(&bch->lock);
> +
> + jz4725b_bch_init(bch, params, false);
> + jz4725b_bch_write_data(bch, buf, params->size);
> + jz4725b_bch_write_data(bch, ecc_code, params->bytes);
> +
> + if (!jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) {
> + dev_err(bch->dev, "timed out while correcting data\n");
> + ret = -ETIMEDOUT;
> + goto out;
> + }
> +
> + if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) {
> + /* Data and ECC is all 0xff or 0x00 - nothing to correct */
> + ret = 0;
> + goto out;
> + }
> +
> + if (reg & BCH_BHINT_UNCOR) {
> + /* Uncorrectable ECC error */
> + ret = -EBADMSG;
> + goto out;
> + }
> +
> + errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
> +
> + /* Correct any detected errors. */
> + for (i = 0; i < errors; i++) {
> + if (i & 1) {
> + bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT;
> + } else {
> + reg = readl(bch->base + BCH_BHERR0 + (i * 4));
> + bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT;
> + }
> +
> + buf[(bit >> 3)] ^= BIT(bit & 0x7);
> + }
> +
> +out:
> + jz4725b_bch_disable(bch);
> + mutex_unlock(&bch->lock);
> + return ret;
> +}
> +
> +static const struct ingenic_ecc_ops jz4725b_bch_ops = {
> + .disable = jz4725b_bch_disable,
> + .calculate = jz4725b_calculate,
> + .correct = jz4725b_correct,
> +};
> +
> +static const struct of_device_id jz4725b_bch_dt_match[] = {
> + { .compatible = "ingenic,jz4725b-bch", .data = &jz4725b_bch_ops },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, jz4725b_bch_dt_match);
> +
> +static struct platform_driver jz4725b_bch_driver = {
> + .probe = ingenic_ecc_probe,
> + .driver = {
> + .name = "jz4725b-bch",
> + .of_match_table = jz4725b_bch_dt_match,
> + },
> +};
> +module_platform_driver(jz4725b_bch_driver);
Missing MODULE_ macros? (author, license)
Thanks,
Miquèl
next prev parent reply other threads:[~2019-02-05 14:13 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-02-04 19:04 [PATCH v3 1/9] dt-bindings: mtd: ingenic: Add compatible strings for JZ4740 and JZ4725B Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 2/9] dt-bindings: mtd: ingenic: Change 'BCH' to 'ECC' in documentation Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 3/9] mtd: rawnand: Move drivers for Ingenic SoCs to subfolder Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 4/9] mtd: rawnand: ingenic: Use SPDX license notifiers Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 5/9] mtd: rawnand: ingenic: Rename jz4780_nand driver to ingenic_nand Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 6/9] mtd: rawnand: ingenic: Separate top-level and SoC specific code Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-04 19:04 ` [PATCH v3 7/9] mtd: rawnand: ingenic: Add support for the JZ4740 Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-05 13:54 ` Miquel Raynal
2019-02-05 13:54 ` Miquel Raynal
2019-02-04 19:04 ` [PATCH v3 8/9] mtd: rawnand: ingenic: Add support for the JZ4725B Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
2019-02-05 14:12 ` Miquel Raynal [this message]
2019-02-05 14:12 ` Miquel Raynal
2019-02-05 16:29 ` Paul Cercueil
2019-02-05 16:29 ` Paul Cercueil
2019-02-05 18:54 ` Miquel Raynal
2019-02-05 18:54 ` Miquel Raynal
2019-02-04 19:04 ` [PATCH v3 9/9] mtd: rawnand: ingenic: Add ooblayout for the Qi Ben Nanonote Paul Cercueil
2019-02-04 19:04 ` Paul Cercueil
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190205151256.1bb3dab0@xps13 \
--to=miquel.raynal@bootlin.com \
--cc=bbrezillon@kernel.org \
--cc=computersforpeace@gmail.com \
--cc=devicetree@vger.kernel.org \
--cc=dwmw2@infradead.org \
--cc=harveyhuntnexus@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=malat@debian.org \
--cc=marek.vasut@gmail.com \
--cc=mark.rutland@arm.com \
--cc=paul@crapouillou.net \
--cc=richard@nod.at \
--cc=robh+dt@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.