diff options
-rw-r--r-- | board/freescale/mx35_3stack/config.mk | 2 | ||||
-rw-r--r-- | cpu/arm1136/mx35/interrupts.c | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 1148 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 17 | ||||
-rw-r--r-- | include/asm-arm/arch-mx35/mx35.h | 8 | ||||
-rw-r--r-- | include/asm-arm/arch-mx35/mxc_nand.h | 198 | ||||
-rw-r--r-- | include/configs/mx35_3stack.h | 11 |
9 files changed, 1380 insertions, 14 deletions
diff --git a/board/freescale/mx35_3stack/config.mk b/board/freescale/mx35_3stack/config.mk index d34dc02..70b4271 100644 --- a/board/freescale/mx35_3stack/config.mk +++ b/board/freescale/mx35_3stack/config.mk @@ -1 +1 @@ -TEXT_BASE = 0x87f00000 +TEXT_BASE = 0x87800000 diff --git a/cpu/arm1136/mx35/interrupts.c b/cpu/arm1136/mx35/interrupts.c index 28284b1..40b9d4c 100644 --- a/cpu/arm1136/mx35/interrupts.c +++ b/cpu/arm1136/mx35/interrupts.c @@ -54,6 +54,11 @@ int interrupt_init(void) return 0; } +void reset_timer(void) +{ + reset_timer_masked(); +} + void reset_timer_masked(void) { GPTCR = 0; diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 975a1b8..d2f9435 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -47,6 +47,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o COBJS-$(CONFIG_MX31_NAND) += mx31_nand.o +COBJS-y += mxc_nand.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 0000000..d458d0a --- /dev/null +++ b/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,1148 @@ +/* + * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <nand.h> +#include <linux/mtd/compat.h> +#include <asm-arm/arch/mxc_nand.h> + +struct nand_info { + int status_req; + int large_page; + int auto_mode; + u16 col_addr; + u8 num_of_intlv; + int page_mask; + int hw_ecc; + u8 *data_buf; + u8 *oob_buf; +}; + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_oobinfo nand_hw_eccoob_512 = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{0, 4} } +}; + +static struct nand_oobinfo nand_hw_eccoob_2k = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{2, 4} } +}; + +static struct nand_oobinfo nand_hw_eccoob_4k = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{2, 4} } +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd2.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +/*! + * Half word access.Added for U-boot. + */ +static void *nfc_memcpy(void *dest, const void *src, size_t n) +{ + u16 *dst_16 = (u16 *) dest; + const u16 *src_16 = (u16 *) src; + + while (n > 0) { + *dst_16++ = *src_16++; + n -= 2; + } + + return dest; +} + +/* + * Functions to transfer data to/from spare erea. + */ +static void +copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, int bfrom) +{ + u16 i, j; + u16 m = mtd->oobsize; + u16 n = mtd->oobblock >> 9; + u8 *d = (u8 *) pbuf; + u8 *s = (u8 *) pspare; + u16 t = SPARE_LEN; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + m /= info->num_of_intlv; + n /= info->num_of_intlv; + + j = (m / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&d[i * j], &s[i * t], j); + + /* the last section */ + nfc_memcpy(&d[i * j], &s[i * t], len - i * j); + } else { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&s[i * t], &d[i * j], j); + + /* the last section */ + nfc_memcpy(&s[i * t], &d[i * j], len - i * j); + } +} + +/*! + * This function polls the NFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int max_retries) +{ + + while (max_retries-- > 0) { + if (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) { + WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) & + ~NFC_OPS_STAT), + REG_NFC_OPS_STAT); + break; + } + udelay(1); + } + if (max_retries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "wait: INT not set\n"); +} + +static void send_cmd_atomic(struct mtd_info *mtd, u16 cmd) +{ + /* fill command */ + raw_write(cmd, REG_NFC_FLASH_CMD); + + /* clear status */ + ACK_OPS; + + /* send out command */ + raw_write(NFC_CMD, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +static void send_cmd_auto(struct mtd_info *mtd, u16 cmd) +{ +#ifdef CONFIG_MXC_NFC_SP_AUTO + switch (cmd) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_SEQIN: + case NAND_CMD_ERASE1: + raw_write(cmd, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE2: + case NAND_CMD_READSTART: + raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT, + REG_NFC_FLASH_CMD); + send_cmd_interleave(mtd, cmd); + break; + case NAND_CMD_READID: + send_atomic_cmd(cmd); + send_addr(0); + break; + case NAND_CMD_RESET: + send_cmd_interleave(mtd, cmd); + case NAND_CMD_STATUS: + break; + default: + break; + } +#endif +} + +/*! + * This function handle the interleave related work + * @param mtd mtd info + * @param cmd command + */ +static void send_cmd_interleave(struct mtd_info *mtd, u16 cmd) +{ +#ifdef CONFIG_MXC_NFC_SP_AUTO + u32 i; + u32 j = num_of_intlv; + struct nand_chip *this = mtd->priv; + u32 addr_low = raw_read(NFC_FLASH_ADDR0); + u32 addr_high = raw_read(NFC_FLASH_ADDR8); + u32 page_addr = addr_low >> 16 | addr_high << 16; + u8 *dbuf = mtd->info.data_buf; + u8 *obuf = mtd->info.oob_buf; + u32 dlen = mtd->oobblock / j; + u32 olen = mtd->oobsize / j; + + /* adjust the addr value + * since ADD_OP mode is 01 + */ + if (j > 1) + page_addr *= j; + else + page_addr *= this->numchips; + + for (i = 0; i < j; i++) { + if (cmd == NAND_CMD_PAGEPROG) { + + /* reset addr cycle */ + if (j > 1) + mxc_nand_addr_cycle(mtd, 0, page_addr++); + + /* data transfer */ + nfc_memcpy(MAIN_AREA0, dbuf, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, 0); + + /* update the value */ + dbuf += dlen; + obuf += olen; + + NFC_SET_RBA(0); + raw_write(0, REG_NFC_OPS_STAT); + raw_write(NFC_AUTO_PROG, REG_NFC_OPS); + + /* wait auto_prog_done bit set */ + if (i < j - 1) { + while (! + (raw_read(REG_NFC_OPS_STAT) & 1 << 30)) + ; + } else { + wait_op_done(TROP_US_DELAY); + } + } else if (cmd == NAND_CMD_READSTART) { + /* reset addr cycle */ + if (j > 1) + mxc_nand_addr_cycle(mtd, 0, page_addr++); + + NFC_SET_RBA(0); + raw_write(0, REG_NFC_OPS_STAT); + raw_write(NFC_AUTO_READ, REG_NFC_OPS); + wait_op_done(TROP_US_DELAY); + + /* check ecc error */ + mxc_nand_ecc_status(mtd); + + /* data transfer */ + nfc_memcpy(dbuf, MAIN_AREA0, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, 1); + + /* update the value */ + dbuf += dlen; + obuf += olen; + } else if (cmd == NAND_CMD_ERASE2) { + if (!i) { + page_addr = addr_low; + page_addr *= (j > 1 ? j : this->numchips); + } + mxc_nand_addr_cycle(mtd, -1, page_addr++); + raw_write(NFC_AUTO_ERASE, REG_NFC_OPS); + wait_op_done(TROP_US_DELAY); + } else if (cmd == NAND_CMD_RESET) { + NFC_SET_NFC_ACTIVE_CS(i); + send_atomic_cmd(cmd); + } + } +#endif +} + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(struct mtd_info *mtd, u16 cmd) +{ + + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (info->auto_mode) + send_cmd_auto(mtd, cmd); + else + send_cmd_atomic(mtd, cmd); + + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd); +} + +/*! + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param useirq True if IRQ should be used rather than polling + */ +static void send_addr(u16 addr) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr); + + /* fill address */ + raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR); + + /* clear status */ + ACK_OPS; + + /* send out address */ + raw_write(NFC_ADDR, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +/*! + * This function requests the NFC to initate the transfer + * of data currently in the NFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_prog_page(struct mtd_info *mtd, u8 buf_id) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (!info->auto_mode) { + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* clear status */ + ACK_OPS; + + /* transfer data from NFC ram to nand */ + raw_write(NFC_INPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __func__); + } +} + +/*! + * This function requests the NFC to initated the transfer + * of data from the NAND device into in the NFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_read_page(struct mtd_info *mtd, u8 buf_id) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (!info->auto_mode) { + + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* clear status */ + ACK_OPS; + + /* transfer data from nand to NFC ram */ + raw_write(NFC_OUTPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __func__, buf_id); + + } + +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + /* Set RBA bits for BUFFER0 */ + NFC_SET_RBA(0); + + /* clear status */ + ACK_OPS; + + /* Read ID into main buffer */ + raw_write(NFC_ID, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + +} + +static u16 mxc_do_status_auto(struct mtd_info *mtd) +{ + u16 status = 0; +#ifdef CONFIG_MXC_NFC_SP_AUTO + int i = 0; + u32 mask = 0xFF << 16; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + for (; i < info->num_of_intlv; i++) { + + /* set ative cs */ + NFC_SET_NFC_ACTIVE_CS(i); + + raw_write(NFC_AUTO_STATE, REG_NFC_OPS); + + /* FIXME, NFC Auto erase may have + * problem, have to pollingit until + * the nand get idle, otherwise + * it may get error + */ + do { + status = (raw_read(NFC_CONFIG1) & mask) >> 16; + } while ((status & NAND_STATUS_READY) == 0); + + if (status & NAND_STATUS_FAIL) + break; + } +#endif + return status; +} + +static u16 mxc_do_status_atomic(struct mtd_info *mtd) +{ + volatile u16 *mainBuf = MAIN_AREA1; + u8 val = 1; + u16 ret; + + /* Set ram buffer id */ + NFC_SET_RBA(val); + + /* clear status */ + ACK_OPS; + + /* Read status into main buffer */ + raw_write(NFC_STATUS, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = *mainBuf; + + return ret; +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 mxc_nand_get_status(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 status; + + if (info->auto_mode) + status = mxc_do_status_auto(mtd); + else + status = mxc_do_status_atomic(mtd); + + return status; + +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN); + return; +} + +/* + * Function to record the ECC corrected/uncorrected errors resulted + * after a page read. This NFC detects and corrects upto to 4 symbols + * of 9-bits each. + */ +static int mxc_nand_ecc_status(struct mtd_info *mtd) +{ + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask, err_limit; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + ecc_bit_mask = (IS_4BIT_ECC ? 0x7 : 0xf); + err_limit = (IS_4BIT_ECC ? 0x4 : 0x8); + + no_subpages = mtd->oobblock >> 9; + + no_subpages /= info->num_of_intlv; + + ecc_stat = GET_NFC_ECC_STATUS(); + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + DEBUG(MTD_DEBUG_LEVEL3, "%d Symbol Correctable RS-ECC Error\n", ret); + + return ret; +} + +/* + * Function to correct the detected errors. This NFC corrects all the errors + * detected. So this function just return 0. + */ +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + return 0; +} + +/* + * Function to calculate the ECC for the data to be stored in the Nand device. + * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC + * CONTROL blocks are responsible for detection and correction of up to + * 8 symbols of 9 bits each in 528 byte page. + * So this function is just return 0. + */ + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/*! + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 col = info->col_addr; + u8 *data_buf = info->data_buf; + u8 *oob_buf = info->oob_buf; + + if (mtd->oobblock) { + + int j = mtd->oobblock - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(buf, &data_buf[col], j); + memcpy(buf + j, &oob_buf[0], n - j); + } else { + memcpy(buf, &data_buf[col], n); + } + } else { + col -= mtd->oobblock; + memcpy(buf, &oob_buf[col], len); + } + + /* update */ + info->col_addr += n; + + } else { + /* At flash identify phase, + * mtd->oobblock has not been + * set correctly, it should + * be zero.And len will less 2 + */ + memcpy(buf, &data_buf[col], len); + + /* update */ + info->col_addr += len; + } + +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static uint8_t mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + uint8_t ret; + + /* Check for status request */ + if (info->status_req) + return mxc_nand_get_status(mtd) & 0xFF; + + mxc_nand_read_buf(mtd, &ret, 1); + + return ret; +} + +/*! + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 ret; + + mxc_nand_read_buf(mtd, (uint8_t *) &ret, sizeof(u16)); + + return ret; +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + /* Check for status request */ + if (info->status_req) + return mxc_nand_get_status(mtd) & 0xFF; + + return mxc_nand_read_word(mtd) & 0xFF; +} + +/*! + * This function writes data of length \b len from buffer \b buf to the NAND + * internal RAM buffer's MAIN area 0. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 col = info->col_addr; + u8 *data_buf = info->data_buf; + u8 *oob_buf = info->oob_buf; + int j = mtd->oobblock - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(&data_buf[col], buf, j); + memcpy(&oob_buf[0], buf + j, n - j); + } else { + memcpy(&data_buf[col], buf, n); + } + } else { + col -= mtd->oobblock; + memcpy(&oob_buf[col], buf, len); + } + + /* update */ + info->col_addr += n; +} + +/*! + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, + int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u_char *s = info->data_buf; + + const u_char *p = buf; + + for (; len > 0; len--) { + if (*p++ != *s++) + return -1; + } + + return 0; +} + +/*! + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + + switch (chip) { + case -1: + break; + + case 0 ... 7: + NFC_SET_NFC_ACTIVE_CS(chip); + break; + + default: + break; + } +} + +static void mxc_do_addr_cycle_auto(struct mtd_info *mtd, int column, + int page_addr) +{ +#ifdef CONFIG_MXC_NFC_SP_AUTO + if (page_addr != -1 && column != -1) { + u32 mask = 0xFFFF; + /* the column address */ + raw_write(column & mask, NFC_FLASH_ADDR0); + raw_write((raw_read(NFC_FLASH_ADDR0) | + ((page_addr & mask) << 16)), NFC_FLASH_ADDR0); + /* the row address */ + raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) | + ((page_addr & (mask << 16)) >> 16)), + NFC_FLASH_ADDR8); + } else if (page_addr != -1) { + raw_write(page_addr, NFC_FLASH_ADDR0); + } + + DEBUG(MTD_DEBUG_LEVEL3, + "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n", + raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8)); +#endif +} + +static void mxc_do_addr_cycle_atomic(struct mtd_info *mtd, int column, + int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + u32 page_mask = info->page_mask; + + if (column != -1) { + send_addr(column & 0xFF); + if (IS_2K_PAGE_NAND) { + /* another col addr cycle for 2k page */ + send_addr((column >> 8) & 0xF); + } else if (IS_4K_PAGE_NAND) { + /* another col addr cycle for 4k page */ + send_addr((column >> 8) & 0x1F); + } + } + if (page_addr != -1) { + do { + send_addr(page_addr & 0xff); + page_mask >>= 8; + page_addr >>= 8; + } while (page_mask != 0); + } +} + + +/* + * Function to perform the address cycles. + */ +static void mxc_nand_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (info->auto_mode) + mxc_do_addr_cycle_auto(mtd, column, page_addr); + else + mxc_do_addr_cycle_atomic(mtd, column, page_addr); +} + +/*! + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + /* + * Reset command state information + */ + info->status_req = 0; + + /* + * Command pre-processing step + */ + switch (command) { + case NAND_CMD_STATUS: + info->col_addr = 0; + info->status_req = 1; + break; + + case NAND_CMD_READ0: + info->col_addr = column; + break; + + case NAND_CMD_READOOB: + info->col_addr = column; + command = NAND_CMD_READ0; + break; + + case NAND_CMD_SEQIN: + if (column != 0) { + + /* FIXME: before send SEQIN command for + * partial write,We need read one page out. + * FSL NFC does not support partial write + * It alway send out 512+ecc+512+ecc ... + * for large page nand flash. But for small + * page nand flash, it did support SPARE + * ONLY operation. But to make driver + * simple. We take the same as large page,read + * whole page out and update. As for MLC nand + * NOP(num of operation) = 1. Partial written + * on one programed page is not allowed! We + * can't limit it on the driver, it need the + * upper layer applicaiton take care it + */ + + mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); + } + + info->col_addr = column; + break; + + case NAND_CMD_PAGEPROG: + if (!info->auto_mode) { + nfc_memcpy(MAIN_AREA0, info->data_buf, mtd->oobblock); + copy_spare(mtd, info->oob_buf, SPARE_AREA0, + mtd->oobsize, 0); + } + + send_prog_page(mtd, 0); + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(mtd, command); + + mxc_nand_addr_cycle(mtd, column, page_addr); + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (info->large_page) + /* send read confirm command */ + send_cmd(mtd, NAND_CMD_READSTART); + + send_read_page(mtd, 0); + + if (!info->auto_mode) { + mxc_nand_ecc_status(mtd); + nfc_memcpy(info->data_buf, MAIN_AREA0, mtd->oobblock); + copy_spare(mtd, info->oob_buf, SPARE_AREA0, + mtd->oobsize, 1); + } + break; + + case NAND_CMD_READID: + send_read_id(); + info->col_addr = column; + nfc_memcpy(info->data_buf, MAIN_AREA0, 2048); + break; + } +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + info->page_mask = this->pagemask; + + if (IS_2K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->autooob = &nand_hw_eccoob_2k; + info->large_page = 1; + } else if (IS_4K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->autooob = &nand_hw_eccoob_4k; + info->large_page = 1; + } else { + this->autooob = &nand_hw_eccoob_512; + info->large_page = 0; + } + + /* reconfig for interleave mode */ + + if (this->numchips > 1 && info->auto_mode) { + info->num_of_intlv = this->numchips; + this->numchips = 1; + + /* FIXEME:need remove it + * when kernel support + * 4G larger space + */ + mtd->size = this->chipsize; + mtd->erasesize *= info->num_of_intlv; + mtd->oobblock *= info->num_of_intlv; + mtd->oobsize *= info->num_of_intlv; + this->page_shift = ffs(mtd->oobblock) - 1; + this->bbt_erase_shift = + this->phys_erase_shift = ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + /*this->oob_poi = this->buffers->databuf + mtd->oobblock;*/ + } + + /* propagate ecc.layout to mtd_info */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + + /* jffs2 not write oob */ + /*mtd->flags &= ~MTD_OOB_WRITEABLE;*/ + + /* use flash based bbt */ + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +static void mxc_nfc_init(void) +{ + /* Disable interrupt */ + raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT); + + /* disable spare enable */ + raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN); + + /* Unlock the internal RAM Buffer */ + raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS); + + /* Blocks to be unlocked */ + UNLOCK_ADDR(0x0, 0xFFFF); + + /* Unlock Block Command for given address range */ + raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC); +} + +static int mxc_alloc_buf(struct nand_info *info) +{ + int err = 0; + + info->data_buf = kmalloc(NAND_MAX_PAGESIZE, GFP_KERNEL); + if (!info->data_buf) { + printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__); + err = -ENOMEM; + return err; + } + memset(info->data_buf, 0, NAND_MAX_PAGESIZE); + + info->oob_buf = kmalloc(NAND_MAX_OOBSIZE, GFP_KERNEL); + if (!info->oob_buf) { + printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__); + err = -ENOMEM; + return err; + } + memset(info->oob_buf, 0, NAND_MAX_OOBSIZE); + + return err; +} + +static void mxc_free_buf(struct nand_info *info) +{ + kfree(info->data_buf); + kfree(info->oob_buf); +} + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +int board_nand_init(struct nand_chip *nand) +{ + struct nand_info *info; + struct nand_chip *this = nand; + int err; + + info = kmalloc(sizeof(struct nand_info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "%s: failed to allocate nand_info\n", + __func__); + err = -ENOMEM; + return err; + } + memset(info, 0, sizeof(struct nand_info)); + + if (mxc_alloc_buf(info)) { + err = -ENOMEM; + return err; + } + + info->num_of_intlv = 1; + +#ifdef CONFIG_MXC_NFC_SP_AUTO + info->auto_mode = 1; +#endif + /* init the nfc */ + mxc_nfc_init(); + + this->priv = info; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + this->calculate_ecc = mxc_nand_calculate_ecc; + this->correct_data = mxc_nand_correct_data; + this->enable_hwecc = mxc_nand_enable_hwecc; + this->eccmode = NAND_ECC_HW3_512; + this->eccbytes = 9; + this->eccsize = 512; + + return 0; + +} diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 360b070..29c0d2d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -867,8 +867,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) while (1) { if (get_timer(0) > timeo) { - printf("Timeout!"); - return 0x01; + /*printf("Timeout!");*/ + /*return 0x01;*/ } if (this->dev_ready) { diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 077c305..f48cccb 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -23,14 +23,13 @@ * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { - -#ifdef CONFIG_MTD_NAND_MUSEUM_IDS - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, +#if 0 + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, @@ -41,7 +40,6 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, #endif - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, @@ -124,7 +122,6 @@ struct nand_flash_dev nand_flash_ids[] = { NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH }, - {NULL,} }; diff --git a/include/asm-arm/arch-mx35/mx35.h b/include/asm-arm/arch-mx35/mx35.h index a6af3c9..ba30597 100644 --- a/include/asm-arm/arch-mx35/mx35.h +++ b/include/asm-arm/arch-mx35/mx35.h @@ -189,6 +189,14 @@ MXC_IPG_PERCLK, MXC_UART_CLK, }; +/*! + * NFMS bit in RCSR register for pagesize of nandflash + */ +#define NFMS (*((volatile u32 *)(CCM_BASE_ADDR+0x18))) +#define NFMS_BIT 8 +#define NFMS_NF_DWIDTH 14 +#define NFMS_NF_PG_SZ 8 + extern unsigned int mxc_get_clock(enum mxc_clock clk); #define fixup_before_linux \ diff --git a/include/asm-arm/arch-mx35/mxc_nand.h b/include/asm-arm/arch-mx35/mxc_nand.h new file mode 100644 index 0000000..f4d2ae7 --- /dev/null +++ b/include/asm-arm/arch-mx35/mxc_nand.h @@ -0,0 +1,198 @@ +/* + * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file mxc_nd2.h + * + * @brief This file contains the NAND Flash Controller register information. + * + * + * @ingroup NAND_MTD + */ + +#ifndef __MXC_NAND_H__ +#define __MXC_NAND_H__ + +#include <asm/arch/mx35.h> + +#define IS_2K_PAGE_NAND ((mtd->oobblock / info->num_of_intlv) \ + == NAND_PAGESIZE_2KB) +#define IS_4K_PAGE_NAND ((mtd->oobblock / info->num_of_intlv) \ + == NAND_PAGESIZE_4KB) +#define IS_LARGE_PAGE_NAND ((mtd->oobblock / info->num_of_intlv) > 512) + +#define NAND_PAGESIZE_2KB 2048 +#define NAND_PAGESIZE_4KB 4096 +#define NAND_MAX_PAGESIZE 4096 + +/* + * Addresses for NFC registers + */ +#define NFC_REG_BASE (NFC_BASE_ADDR + 0x1000) +#define NFC_BUF_ADDR (NFC_REG_BASE + 0xE04) +#define NFC_FLASH_ADDR (NFC_REG_BASE + 0xE06) +#define NFC_FLASH_CMD (NFC_REG_BASE + 0xE08) +#define NFC_CONFIG (NFC_REG_BASE + 0xE0A) +#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C) +#define NFC_SPAS (NFC_REG_BASE + 0xE10) +#define NFC_WRPROT (NFC_REG_BASE + 0xE12) +#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE20) +#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE22) +#define NFC_CONFIG1 (NFC_REG_BASE + 0xE1A) +#define NFC_CONFIG2 (NFC_REG_BASE + 0xE1C) + +/*! + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (u16 *)(NFC_BASE_ADDR + 0x000) +#define MAIN_AREA1 (u16 *)(NFC_BASE_ADDR + 0x200) + +/*! + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#define SPARE_AREA0 (u16 *)(NFC_BASE_ADDR + 0x1000) +#define SPARE_LEN 64 +#define SPARE_COUNT 8 +#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT) + + +#define SPAS_SHIFT (0) +#define SPAS_MASK (0xFF00) +#define IS_4BIT_ECC \ + ((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_4) >> 0) + +#define NFC_SET_SPAS(v) \ + raw_write(((raw_read(REG_NFC_SPAS) & SPAS_MASK) | \ + ((v<<SPAS_SHIFT))), \ + REG_NFC_SPAS) + +#define NFC_SET_ECC_MODE(v) \ +do { \ + if ((v) == NFC_SPAS_218) { \ + raw_write((raw_read(REG_NFC_ECC_MODE) & \ + NFC_ECC_MODE_8), \ + REG_NFC_ECC_MODE); \ + } else { \ + raw_write((raw_read(REG_NFC_ECC_MODE) | \ + NFC_ECC_MODE_4), \ + REG_NFC_ECC_MODE); \ + } \ +} while (0) + +#define GET_ECC_STATUS() \ + __raw_readl(REG_NFC_ECC_STATUS_RESULT); + +#define NFC_SET_NFMS(v) \ +do { \ + (NFMS |= (v)); \ + if (((v) & (1 << NFMS_NF_PG_SZ))) { \ + if (IS_2K_PAGE_NAND) { \ + NFC_SET_SPAS(NFC_SPAS_64); \ + } else if (IS_4K_PAGE_NAND) { \ + NFC_SET_SPAS(NFC_SPAS_128); \ + } else { \ + NFC_SET_SPAS(NFC_SPAS_16); \ + } \ + NFC_SET_ECC_MODE(NFC_SPAS_128); \ + } \ +} while (0) + + +#define WRITE_NFC_IP_REG(val, reg) \ + raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \ + REG_NFC_OPS_STAT) + +#define GET_NFC_ECC_STATUS() \ + raw_read(REG_NFC_ECC_STATUS_RESULT); + +/*! + * Set INT to 0, Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for + * Specific operation + */ +#define NFC_CMD 0x1 +#define NFC_ADDR 0x2 +#define NFC_INPUT 0x4 +#define NFC_OUTPUT 0x8 +#define NFC_ID 0x10 +#define NFC_STATUS 0x20 + +/* Bit Definitions */ +#define NFC_OPS_STAT (1 << 15) +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_BLS_LOCKED 0 +#define NFC_BLS_LOCKED_DEFAULT 1 +#define NFC_BLS_UNLCOKED 2 +#define NFC_WPC_LOCK_TIGHT 1 +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) +#define NFC_FLASH_ADDR_SHIFT 0 +#define NFC_UNLOCK_END_ADDR_SHIFT 0 + +#define NFC_ECC_MODE_4 (1<<0) +#define NFC_ECC_MODE_8 (~(1<<0)) +#define NFC_SPAS_16 8 +#define NFC_SPAS_64 32 +#define NFC_SPAS_128 64 +#define NFC_SPAS_218 109 + +/* NFC Register Mapping */ +#define REG_NFC_OPS_STAT NFC_CONFIG2 +#define REG_NFC_INTRRUPT NFC_CONFIG1 +#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR +#define REG_NFC_FLASH_CMD NFC_FLASH_CMD +#define REG_NFC_OPS NFC_CONFIG2 +#define REG_NFC_SET_RBA NFC_BUF_ADDR +#define REG_NFC_ECC_EN NFC_CONFIG1 +#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT +#define REG_NFC_CE NFC_CONFIG1 +#define REG_NFC_SP_EN NFC_CONFIG1 +#define REG_NFC_BLS NFC_CONFIG +#define REG_NFC_WPC NFC_WRPROT +#define REG_START_BLKADDR NFC_UNLOCKSTART_BLKADDR +#define REG_END_BLKADDR NFC_UNLOCKEND_BLKADDR +#define REG_NFC_RST NFC_CONFIG1 +#define REG_NFC_ECC_MODE NFC_CONFIG1 +#define REG_NFC_SPAS NFC_SPAS + + +/* NFC V1/V2 Specific MACRO functions definitions */ + +#define raw_write(v, a) __raw_writew(v, a) +#define raw_read(a) __raw_readw(a) + +#define NFC_SET_BLS(val) val + +#define UNLOCK_ADDR(start_addr, end_addr) \ +{ \ + raw_write(start_addr, REG_START_BLKADDR); \ + raw_write(end_addr, REG_END_BLKADDR); \ +} + +#define NFC_SET_NFC_ACTIVE_CS(val) +#define NFC_SET_WPC(val) val + +/* NULL Definitions */ +#define ACK_OPS +#define NFC_SET_RBA(val) raw_write(val, REG_NFC_SET_RBA); + +#define READ_PAGE() send_read_page(0) +#define PROG_PAGE() send_prog_page(0) +#define CHECK_NFC_RB 1 + +#endif /* __MXC_NAND_H__ */ diff --git a/include/configs/mx35_3stack.h b/include/configs/mx35_3stack.h index 96fef22..39be55c 100644 --- a/include/configs/mx35_3stack.h +++ b/include/configs/mx35_3stack.h @@ -54,7 +54,7 @@ /* * Size of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128 * 1024) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 512 * 1024) #define CONFIG_SYS_GBL_DATA_SIZE 128/* size in bytes reserved for initial data */ /* @@ -82,6 +82,9 @@ #define CONFIG_CMD_PING #define CONFIG_CMD_DHCP +/*#define CONFIG_CMD_SPI*/ +/*#define CONFIG_CMD_DATE*/ +#define CONFIG_CMD_NAND #define CONFIG_CMD_I2C #define CONFIG_CMD_MII @@ -209,6 +212,12 @@ #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1/* Use buffered writes (~10x faster) */ #define CONFIG_SYS_FLASH_PROTECTION 1/* Use hardware sector protection */ +/*----------------------------------------------------------------------- + * NAND FLASH driver setup + */ +#define NAND_MAX_CHIPS 1 +#define CFG_MAX_NAND_DEVICE 1 +#define CFG_NAND_BASE 0x40000000 /* * JFFS2 partitions */ |