From c3754e9cc23af1bbd15fb10034c0e7c8ee62b110 Mon Sep 17 00:00:00 2001 From: Mansoor Ahamed Date: Tue, 6 Nov 2012 13:06:33 +0000 Subject: omap_gpmc: BCH8 support (ELM based) This patch adds support for BCH8 error correction code to omap_gpmc driver. We use GPMC to generate codes/syndromes but we need ELM to find error locations from given syndrome. Signed-off-by: Mansoor Ahamed [ilya: merge it with omap_gpmc driver, some fixes and cleanup] Signed-off-by: Ilya Yanok --- drivers/mtd/nand/omap_gpmc.c | 403 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 402 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index f1469d1..cee394e 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -29,6 +29,9 @@ #include #include #include +#ifdef CONFIG_AM33XX +#include +#endif static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = @@ -234,6 +237,370 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } } +/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX +struct nand_bch_priv { + uint8_t mode; + uint8_t type; + uint8_t nibbles; +}; + +/* bch types */ +#define ECC_BCH4 0 +#define ECC_BCH8 1 +#define ECC_BCH16 2 + +/* 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 = { + .mode = NAND_ECC_HW_BCH, + .type = ECC_BCH8, + .nibbles = ECC_BCH8_NIBBLES +}; + +/* + * omap_read_bch8_result - Read BCH result for BCH8 level + * + * @mtd: MTD device structure + * @big_endian: When set read register 3 first + * @ecc_code: Read syndrome from BCH result registers + */ +static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, + uint8_t *ecc_code) +{ + uint32_t *ptr; + int8_t i = 0, j; + + if (big_endian) { + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; + ecc_code[i++] = readl(ptr) & 0xFF; + ptr--; + for (j = 0; j < 3; j++) { + ecc_code[i++] = (readl(ptr) >> 24) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 16) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 8) & 0xFF; + ecc_code[i++] = readl(ptr) & 0xFF; + ptr--; + } + } else { + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[0]; + for (j = 0; j < 3; j++) { + ecc_code[i++] = readl(ptr) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 8) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 16) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 24) & 0xFF; + ptr++; + } + ecc_code[i++] = readl(ptr) & 0xFF; + ecc_code[i++] = 0; /* 14th byte is always zero */ + } +} + +/* + * 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 + * + * @mtd: MTD device structure + * @calc_ecc: ECC read from ECC registers + * @syndrome: Rotated syndrome will be retuned in this array + * + */ +static void omap_rotate_ecc_bch(struct mtd_info *mtd, uint8_t *calc_ecc, + uint8_t *syndrome) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t n_bytes = 0; + int8_t i, j; + + switch (bch->type) { + case ECC_BCH4: + n_bytes = 8; + break; + + case ECC_BCH16: + n_bytes = 28; + break; + + case ECC_BCH8: + default: + n_bytes = 13; + break; + } + + for (i = 0, j = (n_bytes-1); i < n_bytes; i++, j--) + syndrome[i] = calc_ecc[j]; +} + +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD structure + * @dat: unused + * @ecc_code: ecc_code buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t big_endian = 1; + int8_t ret = 0; + + if (bch->type == ECC_BCH8) + omap_read_bch8_result(mtd, big_endian, ecc_code); + else /* BCH4 and BCH16 currently not supported */ + ret = -1; + + /* + * 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_fix_errors_bch - Correct bch error in the data + * + * @mtd: MTD device structure + * @data: Data read from flash + * @error_count:Number of errors in data + * @error_loc: Locations of errors in the data + * + */ +static void omap_fix_errors_bch(struct mtd_info *mtd, uint8_t *data, + uint32_t error_count, uint32_t *error_loc) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t count = 0; + uint32_t error_byte_pos; + uint32_t error_bit_mask; + uint32_t last_bit = (bch->nibbles * 4) - 1; + + /* Flip all bits as specified by the error location array. */ + /* FOR( each found error location flip the bit ) */ + for (count = 0; count < error_count; count++) { + if (error_loc[count] > last_bit) { + /* Remove the ECC spare bits from correction. */ + error_loc[count] -= (last_bit + 1); + /* Offset bit in data region */ + error_byte_pos = ((512 * 8) - + (error_loc[count]) - 1) / 8; + /* Error Bit mask */ + error_bit_mask = 0x1 << (error_loc[count] % 8); + /* Toggle the error bit to make the correction. */ + data[error_byte_pos] ^= error_bit_mask; + } + } +} + +/* + * omap_correct_data_bch - Compares the ecc read from nand spare area + * with ECC registers values and corrects one bit error if it has occured + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash (ignored) + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t syndrome[28]; + uint32_t error_count = 0; + uint32_t error_loc[8]; + uint32_t i, ecc_flag; + + ecc_flag = 0; + for (i = 0; i < chip->ecc.bytes; i++) + if (read_ecc[i] != 0xff) + ecc_flag = 1; + + if (!ecc_flag) + return 0; + + elm_reset(); + elm_config((enum bch_level)(bch->type)); + + /* + * while reading ECC result we read it in big endian. + * Hence while loading to ELM we have rotate to get the right endian. + */ + omap_rotate_ecc_bch(mtd, calc_ecc, syndrome); + + /* use elm module to check for errors */ + if (elm_check_error(syndrome, bch->nibbles, &error_count, + error_loc) != 0) { + printf("ECC: uncorrectable.\n"); + return -1; + } + + /* correct bch error */ + if (error_count > 0) + omap_fix_errors_bch(mtd, dat, error_count, error_loc); + + 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 + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *oob = chip->oob_poi; + uint32_t data_pos; + uint32_t oob_pos; + + data_pos = 0; + /* oob area start */ + oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; + oob += chip->ecc.layout->eccpos[0]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, + oob += eccbytes) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + /* read data */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page); + chip->read_buf(mtd, p, eccsize); + + /* read respective ecc from oob area */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page); + chip->read_buf(mtd, oob, eccbytes); + /* read syndrome */ + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + } + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} +#endif /* CONFIG_AM33XX */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. @@ -269,7 +636,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL; /* Setup the ecc configurations again */ - if (hardware) { + if (hardware == 1) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_nand_oob; nand->ecc.size = 512; @@ -279,6 +646,19 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); printf("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"); +#endif } else { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ @@ -350,7 +730,27 @@ int board_nand_init(struct nand_chip *nand) nand->options |= NAND_BUSWIDTH_16; nand->chip_delay = 100; + +#ifdef CONFIG_AM33XX + /* required in case of BCH */ + elm_init(); + + /* BCH info that will be correct for SPL or overridden otherwise. */ + nand->priv = &bch_priv; +#endif + /* Default ECC mode */ +#ifdef CONFIG_AM33XX + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; + nand->ecc.hwctl = omap_enable_ecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + nand->ecc.read_page = omap_read_page_bch; + omap_hwecc_init_bch(nand, NAND_ECC_READ); +#else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) nand->ecc.mode = NAND_ECC_SOFT; #else @@ -363,6 +763,7 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); #endif +#endif #ifdef CONFIG_SPL_BUILD if (nand->options & NAND_BUSWIDTH_16) -- cgit v1.1 From 5846b11e8810f0ecc15e78b383b7709b9b785580 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Tue, 6 Nov 2012 13:06:34 +0000 Subject: am33xx_spl_bch: simple SPL nand loader for AM33XX AM33XX with BCH8 can't work with nand_spl_simple correctly because custom read_page implementation is required for proper syndrome generation. This simple driver mostly duplicates nand_spl_simple but has nand_read_page changed to suit our needs. Signed-off-by: Ilya Yanok --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/am335x_spl_bch.c | 238 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 drivers/mtd/nand/am335x_spl_bch.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index beb99ca..5322f3a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_NAND_SIMPLE COBJS-y += nand_spl_simple.o endif +COBJS-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o ifdef CONFIG_SPL_NAND_LOAD COBJS-y += nand_spl_load.o endif diff --git a/drivers/mtd/nand/am335x_spl_bch.c b/drivers/mtd/nand/am335x_spl_bch.c new file mode 100644 index 0000000..b84528b --- /dev/null +++ b/drivers/mtd/nand/am335x_spl_bch.c @@ -0,0 +1,238 @@ +/* + * (C) Copyright 2012 + * Konstantin Kozhevnikov, Cogent Embedded + * + * based on nand_spl_simple code + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@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. + */ + +#include +#include +#include +#include + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static nand_info_t mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(&mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Begin command latch cycle */ + hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_RESET) { + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!this->dev_ready(&mtd)) + ; + return 0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(&mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(&mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_READ0) { + /* Latch in address */ + hwctrl(&mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + } else if (cmd == NAND_CMD_RNDOUT) { + hwctrl(&mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | + NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + } + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + uint32_t data_pos = 0; + uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; + uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&mtd, NAND_ECC_READ); + nand_command(block, page, data_pos, NAND_CMD_RNDOUT); + + this->read_buf(&mtd, p, eccsize); + + nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); + + this->read_buf(&mtd, oob, eccbytes); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + oob += eccbytes; + } + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + /* + * offs has to be aligned to a page address! + */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* + * Skip bad blocks + */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd.priv = &nand_chip; + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); + + /* NAND chip may require reset after power-on */ + nand_command(0, 0, 0, NAND_CMD_RESET); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, -1); +} -- cgit v1.1 From cc1182beea3712c948dd91d3877cacbbdab3d6ae Mon Sep 17 00:00:00 2001 From: ajoy Date: Sat, 17 Nov 2012 21:10:15 +0000 Subject: OMAP3 SPI : Fixed bugs related to SPI transfer Added posted writes (read after writes) to effect the change immediately for channel confiuration and channel enable register Disable the channel to purge receieve data in TX_ONLY mode transfer otherwise rx data will get affected by the next immediate RX_ONLY mode transfer Wait for the EOT bit to be set after last byte has been loaded to TX shift register in the the TX_ONLY mode.This ensures TX data has been completely shifted out Disable the channel in RX_ONLY mode before reading the last data from RXX register to prevent the SPI slave to transmit next word Signed-off-by: Ajoy Kumar Das Cc: Tom Rini Cc: jacopo mondi --- drivers/spi/omap3_spi.c | 76 ++++++++++++++++++++++++++++--------------------- drivers/spi/omap3_spi.h | 1 + 2 files changed, 45 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 6791a7e..344d5b8 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -57,6 +57,20 @@ static void spi_reset(struct omap3_spi_slave *ds) writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); } +static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val) +{ + writel(val, &ds->regs->channel[ds->slave.cs].chconf); + /* Flash post writes to make immediate effect */ + readl(&ds->regs->channel[ds->slave.cs].chconf); +} + +static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable) +{ + writel(enable, &ds->regs->channel[ds->slave.cs].chctrl); + /* Flash post writes to make immediate effect */ + readl(&ds->regs->channel[ds->slave.cs].chctrl); +} + void spi_init() { /* do nothing */ @@ -212,7 +226,7 @@ int spi_claim_bus(struct spi_slave *slave) /* Transmit & receive mode */ conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; - writel(conf, &ds->regs->channel[ds->slave.cs].chconf); + omap3_spi_write_chconf(ds,conf); return 0; } @@ -233,14 +247,13 @@ int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp, int timeout = SPI_WAIT_TIMEOUT; int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); - if (flags & SPI_XFER_BEGIN) - writel(OMAP3_MCSPI_CHCTRL_EN, - &ds->regs->channel[ds->slave.cs].chctrl); + /* Enable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; chconf |= OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + omap3_spi_write_chconf(ds,chconf); for (i = 0; i < len; i++) { /* wait till TX register is empty (TXS == 1) */ @@ -256,15 +269,17 @@ int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp, writel(txp[i], &ds->regs->channel[ds->slave.cs].tx); } + /* wait to finish of transfer */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_EOT)); + + /* Disable the channel otherwise the next immediate RX will get affected */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + if (flags & SPI_XFER_END) { - /* wait to finish of transfer */ - while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & - OMAP3_MCSPI_CHSTAT_EOT)); chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); - - writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_write_chconf(ds,chconf); } return 0; } @@ -277,14 +292,13 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, int timeout = SPI_WAIT_TIMEOUT; int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); - if (flags & SPI_XFER_BEGIN) - writel(OMAP3_MCSPI_CHCTRL_EN, - &ds->regs->channel[ds->slave.cs].chctrl); + /* Enable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; chconf |= OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + omap3_spi_write_chconf(ds,chconf); writel(0, &ds->regs->channel[ds->slave.cs].tx); @@ -298,15 +312,18 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, return -1; } } + + /* Disable the channel to prevent furher receiving */ + if(i == (len - 1)) + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); + /* Read the data */ rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx); } if (flags & SPI_XFER_END) { chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); - - writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_write_chconf(ds,chconf); } return 0; @@ -323,14 +340,12 @@ int omap3_spi_txrx(struct spi_slave *slave, int i=0; /*Enable SPI channel*/ - if (flags & SPI_XFER_BEGIN) - writel(OMAP3_MCSPI_CHCTRL_EN, - &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); /*set TRANSMIT-RECEIVE Mode*/ chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; chconf |= OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + omap3_spi_write_chconf(ds,chconf); /*Shift in and out 1 byte at time*/ for (i=0; i < len; i++){ @@ -359,13 +374,13 @@ int omap3_spi_txrx(struct spi_slave *slave, /* Read the data */ rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx); } + /* Disable the channel */ + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); /*if transfer must be terminated disable the channel*/ if (flags & SPI_XFER_END) { chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); - - writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_write_chconf(ds,chconf); } return 0; @@ -389,17 +404,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); if (flags & SPI_XFER_BEGIN) { - writel(OMAP3_MCSPI_CHCTRL_EN, - &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); chconf |= OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, - &ds->regs->channel[ds->slave.cs].chconf); + omap3_spi_write_chconf(ds,chconf); } if (flags & SPI_XFER_END) { chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; - writel(chconf, - &ds->regs->channel[ds->slave.cs].chconf); - writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + omap3_spi_write_chconf(ds,chconf); + omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); } ret = 0; } else { diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h index bffa43c..5e00208 100644 --- a/drivers/spi/omap3_spi.h +++ b/drivers/spi/omap3_spi.h @@ -99,6 +99,7 @@ struct mcspi { #define OMAP3_MCSPI_CHSTAT_EOT (1 << 2) #define OMAP3_MCSPI_CHCTRL_EN (1 << 0) +#define OMAP3_MCSPI_CHCTRL_DIS (0 << 0) #define OMAP3_MCSPI_WAKEUPENABLE_WKEN (1 << 0) -- cgit v1.1 From cdd0729ead6845262c3b22f6f5a51d67dd0010ed Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Mon, 26 Nov 2012 04:03:16 +0000 Subject: am335x: cpsw: make phy address configurable Signed-off-by: Yegor Yefremov --- drivers/net/cpsw.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index af3d859..db04795 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -920,7 +920,10 @@ static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave) SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full); - phydev = phy_connect(priv->bus, 0, dev, slave->data->phy_if); + phydev = phy_connect(priv->bus, + CONFIG_PHY_ADDR, + dev, + slave->data->phy_if); phydev->supported &= supported; phydev->advertising = phydev->supported; -- cgit v1.1 From dcee1ab3207327f142635b08e834af5e9f9f8cc9 Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Tue, 27 Nov 2012 22:40:57 +0000 Subject: gpio: add gpio_is_valid() to omap_gpio API Add gpio_is_valid() to omap_gpio API Signed-off-by: Nikita Kiryanov Signed-off-by: Igor Grinberg --- drivers/gpio/omap_gpio.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c index fc89f2a..a30d7f0 100644 --- a/drivers/gpio/omap_gpio.c +++ b/drivers/gpio/omap_gpio.c @@ -53,18 +53,14 @@ static inline int get_gpio_index(int gpio) return gpio & 0x1f; } -static inline int gpio_valid(int gpio) +int gpio_is_valid(int gpio) { - if (gpio < 0) - return -1; - if (gpio < 192) - return 0; - return -1; + return (gpio >= 0) && (gpio < 192); } static int check_gpio(int gpio) { - if (gpio_valid(gpio) < 0) { + if (!gpio_is_valid(gpio)) { printf("ERROR : check_gpio: invalid GPIO %d\n", gpio); return -1; } -- cgit v1.1 From febc4cd48a5c030c7fd411be519505701c100838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Mon, 3 Dec 2012 05:23:16 +0000 Subject: omap24xx_i2c: Handle wait_for_bb error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We add a return code to wait_for_bb() to be able to report errors to the callers properly. We in turn handle this new error code in i2c_read, i2c_write and i2c_probe. Signed-off-by: Vincent Stehlé --- drivers/i2c/omap24xx_i2c.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 094305f..1bbb2ca 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -31,7 +31,7 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_TIMEOUT 1000 -static void wait_for_bb(void); +static int wait_for_bb(void); static u16 wait_for_pin(void); static void flush_fifo(void); @@ -159,7 +159,8 @@ static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) u16 w; /* wait until bus not busy */ - wait_for_bb(); + if (wait_for_bb()) + return 1; /* one byte only */ writew(alen, &i2c_base->cnt); @@ -260,7 +261,8 @@ int i2c_probe(uchar chip) return res; /* wait until bus not busy */ - wait_for_bb(); + if (wait_for_bb()) + return res; /* try to read one byte */ writew(1, &i2c_base->cnt); @@ -279,7 +281,10 @@ int i2c_probe(uchar chip) res = 1; writew(0xff, &i2c_base->stat); writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con); - wait_for_bb (); + + if (wait_for_bb()) + res = 1; + break; } if (status & I2C_STAT_ARDY) { @@ -351,7 +356,8 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) } /* wait until bus not busy */ - wait_for_bb(); + if (wait_for_bb()) + return 1; /* start address phase - will write regoffset + len bytes data */ /* TODO consider case when !CONFIG_OMAP243X/34XX/44XX */ @@ -394,7 +400,7 @@ write_exit: return i2c_error; } -static void wait_for_bb(void) +static int wait_for_bb(void) { int timeout = I2C_TIMEOUT; u16 stat; @@ -408,8 +414,10 @@ static void wait_for_bb(void) if (timeout <= 0) { printf("timed out in wait_for_bb: I2C_STAT=%x\n", readw(&i2c_base->stat)); + return 1; } writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ + return 0; } static u16 wait_for_pin(void) -- cgit v1.1 From ce851c8b3c8d3318b7afd46019f8ac50a8587ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Mon, 3 Dec 2012 05:23:17 +0000 Subject: power: twl6035: complain on LDO9 error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We handle i2c_write return code and complain in case of error. We propagate the error, too, to allow better handling at the upper level in the future. Signed-off-by: Vincent Stehlé --- drivers/power/twl6035.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/twl6035.c b/drivers/power/twl6035.c index 624c09e..d3de698 100644 --- a/drivers/power/twl6035.c +++ b/drivers/power/twl6035.c @@ -50,16 +50,25 @@ void twl6035_init_settings(void) return; } -void twl6035_mmc1_poweron_ldo(void) +int twl6035_mmc1_poweron_ldo(void) { u8 val = 0; /* set LDO9 TWL6035 to 3V */ val = 0x2b; /* (3 -.9)*28 +1 */ - palmas_write_u8(0x48, LDO9_VOLTAGE, val); + + if (palmas_write_u8(0x48, LDO9_VOLTAGE, val)) { + printf("twl6035: could not set LDO9 voltage.\n"); + return 1; + } /* TURN ON LDO9 */ val = LDO_ON | LDO_MODE_SLEEP | LDO_MODE_ACTIVE; - palmas_write_u8(0x48, LDO9_CTRL, val); - return; + + if (palmas_write_u8(0x48, LDO9_CTRL, val)) { + printf("twl6035: could not turn on LDO9.\n"); + return 1; + } + + return 0; } -- cgit v1.1