diff options
-rw-r--r-- | arch/arm/mach-omap/am33xx_bbu_nand.c | 146 | ||||
-rw-r--r-- | arch/arm/mach-omap/am35xx_emif4.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap/include/mach/bbu.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-omap/include/mach/gpmc_nand.h | 1 | ||||
-rw-r--r-- | drivers/bus/omap-gpmc.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_omap_gpmc.c | 333 | ||||
-rw-r--r-- | drivers/mtd/nand/omap_elm.c | 413 | ||||
-rw-r--r-- | drivers/mtd/peb.c | 84 | ||||
-rw-r--r-- | drivers/net/cpsw.c | 178 | ||||
-rw-r--r-- | include/linux/mtd/mtd-abi.h | 2 | ||||
-rw-r--r-- | include/mtd/mtd-peb.h | 2 | ||||
-rw-r--r-- | include/platform_data/elm.h | 53 |
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 */ |