From 5bf299bc4f3a7a84a2691976e00b45505aa732f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Tue, 2 Apr 2013 06:05:54 +0000 Subject: asm/omap_gpmc.h: consolidate common defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arch/arm/include/asm/arch-am33xx/omap_gpmc.h and arch/arm/include/asm/arch-omap3/omap_gpmc.h are almost the same, consolidate the common parts into a new header. Introduce a new asm/omap_gpmc.h which defines the command part and pulls in the architecture specific one. Signed-off-by: Andreas Bießmann Cc: Tom Rini Reviewed-by: Tom Rini --- drivers/mtd/nand/omap_gpmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index bbf5443ec8..c7d4999903 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.1 From da634ae3567cc2df435f8617dbc95db2d079bf11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Thu, 4 Apr 2013 23:52:50 +0000 Subject: omap_gpmc: change nandecc command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend. In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also. Signed-off-by: Andreas Bießmann Cc: Tom Rini Cc: Thomas Weber --- drivers/mtd/nand/omap_gpmc.c | 57 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index c7d4999903..3468c7805e 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -604,13 +604,14 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, #ifndef CONFIG_SPL_BUILD /* - * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. - * The default is to come up on s/w ecc - * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) { struct nand_chip *nand; struct mtd_info *mtd; @@ -628,6 +629,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->options |= NAND_OWN_BUFFERS; /* Reset ecc interface */ + nand->ecc.mode = NAND_ECC_NONE; nand->ecc.read_page = NULL; nand->ecc.write_page = NULL; nand->ecc.read_oob = NULL; @@ -637,28 +639,31 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL; /* Setup the ecc configurations again */ - if (hardware == 1) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.calculate = omap_calculate_ecc; - omap_hwecc_init(nand); - printf("HW ECC selected\n"); + if (hardware) { + if (eccstrength == 1) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + printf("1-bit hamming HW ECC selected\n"); + } #ifdef CONFIG_AM33XX - } else if (hardware == 2) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_bch8_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 14; - nand->ecc.read_page = omap_read_page_bch; - nand->ecc.hwctl = omap_enable_ecc_bch; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc_bch; - omap_hwecc_init_bch(nand, NAND_ECC_READ); - printf("HW BCH8 selected\n"); + else if (eccstrength == 8) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 14; + nand->ecc.read_page = omap_read_page_bch; + nand->ecc.hwctl = omap_enable_ecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_hwecc_init_bch(nand, NAND_ECC_READ); + printf("8-bit BCH HW ECC selected\n"); + } #endif } else { nand->ecc.mode = NAND_ECC_SOFT; -- cgit v1.2.1 From 4a0930069b596ae27267a0e7cd44199e2270afa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Fri, 5 Apr 2013 04:55:21 +0000 Subject: omap_gpmc: add support for hw assisted BCH8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel states: ---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8--- And we do so in u-boot. This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB. Signed-off-by: Andreas Bießmann Cc: Tom Rini Cc: Ilya Yanok Cc: Scott Wood Cc: Mansoor Ahamed Cc: Thomas Weber --- drivers/mtd/nand/omap_gpmc.c | 367 ++++++++++++++++++++++++++++++++----------- 1 file changed, 276 insertions(+), 91 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 3468c7805e..bc1bcad3ba 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_AM33XX @@ -37,6 +38,8 @@ static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob = + GPMC_NAND_HW_BCH8_ECC_LAYOUT; /* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -239,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } /* - * BCH8 support (needs ELM and thus AM33xx-only) + * Generic BCH interface */ -#ifdef CONFIG_AM33XX struct nand_bch_priv { uint8_t mode; uint8_t type; uint8_t nibbles; + struct bch_control *control; }; /* bch types */ @@ -253,20 +256,145 @@ struct nand_bch_priv { #define ECC_BCH8 1 #define ECC_BCH16 2 +/* GPMC ecc engine settings */ +#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ +#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ + /* BCH nibbles for diff bch levels */ #define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1) #define ECC_BCH4_NIBBLES 13 #define ECC_BCH8_NIBBLES 26 #define ECC_BCH16_NIBBLES 52 -static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; - -static struct nand_bch_priv bch_priv = { +/* + * This can be a single instance cause all current users have only one NAND + * with nearly the same setup (BCH8, some with ELM and others with sw BCH + * library). + * When some users with other BCH strength will exists this have to change! + */ +static __maybe_unused struct nand_bch_priv bch_priv = { .mode = NAND_ECC_HW_BCH, .type = ECC_BCH8, - .nibbles = ECC_BCH8_NIBBLES + .nibbles = ECC_BCH8_NIBBLES, + .control = NULL }; +/* + * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val; + uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; +#ifdef CONFIG_AM33XX + uint32_t unused_length = 0; +#endif + uint32_t wr_mode = BCH_WRAPMODE_6; + struct nand_bch_priv *bch = chip->priv; + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + +#ifdef CONFIG_AM33XX + wr_mode = BCH_WRAPMODE_1; + + switch (bch->nibbles) { + case ECC_BCH4_NIBBLES: + unused_length = 3; + break; + case ECC_BCH8_NIBBLES: + unused_length = 2; + break; + case ECC_BCH16_NIBBLES: + unused_length = 0; + break; + } + + /* + * This is ecc_size_config for ELM mode. + * Here we are using different settings for read and write access and + * also depending on BCH strength. + */ + switch (mode) { + case NAND_ECC_WRITE: + /* write access only setup eccsize1 config */ + val = ((unused_length + bch->nibbles) << 22); + break; + + case NAND_ECC_READ: + default: + /* + * by default eccsize0 selected for ecc1resultsize + * eccsize0 config. + */ + val = (bch->nibbles << 12); + /* eccsize1 config */ + val |= (unused_length << 22); + break; + } +#else + /* + * This ecc_size_config setting is for BCH sw library. + * + * Note: we only support BCH8 currently with BCH sw library! + * Should be really easy to adobt to BCH4, however some omap3 have + * flaws with BCH4. + * + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + val = (32 << 22) | (0 << 12); +#endif + /* ecc size configuration */ + writel(val, &gpmc_cfg->ecc_size_config); + + /* + * Configure the ecc engine in gpmc + * We assume 512 Byte sector pages for access to NAND. + */ + val = (1 << 16); /* enable BCH mode */ + val |= (bch->type << 12); /* setup BCH type */ + val |= (wr_mode << 8); /* setup wrapping mode */ + val |= (dev_width << 7); /* setup device width (16 or 8 bit) */ + val |= (cs << 1); /* setup chip select to work on */ + debug("set ECC_CONFIG=0x%08x\n", val); + writel(val, &gpmc_cfg->ecc_config); +} + +/* + * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_hwecc_init_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + */ +static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config); +} + +/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX /* * omap_read_bch8_result - Read BCH result for BCH8 level * @@ -305,18 +433,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, } } -/* - * omap_ecc_disable - Disable H/W ECC calculation - * - * @mtd: MTD device structure - * - */ -static void omap_ecc_disable(struct mtd_info *mtd) -{ - writel((readl(&gpmc_cfg->ecc_config) & ~0x1), - &gpmc_cfg->ecc_config); -} - /* * omap_rotate_ecc_bch - Rotate the syndrome bytes * @@ -468,76 +584,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, return 0; } -/* - * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in - * GPMC controller - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) -{ - uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; - uint32_t unused_length = 0; - struct nand_bch_priv *bch = chip->priv; - - switch (bch->nibbles) { - case ECC_BCH4_NIBBLES: - unused_length = 3; - break; - case ECC_BCH8_NIBBLES: - unused_length = 2; - break; - case ECC_BCH16_NIBBLES: - unused_length = 0; - break; - } - - /* Clear the ecc result registers, select ecc reg as 1 */ - writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); - - switch (mode) { - case NAND_ECC_WRITE: - /* eccsize1 config */ - val = ((unused_length + bch->nibbles) << 22); - break; - - case NAND_ECC_READ: - default: - /* by default eccsize0 selected for ecc1resultsize */ - /* eccsize0 config */ - val = (bch->nibbles << 12); - /* eccsize1 config */ - val |= (unused_length << 22); - break; - } - /* ecc size configuration */ - writel(val, &gpmc_cfg->ecc_size_config); - /* by default 512bytes sector page is selected */ - /* set bch mode */ - val = (1 << 16); - /* bch4 / bch8 / bch16 */ - val |= (bch->type << 12); - /* set wrap mode to 1 */ - val |= (1 << 8); - val |= (dev_width << 7); - val |= (cs << 1); - writel(val, &gpmc_cfg->ecc_config); -} - -/* - * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality - * @mtd: MTD device structure - * @mode: Read/Write mode - * - */ -static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) -{ - struct nand_chip *chip = mtd->priv; - - omap_hwecc_init_bch(chip, mode); - /* enable ecc */ - writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); -} /** * omap_read_page_bch - hardware ecc based page read function @@ -602,6 +648,127 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } #endif /* CONFIG_AM33XX */ +/* + * OMAP3 BCH8 support (with BCH library) + */ +#ifdef CONFIG_NAND_OMAP_BCH8 +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed (unused here) + * @ecc: The ECC output buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc) +{ + int ret = 0; + size_t i; + unsigned long nsectors, val1, val2, val3, val4; + + nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + /* Read hw-computed remainder */ + val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]); + val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]); + val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]); + val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + omap_ecc_disable(mtd); + + return ret; +} + +/** + * omap_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = chip_priv->control; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = NULL; + + if (chip_priv) + bch = chip_priv->control; + + if (bch) { + free_bch(bch); + chip_priv->control = NULL; + } +} +#endif /* CONFIG_NAND_OMAP_BCH8 */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation between different engines @@ -651,13 +818,17 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) omap_hwecc_init(nand); printf("1-bit hamming HW ECC selected\n"); } -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) else if (eccstrength == 8) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = 512; +#ifdef CONFIG_AM33XX nand->ecc.bytes = 14; nand->ecc.read_page = omap_read_page_bch; +#else + nand->ecc.bytes = 13; +#endif nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; @@ -737,16 +908,28 @@ int board_nand_init(struct nand_chip *nand) nand->chip_delay = 100; +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) #ifdef CONFIG_AM33XX + /* AM33xx uses the ELM */ /* required in case of BCH */ elm_init(); - +#else + /* + * Whereas other OMAP based SoC do not have the ELM, they use the BCH + * SW library. + */ + bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */); + if (!bch_priv.control) { + puts("Could not init_bch()\n"); + return -ENODEV; + } +#endif /* BCH info that will be correct for SPL or overridden otherwise. */ nand->priv = &bch_priv; #endif /* Default ECC mode */ -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; @@ -754,7 +937,9 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; +#ifdef CONFIG_AM33XX nand->ecc.read_page = omap_read_page_bch; +#endif omap_hwecc_init_bch(nand, NAND_ECC_READ); #else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) -- cgit v1.2.1 From 66bd1846ef08412c5c850b8a672d6fdc0eab539a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 11 Apr 2013 09:35:34 +0000 Subject: mtd: nand: Introduce CONFIG_SYS_NAND_BUSWIDTH_16BIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce CONFIG_SYS_NAND_BUSWIDTH_16BIT option so that other NAND controller drivers could use it when a 16-bit NAND is deployed. drivers/mtd/nand/ndfc has CONFIG_SYS_NDFC_16BIT, so just rename it, so that other NAND drivers could reuse the same symbol. Signed-off-by: Fabio Estevam Acked-by: Scott Wood Reviewed-by: Benoît Thébaudeau --- drivers/mtd/nand/ndfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 6ebbb5ebee..213d2c945a 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -156,7 +156,7 @@ static uint8_t ndfc_read_byte(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; -#ifdef CONFIG_SYS_NDFC_16BIT +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT return (uint8_t) readw(chip->IO_ADDR_R); #else return readb(chip->IO_ADDR_R); @@ -218,7 +218,7 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.bytes = 3; nand->select_chip = ndfc_select_chip; -#ifdef CONFIG_SYS_NDFC_16BIT +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT nand->options |= NAND_BUSWIDTH_16; #endif -- cgit v1.2.1 From a430e91643384a944abf32e1079f79f66ef87d97 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 11 Apr 2013 09:35:35 +0000 Subject: mtd: nand: mxc_nand: Fix is_16bit_nand() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently is_16bit_nand() is a per SoC function and it decides the bus nand width by reading some boot related registers. This method works when NAND is the boot medium, but does not work if another boot medium is used. For example: booting from a SD card and then using NAND to store the environment variables, would lead to the following error: NAND bus width 16 instead 8 bit No NAND device found!!! 0 MiB Use CONFIG_SYS_NAND_BUSWIDTH_16BIT symbol to decide the bus width. If it is defined in the board file, then consider 16-bit NAND bus-width, otherwise assume 8-bit NAND is used. This also aligns with Documentation/devicetree/bindings/mtd/nand.txt, which states: nand-bus-width : 8 or 16 bus width if not present 8 Signed-off-by: Fabio Estevam Acked-by: Scott Wood Reviewed-by: Benoît Thébaudeau --- drivers/mtd/nand/mxc_nand.c | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index d0ded483e2..bb475f2f68 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -98,45 +98,14 @@ static struct nand_ecclayout nand_hw_eccoob2k = { #endif #endif -#ifdef CONFIG_MX27 static int is_16bit_nand(void) { - struct system_control_regs *sc_regs = - (struct system_control_regs *)IMX_SYSTEM_CTL_BASE; - - if (readl(&sc_regs->fmcr) & NF_16BIT_SEL) - return 1; - else - return 0; -} -#elif defined(CONFIG_MX31) -static int is_16bit_nand(void) -{ - struct clock_control_regs *sc_regs = - (struct clock_control_regs *)CCM_BASE; - - if (readl(&sc_regs->rcsr) & CCM_RCSR_NF16B) - return 1; - else - return 0; -} -#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) -static int is_16bit_nand(void) -{ - struct ccm_regs *ccm = (struct ccm_regs *)IMX_CCM_BASE; - - if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL) - return 1; - else - return 0; -} +#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) + return 1; #else -#warning "8/16 bit NAND autodetection not supported" -static int is_16bit_nand(void) -{ return 0; -} #endif +} static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) { -- cgit v1.2.1 From 2dc0aa0227f964562ad4842a26f488df0eb811da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:35:36 +0000 Subject: nand: mxc: Prepare to add support for i.MX5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some abstraction to NFC definitions so that some parts of the current code can also be used for future i.MX5 code. Clean up a few things by the way. Signed-off-by: Benoît Thébaudeau Acked-by: Scott Wood Tested-by: Fabio Estevam --- drivers/mtd/nand/mxc_nand.c | 92 ++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 46 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index bb475f2f68..6ae95d6bee 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -119,7 +119,7 @@ static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size /* * This function polls the NANDFC to wait for the basic operation to - * complete by checking the INT bit of config2 register. + * complete by checking the INT bit. */ static void wait_op_done(struct mxc_nand_host *host, int max_retries, uint16_t param) @@ -127,10 +127,10 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, uint32_t tmp; while (max_retries-- > 0) { - if (readw(&host->regs->config2) & NFC_INT) { - tmp = readw(&host->regs->config2); - tmp &= ~NFC_INT; - writew(tmp, &host->regs->config2); + tmp = readnfc(&host->regs->config2); + if (tmp & NFC_V1_V2_CONFIG2_INT) { + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &host->regs->config2); break; } udelay(1); @@ -149,8 +149,8 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) { MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd); - writew(cmd, &host->regs->flash_cmd); - writew(NFC_CMD, &host->regs->config2); + writenfc(cmd, &host->regs->flash_cmd); + writenfc(NFC_CMD, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, cmd); @@ -165,8 +165,8 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr) { MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr); - writew(addr, &host->regs->flash_addr); - writew(NFC_ADDR, &host->regs->config2); + writenfc(addr, &host->regs->flash_addr); + writenfc(NFC_ADDR, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, addr); @@ -198,19 +198,19 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, } } - writew(buf_id, &host->regs->buf_addr); + writenfc(buf_id, &host->regs->buf_addr); /* Configure spare or page+spare access */ if (!host->pagesize_2k) { - uint16_t config1 = readw(&host->regs->config1); + uint16_t config1 = readnfc(&host->regs->config1); if (spare_only) - config1 |= NFC_SP_EN; + config1 |= NFC_CONFIG1_SP_EN; else - config1 &= ~NFC_SP_EN; - writew(config1, &host->regs->config1); + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); } - writew(NFC_INPUT, &host->regs->config2); + writenfc(NFC_INPUT, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, spare_only); @@ -225,19 +225,19 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, { MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); - writew(buf_id, &host->regs->buf_addr); + writenfc(buf_id, &host->regs->buf_addr); /* Configure spare or page+spare access */ if (!host->pagesize_2k) { - uint32_t config1 = readw(&host->regs->config1); + uint32_t config1 = readnfc(&host->regs->config1); if (spare_only) - config1 |= NFC_SP_EN; + config1 |= NFC_CONFIG1_SP_EN; else - config1 &= ~NFC_SP_EN; - writew(config1, &host->regs->config1); + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); } - writew(NFC_OUTPUT, &host->regs->config2); + writenfc(NFC_OUTPUT, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, spare_only); @@ -265,14 +265,14 @@ static void send_read_id(struct mxc_nand_host *host) uint16_t tmp; /* NANDFC buffer 0 is used for device ID output */ - writew(0x0, &host->regs->buf_addr); + writenfc(0x0, &host->regs->buf_addr); /* Read ID into main buffer */ - tmp = readw(&host->regs->config1); - tmp &= ~NFC_SP_EN; - writew(tmp, &host->regs->config1); + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); - writew(NFC_ID, &host->regs->config2); + writenfc(NFC_ID, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, 0); @@ -292,14 +292,14 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) /* store the main area1 first word, later do recovery */ store = readl(main_buf); /* NANDFC buffer 1 is used for device status */ - writew(1, &host->regs->buf_addr); + writenfc(1, &host->regs->buf_addr); /* Read status into main buffer */ - tmp = readw(&host->regs->config1); - tmp &= ~NFC_SP_EN; - writew(tmp, &host->regs->config1); + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); - writew(NFC_STATUS, &host->regs->config2); + writenfc(NFC_STATUS, &host->regs->operation); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, 0); @@ -328,13 +328,13 @@ static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint16_t tmp = readw(&host->regs->config1); + uint16_t tmp = readnfc(&host->regs->config1); if (on) - tmp |= NFC_ECC_EN; + tmp |= NFC_V1_V2_CONFIG1_ECC_EN; else - tmp &= ~NFC_ECC_EN; - writew(tmp, &host->regs->config1); + tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; + writenfc(tmp, &host->regs->config1); } #ifdef CONFIG_MXC_NAND_HWECC @@ -667,7 +667,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(&host->regs->ecc_status_result); + uint16_t ecc_status = readnfc(&host->regs->ecc_status_result); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { MTDDEBUG(MTD_DEBUG_LEVEL0, @@ -1210,24 +1210,24 @@ int board_nand_init(struct nand_chip *this) #endif #ifdef MXC_NFC_V2_1 - tmp = readw(&host->regs->config1); - tmp |= NFC_ONE_CYCLE; - tmp |= NFC_4_8N_ECC; - writew(tmp, &host->regs->config1); + tmp = readnfc(&host->regs->config1); + tmp |= NFC_V2_CONFIG1_ONE_CYCLE; + tmp |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(tmp, &host->regs->config1); if (host->pagesize_2k) - writew(64/2, &host->regs->spare_area_size); + writenfc(64/2, &host->regs->spare_area_size); else - writew(16/2, &host->regs->spare_area_size); + writenfc(16/2, &host->regs->spare_area_size); #endif /* * preset operation * Unlock the internal RAM Buffer */ - writew(0x2, &host->regs->config); + writenfc(0x2, &host->regs->config); /* Blocks to be unlocked */ - writew(0x0, &host->regs->unlockstart_blkaddr); + writenfc(0x0, &host->regs->unlockstart_blkaddr); /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the * unlockend_blkaddr, but the magic 0x4000 does not always work * when writing more than some 32 megabytes (on 2k page nands) @@ -1239,10 +1239,10 @@ int board_nand_init(struct nand_chip *this) * This might be NAND chip specific and the i.MX31 datasheet is * extremely vague about the semantics of this register. */ - writew(0xFFFF, &host->regs->unlockend_blkaddr); + writenfc(0xFFFF, &host->regs->unlockend_blkaddr); /* Unlock Block Command for given address range */ - writew(0x4, &host->regs->wrprot); + writenfc(0x4, &host->regs->wrprot); return 0; } -- cgit v1.2.1 From 35537bc77332feac63f35d538d363e7e5b1a03f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:35:37 +0000 Subject: nand: mxc: Add support for i.MX5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Benoît Thébaudeau Acked-by: Scott Wood Tested-by: Fabio Estevam --- drivers/mtd/nand/mxc_nand.c | 119 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 11 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 6ae95d6bee..db72cdc7b9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -22,7 +22,8 @@ #include #include #include -#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) #include #endif #include @@ -36,6 +37,9 @@ struct mxc_nand_host { struct nand_chip *nand; struct fsl_nfc_regs __iomem *regs; +#ifdef MXC_NFC_V3_2 + struct fsl_nfc_ip_regs __iomem *ip_regs; +#endif int spare_only; int status_request; int pagesize_2k; @@ -77,7 +81,7 @@ static struct nand_ecclayout nand_hw_eccoob2k = { .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, }; #endif -#elif defined(MXC_NFC_V2_1) +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) #ifndef CONFIG_SYS_NAND_LARGEPAGE static struct nand_ecclayout nand_hw_eccoob = { .eccbytes = 9, @@ -127,10 +131,17 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, uint32_t tmp; while (max_retries-- > 0) { +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) tmp = readnfc(&host->regs->config2); if (tmp & NFC_V1_V2_CONFIG2_INT) { tmp &= ~NFC_V1_V2_CONFIG2_INT; writenfc(tmp, &host->regs->config2); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->ip_regs->ipc); + if (tmp & NFC_V3_IPC_INT) { + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &host->ip_regs->ipc); +#endif break; } udelay(1); @@ -182,7 +193,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, if (spare_only) MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only); - if (is_mxc_nfc_21()) { + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { int i; /* * The controller copies the 64 bytes of spare data from @@ -198,11 +209,18 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, } } +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif /* Configure spare or page+spare access */ if (!host->pagesize_2k) { - uint16_t config1 = readnfc(&host->regs->config1); + uint32_t config1 = readnfc(&host->regs->config1); if (spare_only) config1 |= NFC_CONFIG1_SP_EN; else @@ -225,7 +243,14 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, { MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif /* Configure spare or page+spare access */ if (!host->pagesize_2k) { @@ -242,7 +267,7 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, spare_only); - if (is_mxc_nfc_21()) { + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { int i; /* @@ -262,10 +287,16 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, /* Request the NANDFC to perform a read of the NAND device ID. */ static void send_read_id(struct mxc_nand_host *host) { - uint16_t tmp; + uint32_t tmp; +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) /* NANDFC buffer 0 is used for device ID output */ writenfc(0x0, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(tmp, &host->regs->config1); +#endif /* Read ID into main buffer */ tmp = readnfc(&host->regs->config1); @@ -284,15 +315,19 @@ static void send_read_id(struct mxc_nand_host *host) */ static uint16_t get_dev_status(struct mxc_nand_host *host) { +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) void __iomem *main_buf = host->regs->main_area[1]; uint32_t store; - uint16_t ret, tmp; +#endif + uint32_t ret, tmp; /* Issue status request to NAND device */ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) /* store the main area1 first word, later do recovery */ store = readl(main_buf); /* NANDFC buffer 1 is used for device status */ writenfc(1, &host->regs->buf_addr); +#endif /* Read status into main buffer */ tmp = readnfc(&host->regs->config1); @@ -304,12 +339,16 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, 0); +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) /* * Status is placed in first word of main buffer * get status, then recovery area 1 data */ ret = readw(main_buf); writel(store, main_buf); +#elif defined(MXC_NFC_V3_2) + ret = readnfc(&host->regs->config1) >> 16; +#endif return ret; } @@ -328,6 +367,7 @@ static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) uint16_t tmp = readnfc(&host->regs->config1); if (on) @@ -335,6 +375,15 @@ static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) else tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; writenfc(tmp, &host->regs->config1); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->ip_regs->config2); + + if (on) + tmp |= NFC_V3_CONFIG2_ECC_EN; + else + tmp &= ~NFC_V3_CONFIG2_ECC_EN; + writenfc(tmp, &host->ip_regs->config2); +#endif } #ifdef CONFIG_MXC_NAND_HWECC @@ -346,7 +395,7 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) */ } -#ifdef MXC_NFC_V2_1 +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) @@ -1136,8 +1185,8 @@ static struct nand_bbt_descr bbt_mirror_descr = { int board_nand_init(struct nand_chip *this) { struct mtd_info *mtd; -#ifdef MXC_NFC_V2_1 - uint16_t tmp; +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + uint32_t tmp; #endif #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT @@ -1165,13 +1214,17 @@ int board_nand_init(struct nand_chip *this) this->verify_buf = mxc_nand_verify_buf; host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; +#ifdef MXC_NFC_V3_2 + host->ip_regs = + (struct fsl_nfc_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; +#endif host->clk_act = 1; #ifdef CONFIG_MXC_NAND_HWECC this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; - if (is_mxc_nfc_21()) { + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { this->ecc.mode = NAND_ECC_HW_SYNDROME; this->ecc.read_page = mxc_nand_read_page_syndrome; this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; @@ -1209,6 +1262,7 @@ int board_nand_init(struct nand_chip *this) this->ecc.layout = &nand_hw_eccoob; #endif +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) #ifdef MXC_NFC_V2_1 tmp = readnfc(&host->regs->config1); tmp |= NFC_V2_CONFIG1_ONE_CYCLE; @@ -1243,6 +1297,49 @@ int board_nand_init(struct nand_chip *this) /* Unlock Block Command for given address range */ writenfc(0x4, &host->regs->wrprot); +#elif defined(MXC_NFC_V3_2) + writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1); + writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc); + + /* Unlock the internal RAM Buffer */ + writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + &host->ip_regs->wrprot); + + /* Blocks to be unlocked */ + for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++) + writenfc(0x0 | 0xFFFF << 16, + &host->ip_regs->wrprot_unlock_blkaddr[tmp]); + + writenfc(0, &host->ip_regs->ipc); + + tmp = readnfc(&host->ip_regs->config2); + tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK | + NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK); + tmp |= NFC_V3_CONFIG2_ONE_CYCLE; + + if (host->pagesize_2k) { + tmp |= NFC_V3_CONFIG2_SPAS(64/2); + tmp |= NFC_V3_CONFIG2_PS_2048; + } else { + tmp |= NFC_V3_CONFIG2_SPAS(16/2); + tmp |= NFC_V3_CONFIG2_PS_512; + } + + writenfc(tmp, &host->ip_regs->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(this->options & NAND_BUSWIDTH_16)) + tmp |= NFC_V3_CONFIG3_FW8; + + writenfc(tmp, &host->ip_regs->config3); + + writenfc(0, &host->ip_regs->delay_line); +#endif return 0; } -- cgit v1.2.1 From 78ee7b1729f69215a07c44b9895f69bd9a4b58a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:35:40 +0000 Subject: nand: mxc: Fix debug trace in mxc_nand_read_oob_syndrome() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page number indicated in the debug trace of mxc_nand_read_oob_syndrome() did not match the page being worked on. By the way, replace the GCC-specific __FUNCTION__ with __func__. Signed-off-by: Benoît Thébaudeau Acked-by: Scott Wood --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index db72cdc7b9..62d6965063 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -409,7 +409,7 @@ static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Reading OOB area of page %u to oob %p\n", - __FUNCTION__, host->page_addr, buf); + __func__, page, buf); chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); for (i = 0; i < chip->ecc.steps; i++) { -- cgit v1.2.1 From 3ec9d6eb09ca799f2632a58012ef9603982a642d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:35:41 +0000 Subject: nand: mxc: Use appropriate page number in syndrome functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The syndrome functions should use the page number passed as argument instead of the page number saved upon NAND_CMD_READ0. This does not make any difference if the NAND_NO_AUTOINCR option is set, but otherwise this fixes accesses to the wrong pages. Signed-off-by: Benoît Thébaudeau Acked-by: Scott Wood --- drivers/mtd/nand/mxc_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 62d6965063..29ceab37c9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -463,7 +463,7 @@ static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, int n; _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, host->page_addr); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { host->col_addr = n * eccsize; @@ -507,7 +507,7 @@ static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; MTDDEBUG(MTD_DEBUG_LEVEL1, "Reading page %u to buf %p oob %p\n", - host->page_addr, buf, oob); + page, buf, oob); /* first read the data area and the available portion of OOB */ for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { @@ -545,7 +545,7 @@ static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, /* Then switch ECC off and read the OOB area to get the ECC code */ _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, host->page_addr); + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); eccsteps = chip->ecc.steps; oob = chip->oob_poi + chip->ecc.prepad; for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { -- cgit v1.2.1 From da962b71758d52b38a4ed89380d296ed25920d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:35:51 +0000 Subject: nand: mxc: Switch NAND SPL to generic SPL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixes support for mx31pdk and tx25, which had been broken by commit e05e5de7fae5bec79617e113916dac6631251156. Signed-off-by: Benoît Thébaudeau Acked-by: Scott Wood Tested-by: Fabio Estevam --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/mxc_nand.c | 10 +- drivers/mtd/nand/mxc_nand.h | 225 ++++++++++++++++++++++++ drivers/mtd/nand/mxc_nand_spl.c | 366 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 597 insertions(+), 5 deletions(-) create mode 100644 drivers/mtd/nand/mxc_nand.h create mode 100644 drivers/mtd/nand/mxc_nand_spl.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index c77c0c4f0f..bcb71619b5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -82,6 +82,7 @@ COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o else # minimal SPL drivers COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o endif # drivers endif # nand diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 29ceab37c9..507bbc2544 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -26,7 +26,7 @@ defined(CONFIG_MX51) || defined(CONFIG_MX53) #include #endif -#include +#include "mxc_nand.h" #define DRIVER_NAME "mxc_nand" @@ -36,9 +36,9 @@ struct mxc_nand_host { struct mtd_info mtd; struct nand_chip *nand; - struct fsl_nfc_regs __iomem *regs; + struct mxc_nand_regs __iomem *regs; #ifdef MXC_NFC_V3_2 - struct fsl_nfc_ip_regs __iomem *ip_regs; + struct mxc_nand_ip_regs __iomem *ip_regs; #endif int spare_only; int status_request; @@ -1213,10 +1213,10 @@ int board_nand_init(struct nand_chip *this) this->read_buf = mxc_nand_read_buf; this->verify_buf = mxc_nand_verify_buf; - host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; + host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; #ifdef MXC_NFC_V3_2 host->ip_regs = - (struct fsl_nfc_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; + (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; #endif host->clk_act = 1; diff --git a/drivers/mtd/nand/mxc_nand.h b/drivers/mtd/nand/mxc_nand.h new file mode 100644 index 0000000000..308ff8d8a8 --- /dev/null +++ b/drivers/mtd/nand/mxc_nand.h @@ -0,0 +1,225 @@ +/* + * (c) 2009 Magnus Lilja + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __MXC_NAND_H +#define __MXC_NAND_H + +/* + * Register map and bit definitions for the Freescale NAND Flash Controller + * present in various i.MX devices. + * + * MX31 and MX27 have version 1, which has: + * 4 512-byte main buffers and + * 4 16-byte spare buffers + * to support up to 2K byte pagesize nand. + * Reading or writing a 2K page requires 4 FDI/FDO cycles. + * + * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which + * have: + * 8 512-byte main buffers and + * 8 64-byte spare buffers + * to support up to 4K byte pagesize nand. + * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle. + * Also some of registers are moved and/or changed meaning as seen below. + */ +#if defined(CONFIG_MX27) || defined(CONFIG_MX31) +#define MXC_NFC_V1 +#define is_mxc_nfc_1() 1 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) +#define MXC_NFC_V2_1 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 1 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define MXC_NFC_V3 +#define MXC_NFC_V3_2 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 1 +#else +#error "MXC NFC implementation not supported" +#endif +#define is_mxc_nfc_3() is_mxc_nfc_32() + +#if defined(MXC_NFC_V1) +#define NAND_MXC_NR_BUFS 4 +#define NAND_MXC_SPARE_BUF_SIZE 16 +#define NAND_MXC_REG_OFFSET 0xe00 +#define NAND_MXC_2K_MULTI_CYCLE +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +#define NAND_MXC_NR_BUFS 8 +#define NAND_MXC_SPARE_BUF_SIZE 64 +#define NAND_MXC_REG_OFFSET 0x1e00 +#endif + +struct mxc_nand_regs { + u8 main_area[NAND_MXC_NR_BUFS][0x200]; + u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; + /* + * reserved size is offset of nfc registers + * minus total main and spare sizes + */ + u8 reserved1[NAND_MXC_REG_OFFSET + - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; +#if defined(MXC_NFC_V1) + u16 buf_size; + u16 reserved2; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u16 ecc_status_result; + u16 rsltmain_area; + u16 rsltspare_area; + u16 wrprot; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 nf_wrprst; + u16 config1; + u16 config2; +#elif defined(MXC_NFC_V2_1) + u16 reserved2[2]; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u32 ecc_status_result; + u16 spare_area_size; + u16 wrprot; + u16 reserved3[2]; + u16 nf_wrprst; + u16 config1; + u16 config2; + u16 reserved4; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 unlockstart_blkaddr1; + u16 unlockend_blkaddr1; + u16 unlockstart_blkaddr2; + u16 unlockend_blkaddr2; + u16 unlockstart_blkaddr3; + u16 unlockend_blkaddr3; +#elif defined(MXC_NFC_V3_2) + u32 flash_cmd; + u32 flash_addr[12]; + u32 config1; + u32 ecc_status_result; + u32 status_sum; + u32 launch; +#endif +}; + +#ifdef MXC_NFC_V3_2 +struct mxc_nand_ip_regs { + u32 wrprot; + u32 wrprot_unlock_blkaddr[8]; + u32 config2; + u32 config3; + u32 ipc; + u32 err_addr; + u32 delay_line; +}; +#endif + +/* Set FCMD to 1, rest to 0 for Command operation */ +#define NFC_CMD 0x1 + +/* Set FADD to 1, rest to 0 for Address operation */ +#define NFC_ADDR 0x2 + +/* Set FDI to 1, rest to 0 for Input operation */ +#define NFC_INPUT 0x4 + +/* Set FDO to 001, rest to 0 for Data Output operation */ +#define NFC_OUTPUT 0x8 + +/* Set FDO to 010, rest to 0 for Read ID operation */ +#define NFC_ID 0x10 + +/* Set FDO to 100, rest to 0 for Read Status operation */ +#define NFC_STATUS 0x20 + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define NFC_CONFIG1_SP_EN (1 << 2) +#define NFC_CONFIG1_RST (1 << 6) +#define NFC_CONFIG1_CE (1 << 7) +#elif defined(MXC_NFC_V3_2) +#define NFC_CONFIG1_SP_EN (1 << 0) +#define NFC_CONFIG1_CE (1 << 1) +#define NFC_CONFIG1_RST (1 << 2) +#endif +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) +#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) +#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7) +#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9) +#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9) +#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) +#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) + +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define operation config2 +#define readnfc readw +#define writenfc writew +#elif defined(MXC_NFC_V3_2) +#define operation launch +#define readnfc readl +#define writenfc writel +#endif + +#endif /* __MXC_NAND_H */ diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c new file mode 100644 index 0000000000..09f23c30c4 --- /dev/null +++ b/drivers/mtd/nand/mxc_nand_spl.c @@ -0,0 +1,366 @@ +/* + * (C) Copyright 2009 + * Magnus Lilja + * + * (C) Copyright 2008 + * Maxim Artamonov, + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr at denx.de. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "mxc_nand.h" + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR; +#elif defined(MXC_NFC_V3_2) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI; +static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR; +#endif + +static void nfc_wait_ready(void) +{ + uint32_t tmp; + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc->config2); + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &nfc->config2); +#elif defined(MXC_NFC_V3_2) + while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc_ip->ipc); + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &nfc_ip->ipc); +#endif +} + +static void nfc_nand_init(void) +{ +#if defined(MXC_NFC_V3_2) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int tmp; + + tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK | + NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) | + NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) | + NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN | + NFC_V3_CONFIG2_ONE_CYCLE; + if (CONFIG_SYS_NAND_PAGE_SIZE == 4096) + tmp |= NFC_V3_CONFIG2_PS_4096; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048) + tmp |= NFC_V3_CONFIG2_PS_2048; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 512) + tmp |= NFC_V3_CONFIG2_PS_512; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + tmp |= NFC_V3_CONFIG2_ECC_MODE_8; + else + tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8; + writenfc(tmp, &nfc_ip->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); +#ifndef CONFIG_SYS_NAND_BUSWIDTH_16 + tmp |= NFC_V3_CONFIG3_FW8; +#endif + writenfc(tmp, &nfc_ip->config3); + + writenfc(0, &nfc_ip->delay_line); +#elif defined(MXC_NFC_V2_1) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int config1; + + writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size); + + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN | + NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE | + NFC_V2_CONFIG1_FP_INT; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4; + else + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(config1, &nfc->config1); +#elif defined(MXC_NFC_V1) + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK, + &nfc->config1); +#endif +} + +static void nfc_nand_command(unsigned short command) +{ + writenfc(command, &nfc->flash_cmd); + writenfc(NFC_CMD, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_address(unsigned short address) +{ + writenfc(address, &nfc->flash_addr); + writenfc(NFC_ADDR, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_page_address(unsigned int page_address) +{ + unsigned int page_count; + + nfc_nand_address(0x00); + + /* code only for large page flash */ + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_address(0x00); + + page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE; + + if (page_address <= page_count) { + page_count--; /* transform 0x01000000 to 0x00ffffff */ + do { + nfc_nand_address(page_address & 0xff); + page_address = page_address >> 8; + page_count = page_count >> 8; + } while (page_count); + } + + nfc_nand_address(0x00); +} + +static void nfc_nand_data_output(void) +{ +#ifdef NAND_MXC_2K_MULTI_CYCLE + int i; +#endif + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); +#ifdef NAND_MXC_2K_MULTI_CYCLE + /* + * This NAND controller requires multiple input commands + * for pages larger than 512 bytes. + */ + for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) { + writenfc(i, &nfc->buf_addr); + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); + } +#endif +} + +static int nfc_nand_check_ecc(void) +{ +#if defined(MXC_NFC_V1) + u16 ecc_status = readw(&nfc->ecc_status_result); + return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2; +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + u32 ecc_status = readl(&nfc->ecc_status_result); + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4; + int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512; + + do { + if ((ecc_status & 0xf) > err_limit) + return 1; + ecc_status >>= 4; + } while (--subpages); + + return 0; +#endif +} + +static void nfc_nand_read_page(unsigned int page_address) +{ + /* read in first 0 buffer */ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + nfc_nand_command(NAND_CMD_READ0); + nfc_nand_page_address(page_address); + + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_command(NAND_CMD_READSTART); + + nfc_nand_data_output(); /* fill the main buffer 0 */ +} + +static int nfc_read_page(unsigned int page_address, unsigned char *buf) +{ + int i; + u32 *src; + u32 *dst; + + nfc_nand_read_page(page_address); + + if (nfc_nand_check_ecc()) + return -1; + + src = (u32 *)&nfc->main_area[0][0]; + dst = (u32 *)buf; + + /* main copy loop from NAND-buffer to SDRAM memory */ + for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) { + writel(readl(src), dst); + src++; + dst++; + } + + return 0; +} + +static int is_badblock(int pagenumber) +{ + int page = pagenumber; + u32 badblock; + u32 *src; + + /* Check the first two pages for bad block markers */ + for (page = pagenumber; page < pagenumber + 2; page++) { + nfc_nand_read_page(page); + + src = (u32 *)&nfc->spare_area[0][0]; + + /* + * IMPORTANT NOTE: The nand flash controller uses a non- + * standard layout for large page devices. This can + * affect the position of the bad block marker. + */ + /* Get the bad block marker */ + badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]); + badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4); + badblock &= 0xff; + + /* bad block marker verify */ + if (badblock != 0xff) + return 1; /* potential bad block */ + } + + return 0; +} + +static int nand_load(unsigned int from, unsigned int size, unsigned char *buf) +{ + int i; + unsigned int page; + unsigned int maxpages = CONFIG_SYS_NAND_SIZE / + CONFIG_SYS_NAND_PAGE_SIZE; + + nfc_nand_init(); + + /* Convert to page number */ + page = from / CONFIG_SYS_NAND_PAGE_SIZE; + i = 0; + + while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) { + if (nfc_read_page(page, buf) < 0) + return -1; + + page++; + i++; + buf = buf + CONFIG_SYS_NAND_PAGE_SIZE; + + /* + * Check if we have crossed a block boundary, and if so + * check for bad block. + */ + if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { + /* + * Yes, new block. See if this block is good. If not, + * loop until we find a good block. + */ + while (is_badblock(page)) { + page = page + CONFIG_SYS_NAND_PAGE_COUNT; + /* Check i we've reached the end of flash. */ + if (page >= maxpages) + return -1; + } + } + } + + return 0; +} + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* + * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must + * be aligned to full pages + */ + if (!nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { + /* Copy from NAND successful, start U-boot */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); + } else { + /* Unrecoverable error when copying from NAND */ + hang(); + } +} + +/* + * Called in case of an exception. + */ +void hang(void) +{ + /* Loop forever */ + while (1) ; +} -- cgit v1.2.1 From e53232250b9dcff4915a6024f9951f52eadb4956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Thu, 11 Apr 2013 09:36:00 +0000 Subject: arm: Remove support for unused s3c64xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the removal of the smdk6400 board, the s3c64xx SoC becomes unused, so remove associated code. It will still be possible to restore it later from the Git history if necessary. Signed-off-by: Benoît Thébaudeau --- drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/s3c64xx.c | 295 ------------------------------------- drivers/mtd/onenand/onenand_base.c | 4 - drivers/mtd/onenand/samsung.c | 60 +------- 4 files changed, 4 insertions(+), 356 deletions(-) delete mode 100644 drivers/mtd/nand/s3c64xx.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bcb71619b5..35769c5ea3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -73,7 +73,6 @@ COBJS-$(CONFIG_NAND_MXS) += mxs_nand.o COBJS-$(CONFIG_NAND_NDFC) += ndfc.o COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o -COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o diff --git a/drivers/mtd/nand/s3c64xx.c b/drivers/mtd/nand/s3c64xx.c deleted file mode 100644 index 87f0341066..0000000000 --- a/drivers/mtd/nand/s3c64xx.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * (C) Copyright 2006 DENX Software Engineering - * - * Implementation for U-Boot 1.1.6 by Samsung - * - * (C) Copyright 2008 - * Guennadi Liakhovetki, DENX Software Engineering, - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include - -#include -#include - -#include - -#include -#include - -#define MAX_CHIPS 2 -static int nand_cs[MAX_CHIPS] = {0, 1}; - -#ifdef CONFIG_NAND_SPL -#define printf(arg...) do {} while (0) -#endif - -/* Nand flash definition values by jsgood */ -#ifdef S3C_NAND_DEBUG -/* - * Function to print out oob buffer for debugging - * Written by jsgood - */ -static void print_oob(const char *header, struct mtd_info *mtd) -{ - int i; - struct nand_chip *chip = mtd->priv; - - printf("%s:\t", header); - - for (i = 0; i < 64; i++) - printf("%02x ", chip->oob_poi[i]); - - printf("\n"); -} -#endif /* S3C_NAND_DEBUG */ - -static void s3c_nand_select_chip(struct mtd_info *mtd, int chip) -{ - int ctrl = readl(NFCONT); - - switch (chip) { - case -1: - ctrl |= 6; - break; - case 0: - ctrl &= ~2; - break; - case 1: - ctrl &= ~4; - break; - default: - return; - } - - writel(ctrl, NFCONT); -} - -/* - * Hardware specific access to control-lines function - * Written by jsgood - */ -static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - if (ctrl & NAND_CLE) - this->IO_ADDR_W = (void __iomem *)NFCMMD; - else if (ctrl & NAND_ALE) - this->IO_ADDR_W = (void __iomem *)NFADDR; - else - this->IO_ADDR_W = (void __iomem *)NFDATA; - if (ctrl & NAND_NCE) - s3c_nand_select_chip(mtd, *(int *)this->priv); - else - s3c_nand_select_chip(mtd, -1); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -/* - * Function for checking device ready pin - * Written by jsgood - */ -static int s3c_nand_device_ready(struct mtd_info *mtdinfo) -{ - return !!(readl(NFSTAT) & NFSTAT_RnB); -} - -#ifdef CONFIG_SYS_S3C_NAND_HWECC -/* - * This function is called before encoding ecc codes to ready ecc engine. - * Written by jsgood - */ -static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - u_long nfcont, nfconf; - - /* - * The original driver used 4-bit ECC for "new" MLC chips, i.e., for - * those with non-zero ID[3][3:2], which anyway only holds for ST - * (Numonyx) chips - */ - nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT; - - writel(nfconf, NFCONF); - - /* Initialize & unlock */ - nfcont = readl(NFCONT); - nfcont |= NFCONT_INITECC; - nfcont &= ~NFCONT_MECCLOCK; - - if (mode == NAND_ECC_WRITE) - nfcont |= NFCONT_ECC_ENC; - else if (mode == NAND_ECC_READ) - nfcont &= ~NFCONT_ECC_ENC; - - writel(nfcont, NFCONT); -} - -/* - * This function is called immediately after encoding ecc codes. - * This function returns encoded ecc codes. - * Written by jsgood - */ -static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - u_long nfcont, nfmecc0; - - /* Lock */ - nfcont = readl(NFCONT); - nfcont |= NFCONT_MECCLOCK; - writel(nfcont, NFCONT); - - nfmecc0 = readl(NFMECC0); - - ecc_code[0] = nfmecc0 & 0xff; - ecc_code[1] = (nfmecc0 >> 8) & 0xff; - ecc_code[2] = (nfmecc0 >> 16) & 0xff; - ecc_code[3] = (nfmecc0 >> 24) & 0xff; - - return 0; -} - -/* - * This function determines whether read data is good or not. - * If SLC, must write ecc codes to controller before reading status bit. - * If MLC, status bit is already set, so only reading is needed. - * If status bit is good, return 0. - * If correctable errors occured, do that. - * If uncorrectable errors occured, return -1. - * Written by jsgood - */ -static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - int ret = -1; - u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr; - u_char err_type, repaired; - - /* SLC: Write ecc to compare */ - nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0]; - nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2]; - writel(nfmeccdata0, NFMECCDATA0); - writel(nfmeccdata1, NFMECCDATA1); - - /* Read ecc status */ - nfestat0 = readl(NFESTAT0); - err_type = nfestat0 & 0x3; - - switch (err_type) { - case 0: /* No error */ - ret = 0; - break; - - case 1: - /* - * 1 bit error (Correctable) - * (nfestat0 >> 7) & 0x7ff :error byte number - * (nfestat0 >> 4) & 0x7 :error bit number - */ - err_byte_addr = (nfestat0 >> 7) & 0x7ff; - repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7)); - - printf("S3C NAND: 1 bit error detected at byte %ld. " - "Correcting from 0x%02x to 0x%02x...OK\n", - err_byte_addr, dat[err_byte_addr], repaired); - - dat[err_byte_addr] = repaired; - - ret = 1; - break; - - case 2: /* Multiple error */ - case 3: /* ECC area error */ - printf("S3C NAND: ECC uncorrectable error detected. " - "Not correctable.\n"); - ret = -1; - break; - } - - return ret; -} -#endif /* CONFIG_SYS_S3C_NAND_HWECC */ - -/* - * Board-specific NAND initialization. The following members of the - * argument are board-specific (per include/linux/mtd/nand.h): - * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device - * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device - * - hwcontrol: hardwarespecific function for accesing control-lines - * - dev_ready: hardwarespecific function for accesing device ready/busy line - * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must - * only be provided if a hardware ECC is available - * - eccmode: mode of ecc, see defines - * - chip_delay: chip dependent delay for transfering data from array to - * read regs (tR) - * - options: various chip options. They can partly be set to inform - * nand_scan about special functionality. See the defines for further - * explanation - * Members with a "?" were not set in the merged testing-NAND branch, - * so they are not set here either. - */ -int board_nand_init(struct nand_chip *nand) -{ - static int chip_n; - - if (chip_n >= MAX_CHIPS) - return -ENODEV; - - NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6; - - nand->IO_ADDR_R = (void __iomem *)NFDATA; - nand->IO_ADDR_W = (void __iomem *)NFDATA; - nand->cmd_ctrl = s3c_nand_hwcontrol; - nand->dev_ready = s3c_nand_device_ready; - nand->select_chip = s3c_nand_select_chip; - nand->options = 0; -#ifdef CONFIG_NAND_SPL - nand->read_byte = nand_read_byte; - nand->write_buf = nand_write_buf; - nand->read_buf = nand_read_buf; -#endif - -#ifdef CONFIG_SYS_S3C_NAND_HWECC - nand->ecc.hwctl = s3c_nand_enable_hwecc; - nand->ecc.calculate = s3c_nand_calculate_ecc; - nand->ecc.correct = s3c_nand_correct_data; - - /* - * If you get more than 1 NAND-chip with different page-sizes on the - * board one day, it will get more complicated... - */ - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; - nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif /* ! CONFIG_SYS_S3C_NAND_HWECC */ - - nand->priv = nand_cs + chip_n++; - - return 0; -} diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 1a7b40eaa3..858e322743 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -632,10 +632,6 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) int blockpage, found = 0; unsigned int i; -#ifdef CONFIG_S3C64XX - return 0; -#endif - if (ONENAND_IS_2PLANE(this)) blockpage = onenand_get_2x_blockpage(mtd, addr); else diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 0d94ea5b1f..5eb2b3a424 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -1,5 +1,5 @@ /* - * S3C64XX/S5PC100 OneNAND driver at U-Boot + * S5PC100 OneNAND driver at U-Boot * * Copyright (C) 2008-2009 Samsung Electronics * Kyungmin Park @@ -62,12 +62,7 @@ do { \ #define ONENAND_MAIN_SPARE_ACCESS 0x16 #define ONENAND_PIPELINE_READ 0x4000 -#if defined(CONFIG_S3C64XX) -#define MAP_00 (0x0 << 24) -#define MAP_01 (0x1 << 24) -#define MAP_10 (0x2 << 24) -#define MAP_11 (0x3 << 24) -#elif defined(CONFIG_S5P) +#if defined(CONFIG_S5P) #define MAP_00 (0x0 << 26) #define MAP_01 (0x1 << 26) #define MAP_10 (0x2 << 26) @@ -116,12 +111,7 @@ static void s3c_write_cmd(int value, unsigned int cmd) * return the buffer address on the memory device * It will be combined with CMD_MAP_XX */ -#if defined(CONFIG_S3C64XX) -static unsigned int s3c_mem_addr(int fba, int fpa, int fsa) -{ - return (fba << 12) | (fpa << 6) | (fsa << 4); -} -#elif defined(CONFIG_S5P) +#if defined(CONFIG_S5P) static unsigned int s3c_mem_addr(int fba, int fpa, int fsa) { return (fba << 13) | (fpa << 7) | (fsa << 5); @@ -550,45 +540,6 @@ static void s3c_onenand_unlock_all(struct mtd_info *mtd) s3c_onenand_check_lock_status(mtd); } -#ifdef CONFIG_S3C64XX -static void s3c_set_width_regs(struct onenand_chip *this) -{ - int dev_id, density; - int fba, fpa, fsa; - int dbs_dfs; - - dev_id = DEVICE_ID0_REG; - - density = (dev_id >> ONENAND_DEVICE_DENSITY_SHIFT) & 0xf; - dbs_dfs = !!(dev_id & ONENAND_DEVICE_IS_DDP); - - fba = density + 7; - if (dbs_dfs) - fba--; /* Decrease the fba */ - fpa = 6; - if (density >= ONENAND_DEVICE_DENSITY_512Mb) - fsa = 2; - else - fsa = 1; - - DPRINTK("FBA %lu, FPA %lu, FSA %lu, DDP %lu", - FBA_WIDTH0_REG, FPA_WIDTH0_REG, FSA_WIDTH0_REG, - DDP_DEVICE_REG); - - DPRINTK("mem_cfg0 0x%lx, sync mode %lu, " - "dev_page_size %lu, BURST LEN %lu", - MEM_CFG0_REG, SYNC_MODE_REG, - DEV_PAGE_SIZE_REG, BURST_LEN0_REG); - - DEV_PAGE_SIZE_REG = 0x1; - - FBA_WIDTH0_REG = fba; - FPA_WIDTH0_REG = fpa; - FSA_WIDTH0_REG = fsa; - DBS_DFS_WIDTH0_REG = dbs_dfs; -} -#endif - int s5pc110_chip_probe(struct mtd_info *mtd) { return 0; @@ -620,10 +571,7 @@ void s3c_onenand_init(struct mtd_info *mtd) onenand->mtd = mtd; -#if defined(CONFIG_S3C64XX) - onenand->base = (void *)0x70100000; - onenand->ahb_addr = (void *)0x20000000; -#elif defined(CONFIG_S5P) +#if defined(CONFIG_S5P) onenand->base = (void *)0xE7100000; onenand->ahb_addr = (void *)0xB0000000; #endif -- cgit v1.2.1 From 7070b550bee9341bcb53ceda15f9e0a58df88140 Mon Sep 17 00:00:00 2001 From: Gerlando Falauto Date: Tue, 15 Jan 2013 22:34:28 +0000 Subject: kirkwood_nand: allow usage of NAND_ECC_SOFT_BCH If CONFIG_NAND_ECC_BCH is set use 4-bit error correction code instead of the 1-bit error correction code on the NAND device. Signed-off-by: Gerlando Falauto Signed-off-by: Holger Brunck cc: Valentin Longchamp cc: Prafulla Wadaskar Acked-by: Prafulla Wadaskar Acked-by: Scott Wood --- drivers/mtd/nand/kirkwood_nand.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c index bdab5aa795..0a99a10de1 100644 --- a/drivers/mtd/nand/kirkwood_nand.c +++ b/drivers/mtd/nand/kirkwood_nand.c @@ -74,7 +74,11 @@ void kw_nand_select_chip(struct mtd_info *mtd, int chip) int board_nand_init(struct nand_chip *nand) { nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else nand->ecc.mode = NAND_ECC_SOFT; +#endif nand->cmd_ctrl = kw_nand_hwcontrol; nand->chip_delay = 40; nand->select_chip = kw_nand_select_chip; -- cgit v1.2.1