* [PATCH 1/6] ARM: omap: am35xx_emif4: Fix bit polling
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
@ 2019-08-28 10:23 ` Sascha Hauer
2019-08-28 10:23 ` [PATCH 2/6] mtd: nand: gpmc: Add support for ELM Sascha Hauer
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:23 UTC (permalink / raw)
To: Barebox List
((x & (1 << 10)) == 0x1) can never be true as the compiler mourns about.
Fix this to actually do what the comment says: Wait till bit 10 is
cleared. Looking at the corresponding U-Boot code also suggests that
this is the right thing to do.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/mach-omap/am35xx_emif4.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-omap/am35xx_emif4.c b/arch/arm/mach-omap/am35xx_emif4.c
index 38fc0f02d2..678a338fd6 100644
--- a/arch/arm/mach-omap/am35xx_emif4.c
+++ b/arch/arm/mach-omap/am35xx_emif4.c
@@ -37,7 +37,7 @@ void am35xx_emif4_init(void)
writel(regval, &emif4_base->sdram_iodft_tlgc);
/* Wait till that bit clears*/
- while ((readl(&emif4_base->sdram_iodft_tlgc) & (1 << 10)) == 0x1);
+ while (readl(&emif4_base->sdram_iodft_tlgc) & (1 << 10));
/* Re-verify the DDR PHY status*/
while ((readl(&emif4_base->sdram_sts) & (1 << 2)) == 0x0);
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/6] mtd: nand: gpmc: Add support for ELM
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
2019-08-28 10:23 ` [PATCH 1/6] ARM: omap: am35xx_emif4: Fix bit polling Sascha Hauer
@ 2019-08-28 10:23 ` Sascha Hauer
2019-08-28 10:23 ` [PATCH 3/6] mtd: nand: gpmc: Add BCH16 support Sascha Hauer
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:23 UTC (permalink / raw)
To: Barebox List
This adds support for the ELM (Error Location Module) found on
not-too-old OMAP SoCs. The driver has been taken from Linux-5.3-rc6
with interrupt support removed.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/nand/Kconfig | 10 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/omap_elm.c | 413 ++++++++++++++++++++++++++++++++++++
include/platform_data/elm.h | 53 +++++
4 files changed, 477 insertions(+)
create mode 100644 drivers/mtd/nand/omap_elm.c
create mode 100644 include/platform_data/elm.h
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2b4a478a03..fadfe1f99b 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -90,6 +90,16 @@ config NAND_OMAP_GPMC
Support for NAND flash using GPMC. GPMC is a common memory
interface found on Texas Instrument's OMAP platforms
+config MTD_NAND_OMAP_ELM
+ bool "Support for ELM (Error Location Module) on OMAP platforms"
+ depends on NAND_OMAP_GPMC
+ help
+ This config enables the ELM hardware engine, which can be used to
+ locate and correct errors when using BCH ECC scheme. This offloads
+ the cpu from doing ECC error searching and correction. However some
+ legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine
+ so this is optional for them.
+
config NAND_ORION
bool
prompt "Marvell Orion NAND driver"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 6088512745..a4066ba778 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
obj-$(CONFIG_NAND_IMX) += nand_imx.o
obj-$(CONFIG_NAND_IMX_BBM) += nand_imx_bbm.o
obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o
+obj-$(CONFIG_MTD_NAND_OMAP_ELM) += omap_elm.o
obj-$(CONFIG_NAND_ORION) += nand_orion.o
obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o
obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c
new file mode 100644
index 0000000000..583235fc78
--- /dev/null
+++ b/drivers/mtd/nand/omap_elm.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Error Location Module
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#define DRIVER_NAME "omap-elm"
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <platform_data/elm.h>
+
+#define ELM_SYSCONFIG 0x010
+#define ELM_IRQSTATUS 0x018
+#define ELM_IRQENABLE 0x01c
+#define ELM_LOCATION_CONFIG 0x020
+#define ELM_PAGE_CTRL 0x080
+#define ELM_SYNDROME_FRAGMENT_0 0x400
+#define ELM_SYNDROME_FRAGMENT_1 0x404
+#define ELM_SYNDROME_FRAGMENT_2 0x408
+#define ELM_SYNDROME_FRAGMENT_3 0x40c
+#define ELM_SYNDROME_FRAGMENT_4 0x410
+#define ELM_SYNDROME_FRAGMENT_5 0x414
+#define ELM_SYNDROME_FRAGMENT_6 0x418
+#define ELM_LOCATION_STATUS 0x800
+#define ELM_ERROR_LOCATION_0 0x880
+
+/* ELM Interrupt Status Register */
+#define INTR_STATUS_PAGE_VALID BIT(8)
+
+/* ELM Interrupt Enable Register */
+#define INTR_EN_PAGE_MASK BIT(8)
+
+/* ELM Location Configuration Register */
+#define ECC_BCH_LEVEL_MASK 0x3
+
+/* ELM syndrome */
+#define ELM_SYNDROME_VALID BIT(16)
+
+/* ELM_LOCATION_STATUS Register */
+#define ECC_CORRECTABLE_MASK BIT(8)
+#define ECC_NB_ERRORS_MASK 0x1f
+
+/* ELM_ERROR_LOCATION_0-15 Registers */
+#define ECC_ERROR_LOCATION_MASK 0x1fff
+
+#define ELM_ECC_SIZE 0x7ff
+
+#define SYNDROME_FRAGMENT_REG_SIZE 0x40
+#define ERROR_LOCATION_SIZE 0x100
+
+struct elm_registers {
+ u32 elm_irqenable;
+ u32 elm_sysconfig;
+ u32 elm_location_config;
+ u32 elm_page_ctrl;
+ u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];
+ u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];
+};
+
+struct elm_info {
+ struct device_d *dev;
+ void __iomem *elm_base;
+ struct list_head list;
+ enum bch_ecc bch_type;
+ struct elm_registers elm_regs;
+ int ecc_steps;
+ int ecc_syndrome_size;
+};
+
+static struct elm_info *ginfo;
+
+static void elm_write_reg(struct elm_info *info, int offset, u32 val)
+{
+ writel(val, info->elm_base + offset);
+}
+
+static u32 elm_read_reg(struct elm_info *info, int offset)
+{
+ return readl(info->elm_base + offset);
+}
+
+/**
+ * elm_config - Configure ELM module
+ * @dev: ELM device
+ * @bch_type: Type of BCH ecc
+ */
+int elm_config(enum bch_ecc bch_type, int ecc_steps, int ecc_step_size,
+ int ecc_syndrome_size)
+{
+ struct elm_info *info = ginfo;
+ u32 reg_val;
+
+ if (!info)
+ return -ENODEV;
+ /* ELM cannot detect ECC errors for chunks > 1KB */
+ if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
+ dev_err(info->dev, "unsupported config ecc-size=%d\n", ecc_step_size);
+ return -EINVAL;
+ }
+ /* ELM support 8 error syndrome process */
+ if (ecc_steps > ERROR_VECTOR_MAX) {
+ dev_err(info->dev, "unsupported config ecc-step=%d\n", ecc_steps);
+ return -EINVAL;
+ }
+
+ reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
+ elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
+ info->bch_type = bch_type;
+ info->ecc_steps = ecc_steps;
+ info->ecc_syndrome_size = ecc_syndrome_size;
+
+ return 0;
+}
+
+/**
+ * elm_configure_page_mode - Enable/Disable page mode
+ * @info: elm info
+ * @index: index number of syndrome fragment vector
+ * @enable: enable/disable flag for page mode
+ *
+ * Enable page mode for syndrome fragment index
+ */
+static void elm_configure_page_mode(struct elm_info *info, int index,
+ bool enable)
+{
+ u32 reg_val;
+
+ reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
+ if (enable)
+ reg_val |= BIT(index); /* enable page mode */
+ else
+ reg_val &= ~BIT(index); /* disable page mode */
+
+ elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
+}
+
+/**
+ * elm_load_syndrome - Load ELM syndrome reg
+ * @info: elm info
+ * @err_vec: elm error vectors
+ * @ecc: buffer with calculated ecc
+ *
+ * Load syndrome fragment registers with calculated ecc in reverse order.
+ */
+static void elm_load_syndrome(struct elm_info *info,
+ struct elm_errorvec *err_vec, u8 *ecc)
+{
+ int i, offset;
+ u32 val;
+
+ for (i = 0; i < info->ecc_steps; i++) {
+
+ /* Check error reported */
+ if (err_vec[i].error_reported) {
+ elm_configure_page_mode(info, i, true);
+ offset = ELM_SYNDROME_FRAGMENT_0 +
+ SYNDROME_FRAGMENT_REG_SIZE * i;
+ switch (info->bch_type) {
+ case BCH8_ECC:
+ /* syndrome fragment 0 = ecc[9-12B] */
+ val = cpu_to_be32(*(u32 *) &ecc[9]);
+ elm_write_reg(info, offset, val);
+
+ /* syndrome fragment 1 = ecc[5-8B] */
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[5]);
+ elm_write_reg(info, offset, val);
+
+ /* syndrome fragment 2 = ecc[1-4B] */
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[1]);
+ elm_write_reg(info, offset, val);
+
+ /* syndrome fragment 3 = ecc[0B] */
+ offset += 4;
+ val = ecc[0];
+ elm_write_reg(info, offset, val);
+ break;
+ case BCH4_ECC:
+ /* syndrome fragment 0 = ecc[20-52b] bits */
+ val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
+ ((ecc[2] & 0xf) << 28);
+ elm_write_reg(info, offset, val);
+
+ /* syndrome fragment 1 = ecc[0-20b] bits */
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
+ elm_write_reg(info, offset, val);
+ break;
+ case BCH16_ECC:
+ val = cpu_to_be32(*(u32 *) &ecc[22]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[18]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[14]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[10]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[6]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[2]);
+ elm_write_reg(info, offset, val);
+ offset += 4;
+ val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16;
+ elm_write_reg(info, offset, val);
+ break;
+ default:
+ pr_err("invalid config bch_type\n");
+ }
+ }
+
+ /* Update ecc pointer with ecc byte size */
+ ecc += info->ecc_syndrome_size;
+ }
+}
+
+/**
+ * elm_start_processing - start elm syndrome processing
+ * @info: elm info
+ * @err_vec: elm error vectors
+ *
+ * Set syndrome valid bit for syndrome fragment registers for which
+ * elm syndrome fragment registers are loaded. This enables elm module
+ * to start processing syndrome vectors.
+ */
+static void elm_start_processing(struct elm_info *info,
+ struct elm_errorvec *err_vec)
+{
+ int i, offset;
+ u32 reg_val;
+
+ /*
+ * Set syndrome vector valid, so that ELM module
+ * will process it for vectors error is reported
+ */
+ for (i = 0; i < info->ecc_steps; i++) {
+ if (err_vec[i].error_reported) {
+ offset = ELM_SYNDROME_FRAGMENT_6 +
+ SYNDROME_FRAGMENT_REG_SIZE * i;
+ reg_val = elm_read_reg(info, offset);
+ reg_val |= ELM_SYNDROME_VALID;
+ elm_write_reg(info, offset, reg_val);
+ }
+ }
+}
+
+/**
+ * elm_error_correction - locate correctable error position
+ * @info: elm info
+ * @err_vec: elm error vectors
+ *
+ * On completion of processing by elm module, error location status
+ * register updated with correctable/uncorrectable error information.
+ * In case of correctable errors, number of errors located from
+ * elm location status register & read the positions from
+ * elm error location register.
+ */
+static void elm_error_correction(struct elm_info *info,
+ struct elm_errorvec *err_vec)
+{
+ int i, j, errors = 0;
+ int offset;
+ u32 reg_val;
+
+ for (i = 0; i < info->ecc_steps; i++) {
+
+ /* Check error reported */
+ if (err_vec[i].error_reported) {
+ offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
+ reg_val = elm_read_reg(info, offset);
+
+ /* Check correctable error or not */
+ if (reg_val & ECC_CORRECTABLE_MASK) {
+ offset = ELM_ERROR_LOCATION_0 +
+ ERROR_LOCATION_SIZE * i;
+
+ /* Read count of correctable errors */
+ err_vec[i].error_count = reg_val &
+ ECC_NB_ERRORS_MASK;
+
+ /* Update the error locations in error vector */
+ for (j = 0; j < err_vec[i].error_count; j++) {
+
+ reg_val = elm_read_reg(info, offset);
+ err_vec[i].error_loc[j] = reg_val &
+ ECC_ERROR_LOCATION_MASK;
+
+ /* Update error location register */
+ offset += 4;
+ }
+
+ errors += err_vec[i].error_count;
+ } else {
+ err_vec[i].error_uncorrectable = true;
+ }
+
+ /* Clearing interrupts for processed error vectors */
+ elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
+
+ /* Disable page mode */
+ elm_configure_page_mode(info, i, false);
+ }
+ }
+}
+
+static int elm_poll(struct elm_info *info)
+{
+ u32 reg_val;
+
+ reg_val = elm_read_reg(info, ELM_IRQSTATUS);
+
+ /* All error vectors processed */
+ if (reg_val & INTR_STATUS_PAGE_VALID) {
+ elm_write_reg(info, ELM_IRQSTATUS,
+ reg_val & INTR_STATUS_PAGE_VALID);
+ return 0;
+ }
+
+ return -EINPROGRESS;
+}
+
+/**
+ * elm_decode_bch_error_page - Locate error position
+ * @dev: device pointer
+ * @ecc_calc: calculated ECC bytes from GPMC
+ * @err_vec: elm error vectors
+ *
+ * Called with one or more error reported vectors & vectors with
+ * error reported is updated in err_vec[].error_reported
+ */
+int elm_decode_bch_error_page(u8 *ecc_calc, struct elm_errorvec *err_vec)
+{
+ struct elm_info *info = ginfo;
+ u32 reg_val;
+ uint64_t start;
+
+ if (!info)
+ return -ENODEV;
+
+ /* Enable page mode interrupt */
+ reg_val = elm_read_reg(info, ELM_IRQSTATUS);
+ elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
+ elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
+
+ /* Load valid ecc byte to syndrome fragment register */
+ elm_load_syndrome(info, err_vec, ecc_calc);
+
+ /* Enable syndrome processing for which syndrome fragment is updated */
+ elm_start_processing(info, err_vec);
+
+ /* Wait for ELM module to finish locating error correction */
+ start = get_time_ns();
+ while (elm_poll(info) < 0) {
+ if (is_timeout(start, SECOND))
+ return -ETIMEDOUT;
+ }
+
+ /* Disable page mode interrupt */
+ reg_val = elm_read_reg(info, ELM_IRQENABLE);
+ elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
+ elm_error_correction(info, err_vec);
+
+ return 0;
+}
+
+static int elm_probe(struct device_d *dev)
+{
+ struct resource *res;
+ struct elm_info *info;
+
+ info = xzalloc(sizeof(*info));
+
+ info->dev = dev;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+ info->elm_base = IOMEM(res->start);
+
+ dev->priv = info;
+
+ ginfo = info;
+
+ return 0;
+}
+
+static struct of_device_id elm_compatible[] = {
+ {
+ .compatible = "ti,am3352-elm",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d omap_elm_driver = {
+ .name = "omap-elm",
+ .probe = elm_probe,
+ .of_compatible = DRV_OF_COMPAT(elm_compatible)
+};
+coredevice_platform_driver(omap_elm_driver);
diff --git a/include/platform_data/elm.h b/include/platform_data/elm.h
new file mode 100644
index 0000000000..bc5d2edb1d
--- /dev/null
+++ b/include/platform_data/elm.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * BCH Error Location Module
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef __ELM_H
+#define __ELM_H
+
+enum bch_ecc {
+ BCH4_ECC = 0,
+ BCH8_ECC,
+ BCH16_ECC,
+};
+
+/* ELM support 8 error syndrome process */
+#define ERROR_VECTOR_MAX 8
+
+/**
+ * struct elm_errorvec - error vector for elm
+ * @error_reported: set true for vectors error is reported
+ * @error_uncorrectable: number of uncorrectable errors
+ * @error_count: number of correctable errors in the sector
+ * @error_loc: buffer for error location
+ *
+ */
+struct elm_errorvec {
+ bool error_reported;
+ bool error_uncorrectable;
+ int error_count;
+ int error_loc[16];
+};
+
+#if IS_ENABLED(CONFIG_MTD_NAND_OMAP_ELM)
+int elm_decode_bch_error_page(u8 *ecc_calc, struct elm_errorvec *err_vec);
+int elm_config(enum bch_ecc bch_type, int ecc_steps, int ecc_step_size,
+ int ecc_syndrome_size);
+#else
+static inline int
+elm_decode_bch_error_page(u8 *ecc_calc, struct elm_errorvec *err_vec)
+{
+ return -ENODEV;
+}
+
+static inline int elm_config(enum bch_ecc bch_type, int ecc_steps,
+ int ecc_step_size, int ecc_syndrome_size)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MTD_NAND_OMAP_ELM */
+
+#endif /* __ELM_H */
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/6] mtd: nand: gpmc: Add BCH16 support
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
2019-08-28 10:23 ` [PATCH 1/6] ARM: omap: am35xx_emif4: Fix bit polling Sascha Hauer
2019-08-28 10:23 ` [PATCH 2/6] mtd: nand: gpmc: Add support for ELM Sascha Hauer
@ 2019-08-28 10:23 ` Sascha Hauer
2019-08-28 10:24 ` [PATCH 4/6] net: cpsw: Make phy its own driver Sascha Hauer
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:23 UTC (permalink / raw)
To: Barebox List
This adds support for BCH16 ECC encoding. The support is mostly taken
from Linux-5.3-rc6. One major change is the different wrap mode used.
The Kernel uses wrapmode 1, which means "pass all data through the BCH
engine". Still the Kernel has to skip the OOB marker which is done by
reading all user data, then use NAND_CMD_RNDOUT to position right
behind the OOB marker and then read the ECC data. Instead of doing this
we use wrap mode 4 which allows us to bypass the OOB marker from the
BCH engine automatically. This explains
bch_wrapmode = 1, eccsize0 = 0, eccsize1 = 52 vs.
bch_wrapmode = 4, eccsize0 = 4, eccsize1 = 52
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/mach-omap/include/mach/gpmc_nand.h | 1 +
drivers/bus/omap-gpmc.c | 3 +
drivers/mtd/nand/nand_omap_gpmc.c | 333 +++++++++++++++++++-
include/linux/mtd/mtd-abi.h | 2 +-
4 files changed, 330 insertions(+), 9 deletions(-)
diff --git a/arch/arm/mach-omap/include/mach/gpmc_nand.h b/arch/arm/mach-omap/include/mach/gpmc_nand.h
index c9730a9454..f172b576eb 100644
--- a/arch/arm/mach-omap/include/mach/gpmc_nand.h
+++ b/arch/arm/mach-omap/include/mach/gpmc_nand.h
@@ -34,6 +34,7 @@ enum gpmc_ecc_mode {
OMAP_ECC_HAMMING_CODE_HW_ROMCODE,
OMAP_ECC_BCH8_CODE_HW,
OMAP_ECC_BCH8_CODE_HW_ROMCODE,
+ OMAP_ECC_BCH16_CODE_HW,
};
/** omap nand platform data structure */
diff --git a/drivers/bus/omap-gpmc.c b/drivers/bus/omap-gpmc.c
index a3fccb2182..8fd7a91740 100644
--- a/drivers/bus/omap-gpmc.c
+++ b/drivers/bus/omap-gpmc.c
@@ -448,6 +448,9 @@ static struct dt_eccmode modes[] = {
}, {
.name = "bch8",
.mode = OMAP_ECC_BCH8_CODE_HW_ROMCODE,
+ }, {
+ .name = "bch16",
+ .mode = OMAP_ECC_BCH16_CODE_HW,
},
};
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 2dcf7e723b..ba2c2c4314 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -70,6 +70,7 @@
#include <io.h>
#include <mach/gpmc.h>
#include <mach/gpmc_nand.h>
+#include <platform_data/elm.h>
#include "nand_omap_bch_decoder.h"
@@ -91,6 +92,10 @@
static const uint8_t bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2,
0xbe, 0xcc, 0xac, 0x6b, 0xff, 0x99, 0x7b};
+static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
+ 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
+ 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
+ 0x07, 0x0e};
static const char *ecc_mode_strings[] = {
"software",
@@ -232,7 +237,7 @@ static int __omap_calculate_ecc(struct mtd_info *mtd, uint8_t *ecc_code,
{
struct nand_chip *nand = (struct nand_chip *)(mtd->priv);
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
- unsigned int reg;
+ unsigned int reg, reg1, val;
unsigned int val1 = 0x0, val2 = 0x0;
unsigned int val3 = 0x0, val4 = 0x0;
int ecc_size = 8;
@@ -274,6 +279,45 @@ static int __omap_calculate_ecc(struct mtd_info *mtd, uint8_t *ecc_code,
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
*ecc_code++ = ((val1 >> 8) & 0x0f) | ((val1 >> 20) & 0xf0);
break;
+
+ case OMAP_ECC_BCH16_CODE_HW:
+ reg = GPMC_ECC_BCH_RESULT_0 + (0x10 * sblock);
+ reg1 = 0x300 + (0x10 * sblock);
+
+ val = readl(oinfo->gpmc_base + reg1 + 0x8);
+ ecc_code[0] = ((val >> 8) & 0xFF);
+ ecc_code[1] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg1 + 0x4);
+ ecc_code[2] = ((val >> 24) & 0xFF);
+ ecc_code[3] = ((val >> 16) & 0xFF);
+ ecc_code[4] = ((val >> 8) & 0xFF);
+ ecc_code[5] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg1 + 0x0);
+ ecc_code[6] = ((val >> 24) & 0xFF);
+ ecc_code[7] = ((val >> 16) & 0xFF);
+ ecc_code[8] = ((val >> 8) & 0xFF);
+ ecc_code[9] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg + 0xc);
+ ecc_code[10] = ((val >> 24) & 0xFF);
+ ecc_code[11] = ((val >> 16) & 0xFF);
+ ecc_code[12] = ((val >> 8) & 0xFF);
+ ecc_code[13] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg + 0x8);
+ ecc_code[14] = ((val >> 24) & 0xFF);
+ ecc_code[15] = ((val >> 16) & 0xFF);
+ ecc_code[16] = ((val >> 8) & 0xFF);
+ ecc_code[17] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg + 0x4);
+ ecc_code[18] = ((val >> 24) & 0xFF);
+ ecc_code[19] = ((val >> 16) & 0xFF);
+ ecc_code[20] = ((val >> 8) & 0xFF);
+ ecc_code[21] = ((val >> 0) & 0xFF);
+ val = readl(oinfo->gpmc_base + reg + 0x0);
+ ecc_code[22] = ((val >> 24) & 0xFF);
+ ecc_code[23] = ((val >> 16) & 0xFF);
+ ecc_code[24] = ((val >> 8) & 0xFF);
+ ecc_code[25] = ((val >> 0) & 0xFF);
+ break;
default:
return -EINVAL;
}
@@ -294,7 +338,7 @@ static int omap_correct_bch(struct mtd_info *mtd, uint8_t *dat,
struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv);
int j, actual_eccsize;
const uint8_t *erased_ecc_vec;
- unsigned int err_loc[8];
+ unsigned int err_loc[16];
int bch_max_err;
int bitflip_count = 0;
bool eccflag = 0;
@@ -311,6 +355,11 @@ static int omap_correct_bch(struct mtd_info *mtd, uint8_t *dat,
erased_ecc_vec = bch8_vector;
bch_max_err = BCH8_MAX_ERROR;
break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ actual_eccsize = eccsize;
+ erased_ecc_vec = bch16_vector;
+ bch_max_err = 16;
+ break;
default:
dev_err(oinfo->pdev, "invalid driver configuration\n");
return -EINVAL;
@@ -441,13 +490,27 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
case OMAP_ECC_BCH8_CODE_HW:
case OMAP_ECC_BCH8_CODE_HW_ROMCODE:
if (mode == NAND_ECC_READ) {
- eccsize1 = 0x1A; eccsize0 = 0x18;
+ eccsize0 = 24;
+ eccsize1 = 26;
bch_mod = 1;
- bch_wrapmode = 0x04;
+ bch_wrapmode = 4;
} else {
- eccsize1 = 0x20; eccsize0 = 0x00;
+ eccsize0 = 0;
+ eccsize1 = 32;
bch_mod = 1;
- bch_wrapmode = 0x06;
+ bch_wrapmode = 6;
+ }
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ bch_mod = 2;
+ if (mode == NAND_ECC_READ) {
+ bch_wrapmode = 4;
+ eccsize0 = 4; /* ECC bits in nibbles per sector */
+ eccsize1 = 52; /* non-ECC bits in nibbles per sector */
+ } else {
+ bch_wrapmode = 4;
+ eccsize0 = 4; /* extra bits in nibbles per sector */
+ eccsize1 = 52; /* OOB bits in nibbles per sector */
}
break;
case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
@@ -483,7 +546,7 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
GPMC_ECC_CONFIG_ECCBCHTSEL(bch_mod) |
GPMC_ECC_CONFIG_ECCWRAPMODE(bch_wrapmode) |
dev_width |
- GPMC_ECC_CONFIG_ECCTOPSECTOR(3) |
+ GPMC_ECC_CONFIG_ECCTOPSECTOR(nand->ecc.steps - 1) |
GPMC_ECC_CONFIG_ECCCS(cs) |
GPMC_ECC_CONFIG_ECCENABLE);
}
@@ -689,6 +752,234 @@ static int omap_gpmc_read_page_bch_rom_mode(struct mtd_info *mtd,
return max_bitflips;
}
+/**
+ * erased_sector_bitflips - count bit flips
+ * @data: data sector buffer
+ * @oob: oob buffer
+ * oinfo: gpmc_nand_info
+ *
+ * Check the bit flips in erased page falls below correctable level.
+ * If falls below, report the page as erased with correctable bit
+ * flip, else report as uncorrectable page.
+ */
+static int erased_sector_bitflips(u_char *data, u_char *oob,
+ struct gpmc_nand_info *oinfo)
+{
+ int flip_bits = 0, i;
+
+ for (i = 0; i < oinfo->nand.ecc.size; i++) {
+ flip_bits += hweight8(~data[i]);
+ if (flip_bits > oinfo->nand.ecc.strength)
+ return 0;
+ }
+
+ for (i = 0; i < oinfo->nand.ecc.bytes - 1; i++) {
+ flip_bits += hweight8(~oob[i]);
+ if (flip_bits > oinfo->nand.ecc.strength)
+ return 0;
+ }
+
+ /*
+ * Bit flips falls in correctable level.
+ * Fill data area with 0xFF
+ */
+ if (flip_bits) {
+ memset(data, 0xFF, oinfo->nand.ecc.size);
+ memset(oob, 0xFF, oinfo->nand.ecc.bytes);
+ }
+
+ return flip_bits;
+}
+
+/**
+ * omap_elm_correct_data - corrects page data area in case error reported
+ * @chip: NAND chip object
+ * @data: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ *
+ * Calculated ecc vector reported as zero in case of non-error pages.
+ * In case of non-zero ecc vector, first filter out erased-pages, and
+ * then process data via ELM to detect bit-flips.
+ */
+static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct gpmc_nand_info *oinfo = chip->priv;
+ struct nand_ecc_ctrl *ecc = &oinfo->nand.ecc;
+ int eccsteps = oinfo->nand.ecc.steps;
+ int i , j, stat = 0;
+ int eccflag, actual_eccbytes;
+ struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
+ u_char *ecc_vec = calc_ecc;
+ u_char *spare_ecc = read_ecc;
+ const u_char *erased_ecc_vec;
+ u_char *buf;
+ int bitflip_count;
+ bool is_error_reported = false;
+ u32 bit_pos, byte_pos, error_max, pos;
+ int err;
+
+ switch (oinfo->ecc_mode) {
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* omit 14th ECC byte reserved for ROM code compatibility */
+ actual_eccbytes = ecc->bytes - 1;
+ erased_ecc_vec = bch8_vector;
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ actual_eccbytes = ecc->bytes;
+ erased_ecc_vec = bch16_vector;
+ break;
+ default:
+ dev_err(oinfo->pdev, "invalid driver configuration\n");
+ return -EINVAL;
+ }
+
+ /* Initialize elm error vector to zero */
+ memset(err_vec, 0, sizeof(err_vec));
+
+ for (i = 0; i < eccsteps ; i++) {
+ eccflag = 0; /* initialize eccflag */
+
+ /*
+ * Check any error reported,
+ * In case of error, non zero ecc reported.
+ */
+ for (j = 0; j < actual_eccbytes; j++) {
+ if (calc_ecc[j] != 0) {
+ eccflag = 1; /* non zero ecc, error present */
+ break;
+ }
+ }
+
+ if (eccflag == 1) {
+ if (memcmp(calc_ecc, erased_ecc_vec,
+ actual_eccbytes) == 0) {
+ /*
+ * calc_ecc[] matches pattern for ECC(all 0xff)
+ * so this is definitely an erased-page
+ */
+ } else {
+ buf = &data[oinfo->nand.ecc.size * i];
+ /*
+ * count number of 0-bits in read_buf.
+ * This check can be removed once a similar
+ * check is introduced in generic NAND driver
+ */
+ bitflip_count = erased_sector_bitflips(
+ buf, read_ecc, oinfo);
+ if (bitflip_count) {
+ /*
+ * number of 0-bits within ECC limits
+ * So this may be an erased-page
+ */
+ stat += bitflip_count;
+ } else {
+ /*
+ * Too many 0-bits. It may be a
+ * - programmed-page, OR
+ * - erased-page with many bit-flips
+ * So this page requires check by ELM
+ */
+ err_vec[i].error_reported = true;
+ is_error_reported = true;
+ }
+ }
+ }
+
+ /* Update the ecc vector */
+ calc_ecc += ecc->bytes;
+ read_ecc += ecc->bytes;
+ }
+
+ /* Check if any error reported */
+ if (!is_error_reported)
+ return stat;
+
+ /* Decode BCH error using ELM module */
+ elm_decode_bch_error_page(ecc_vec, err_vec);
+
+ err = 0;
+ for (i = 0; i < eccsteps; i++) {
+ if (err_vec[i].error_uncorrectable) {
+ dev_err(oinfo->pdev,
+ "uncorrectable bit-flips found\n");
+ err = -EBADMSG;
+ } else if (err_vec[i].error_reported) {
+ for (j = 0; j < err_vec[i].error_count; j++) {
+ switch (oinfo->ecc_mode) {
+ case OMAP_ECC_BCH8_CODE_HW:
+ case OMAP_ECC_BCH16_CODE_HW:
+ pos = err_vec[i].error_loc[j];
+ break;
+ default:
+ return -EINVAL;
+ }
+ error_max = (ecc->size + actual_eccbytes) * 8;
+ /* Calculate bit position of error */
+ bit_pos = pos % 8;
+
+ /* Calculate byte position of error */
+ byte_pos = (error_max - pos - 1) / 8;
+
+ if (pos < error_max) {
+ if (byte_pos < 512) {
+ pr_debug("bitflip@dat[%d]=%x\n",
+ byte_pos, data[byte_pos]);
+ data[byte_pos] ^= 1 << bit_pos;
+ } else {
+ pr_debug("bitflip@oob[%d]=%x\n",
+ (byte_pos - 512),
+ spare_ecc[byte_pos - 512]);
+ spare_ecc[byte_pos - 512] ^=
+ 1 << bit_pos;
+ }
+ } else {
+ dev_err(oinfo->pdev,
+ "invalid bit-flip @ %d:%d\n",
+ byte_pos, bit_pos);
+ err = -EBADMSG;
+ }
+ }
+ }
+
+ /* Update number of correctable errors */
+ stat = max_t(unsigned int, stat, err_vec[i].error_count);
+
+ /* Update page data with sector size */
+ data += ecc->size;
+ spare_ecc += ecc->bytes;
+ }
+
+ return (err) ? err : stat;
+}
+
+static int gpmc_read_page_hwecc_elm(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ int i;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+
+ for (i = 0; eccsteps; eccsteps--, i++)
+ __omap_calculate_ecc(mtd, &ecc_calc[i * eccbytes], i);
+
+ return omap_elm_correct_data(chip, buf, ecc_code, ecc_calc);
+}
+
static int gpmc_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
@@ -732,7 +1023,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
{
struct mtd_info *minfo = &oinfo->minfo;
struct nand_chip *nand = &oinfo->nand;
- int offset;
+ int offset, err;
int i, j;
if (nand->options & NAND_BUSWIDTH_16)
@@ -807,6 +1098,32 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
for (i = 2; i < 58; i++)
omap_oobinfo.eccpos[j++] = i;
break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ oinfo->nand.ecc.size = 512;
+ oinfo->nand.ecc.bytes = 26;
+ oinfo->nand.ecc.strength = 16;
+ oinfo->nand.ecc.steps =
+ minfo->writesize / oinfo->nand.ecc.size;
+ oinfo->nand.ecc.total =
+ oinfo->nand.ecc.steps * oinfo->nand.ecc.bytes;
+ omap_oobinfo.eccbytes = oinfo->nand.ecc.total;
+
+ for (i = 0; i < oinfo->nand.ecc.total; i++)
+ omap_oobinfo.eccpos[i] = i + 2;
+
+ /* reserved marker already included in ecclayout->eccbytes */
+ omap_oobinfo.oobfree->offset =
+ omap_oobinfo.eccpos[oinfo->nand.ecc.total - 1] + 1;
+
+ err = elm_config(BCH16_ECC,
+ oinfo->minfo.writesize / nand->ecc.size,
+ nand->ecc.size, nand->ecc.bytes);
+ if (err < 0)
+ return err;
+
+ nand->ecc.read_page = gpmc_read_page_hwecc_elm;
+
+ break;
case OMAP_ECC_SOFT:
nand->ecc.layout = NULL;
nand->ecc.mode = NAND_ECC_SOFT;
diff --git a/include/linux/mtd/mtd-abi.h b/include/linux/mtd/mtd-abi.h
index 9bca9b5e06..dfcb3554fb 100644
--- a/include/linux/mtd/mtd-abi.h
+++ b/include/linux/mtd/mtd-abi.h
@@ -137,7 +137,7 @@ struct nand_oobfree {
};
#define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
-#define MTD_MAX_ECCPOS_ENTRIES_LARGE 128 /* FIXME : understand why 448 is not working */
+#define MTD_MAX_ECCPOS_ENTRIES_LARGE 640
/*
* ECC layout control structure. Exported to userspace for
* diagnosis and to allow creation of raw images
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/6] net: cpsw: Make phy its own driver
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
` (2 preceding siblings ...)
2019-08-28 10:23 ` [PATCH 3/6] mtd: nand: gpmc: Add BCH16 support Sascha Hauer
@ 2019-08-28 10:24 ` Sascha Hauer
2019-08-28 10:24 ` [PATCH 5/6] mtd: peb: Add function to write file Sascha Hauer
2019-08-28 10:24 ` [PATCH 6/6] ARM: OMAP: bbu: Add an all-in-one NAND update handler Sascha Hauer
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:24 UTC (permalink / raw)
To: Barebox List
This makes of_phy_device_connect() work properly when the phy is
specified in the device tree. Without it of_mdio_find_phy() will
not find the right device. It will match:
bus->parent->device_node == phy_node->parent
Without this patch bus->parent->device_node will be the ethernet node
and phy_node->parent will be the ti,cpsw-mdio node. With the MDIO device
node registered as device of its own both nodes above will be the
ti,cpsw-mdio node.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/net/cpsw.c | 178 ++++++++++++++++++++++++---------------------
1 file changed, 96 insertions(+), 82 deletions(-)
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
index df8981d02e..b9a6575009 100644
--- a/drivers/net/cpsw.c
+++ b/drivers/net/cpsw.c
@@ -191,7 +191,6 @@ struct cpdma_chan {
struct cpsw_priv {
struct device_d *dev;
- struct mii_bus miibus;
u32 version;
struct cpsw_platform_data data;
@@ -199,7 +198,6 @@ struct cpsw_priv {
uint8_t mac_addr[6];
struct cpsw_regs *regs;
- struct cpsw_mdio_regs *mdio_regs;
void *dma_regs;
struct cpsw_host_regs *host_port_regs;
void *ale_regs;
@@ -219,6 +217,12 @@ struct cpsw_priv {
struct cpsw_slave *slaves;
};
+struct cpsw_mdio_priv {
+ struct device_d *dev;
+ struct mii_bus miibus;
+ struct cpsw_mdio_regs *mdio_regs;
+};
+
static int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
{
int idx;
@@ -450,7 +454,7 @@ static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port,
}
/* wait until hardware is ready for another user access */
-static u32 wait_for_user_access(struct cpsw_priv *priv)
+static u32 wait_for_user_access(struct cpsw_mdio_priv *priv)
{
struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
u32 tmp;
@@ -473,7 +477,7 @@ static u32 wait_for_user_access(struct cpsw_priv *priv)
static int cpsw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
{
- struct cpsw_priv *priv = bus->priv;
+ struct cpsw_mdio_priv *priv = bus->priv;
struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
u32 tmp;
@@ -494,7 +498,7 @@ static int cpsw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 value)
{
- struct cpsw_priv *priv = bus->priv;
+ struct cpsw_mdio_priv *priv = bus->priv;
struct cpsw_mdio_regs *mdio_regs = priv->mdio_regs;
u32 tmp;
@@ -510,6 +514,81 @@ static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 val
return 0;
}
+static int cpsw_mdio_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct cpsw_mdio_priv *priv;
+ uint64_t start;
+ uint32_t phy_mask;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ priv->mdio_regs = IOMEM(iores->start);
+ priv->miibus.read = cpsw_mdio_read;
+ priv->miibus.write = cpsw_mdio_write;
+ priv->miibus.priv = priv;
+ priv->miibus.parent = dev;
+
+ /*
+ * set enable and clock divider
+ *
+ * FIXME: Use a clock to calculate the divider
+ */
+ writel(0xff | CONTROL_ENABLE, &priv->mdio_regs->control);
+
+ /*
+ * wait for scan logic to settle:
+ * the scan time consists of (a) a large fixed component, and (b) a
+ * small component that varies with the mii bus frequency. These
+ * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
+ * silicon. Since the effect of (b) was found to be largely
+ * negligible, we keep things simple here.
+ */
+ udelay(1000);
+
+ start = get_time_ns();
+ while (1) {
+ phy_mask = readl(&priv->mdio_regs->alive);
+ if (phy_mask) {
+ dev_info(dev, "detected phy mask 0x%x\n", phy_mask);
+ phy_mask = ~phy_mask;
+ break;
+ }
+ if (is_timeout(start, 256 * MSECOND)) {
+ dev_err(dev, "no live phy, scanning all\n");
+ phy_mask = 0;
+ break;
+ }
+ }
+
+ priv->miibus.phy_mask = phy_mask;
+
+ ret = mdiobus_register(&priv->miibus);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = {
+ {
+ .compatible = "ti,cpsw-mdio",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d cpsw_mdio_driver = {
+ .name = "cpsw-mdio",
+ .probe = cpsw_mdio_probe,
+ .of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids),
+};
+coredevice_platform_driver(cpsw_mdio_driver);
+
static inline void soft_reset(struct cpsw_priv *priv, void *reg)
{
int ret;
@@ -549,9 +628,9 @@ static int cpsw_set_hwaddr(struct eth_device *edev, const unsigned char *mac)
return 0;
}
-static void cpsw_slave_update_link(struct cpsw_slave *slave,
- struct cpsw_priv *priv, int *link)
+static void cpsw_adjust_link(struct eth_device *edev)
{
+ struct cpsw_slave *slave = edev->priv;
struct phy_device *phydev = slave->edev.phydev;
u32 mac_control = 0;
@@ -561,7 +640,6 @@ static void cpsw_slave_update_link(struct cpsw_slave *slave,
return;
if (phydev->link) {
- *link = 1;
mac_control = BIT(5); /* MIIEN */
if (phydev->speed == SPEED_10)
mac_control |= BIT(18); /* In Band mode */
@@ -584,27 +662,6 @@ static void cpsw_slave_update_link(struct cpsw_slave *slave,
writel(mac_control, &slave->sliver->mac_control);
}
-static int cpsw_update_link(struct cpsw_slave *slave, struct cpsw_priv *priv)
-{
- int link = 0;
-
- dev_dbg(&slave->dev, "* %s\n", __func__);
-
- cpsw_slave_update_link(slave, priv, &link);
-
- return link;
-}
-
-static void cpsw_adjust_link(struct eth_device *edev)
-{
- struct cpsw_slave *slave = edev->priv;
- struct cpsw_priv *priv = slave->cpsw;
-
- dev_dbg(&slave->dev, "* %s\n", __func__);
-
- cpsw_update_link(slave, priv);
-}
-
static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
{
if (priv->host_port == 0)
@@ -748,6 +805,11 @@ static int cpsw_open(struct eth_device *edev)
dev_dbg(&slave->dev, "* %s\n", __func__);
+ ret = phy_device_connect(edev, NULL, slave->phy_id,
+ cpsw_adjust_link, 0, slave->phy_if);
+ if (ret)
+ return ret;
+
/* soft reset the controller and initialize priv */
soft_reset(priv, &priv->regs->soft_reset);
@@ -774,7 +836,6 @@ static int cpsw_open(struct eth_device *edev)
cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port);
cpsw_slave_init(slave, priv);
- cpsw_update_link(slave, priv);
/* init descriptor pool */
for (i = 0; i < NUM_DESCS; i++) {
@@ -898,11 +959,6 @@ static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num,
edev->parent = dev;
- ret = phy_device_connect(edev, &priv->miibus, slave->phy_id,
- cpsw_adjust_link, 0, slave->phy_if);
- if (ret)
- goto err_out;
-
dev_set_name(dev, "cpsw-slave");
dev->id = slave->slave_num;
dev->parent = priv->dev;
@@ -936,7 +992,7 @@ err_register_dev:
phy_unregister_device(edev->phydev);
err_register_edev:
unregister_device(dev);
-err_out:
+
slave->slave_num = -1;
return ret;
@@ -1117,13 +1173,15 @@ static int cpsw_probe(struct device_d *dev)
struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data;
struct cpsw_priv *priv;
void __iomem *regs;
- uint64_t start;
- uint32_t phy_mask;
struct cpsw_data *cpsw_data;
int i, ret;
dev_dbg(dev, "* %s\n", __func__);
+ ret = of_platform_populate(dev->device_node, NULL, dev);
+ if (ret)
+ return ret;
+
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -1168,53 +1226,11 @@ static int cpsw_probe(struct device_d *dev)
priv->dma_regs = regs + cpsw_data->cpdma_reg_ofs;
priv->ale_regs = regs + cpsw_data->ale_reg_ofs;
priv->state_ram = regs + cpsw_data->state_ram_ofs;
- priv->mdio_regs = regs + cpsw_data->mdio_reg_ofs;
priv->slave_ofs = cpsw_data->slave_ofs;
priv->slave_size = cpsw_data->slave_size;
priv->sliver_ofs = cpsw_data->sliver_ofs;
- priv->miibus.read = cpsw_mdio_read;
- priv->miibus.write = cpsw_mdio_write;
- priv->miibus.priv = priv;
- priv->miibus.parent = dev;
-
- /*
- * set enable and clock divider
- *
- * FIXME: Use a clock to calculate the divider
- */
- writel(0xff | CONTROL_ENABLE, &priv->mdio_regs->control);
-
- /*
- * wait for scan logic to settle:
- * the scan time consists of (a) a large fixed component, and (b) a
- * small component that varies with the mii bus frequency. These
- * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
- * silicon. Since the effect of (b) was found to be largely
- * negligible, we keep things simple here.
- */
- udelay(1000);
-
- start = get_time_ns();
- while (1) {
- phy_mask = readl(&priv->mdio_regs->alive);
- if (phy_mask) {
- dev_info(dev, "detected phy mask 0x%x\n", phy_mask);
- phy_mask = ~phy_mask;
- break;
- }
- if (is_timeout(start, 256 * MSECOND)) {
- dev_err(dev, "no live phy, scanning all\n");
- phy_mask = 0;
- break;
- }
- }
-
- priv->miibus.phy_mask = phy_mask;
-
- mdiobus_register(&priv->miibus);
-
for (i = 0; i < priv->num_slaves; i++) {
ret = cpsw_slave_setup(&priv->slaves[i], i, priv);
if (ret) {
@@ -1245,8 +1261,6 @@ static void cpsw_remove(struct device_d *dev)
eth_unregister(&slave->edev);
}
-
- mdiobus_unregister(&priv->miibus);
}
static __maybe_unused struct of_device_id cpsw_dt_ids[] = {
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 5/6] mtd: peb: Add function to write file
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
` (3 preceding siblings ...)
2019-08-28 10:24 ` [PATCH 4/6] net: cpsw: Make phy its own driver Sascha Hauer
@ 2019-08-28 10:24 ` Sascha Hauer
2019-08-28 10:24 ` [PATCH 6/6] ARM: OMAP: bbu: Add an all-in-one NAND update handler Sascha Hauer
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:24 UTC (permalink / raw)
To: Barebox List
This adds a function to the mtd peb API to write a file spanning
multiple blocks to a mtd device. Bad blocks are automatically skipped
and before anything is done we check if the image will fit into the
remaining space (honouring bad blocks).
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/mtd/peb.c | 84 +++++++++++++++++++++++++++++++++++++++++++
include/mtd/mtd-peb.h | 2 ++
2 files changed, 86 insertions(+)
diff --git a/drivers/mtd/peb.c b/drivers/mtd/peb.c
index c82b3e8bba..d10a8a024d 100644
--- a/drivers/mtd/peb.c
+++ b/drivers/mtd/peb.c
@@ -437,6 +437,90 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
return err;
}
+/**
+ * mtd_peb_write_file - write data into a mtd device
+ * @mtd: mtd device
+ * @peb_start: The first PEB where to start writing
+ * @max_pebs: Maximum number of PEBs we have space to write to
+ * @buf: buffer with the data to write
+ * @len: how many bytes to write
+ *
+ * This function writes @len bytes of data from buffer @buf to the mtd device
+ * @mtd starting at @peb_start. At maxmimum @max_pebs are used. We always write
+ * full pebs. If the buffer is not peb size aligned the last peb will be padded
+ * with zeroes. This function skips all bad blocks and returns 0 on success or a
+ * negative error code otherwise.
+ */
+int mtd_peb_write_file(struct mtd_info *mtd, int peb_start, int max_pebs,
+ const void *buf, size_t len)
+{
+ int ret, pnum;
+ size_t left;
+
+ pnum = peb_start;
+ left = len;
+
+ /* First pass: Check if file will fit into remaining space */
+ while (1) {
+ if ((uint64_t)pnum * mtd->erasesize >= mtd->size)
+ return -ENOSPC;
+
+ if (pnum >= peb_start + max_pebs)
+ return -ENOSPC;
+
+ if (mtd_peb_is_bad(mtd, pnum)) {
+ pnum++;
+ continue;
+ }
+
+ if (left <= mtd->erasesize)
+ break;
+
+ left -= mtd->erasesize;
+ pnum++;
+ }
+
+ pnum = peb_start;
+ left = len;
+
+ /* Second pass: actually write file */
+ while (left) {
+ size_t now = min_t(size_t, mtd->erasesize, left);
+
+ if (mtd_peb_is_bad(mtd, pnum)) {
+ pnum++;
+ continue;
+ }
+
+ ret = mtd_peb_erase(mtd, pnum);
+ if (ret)
+ goto out;
+
+ if (now < mtd->erasesize) {
+ void *wrbuf = xzalloc(mtd->erasesize);
+
+ memcpy(wrbuf, buf, now);
+
+ ret = mtd_peb_write(mtd, wrbuf, pnum, 0, mtd->erasesize);
+
+ free(wrbuf);
+ } else {
+ ret = mtd_peb_write(mtd, buf, pnum, 0, mtd->erasesize);
+ }
+
+ if (ret)
+ goto out;
+
+ left -= now;
+ pnum++;
+ buf += now;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
/**
* mtd_peb_erase - erase a physical eraseblock.
* @mtd: mtd device
diff --git a/include/mtd/mtd-peb.h b/include/mtd/mtd-peb.h
index 23f89d89a8..311f25c3df 100644
--- a/include/mtd/mtd-peb.h
+++ b/include/mtd/mtd-peb.h
@@ -21,5 +21,7 @@ int mtd_num_pebs(struct mtd_info *mtd);
int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
int len, int num_bitflips, int random,
int info);
+int mtd_peb_write_file(struct mtd_info *mtd, int peb_start, int max_pebs,
+ const void *buf, size_t len);
#endif /* __LINUX_MTD_MTDPEB_H */
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 6/6] ARM: OMAP: bbu: Add an all-in-one NAND update handler
2019-08-28 10:23 [PATCH 0/6] OMAP updates Sascha Hauer
` (4 preceding siblings ...)
2019-08-28 10:24 ` [PATCH 5/6] mtd: peb: Add function to write file Sascha Hauer
@ 2019-08-28 10:24 ` Sascha Hauer
5 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2019-08-28 10:24 UTC (permalink / raw)
To: Barebox List
This adds a NAND update handler which automatically detects on the
filetype which stage shall be updated. It takes a single partition
for both the xload images and the barebox images. It uses a fixed
layout on this partition, so there's no need to configure anything
on the board side.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/mach-omap/am33xx_bbu_nand.c | 146 ++++++++++++++++++++++++++
arch/arm/mach-omap/include/mach/bbu.h | 6 ++
2 files changed, 152 insertions(+)
diff --git a/arch/arm/mach-omap/am33xx_bbu_nand.c b/arch/arm/mach-omap/am33xx_bbu_nand.c
index 4c1a28d37e..8c487c8ebb 100644
--- a/arch/arm/mach-omap/am33xx_bbu_nand.c
+++ b/arch/arm/mach-omap/am33xx_bbu_nand.c
@@ -22,6 +22,8 @@
#include <fcntl.h>
#include <libfile.h>
#include <filetype.h>
+#include <linux/mtd/mtd.h>
+#include <mtd/mtd-peb.h>
#include <mach/bbu.h>
struct nand_bbu_handler {
@@ -134,3 +136,147 @@ int am33xx_bbu_nand_slots_register_handler(const char *name, char **devicefile,
return ret;
}
+
+#define XLOAD_BLOCKS 4
+
+static int nand_update_handler_complete(struct bbu_handler *handler,
+ struct bbu_data *data)
+{
+ const void *image = data->image;
+ size_t size = data->len;
+ enum filetype filetype;
+ struct cdev *cdev;
+ struct mtd_info *mtd;
+ int ret, i;
+ int npebs;
+
+ filetype = file_detect_type(image, size);
+
+ cdev = cdev_by_name(handler->devicefile);
+ if (!cdev) {
+ pr_err("%s: No NAND device found\n", __func__);
+ return -ENODEV;
+ }
+
+ mtd = cdev->mtd;
+ if (!mtd) {
+ pr_err("%s: %s is not a mtd device\n", __func__,
+ handler->devicefile);
+ return -EINVAL;
+ }
+
+ npebs = mtd_div_by_eb(mtd->size, mtd);
+
+ /*
+ * Sanity check: We need at minimum 6 eraseblocks: 4 for the four xload
+ * binaries and 2 for the barebox images.
+ */
+ if (npebs < XLOAD_BLOCKS + 2)
+ return -EINVAL;
+
+ if (filetype == filetype_arm_barebox) {
+ int npebs_bb = (npebs - XLOAD_BLOCKS) / 2;
+
+ pr_info("Barebox image detected, updating 2nd stage\n");
+
+ /* last chance before erasing the flash */
+ ret = bbu_confirm(data);
+ if (ret)
+ goto out;
+
+ ret = mtd_peb_write_file(mtd, XLOAD_BLOCKS, npebs_bb, data->image,
+ data->len);
+ if (ret)
+ goto out;
+
+ ret = mtd_peb_write_file(mtd, XLOAD_BLOCKS + npebs_bb, npebs_bb,
+ data->image, data->len);
+ if (ret)
+ goto out;
+
+ } else if (filetype == filetype_ch_image) {
+ int written = 0;
+ void *buf;
+
+ pr_info("xload image detected, updating 1st stage\n");
+
+ if (data->len > mtd->erasesize) {
+ pr_err("Image is bigger than eraseblock, this is not supported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* last chance before erasing the flash */
+ ret = bbu_confirm(data);
+ if (ret)
+ goto out;
+
+ buf = xzalloc(mtd->erasesize);
+ memcpy(buf, data->image, data->len);
+
+ for (i = 0; i < 4; i++) {
+ if (mtd_peb_is_bad(mtd, i)) {
+ pr_info("PEB%d is bad, skipping\n", i);
+ continue;
+ }
+
+ ret = mtd_peb_erase(mtd, i);
+ if (ret)
+ continue;
+
+ ret = mtd_peb_write(mtd, buf, i, 0, mtd->erasesize);
+ if (ret) {
+ pr_err("Failed to write MLO to PEB%d: %s\n", i,
+ strerror(-ret));
+ continue;
+ }
+ written++;
+ }
+
+ free(buf);
+
+ if (written)
+ ret = 0;
+ else
+ ret = -EIO;
+ } else {
+ pr_err("%s of type %s is not a valid update file image\n",
+ data->imagefile, file_type_to_string(filetype));
+ return -EINVAL;
+ }
+out:
+ return ret;
+}
+
+/**
+ * am33xx_bbu_nand_register_handler - register a NAND update handler
+ * @device: The nand cdev name (usually "nand0.barebox")
+ *
+ * This registers an update handler suitable for updating barebox to NAND. This
+ * update handler takes a single NAND partition for both the xload images and the
+ * barebox images. The first four blocks are used for the 4 xload copies, the
+ * remaining space is divided into two equally sized parts for two barebox images.
+ * The update handler automatically detects based on the filetype if the xload
+ * or the 2nd stage barebox shall be updated.
+ *
+ * FIXME: Currently for actually loading a barebox image from an xload image
+ * flashed with this layout a suitable layout has to be registered by the xload
+ * image using omap_set_barebox_part(). In the next step this should be the
+ * default.
+ */
+int am33xx_bbu_nand_register_handler(const char *device)
+{
+ struct bbu_handler *handler;
+ int ret;
+
+ handler = xzalloc(sizeof(*handler));
+ handler->devicefile = device;
+ handler->handler = nand_update_handler_complete;
+ handler->name = "nand";
+
+ ret = bbu_register_handler(handler);
+ if (ret)
+ free(handler);
+
+ return ret;
+}
diff --git a/arch/arm/mach-omap/include/mach/bbu.h b/arch/arm/mach-omap/include/mach/bbu.h
index c8b0a55acb..94d3f96bb4 100644
--- a/arch/arm/mach-omap/include/mach/bbu.h
+++ b/arch/arm/mach-omap/include/mach/bbu.h
@@ -23,6 +23,7 @@ int am33xx_bbu_nand_xloadslots_register_handler(const char *name,
int num_devicefiles);
int am33xx_bbu_nand_slots_register_handler(const char *name, char **devicefile,
int num_devicefiles);
+int am33xx_bbu_nand_register_handler(const char *device);
#else
static inline int am33xx_bbu_nand_xloadslots_register_handler(const char *name,
char **devicefile,
@@ -37,6 +38,11 @@ static inline int am33xx_bbu_nand_slots_register_handler(const char *name,
{
return 0;
}
+
+static inline int am33xx_bbu_nand_register_handler(const char *device)
+{
+ return 0;
+}
#endif
#ifdef CONFIG_BAREBOX_UPDATE_AM33XX_EMMC
--
2.23.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread