Merge tag 'for-linus-3.5-20120601' of git://git.infradead.org/linux-mtd
Pull mtd update from David Woodhouse: - More robust parsing especially of xattr data in JFFS2 - Updates to mxc_nand and gpmi drivers to support new boards and device tree - Improve consistency of information about ECC strength in NAND devices - Clean up partition handling of plat_nand - Support NAND drivers without dedicated access to OOB area - BCH hardware ECC support for OMAP - Other fixes and cleanups, and a few new device IDs Fixed trivial conflict in drivers/mtd/nand/gpmi-nand/gpmi-nand.c due to added include files next to each other. * tag 'for-linus-3.5-20120601' of git://git.infradead.org/linux-mtd: (75 commits) mtd: mxc_nand: move ecc strengh setup before nand_scan_tail mtd: block2mtd: fix recursive call of mtd_writev mtd: gpmi-nand: define ecc.strength mtd: of_parts: fix breakage in Kconfig mtd: nand: fix scan_read_raw_oob mtd: docg3 fix in-middle of blocks reads mtd: cfi_cmdset_0002: Slight cleanup of fixup messages mtd: add fixup for S29NS512P NOR flash. jffs2: allow to complete xattr integrity check on first GC scan jffs2: allow to discriminate between recoverable and non-recoverable errors mtd: nand: omap: add support for hardware BCH ecc ARM: OMAP3: gpmc: add BCH ecc api and modes mtd: nand: check the return code of 'read_oob/read_oob_raw' mtd: nand: remove 'sndcmd' parameter of 'read_oob/read_oob_raw' mtd: m25p80: Add support for Winbond W25Q80BW jffs2: get rid of jffs2_sync_super jffs2: remove unnecessary GC pass on sync jffs2: remove unnecessary GC pass on umount jffs2: remove lock_super mtd: gpmi: add gpmi support for mx6q ...
This commit is contained in:
@@ -32,6 +32,8 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mtd.h>
|
||||
|
||||
#include <asm/mach/flash.h>
|
||||
#include <mach/mxc_nand.h>
|
||||
@@ -140,13 +142,47 @@
|
||||
|
||||
#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34)
|
||||
|
||||
struct mxc_nand_host;
|
||||
|
||||
struct mxc_nand_devtype_data {
|
||||
void (*preset)(struct mtd_info *);
|
||||
void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_page)(struct mtd_info *, unsigned int);
|
||||
void (*send_read_id)(struct mxc_nand_host *);
|
||||
uint16_t (*get_dev_status)(struct mxc_nand_host *);
|
||||
int (*check_int)(struct mxc_nand_host *);
|
||||
void (*irq_control)(struct mxc_nand_host *, int);
|
||||
u32 (*get_ecc_status)(struct mxc_nand_host *);
|
||||
struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k;
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*correct_data)(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc);
|
||||
|
||||
/*
|
||||
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
|
||||
* (CONFIG1:INT_MSK is set). To handle this the driver uses
|
||||
* enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
|
||||
*/
|
||||
int irqpending_quirk;
|
||||
int needs_ip;
|
||||
|
||||
size_t regs_offset;
|
||||
size_t spare0_offset;
|
||||
size_t axi_offset;
|
||||
|
||||
int spare_len;
|
||||
int eccbytes;
|
||||
int eccsize;
|
||||
};
|
||||
|
||||
struct mxc_nand_host {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip nand;
|
||||
struct device *dev;
|
||||
|
||||
void *spare0;
|
||||
void *main_area0;
|
||||
void __iomem *spare0;
|
||||
void __iomem *main_area0;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *regs;
|
||||
@@ -163,16 +199,9 @@ struct mxc_nand_host {
|
||||
|
||||
uint8_t *data_buf;
|
||||
unsigned int buf_start;
|
||||
int spare_len;
|
||||
|
||||
void (*preset)(struct mtd_info *);
|
||||
void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_page)(struct mtd_info *, unsigned int);
|
||||
void (*send_read_id)(struct mxc_nand_host *);
|
||||
uint16_t (*get_dev_status)(struct mxc_nand_host *);
|
||||
int (*check_int)(struct mxc_nand_host *);
|
||||
void (*irq_control)(struct mxc_nand_host *, int);
|
||||
const struct mxc_nand_devtype_data *devtype_data;
|
||||
struct mxc_nand_platform_data pdata;
|
||||
};
|
||||
|
||||
/* OOB placement block for use with hardware ecc generation */
|
||||
@@ -242,21 +271,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = {
|
||||
}
|
||||
};
|
||||
|
||||
static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mxc_nand_host *host = dev_id;
|
||||
|
||||
if (!host->check_int(host))
|
||||
return IRQ_NONE;
|
||||
|
||||
host->irq_control(host, 0);
|
||||
|
||||
complete(&host->op_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL };
|
||||
|
||||
static int check_int_v3(struct mxc_nand_host *host)
|
||||
{
|
||||
@@ -280,26 +295,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host)
|
||||
if (!(tmp & NFC_V1_V2_CONFIG2_INT))
|
||||
return 0;
|
||||
|
||||
if (!cpu_is_mx21())
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* It has been observed that the i.MX21 cannot read the CONFIG2:INT bit
|
||||
* if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the
|
||||
* driver can enable/disable the irq line rather than simply masking the
|
||||
* interrupts.
|
||||
*/
|
||||
static void irq_control_mx21(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
if (activate)
|
||||
enable_irq(host->irq);
|
||||
else
|
||||
disable_irq_nosync(host->irq);
|
||||
}
|
||||
|
||||
static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
uint16_t tmp;
|
||||
@@ -328,6 +329,47 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate)
|
||||
writel(tmp, NFC_V3_CONFIG2);
|
||||
}
|
||||
|
||||
static void irq_control(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
if (host->devtype_data->irqpending_quirk) {
|
||||
if (activate)
|
||||
enable_irq(host->irq);
|
||||
else
|
||||
disable_irq_nosync(host->irq);
|
||||
} else {
|
||||
host->devtype_data->irq_control(host, activate);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v1(struct mxc_nand_host *host)
|
||||
{
|
||||
return readw(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v2(struct mxc_nand_host *host)
|
||||
{
|
||||
return readl(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v3(struct mxc_nand_host *host)
|
||||
{
|
||||
return readl(NFC_V3_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mxc_nand_host *host = dev_id;
|
||||
|
||||
if (!host->devtype_data->check_int(host))
|
||||
return IRQ_NONE;
|
||||
|
||||
irq_control(host, 0);
|
||||
|
||||
complete(&host->op_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* This function polls the NANDFC to wait for the basic operation to
|
||||
* complete by checking the INT bit of config2 register.
|
||||
*/
|
||||
@@ -336,14 +378,14 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
|
||||
int max_retries = 8000;
|
||||
|
||||
if (useirq) {
|
||||
if (!host->check_int(host)) {
|
||||
if (!host->devtype_data->check_int(host)) {
|
||||
INIT_COMPLETION(host->op_completion);
|
||||
host->irq_control(host, 1);
|
||||
irq_control(host, 1);
|
||||
wait_for_completion(&host->op_completion);
|
||||
}
|
||||
} else {
|
||||
while (max_retries-- > 0) {
|
||||
if (host->check_int(host))
|
||||
if (host->devtype_data->check_int(host))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
@@ -374,7 +416,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
|
||||
writew(cmd, NFC_V1_V2_FLASH_CMD);
|
||||
writew(NFC_CMD, NFC_V1_V2_CONFIG2);
|
||||
|
||||
if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
|
||||
if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
|
||||
int max_retries = 100;
|
||||
/* Reset completion is indicated by NFC_CONFIG2 */
|
||||
/* being set to 0 */
|
||||
@@ -433,13 +475,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
|
||||
wait_op_done(host, false);
|
||||
}
|
||||
|
||||
static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
|
||||
static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
||||
/* NANDFC buffer 0 is used for page read/write */
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
|
||||
writew(ops, NFC_V1_V2_CONFIG2);
|
||||
|
||||
/* Wait for operation to complete */
|
||||
wait_op_done(host, true);
|
||||
}
|
||||
|
||||
static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
int bufs, i;
|
||||
|
||||
if (nfc_is_v1() && mtd->writesize > 512)
|
||||
if (mtd->writesize > 512)
|
||||
bufs = 4;
|
||||
else
|
||||
bufs = 1;
|
||||
@@ -463,7 +519,7 @@ static void send_read_id_v3(struct mxc_nand_host *host)
|
||||
|
||||
wait_op_done(host, true);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, 16);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, 16);
|
||||
}
|
||||
|
||||
/* Request the NANDFC to perform a read of the NAND device ID. */
|
||||
@@ -479,7 +535,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)
|
||||
/* Wait for operation to complete */
|
||||
wait_op_done(host, true);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, 16);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, 16);
|
||||
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
/* compress the ID info */
|
||||
@@ -555,7 +611,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
|
||||
* additional correction. 2-Bit errors cannot be corrected by
|
||||
* HW ECC, so we need to return failure
|
||||
*/
|
||||
uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
uint16_t ecc_status = get_ecc_status_v1(host);
|
||||
|
||||
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
|
||||
pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
|
||||
@@ -580,10 +636,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
|
||||
|
||||
no_subpages = mtd->writesize >> 9;
|
||||
|
||||
if (nfc_is_v21())
|
||||
ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
else
|
||||
ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);
|
||||
ecc_stat = host->devtype_data->get_ecc_status(host);
|
||||
|
||||
do {
|
||||
err = ecc_stat & ecc_bit_mask;
|
||||
@@ -616,7 +669,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)
|
||||
|
||||
/* Check for status request */
|
||||
if (host->status_request)
|
||||
return host->get_dev_status(host) & 0xFF;
|
||||
return host->devtype_data->get_dev_status(host) & 0xFF;
|
||||
|
||||
ret = *(uint8_t *)(host->data_buf + host->buf_start);
|
||||
host->buf_start++;
|
||||
@@ -682,7 +735,7 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd,
|
||||
|
||||
/* This function is used by upper layer for select and
|
||||
* deselect of the NAND chip */
|
||||
static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
@@ -701,11 +754,30 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
clk_prepare_enable(host->clk);
|
||||
host->clk_act = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfc_is_v21()) {
|
||||
host->active_cs = chip;
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
||||
if (chip == -1) {
|
||||
/* Disable the NFC clock */
|
||||
if (host->clk_act) {
|
||||
clk_disable(host->clk);
|
||||
host->clk_act = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!host->clk_act) {
|
||||
/* Enable the NFC clock */
|
||||
clk_enable(host->clk);
|
||||
host->clk_act = 1;
|
||||
}
|
||||
|
||||
host->active_cs = chip;
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -718,23 +790,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom)
|
||||
u16 i, j;
|
||||
u16 n = mtd->writesize >> 9;
|
||||
u8 *d = host->data_buf + mtd->writesize;
|
||||
u8 *s = host->spare0;
|
||||
u16 t = host->spare_len;
|
||||
u8 __iomem *s = host->spare0;
|
||||
u16 t = host->devtype_data->spare_len;
|
||||
|
||||
j = (mtd->oobsize / n >> 1) << 1;
|
||||
|
||||
if (bfrom) {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy(d + i * j, s + i * t, j);
|
||||
memcpy_fromio(d + i * j, s + i * t, j);
|
||||
|
||||
/* the last section */
|
||||
memcpy(d + i * j, s + i * t, mtd->oobsize - i * j);
|
||||
memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
|
||||
} else {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy(&s[i * t], &d[i * j], j);
|
||||
memcpy_toio(&s[i * t], &d[i * j], j);
|
||||
|
||||
/* the last section */
|
||||
memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
||||
memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,34 +823,44 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
|
||||
* perform a read/write buf operation, the saved column
|
||||
* address is used to index into the full page.
|
||||
*/
|
||||
host->send_addr(host, 0, page_addr == -1);
|
||||
host->devtype_data->send_addr(host, 0, page_addr == -1);
|
||||
if (mtd->writesize > 512)
|
||||
/* another col addr cycle for 2k page */
|
||||
host->send_addr(host, 0, false);
|
||||
host->devtype_data->send_addr(host, 0, false);
|
||||
}
|
||||
|
||||
/* Write out page address, if necessary */
|
||||
if (page_addr != -1) {
|
||||
/* paddr_0 - p_addr_7 */
|
||||
host->send_addr(host, (page_addr & 0xff), false);
|
||||
host->devtype_data->send_addr(host, (page_addr & 0xff), false);
|
||||
|
||||
if (mtd->writesize > 512) {
|
||||
if (mtd->size >= 0x10000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
host->send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff,
|
||||
false);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 16) & 0xff,
|
||||
true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff, true);
|
||||
} else {
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size >= 0x4000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
host->send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff,
|
||||
false);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 16) & 0xff,
|
||||
true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -800,7 +882,7 @@ static int get_eccsize(struct mtd_info *mtd)
|
||||
return 8;
|
||||
}
|
||||
|
||||
static void preset_v1_v2(struct mtd_info *mtd)
|
||||
static void preset_v1(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
@@ -809,13 +891,40 @@ static void preset_v1_v2(struct mtd_info *mtd)
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
if (nfc_is_v21())
|
||||
config1 |= NFC_V2_CONFIG1_FP_INT;
|
||||
|
||||
if (!cpu_is_mx21())
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
|
||||
|
||||
if (nfc_is_v21() && mtd->writesize) {
|
||||
host->eccsize = 1;
|
||||
|
||||
writew(config1, NFC_V1_V2_CONFIG1);
|
||||
/* preset operation */
|
||||
|
||||
/* Unlock the internal RAM Buffer */
|
||||
writew(0x2, NFC_V1_V2_CONFIG);
|
||||
|
||||
/* Blocks to be unlocked */
|
||||
writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
|
||||
writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
|
||||
|
||||
/* Unlock Block Command for given address range */
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
}
|
||||
|
||||
static void preset_v2(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
uint16_t config1 = 0;
|
||||
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
config1 |= NFC_V2_CONFIG1_FP_INT;
|
||||
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
|
||||
|
||||
if (mtd->writesize) {
|
||||
uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
|
||||
|
||||
host->eccsize = get_eccsize(mtd);
|
||||
@@ -834,20 +943,14 @@ static void preset_v1_v2(struct mtd_info *mtd)
|
||||
writew(0x2, NFC_V1_V2_CONFIG);
|
||||
|
||||
/* Blocks to be unlocked */
|
||||
if (nfc_is_v21()) {
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
|
||||
} else if (nfc_is_v1()) {
|
||||
writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
|
||||
writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
|
||||
} else
|
||||
BUG();
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
|
||||
|
||||
/* Unlock Block Command for given address range */
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
@@ -937,15 +1040,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
||||
/* Command pre-processing step */
|
||||
switch (command) {
|
||||
case NAND_CMD_RESET:
|
||||
host->preset(mtd);
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->preset(mtd);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
break;
|
||||
|
||||
case NAND_CMD_STATUS:
|
||||
host->buf_start = 0;
|
||||
host->status_request = true;
|
||||
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
@@ -958,15 +1061,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
||||
|
||||
command = NAND_CMD_READ0; /* only READ0 is valid */
|
||||
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
|
||||
if (mtd->writesize > 512)
|
||||
host->send_cmd(host, NAND_CMD_READSTART, true);
|
||||
host->devtype_data->send_cmd(host,
|
||||
NAND_CMD_READSTART, true);
|
||||
|
||||
host->send_page(mtd, NFC_OUTPUT);
|
||||
host->devtype_data->send_page(mtd, NFC_OUTPUT);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, mtd->writesize);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize);
|
||||
copy_spare(mtd, true);
|
||||
break;
|
||||
|
||||
@@ -977,28 +1081,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
||||
|
||||
host->buf_start = column;
|
||||
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
memcpy(host->main_area0, host->data_buf, mtd->writesize);
|
||||
memcpy_toio(host->main_area0, host->data_buf, mtd->writesize);
|
||||
copy_spare(mtd, false);
|
||||
host->send_page(mtd, NFC_INPUT);
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_page(mtd, NFC_INPUT);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
host->send_read_id(host);
|
||||
host->devtype_data->send_read_id(host);
|
||||
host->buf_start = column;
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
|
||||
break;
|
||||
@@ -1032,15 +1136,191 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
||||
.pattern = mirror_pattern,
|
||||
};
|
||||
|
||||
/* v1 + irqpending_quirk: i.MX21 */
|
||||
static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
|
||||
.preset = preset_v1,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v1,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v1,
|
||||
.ecclayout_512 = &nandv1_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv1_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v1,
|
||||
.irqpending_quirk = 1,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0xe00,
|
||||
.spare0_offset = 0x800,
|
||||
.spare_len = 16,
|
||||
.eccbytes = 3,
|
||||
.eccsize = 1,
|
||||
};
|
||||
|
||||
/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
|
||||
static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
|
||||
.preset = preset_v1,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v1,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v1,
|
||||
.ecclayout_512 = &nandv1_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv1_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v1,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0xe00,
|
||||
.spare0_offset = 0x800,
|
||||
.axi_offset = 0,
|
||||
.spare_len = 16,
|
||||
.eccbytes = 3,
|
||||
.eccsize = 1,
|
||||
};
|
||||
|
||||
/* v21: i.MX25, i.MX35 */
|
||||
static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
|
||||
.preset = preset_v2,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v2,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v2,
|
||||
.ecclayout_512 = &nandv2_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv2_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv2_hw_eccoob_4k,
|
||||
.select_chip = mxc_nand_select_chip_v2,
|
||||
.correct_data = mxc_nand_correct_data_v2_v3,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0x1e00,
|
||||
.spare0_offset = 0x1000,
|
||||
.axi_offset = 0,
|
||||
.spare_len = 64,
|
||||
.eccbytes = 9,
|
||||
.eccsize = 0,
|
||||
};
|
||||
|
||||
/* v3: i.MX51, i.MX53 */
|
||||
static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
|
||||
.preset = preset_v3,
|
||||
.send_cmd = send_cmd_v3,
|
||||
.send_addr = send_addr_v3,
|
||||
.send_page = send_page_v3,
|
||||
.send_read_id = send_read_id_v3,
|
||||
.get_dev_status = get_dev_status_v3,
|
||||
.check_int = check_int_v3,
|
||||
.irq_control = irq_control_v3,
|
||||
.get_ecc_status = get_ecc_status_v3,
|
||||
.ecclayout_512 = &nandv2_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv2_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v2_v3,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 1,
|
||||
.regs_offset = 0,
|
||||
.spare0_offset = 0x1000,
|
||||
.axi_offset = 0x1e00,
|
||||
.spare_len = 64,
|
||||
.eccbytes = 0,
|
||||
.eccsize = 0,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF_MTD
|
||||
static const struct of_device_id mxcnd_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-nand",
|
||||
.data = &imx21_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx27-nand",
|
||||
.data = &imx27_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx25-nand",
|
||||
.data = &imx25_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx51-nand",
|
||||
.data = &imx51_nand_devtype_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
struct mxc_nand_platform_data *pdata = &host->pdata;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxcnd_dt_ids, host->dev);
|
||||
int buswidth;
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
if (of_get_nand_ecc_mode(np) >= 0)
|
||||
pdata->hw_ecc = 1;
|
||||
|
||||
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
|
||||
|
||||
buswidth = of_get_nand_bus_width(np);
|
||||
if (buswidth < 0)
|
||||
return buswidth;
|
||||
|
||||
pdata->width = buswidth / 8;
|
||||
|
||||
host->devtype_data = of_id->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init mxcnd_probe_pdata(struct mxc_nand_host *host)
|
||||
{
|
||||
struct mxc_nand_platform_data *pdata = host->dev->platform_data;
|
||||
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
host->pdata = *pdata;
|
||||
|
||||
if (nfc_is_v1()) {
|
||||
if (cpu_is_mx21())
|
||||
host->devtype_data = &imx21_nand_devtype_data;
|
||||
else
|
||||
host->devtype_data = &imx27_nand_devtype_data;
|
||||
} else if (nfc_is_v21()) {
|
||||
host->devtype_data = &imx25_nand_devtype_data;
|
||||
} else if (nfc_is_v3_2()) {
|
||||
host->devtype_data = &imx51_nand_devtype_data;
|
||||
} else
|
||||
BUG();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
struct mtd_info *mtd;
|
||||
struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct mxc_nand_host *host;
|
||||
struct resource *res;
|
||||
int err = 0;
|
||||
struct nand_ecclayout *oob_smallpage, *oob_largepage;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE +
|
||||
@@ -1065,7 +1345,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
this->priv = host;
|
||||
this->dev_ready = mxc_nand_dev_ready;
|
||||
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;
|
||||
@@ -1095,36 +1374,26 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
|
||||
host->main_area0 = host->base;
|
||||
|
||||
if (nfc_is_v1() || nfc_is_v21()) {
|
||||
host->preset = preset_v1_v2;
|
||||
host->send_cmd = send_cmd_v1_v2;
|
||||
host->send_addr = send_addr_v1_v2;
|
||||
host->send_page = send_page_v1_v2;
|
||||
host->send_read_id = send_read_id_v1_v2;
|
||||
host->get_dev_status = get_dev_status_v1_v2;
|
||||
host->check_int = check_int_v1_v2;
|
||||
if (cpu_is_mx21())
|
||||
host->irq_control = irq_control_mx21;
|
||||
else
|
||||
host->irq_control = irq_control_v1_v2;
|
||||
}
|
||||
err = mxcnd_probe_dt(host);
|
||||
if (err > 0)
|
||||
err = mxcnd_probe_pdata(host);
|
||||
if (err < 0)
|
||||
goto eirq;
|
||||
|
||||
if (nfc_is_v21()) {
|
||||
host->regs = host->base + 0x1e00;
|
||||
host->spare0 = host->base + 0x1000;
|
||||
host->spare_len = 64;
|
||||
oob_smallpage = &nandv2_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv2_hw_eccoob_largepage;
|
||||
this->ecc.bytes = 9;
|
||||
} else if (nfc_is_v1()) {
|
||||
host->regs = host->base + 0xe00;
|
||||
host->spare0 = host->base + 0x800;
|
||||
host->spare_len = 16;
|
||||
oob_smallpage = &nandv1_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv1_hw_eccoob_largepage;
|
||||
this->ecc.bytes = 3;
|
||||
host->eccsize = 1;
|
||||
} else if (nfc_is_v3_2()) {
|
||||
if (host->devtype_data->regs_offset)
|
||||
host->regs = host->base + host->devtype_data->regs_offset;
|
||||
host->spare0 = host->base + host->devtype_data->spare0_offset;
|
||||
if (host->devtype_data->axi_offset)
|
||||
host->regs_axi = host->base + host->devtype_data->axi_offset;
|
||||
|
||||
this->ecc.bytes = host->devtype_data->eccbytes;
|
||||
host->eccsize = host->devtype_data->eccsize;
|
||||
|
||||
this->select_chip = host->devtype_data->select_chip;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.layout = host->devtype_data->ecclayout_512;
|
||||
|
||||
if (host->devtype_data->needs_ip) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
err = -ENODEV;
|
||||
@@ -1135,42 +1404,22 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
err = -ENOMEM;
|
||||
goto eirq;
|
||||
}
|
||||
host->regs_axi = host->base + 0x1e00;
|
||||
host->spare0 = host->base + 0x1000;
|
||||
host->spare_len = 64;
|
||||
host->preset = preset_v3;
|
||||
host->send_cmd = send_cmd_v3;
|
||||
host->send_addr = send_addr_v3;
|
||||
host->send_page = send_page_v3;
|
||||
host->send_read_id = send_read_id_v3;
|
||||
host->check_int = check_int_v3;
|
||||
host->get_dev_status = get_dev_status_v3;
|
||||
host->irq_control = irq_control_v3;
|
||||
oob_smallpage = &nandv2_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv2_hw_eccoob_largepage;
|
||||
} else
|
||||
BUG();
|
||||
}
|
||||
|
||||
this->ecc.size = 512;
|
||||
this->ecc.layout = oob_smallpage;
|
||||
|
||||
if (pdata->hw_ecc) {
|
||||
if (host->pdata.hw_ecc) {
|
||||
this->ecc.calculate = mxc_nand_calculate_ecc;
|
||||
this->ecc.hwctl = mxc_nand_enable_hwecc;
|
||||
if (nfc_is_v1())
|
||||
this->ecc.correct = mxc_nand_correct_data_v1;
|
||||
else
|
||||
this->ecc.correct = mxc_nand_correct_data_v2_v3;
|
||||
this->ecc.correct = host->devtype_data->correct_data;
|
||||
this->ecc.mode = NAND_ECC_HW;
|
||||
} else {
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
}
|
||||
|
||||
/* NAND bus width determines access funtions used by upper layer */
|
||||
if (pdata->width == 2)
|
||||
/* NAND bus width determines access functions used by upper layer */
|
||||
if (host->pdata.width == 2)
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (pdata->flash_bbt) {
|
||||
if (host->pdata.flash_bbt) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
/* update flash based bbt */
|
||||
@@ -1182,28 +1431,25 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
/*
|
||||
* mask the interrupt. For i.MX21 explicitely call
|
||||
* irq_control_v1_v2 to use the mask bit. We can't call
|
||||
* disable_irq_nosync() for an interrupt we do not own yet.
|
||||
* Use host->devtype_data->irq_control() here instead of irq_control()
|
||||
* because we must not disable_irq_nosync without having requested the
|
||||
* irq.
|
||||
*/
|
||||
if (cpu_is_mx21())
|
||||
irq_control_v1_v2(host, 0);
|
||||
else
|
||||
host->irq_control(host, 0);
|
||||
host->devtype_data->irq_control(host, 0);
|
||||
|
||||
err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
|
||||
if (err)
|
||||
goto eirq;
|
||||
|
||||
host->irq_control(host, 0);
|
||||
|
||||
/*
|
||||
* Now that the interrupt is disabled make sure the interrupt
|
||||
* mask bit is cleared on i.MX21. Otherwise we can't read
|
||||
* the interrupt status bit on this machine.
|
||||
* Now that we "own" the interrupt make sure the interrupt mask bit is
|
||||
* cleared on i.MX21. Otherwise we can't read the interrupt status bit
|
||||
* on this machine.
|
||||
*/
|
||||
if (cpu_is_mx21())
|
||||
irq_control_v1_v2(host, 1);
|
||||
if (host->devtype_data->irqpending_quirk) {
|
||||
disable_irq_nosync(host->irq);
|
||||
host->devtype_data->irq_control(host, 1);
|
||||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) {
|
||||
@@ -1212,18 +1458,12 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Call preset again, with correct writesize this time */
|
||||
host->preset(mtd);
|
||||
host->devtype_data->preset(mtd);
|
||||
|
||||
if (mtd->writesize == 2048)
|
||||
this->ecc.layout = oob_largepage;
|
||||
if (nfc_is_v21() && mtd->writesize == 4096)
|
||||
this->ecc.layout = &nandv2_hw_eccoob_4k;
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
this->ecc.layout = host->devtype_data->ecclayout_2k;
|
||||
else if (mtd->writesize == 4096)
|
||||
this->ecc.layout = host->devtype_data->ecclayout_4k;
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
if (nfc_is_v1())
|
||||
@@ -1232,9 +1472,19 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
mtd_device_parse_register(mtd, part_probes,
|
||||
&(struct mtd_part_parser_data){
|
||||
.of_node = pdev->dev.of_node,
|
||||
},
|
||||
host->pdata.parts,
|
||||
host->pdata.nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
@@ -1275,6 +1525,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
|
||||
static struct platform_driver mxcnd_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mxcnd_dt_ids),
|
||||
},
|
||||
.remove = __devexit_p(mxcnd_remove),
|
||||
};
|
||||
|
Reference in New Issue
Block a user