From mboxrd@z Thu Jan 1 00:00:00 1970 From: Tom Rix Date: Sun, 02 May 2010 19:22:41 -0500 Subject: [U-Boot] [PATCH v2 10/17] SPEAr : FSMC driver support added In-Reply-To: <1272250610-15439-11-git-send-email-vipin.kumar@st.com> References: <1272250610-15439-1-git-send-email-vipin.kumar@st.com> <1272250610-15439-2-git-send-email-vipin.kumar@st.com> <1272250610-15439-3-git-send-email-vipin.kumar@st.com> <1272250610-15439-4-git-send-email-vipin.kumar@st.com> <1272250610-15439-5-git-send-email-vipin.kumar@st.com> <1272250610-15439-6-git-send-email-vipin.kumar@st.com> <1272250610-15439-7-git-send-email-vipin.kumar@st.com> <1272250610-15439-8-git-send-email-vipin.kumar@st.com> <1272250610-15439-9-git-send-email-vipin.kumar@st.com> <1272250610-15439-10-git-send-email-vipin.kumar@st.com> <1272250610-15439-11-git-send-email-vipin.kumar@st.com> Message-ID: <4BDE1751.3090206@bumblecow.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Vipin KUMAR wrote: > Flexible static memory controller is an IP which controls the access > to NAND chips along with many other memory device chips eg NOR, SRAM. > This is an ST peripheral. This patch adds the driver support for FSMC > controller interfacing with NAND memory. IP, ST ? Add meaning of IP and something to say ST is manufacturer and not a class of peripherals. > > Signed-off-by: Vipin Kumar > --- > drivers/mtd/nand/Makefile | 1 + > drivers/mtd/nand/fsmc_nand.c | 365 +++++++++++++++++++++++++++++++++++++++++ > include/linux/mtd/fsmc_nand.h | 104 ++++++++++++ > 3 files changed, 470 insertions(+), 0 deletions(-) > create mode 100644 drivers/mtd/nand/fsmc_nand.c > create mode 100644 include/linux/mtd/fsmc_nand.h > > diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile > index 28f27da..4c6b54f 100644 > --- a/drivers/mtd/nand/Makefile > +++ b/drivers/mtd/nand/Makefile > @@ -38,6 +38,7 @@ COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o > COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o > COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o > COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o > +COBJS-$(CONFIG_NAND_FSMC) += fsmc_nand.o > COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o > COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o > COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o > diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c > new file mode 100644 > index 0000000..bad5606 > --- /dev/null > +++ b/drivers/mtd/nand/fsmc_nand.c > @@ -0,0 +1,365 @@ > +/* > + * (C) Copyright 2009 2009? > + * Vipin Kumar, ST Micoelectronics, vipin.kumar at 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +static u32 fsmc_version; > +static struct fsmc_regs *const fsmc_regs_p = > + (struct fsmc_regs *)CONFIG_SYS_FSMC_BASE; > + CONFIG_SYS_FSMC_BASE is not defined until 11/17 This make this change non-bisectable. Fix. > +/* > + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of > + * data. ECC4 can correct upto 8 bits in 512 bytes of data while ECC1 can 'upto' -> 'up to' > + * correct 1 bit in 512 bytes > + */ > + > +#if defined(CONFIG_SYS_FSMC_NAND_LP) > +static struct nand_ecclayout fsmc_ecc4_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} > + } > +}; > + > +/* > + * ECC placement definitions in oobfree type format > + * There are 13 bytes of ecc for every 512 byte block and it has to be read > + * consicutively and immidiately after the 512 byte data block for hardware to 'consivutively, immidiately' fix spelling > + * 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 consicutive to data bytes. This way is similar to 'consicutive' > + * oobfree structure maintained already in u-boot nand driver > + */ > +static struct fsmc_eccplace fsmc_eccpl = { > + .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} > + } > +}; > + > +#elif defined(CONFIG_SYS_FSMC_NAND_SP) > +static struct nand_ecclayout fsmc_ecc4_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 = { > + .eccplace = { > + {.offset = 0, .length = 4}, > + {.offset = 6, .length = 9} > + } > +}; > + > +#else > +#error Please define one of CONFIG_SYS_FSMC_NAND_SP or CONFIG_SYS_FSMC_NAND_LP Good! > +#endif > + > +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} > + } > +}; > + > +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_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 */ > + u16 err_idx[8]; > + u64 ecc_data[2]; > + u32 num_err, i; > + > + memcpy(ecc_data, calc_ecc, 13); > + > + for (i = 0; i < 8; i++) { > + if (i == 4) { > + err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0]; > + ecc_data[1] >>= 1; > + continue; > + } > + err_idx[i] = (ecc_data[i/4] & 0x1FFF); > + ecc_data[i/4] >>= 13; > + } > + > + num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; > + > + if (num_err == 0xF) > + return -EBADMSG; > + > + i = 0; > + while (num_err--) { > + change_bit(0, &err_idx[i]); > + change_bit(1, &err_idx[i]); > + > + if (err_idx[i] <= 512 * 8) { > + change_bit(err_idx[i], dat); > + i++; > + } > + } > + return i; > +} > + > +static int fsmc_read_hwecc(struct mtd_info *mtd, > + const u_char *data, u_char *ecc) > +{ > + u_int ecc_tmp; > + > + switch (fsmc_version) { > + case FSMC_VER8: > + while (!(readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY)) > + ; Add a comment that this is a busy wait. > + > + 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); > +} > + > +/** extra '*' > + * 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) > +{ > + 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; > + /* > + * ecc_oob is intentionally taken as u16. In 16bit devices, we end up > + * reading 14 bytes (7 words) from oob. The local array is to maintain > + * word alignment > + */ > + uint16_t ecc_oob[7]; > + > + 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[s].offset; > + len = fsmc_eccpl.eccplace[s].length; > + > + /* > + * length is intentionally kept a higher multiple of 2 > + * to read at least 13 bytes even in case of 16 bit NAND > + * devices > + */ > + len = roundup(len, 2); > + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); > + chip->read_buf(mtd, (uint8_t *)&ecc_oob[j], len); > + j += len; > + } > + > + memcpy(&ecc_code[i], ecc_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) > +{ > + u32 peripid2 = readl(&fsmc_regs_p->peripid2); > + > + fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & > + FSMC_REVISION_MSK; > +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) 16BIT is never defined This is dead code Remove > + 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; > + > + switch (fsmc_version) { > + case FSMC_VER8: > + nand->ecc.bytes = 13; > + nand->ecc.layout = &fsmc_ecc4_layout; > + nand->ecc.correct = fsmc_correct_data; > + nand->ecc.read_page = fsmc_read_page_hwecc; > + break; > + default: > + nand->ecc.bytes = 3; > + nand->ecc.layout = &fsmc_ecc1_layout; > + nand->ecc.correct = nand_correct_data; > + break; > + } > + > + return 0; > +} > diff --git a/include/linux/mtd/fsmc_nand.h b/include/linux/mtd/fsmc_nand.h > new file mode 100644 > index 0000000..fd7059e > --- /dev/null > +++ b/include/linux/mtd/fsmc_nand.h > @@ -0,0 +1,104 @@ > +/* > + * (C) Copyright 2009 > + * Vipin Kumar, ST Micoelectronics, vipin.kumar at 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 > + */ > + > +#ifndef __FSMC_NAND_H__ > +#define __FSMC_NAND_H__ > + > +struct fsmc_regs { > + u8 reserved_1[0x40]; > + u32 pc; /* 0x40 */ > + u32 sts; /* 0x44 */ > + u32 comm; /* 0x48 */ > + u32 attrib; /* 0x4c */ > + u32 ioata; /* 0x50 */ > + u32 ecc1; /* 0x54 */ > + u32 ecc2; /* 0x58 */ > + u32 ecc3; /* 0x5c */ > + u8 reserved_2[0xfe0 - 0x60]; > + u32 peripid0; /* 0xfe0 */ > + u32 peripid1; /* 0xfe4 */ > + u32 peripid2; /* 0xfe8 */ > + u32 peripid3; /* 0xfec */ > + u32 pcellid0; /* 0xff0 */ > + u32 pcellid1; /* 0xff4 */ > + u32 pcellid2; /* 0xff8 */ > + u32 pcellid3; /* 0xffc */ > +}; > + > +/* pc register definitions */ > +#define FSMC_RESET (1 << 0) > +#define FSMC_WAITON (1 << 1) > +#define FSMC_ENABLE (1 << 2) > +#define FSMC_DEVTYPE_NAND (1 << 3) > +#define FSMC_DEVWID_8 (0 << 4) > +#define FSMC_DEVWID_16 (1 << 4) > +#define FSMC_ECCEN (1 << 6) > +#define FSMC_ECCPLEN_512 (0 << 7) > +#define FSMC_ECCPLEN_256 (1 << 7) > +#define FSMC_TCLR_1 (1 << 9) > +#define FSMC_TAR_1 (1 << 13) > + > +/* sts register definitions */ > +#define FSMC_CODE_RDY (1 << 15) > + > +/* comm register definitions */ > +#define FSMC_TSET_0 (0 << 0) > +#define FSMC_TWAIT_6 (6 << 8) > +#define FSMC_THOLD_4 (4 << 16) > +#define FSMC_THIZ_1 (1 << 24) > + > +/* peripid2 register definitions */ > +#define FSMC_REVISION_MSK (0xf) > +#define FSMC_REVISION_SHFT (0x4) > + > +enum { > + FSMC_VER1 = 1, > + FSMC_VER2, > + FSMC_VER3, > + FSMC_VER4, > + FSMC_VER5, > + FSMC_VER6, > + FSMC_VER7, > + FSMC_VER8, > +}; > + > +/* > + * There are 13 bytes of ecc for every 512 byte block and it has to be read > + * consicutively and immidiately after the 512 byte data block for hardware to 'consicutively' Please check spelling Tom > + * generate the error bit offsets > + * Managing the ecc bytes in the following way is easier. This way is similar to > + * oobfree structure maintained already in u-boot nand driver > + */ > +#define MAX_ECCPLACE_ENTRIES 32 > + > +struct fsmc_nand_eccplace { > + u32 offset; > + u32 length; > +}; > + > +struct fsmc_eccplace { > + struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES]; > +}; > + > +extern int fsmc_nand_init(struct nand_chip *nand); > +#endif