summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-09-12 07:53:06 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-09-12 07:53:06 +0200
commitd8af9462c9040c2e222d90a6003581abc8fb040e (patch)
treee81bd1d44fd3ee7c8bd4af6904e023d0e28d775c
parentabc71c4cf97470728cc29175d69543cbba4d0d4a (diff)
parent63ea06d03e9f0ef574ff4d68b1bd3b3abff67e8e (diff)
downloadbarebox-d8af9462c9040c2e222d90a6003581abc8fb040e.tar.gz
barebox-d8af9462c9040c2e222d90a6003581abc8fb040e.tar.xz
Merge branch 'for-next/omap'
-rw-r--r--arch/arm/mach-omap/am33xx_bbu_nand.c146
-rw-r--r--arch/arm/mach-omap/am35xx_emif4.c2
-rw-r--r--arch/arm/mach-omap/include/mach/bbu.h6
-rw-r--r--arch/arm/mach-omap/include/mach/gpmc_nand.h1
-rw-r--r--drivers/bus/omap-gpmc.c3
-rw-r--r--drivers/mtd/nand/Kconfig10
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/nand_omap_gpmc.c333
-rw-r--r--drivers/mtd/nand/omap_elm.c413
-rw-r--r--drivers/mtd/peb.c84
-rw-r--r--drivers/net/cpsw.c178
-rw-r--r--include/linux/mtd/mtd-abi.h2
-rw-r--r--include/mtd/mtd-peb.h2
-rw-r--r--include/platform_data/elm.h53
14 files changed, 1142 insertions, 92 deletions
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/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);
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
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/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/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 9c7f5a02a1..c4d0cdfb57 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 = mtd_to_nand(mtd);
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/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/drivers/mtd/peb.c b/drivers/mtd/peb.c
index c82b3e8bba..d10a8a024d 100644
--- a/drivers/mtd/peb.c
+++ b/drivers/mtd/peb.c
@@ -438,6 +438,90 @@ int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
}
/**
+ * 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
* @pnum: physical eraseblock number to erase
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[] = {
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
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 */
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 */