diff options
Diffstat (limited to 'drivers/mtd')
| -rw-r--r-- | drivers/mtd/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/fsmc_nand.c | 486 | ||||
| -rw-r--r-- | drivers/mtd/nand/spr_nand.c | 124 | ||||
| -rw-r--r-- | drivers/mtd/spi/winbond.c | 8 | ||||
| -rw-r--r-- | drivers/mtd/st_smi.c (renamed from drivers/mtd/spr_smi.c) | 255 |
6 files changed, 655 insertions, 221 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5a5ecdfe3c6..543c845ff01 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -35,7 +35,7 @@ COBJS-$(CONFIG_HAS_DATAFLASH) += dataflash.o COBJS-$(CONFIG_FTSMC020) += ftsmc020.o COBJS-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o COBJS-$(CONFIG_MW_EEPROM) += mw_eeprom.o -COBJS-$(CONFIG_SPEARSMI) += spr_smi.o +COBJS-$(CONFIG_ST_SMI) += st_smi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1d1b6286510..29dc20ef5e2 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -49,6 +49,7 @@ COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_FSMC) += fsmc_nand.o COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c new file mode 100644 index 00000000000..7a61d88cc57 --- /dev/null +++ b/drivers/mtd/nand/fsmc_nand.c @@ -0,0 +1,486 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * (C) Copyright 2012 + * Amit Virdi, ST Microelectronics, amit.virdi@st.com. + * + * 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 <common.h> +#include <nand.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/fsmc_nand.h> +#include <asm/arch/hardware.h> + +static u32 fsmc_version; +static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) + CONFIG_SYS_FSMC_BASE; + +/* + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of + * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can + * correct 1 bit in 512 bytes + */ + +static struct nand_ecclayout fsmc_ecc4_lp_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC placement definitions in oobfree type format + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data + * Managing the ecc bytes in the following way makes it easier for software to + * read ecc bytes consecutive to data bytes. This way is similar to + * oobfree structure maintained already in u-boot nand driver + */ +static struct fsmc_eccplace fsmc_eccpl_lp = { + .eccplace = { + {.offset = 2, .length = 13}, + {.offset = 18, .length = 13}, + {.offset = 34, .length = 13}, + {.offset = 50, .length = 13}, + {.offset = 66, .length = 13}, + {.offset = 82, .length = 13}, + {.offset = 98, .length = 13}, + {.offset = 114, .length = 13} + } +}; + +static struct nand_ecclayout fsmc_ecc4_sp_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +static struct fsmc_eccplace fsmc_eccpl_sp = { + .eccplace = { + {.offset = 0, .length = 4}, + {.offset = 6, .length = 9} + } +}; + +static struct nand_ecclayout fsmc_ecc1_layout = { + .eccbytes = 24, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, + 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + {.offset = 72, .length = 8}, + {.offset = 88, .length = 8}, + {.offset = 104, .length = 8}, + {.offset = 120, .length = 8} + } +}; + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (ulong)this->IO_ADDR_W; + + IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + + if (ctrl & NAND_NCE) { + writel(readl(&fsmc_regs_p->pc) | + FSMC_ENABLE, &fsmc_regs_p->pc); + } else { + writel(readl(&fsmc_regs_p->pc) & + ~FSMC_ENABLE, &fsmc_regs_p->pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + /* The calculated ecc is actually the correction index in data */ + u32 err_idx[8]; + u32 num_err, i; + u32 ecc1, ecc2, ecc3, ecc4; + + num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; + + if (likely(num_err == 0)) + return 0; + + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, 13, 8); + int bits_data = count_written_bits(dat, 512, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, 512); + return bits_data + bits_ecc; + } + + return -EBADMSG; + } + + ecc1 = readl(&fsmc_regs_p->ecc1); + ecc2 = readl(&fsmc_regs_p->ecc2); + ecc3 = readl(&fsmc_regs_p->ecc3); + ecc4 = readl(&fsmc_regs_p->sts); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + + i = 0; + while (i < num_err) { + err_idx[i] ^= 3; + + if (err_idx[i] < 512 * 8) + __change_bit(err_idx[i], dat); + + i++; + } + + return num_err; +} + +static int fsmc_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char *ecc) +{ + u_int ecc_tmp; + int timeout = CONFIG_SYS_HZ; + ulong start; + + switch (fsmc_version) { + case FSMC_VER8: + start = get_timer(0); + while (get_timer(start) < timeout) { + /* + * Busy waiting for ecc computation + * to finish for 512 bytes + */ + if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) + break; + } + + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + ecc[3] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc2); + ecc[4] = (u_char) (ecc_tmp >> 0); + ecc[5] = (u_char) (ecc_tmp >> 8); + ecc[6] = (u_char) (ecc_tmp >> 16); + ecc[7] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc3); + ecc[8] = (u_char) (ecc_tmp >> 0); + ecc[9] = (u_char) (ecc_tmp >> 8); + ecc[10] = (u_char) (ecc_tmp >> 16); + ecc[11] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->sts); + ecc[12] = (u_char) (ecc_tmp >> 16); + break; + + default: + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + break; + } + + return 0; +} + +void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, + &fsmc_regs_p->pc); +} + +/* + * fsmc_read_page_hwecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(upto a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct fsmc_eccplace *fsmc_eccpl; + int i, j, s, stat, 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; + int off, len, group = 0; + uint8_t oob[13] __attribute__ ((aligned (2))); + + /* Differentiate between small and large page ecc place definitions */ + if (mtd->writesize == 512) + fsmc_eccpl = &fsmc_eccpl_sp; + else + fsmc_eccpl = &fsmc_eccpl_lp; + + for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + for (j = 0; j < eccbytes;) { + off = fsmc_eccpl->eccplace[group].offset; + len = fsmc_eccpl->eccplace[group].length; + group++; + + /* + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); + chip->read_buf(mtd, oob + j, len); + j += len; + } + + memcpy(&ecc_code[i], oob, 13); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + 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; +} + +int fsmc_nand_init(struct nand_chip *nand) +{ + static int chip_nr; + struct mtd_info *mtd; + int i; + u32 peripid2 = readl(&fsmc_regs_p->peripid2); + + fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & + FSMC_REVISION_MSK; + + writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); + +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#else +#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT +#endif + writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->attrib); + + nand->options = 0; +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + nand->options |= NAND_BUSWIDTH_16; +#endif + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.calculate = fsmc_read_hwecc; + nand->ecc.hwctl = fsmc_enable_hwecc; + nand->cmd_ctrl = fsmc_nand_hwcontrol; + nand->IO_ADDR_R = nand->IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->badblockbits = 7; + + mtd = &nand_info[chip_nr++]; + mtd->priv = nand; + + switch (fsmc_version) { + case FSMC_VER8: + nand->ecc.bytes = 13; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.read_page = fsmc_read_page_hwecc; + if (mtd->writesize == 512) + nand->ecc.layout = &fsmc_ecc4_sp_layout; + else { + if (mtd->oobsize == 224) + nand->ecc.layout = &fsmc_ecc4_224_layout; + else + nand->ecc.layout = &fsmc_ecc4_lp_layout; + } + + break; + default: + nand->ecc.bytes = 3; + nand->ecc.layout = &fsmc_ecc1_layout; + nand->ecc.correct = nand_correct_data; + break; + } + + /* Detect NAND chips */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + if (nand_register(i)) + return -ENXIO; + + return 0; +} diff --git a/drivers/mtd/nand/spr_nand.c b/drivers/mtd/nand/spr_nand.c deleted file mode 100644 index 097d0c60bfc..00000000000 --- a/drivers/mtd/nand/spr_nand.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * (C) Copyright 2009 - * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. - * - * 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 <common.h> -#include <nand.h> -#include <linux/mtd/nand_ecc.h> -#include <asm/io.h> -#include <asm/arch/hardware.h> -#include <asm/arch/spr_nand.h> - -static struct fsmc_regs *const fsmc_regs_p = - (struct fsmc_regs *)CONFIG_SPEAR_FSMCBASE; - -static struct nand_ecclayout spear_nand_ecclayout = { - .eccbytes = 24, - .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, - 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, - .oobfree = { - {.offset = 8, .length = 8}, - {.offset = 24, .length = 8}, - {.offset = 40, .length = 8}, - {.offset = 56, .length = 8}, - {.offset = 72, .length = 8}, - {.offset = 88, .length = 8}, - {.offset = 104, .length = 8}, - {.offset = 120, .length = 8} - } -}; - -static void spear_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) -{ - struct nand_chip *this = mtd->priv; - ulong IO_ADDR_W; - - if (ctrl & NAND_CTRL_CHANGE) { - IO_ADDR_W = (ulong)this->IO_ADDR_W; - - IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); - if (ctrl & NAND_CLE) - IO_ADDR_W |= CONFIG_SYS_NAND_CLE; - if (ctrl & NAND_ALE) - IO_ADDR_W |= CONFIG_SYS_NAND_ALE; - - if (ctrl & NAND_NCE) { - writel(readl(&fsmc_regs_p->genmemctrl_pc) | - FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); - } else { - writel(readl(&fsmc_regs_p->genmemctrl_pc) & - ~FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); - } - this->IO_ADDR_W = (void *)IO_ADDR_W; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -static int spear_read_hwecc(struct mtd_info *mtd, - const u_char *data, u_char ecc[3]) -{ - u_int ecc_tmp; - - /* read the h/w ECC */ - ecc_tmp = readl(&fsmc_regs_p->genmemctrl_ecc); - - ecc[0] = (u_char) (ecc_tmp & 0xFF); - ecc[1] = (u_char) ((ecc_tmp & 0xFF00) >> 8); - ecc[2] = (u_char) ((ecc_tmp & 0xFF0000) >> 16); - - return 0; -} - -void spear_enable_hwecc(struct mtd_info *mtd, int mode) -{ - writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~0x80, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~FSMC_ECCEN, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_ECCEN, - &fsmc_regs_p->genmemctrl_pc); -} - -int spear_nand_init(struct nand_chip *nand) -{ - writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_TCLR_1 | FSMC_TAR_1, - &fsmc_regs_p->genmemctrl_pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->genmemctrl_comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->genmemctrl_attrib); - - nand->options = 0; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &spear_nand_ecclayout; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.calculate = spear_read_hwecc; - nand->ecc.hwctl = spear_enable_hwecc; - nand->ecc.correct = nand_correct_data; - nand->cmd_ctrl = spear_nand_hwcontrol; - return 0; -} diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index a6a66537ba3..c20faa26fcc 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -68,6 +68,14 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25X64", }, { + .id = 0x4014, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 16, + .name = "W25Q80BL", + }, + { .id = 0x4015, .l2_page_size = 8, .pages_per_sector = 16, diff --git a/drivers/mtd/spr_smi.c b/drivers/mtd/st_smi.c index 6d4257a3f50..7507e5d0776 100644 --- a/drivers/mtd/spr_smi.c +++ b/drivers/mtd/st_smi.c @@ -1,6 +1,6 @@ /* * (C) Copyright 2009 - * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. * * See file CREDITS for list of people who contributed to this * project. @@ -24,10 +24,10 @@ #include <common.h> #include <flash.h> #include <linux/err.h> +#include <linux/mtd/st_smi.h> #include <asm/io.h> #include <asm/arch/hardware.h> -#include <asm/arch/spr_smi.h> #if !defined(CONFIG_SYS_NO_FLASH) @@ -37,19 +37,61 @@ static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = CONFIG_SYS_FLASH_ADDR_BASE; flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; -#define ST_M25Pxx_ID 0x00002020 - -static struct flash_dev flash_ids[] = { - {0x10, 0x10000, 2}, /* 64K Byte */ - {0x11, 0x20000, 4}, /* 128K Byte */ - {0x12, 0x40000, 4}, /* 256K Byte */ - {0x13, 0x80000, 8}, /* 512K Byte */ - {0x14, 0x100000, 16}, /* 1M Byte */ - {0x15, 0x200000, 32}, /* 2M Byte */ - {0x16, 0x400000, 64}, /* 4M Byte */ - {0x17, 0x800000, 128}, /* 8M Byte */ - {0x18, 0x1000000, 64}, /* 16M Byte */ - {0x00,} +/* data structure to maintain flash ids from different vendors */ +struct flash_device { + char *name; + u8 erase_cmd; + u32 device_id; + u32 pagesize; + unsigned long sectorsize; + unsigned long size_in_bytes; +}; + +#define FLASH_ID(n, es, id, psize, ssize, size) \ +{ \ + .name = n, \ + .erase_cmd = es, \ + .device_id = id, \ + .pagesize = psize, \ + .sectorsize = ssize, \ + .size_in_bytes = size \ +} + +/* + * List of supported flash devices. + * Currently the erase_cmd field is not used in this driver. + */ +static struct flash_device flash_devices[] = { + FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), + FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), + FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), + FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), + FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), + FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), + FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), + FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), + FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), + FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), + FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), + FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), + FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), + FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), + FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), + FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), + FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), + FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), + FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), + FLASH_ID("wbd w25q128" , 0xd8, 0x001840EF, 0x100, 0x10000, 0x1000000), }; /* @@ -58,13 +100,19 @@ static struct flash_dev flash_ids[] = { * * Wait until TFF is set in status register */ -static void smi_wait_xfer_finish(int timeout) +static int smi_wait_xfer_finish(int timeout) { - while (timeout--) { + ulong start = get_timer(0); + + while (get_timer(start) < timeout) { if (readl(&smicntl->smi_sr) & TFF) - break; - udelay(1000); - } + return 0; + + /* Try after 10 ms */ + udelay(10); + }; + + return -1; } /* @@ -82,7 +130,9 @@ static unsigned int smi_read_id(flash_info_t *info, int banknum) writel(READ_ID, &smicntl->smi_tr); writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); @@ -103,30 +153,30 @@ static unsigned int smi_read_id(flash_info_t *info, int banknum) static ulong flash_get_size(ulong base, int banknum) { flash_info_t *info = &flash_info[banknum]; - struct flash_dev *dev; - unsigned int value; - unsigned int density; + int value; int i; value = smi_read_id(info, banknum); - density = (value >> 16) & 0xff; - - for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; - i++, dev = &flash_ids[i]) { - if (dev->density == density) { - info->size = dev->size; - info->sector_count = dev->sector_count; - break; - } - } - if (dev->density == 0x0) + if (value < 0) { + printf("Flash id could not be read\n"); return 0; + } - info->flash_id = value & 0xffff; - info->start[0] = base; + /* Matches chip-id to entire list of 'serial-nor flash' ids */ + for (i = 0; i < ARRAY_SIZE(flash_devices); i++) { + if (flash_devices[i].device_id == value) { + info->size = flash_devices[i].size_in_bytes; + info->flash_id = value; + info->start[0] = base; + info->sector_count = + info->size/flash_devices[i].sectorsize; - return info->size; + return info->size; + } + } + + return 0; } /* @@ -136,9 +186,9 @@ static ulong flash_get_size(ulong base, int banknum) * This routine will get the status register of the flash chip present at the * given bank */ -static unsigned int smi_read_sr(int bank) +static int smi_read_sr(int bank) { - u32 ctrlreg1; + u32 ctrlreg1, val; /* store the CTRL REG1 state */ ctrlreg1 = readl(&smicntl->smi_cr1); @@ -150,12 +200,15 @@ static unsigned int smi_read_sr(int bank) /* Performing a RSR instruction in HW mode */ writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; + + val = readl(&smicntl->smi_sr); /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); - return readl(&smicntl->smi_sr); + return val; } /* @@ -169,22 +222,20 @@ static unsigned int smi_read_sr(int bank) */ static int smi_wait_till_ready(int bank, int timeout) { - int count; - unsigned int sr; + int sr; + ulong start = get_timer(0); /* One chip guarantees max 5 msec wait here after page writes, but potentially three seconds (!) after page erase. */ - for (count = 0; count < timeout; count++) { - + while (get_timer(start) < timeout) { sr = smi_read_sr(bank); - if (sr < 0) - break; - else if (!(sr & WIP_BIT)) + if ((sr >= 0) && (!(sr & WIP_BIT))) return 0; - /* Try again after 1m-sec */ - udelay(1000); - } + /* Try again after 10 usec */ + udelay(10); + } while (timeout--); + printf("SMI controller is still in wait, timeout=%d\n", timeout); return -EIO; } @@ -199,7 +250,9 @@ static int smi_wait_till_ready(int bank, int timeout) static int smi_write_enable(int bank) { u32 ctrlreg1; + u32 start; int timeout = WMODE_TOUT; + int sr; /* Store the CTRL REG1 state */ ctrlreg1 = readl(&smicntl->smi_cr1); @@ -210,19 +263,21 @@ static int smi_write_enable(int bank) /* Give the Flash, Write Enable command */ writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); - while (timeout--) { - if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) - break; - udelay(1000); - } + start = get_timer(0); + while (get_timer(start) < timeout) { + sr = smi_read_sr(bank); + if ((sr >= 0) && (sr & (1 << (bank + WM_SHIFT)))) + return 0; - if (timeout) - return 0; + /* Try again after 10 usec */ + udelay(10); + }; return -1; } @@ -232,7 +287,7 @@ static int smi_write_enable(int bank) * * SMI initialization routine. Sets SMI control register1. */ -static void smi_init(void) +void smi_init(void) { /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, @@ -275,45 +330,39 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); - if (info->flash_id == ST_M25Pxx_ID) { - /* Wait until finished previous write command. */ - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; + /* Wait until finished previous write command. */ + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; - /* Send write enable, before erase commands. */ - if (smi_write_enable(bank)) - return -EIO; + /* Send write enable, before erase commands. */ + if (smi_write_enable(bank)) + return -EIO; - /* Put SMI in SW mode */ - writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + /* Put SMI in SW mode */ + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); - /* Send Sector Erase command in SW Mode */ - writel(instruction, &smicntl->smi_tr); - writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, + /* Send Sector Erase command in SW Mode */ + writel(instruction, &smicntl->smi_tr); + writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); - return 0; - } else { - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, - &smicntl->smi_cr1); - return -EINVAL; - } + return 0; } /* * smi_write - Write to SMI flash * @src_addr: source buffer * @dst_addr: destination buffer - * @length: length to write in words + * @length: length to write in bytes * @bank: bank base address * * Write to SMI flash @@ -321,7 +370,10 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, unsigned int length, ulong bank_addr) { + u8 *src_addr8 = (u8 *)src_addr; + u8 *dst_addr8 = (u8 *)dst_addr; int banknum; + int i; switch (bank_addr) { case SMIBANK0_BASE: @@ -350,7 +402,7 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -EIO; /* Perform the write command */ - while (length--) { + for (i = 0; i < length; i += 4) { if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) @@ -360,7 +412,18 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -EIO; } - *dst_addr++ = *src_addr++; + if (length < 4) { + int k; + + /* + * Handle special case, where length < 4 (redundant env) + */ + for (k = 0; k < length; k++) + *dst_addr8++ = *src_addr8++; + } else { + /* Normal 32bit write */ + *dst_addr++ = *src_addr++; + } if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) return -EIO; @@ -386,7 +449,7 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) { return smi_write((unsigned int *)src, (unsigned int *)dest_addr, - (length + 3) / 4, info->start[0]); + length, info->start[0]); } /* @@ -429,8 +492,13 @@ void flash_print_info(flash_info_t *info) puts("missing or unknown FLASH type\n"); return; } - printf(" Size: %ld MB in %d Sectors\n", - info->size >> 20, info->sector_count); + + if (info->size >= 0x100000) + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + else + printf(" Size: %ld KB in %d Sectors\n", + info->size >> 10, info->sector_count); puts(" Sector Start Addresses:"); for (i = 0; i < info->sector_count; ++i) { @@ -483,11 +551,6 @@ int flash_erase(flash_info_t *info, int s_first, int s_last) int prot = 0; flash_sect_t sect; - if (info->flash_id != ST_M25Pxx_ID) { - puts("Can't erase unknown flash type - aborted\n"); - return 1; - } - if ((s_first < 0) || (s_first > s_last)) { puts("- no sectors to erase\n"); return 1; |
