summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-06-04 17:12:26 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-11-12 08:32:32 +0100
commitf8f9608d9b7a5b3349605284a4f3876e898413f1 (patch)
tree30f2525e4aa2a0822486d17bad356b90423d7478
parentmxc nand: use resource_size() (diff)
downloadlinux-f8f9608d9b7a5b3349605284a4f3876e898413f1.tar.xz
linux-f8f9608d9b7a5b3349605284a4f3876e898413f1.zip
mxc nand: use buffers
The NAND controller has some limitations how to access the internal buffers. It only allows 32 bit accesses. The driver used to work around this by having special alignment aware copy routines. We now copy the whole page to a buffer in memory and let the access functions use this buffer. This simplifies the driver. A bonnie++ test showed that this has no negative performance impact on the driver. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--drivers/mtd/nand/mxc_nand.c310
1 files changed, 77 insertions, 233 deletions
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 8fcb33da8efc..03d20086170b 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -107,15 +107,17 @@ struct mxc_nand_host {
struct device *dev;
void __iomem *regs;
- int spare_only;
int status_request;
int pagesize_2k;
- uint16_t col_addr;
struct clk *clk;
int clk_act;
int irq;
wait_queue_head_t irq_waitq;
+
+ uint8_t *data_buf;
+ unsigned int buf_start;
+ int spare_len;
};
/* Define delays in microsec for NAND device operations */
@@ -227,23 +229,11 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast)
}
static void send_page(struct mxc_nand_host *host, uint8_t buf_id,
- int spare_only, unsigned int ops)
+ unsigned int ops)
{
- DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only);
-
/* NANDFC buffer 0 is used for page read/write */
writew(buf_id, host->regs + NFC_BUF_ADDR);
- /* Configure spare or page+spare access */
- if (!host->pagesize_2k) {
- uint16_t config1 = readw(host->regs + NFC_CONFIG1);
- if (spare_only)
- config1 |= NFC_SP_EN;
- else
- config1 &= ~(NFC_SP_EN);
- writew(config1, host->regs + NFC_CONFIG1);
- }
-
writew(ops, host->regs + NFC_CONFIG2);
/* Wait for operation to complete */
@@ -278,6 +268,7 @@ static void send_read_id(struct mxc_nand_host *host)
writeb(readb(main_buf + 8), main_buf + 4);
writeb(readb(main_buf + 10), main_buf + 5);
}
+ memcpy(host->data_buf, host->regs + MAIN_AREA0, 16);
}
/* This function requests the NANDFC to perform a read of the
@@ -363,32 +354,14 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- uint8_t ret = 0;
- uint16_t col, rd_word;
- uint16_t __iomem *main_buf = host->regs + MAIN_AREA0;
- uint16_t __iomem *spare_buf = host->regs + SPARE_AREA0;
+ uint8_t ret;
/* Check for status request */
if (host->status_request)
return get_dev_status(host) & 0xFF;
- /* Get column for 16-bit access */
- col = host->col_addr >> 1;
-
- /* If we are accessing the spare region */
- if (host->spare_only)
- rd_word = readw(&spare_buf[col]);
- else
- rd_word = readw(&main_buf[col]);
-
- /* Pick upper/lower byte of word from RAM buffer */
- if (host->col_addr & 0x1)
- ret = (rd_word >> 8) & 0xFF;
- else
- ret = rd_word & 0xFF;
-
- /* Update saved column address */
- host->col_addr++;
+ ret = *(uint8_t *)(host->data_buf + host->buf_start);
+ host->buf_start++;
return ret;
}
@@ -397,33 +370,10 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- uint16_t col, rd_word, ret;
- uint16_t __iomem *p;
+ uint16_t ret;
- DEBUG(MTD_DEBUG_LEVEL3,
- "mxc_nand_read_word(col = %d)\n", host->col_addr);
-
- col = host->col_addr;
- /* Adjust saved column address */
- if (col < mtd->writesize && host->spare_only)
- col += mtd->writesize;
-
- if (col < mtd->writesize)
- p = (host->regs + MAIN_AREA0) + (col >> 1);
- else
- p = (host->regs + SPARE_AREA0) + ((col - mtd->writesize) >> 1);
-
- if (col & 1) {
- rd_word = readw(p);
- ret = (rd_word >> 8) & 0xff;
- rd_word = readw(&p[1]);
- ret |= (rd_word << 8) & 0xff00;
-
- } else
- ret = readw(p);
-
- /* Update saved column address */
- host->col_addr = col + 2;
+ ret = *(uint16_t *)(host->data_buf + host->buf_start);
+ host->buf_start += 2;
return ret;
}
@@ -436,94 +386,14 @@ static void mxc_nand_write_buf(struct mtd_info *mtd,
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- int n, col, i = 0;
-
- DEBUG(MTD_DEBUG_LEVEL3,
- "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr,
- len);
-
- col = host->col_addr;
-
- /* Adjust saved column address */
- if (col < mtd->writesize && host->spare_only)
- col += mtd->writesize;
-
- n = mtd->writesize + mtd->oobsize - col;
- n = min(len, n);
-
- DEBUG(MTD_DEBUG_LEVEL3,
- "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n);
-
- while (n) {
- void __iomem *p;
-
- if (col < mtd->writesize)
- p = host->regs + MAIN_AREA0 + (col & ~3);
- else
- p = host->regs + SPARE_AREA0 -
- mtd->writesize + (col & ~3);
-
- DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__,
- __LINE__, p);
-
- if (((col | (int)&buf[i]) & 3) || n < 16) {
- uint32_t data = 0;
-
- if (col & 3 || n < 4)
- data = readl(p);
-
- switch (col & 3) {
- case 0:
- if (n) {
- data = (data & 0xffffff00) |
- (buf[i++] << 0);
- n--;
- col++;
- }
- case 1:
- if (n) {
- data = (data & 0xffff00ff) |
- (buf[i++] << 8);
- n--;
- col++;
- }
- case 2:
- if (n) {
- data = (data & 0xff00ffff) |
- (buf[i++] << 16);
- n--;
- col++;
- }
- case 3:
- if (n) {
- data = (data & 0x00ffffff) |
- (buf[i++] << 24);
- n--;
- col++;
- }
- }
-
- writel(data, p);
- } else {
- int m = mtd->writesize - col;
+ u16 col = host->buf_start;
+ int n = mtd->oobsize + mtd->writesize - col;
- if (col >= mtd->writesize)
- m += mtd->oobsize;
+ n = min(n, len);
- m = min(n, m) & ~3;
+ memcpy(host->data_buf + col, buf, n);
- DEBUG(MTD_DEBUG_LEVEL3,
- "%s:%d: n = %d, m = %d, i = %d, col = %d\n",
- __func__, __LINE__, n, m, i, col);
-
- memcpy(p, &buf[i], m);
- col += m;
- i += m;
- n -= m;
- }
- }
- /* Update saved column address */
- host->col_addr = col;
+ host->buf_start += n;
}
/* Read the data buffer from the NAND Flash. To read the data from NAND
@@ -534,75 +404,14 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- int n, col, i = 0;
+ u16 col = host->buf_start;
+ int n = mtd->oobsize + mtd->writesize - col;
- DEBUG(MTD_DEBUG_LEVEL3,
- "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len);
-
- col = host->col_addr;
-
- /* Adjust saved column address */
- if (col < mtd->writesize && host->spare_only)
- col += mtd->writesize;
-
- n = mtd->writesize + mtd->oobsize - col;
- n = min(len, n);
-
- while (n) {
- void __iomem *p;
-
- if (col < mtd->writesize)
- p = host->regs + MAIN_AREA0 + (col & ~3);
- else
- p = host->regs + SPARE_AREA0 -
- mtd->writesize + (col & ~3);
-
- if (((col | (int)&buf[i]) & 3) || n < 16) {
- uint32_t data;
-
- data = readl(p);
- switch (col & 3) {
- case 0:
- if (n) {
- buf[i++] = (uint8_t) (data);
- n--;
- col++;
- }
- case 1:
- if (n) {
- buf[i++] = (uint8_t) (data >> 8);
- n--;
- col++;
- }
- case 2:
- if (n) {
- buf[i++] = (uint8_t) (data >> 16);
- n--;
- col++;
- }
- case 3:
- if (n) {
- buf[i++] = (uint8_t) (data >> 24);
- n--;
- col++;
- }
- }
- } else {
- int m = mtd->writesize - col;
+ n = min(n, len);
- if (col >= mtd->writesize)
- m += mtd->oobsize;
-
- m = min(n, m) & ~3;
- memcpy(&buf[i], p, m);
- col += m;
- i += m;
- n -= m;
- }
- }
- /* Update saved column address */
- host->col_addr = col;
+ memcpy(buf, host->data_buf + col, len);
+ host->buf_start += len;
}
/* Used by the upper layer to verify the data in NAND Flash
@@ -641,6 +450,36 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
}
}
+/*
+ * Function to transfer data to/from spare area.
+ */
+static void copy_spare(struct mtd_info *mtd, bool bfrom)
+{
+ struct nand_chip *this = mtd->priv;
+ struct mxc_nand_host *host = this->priv;
+ u16 i, j;
+ u16 n = mtd->writesize >> 9;
+ u8 *d = host->data_buf + mtd->writesize;
+ u8 *s = host->regs + SPARE_AREA0;
+ u16 t = host->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);
+
+ /* the last section */
+ memcpy(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);
+
+ /* the last section */
+ memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j);
+ }
+}
+
static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -707,19 +546,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
switch (command) {
case NAND_CMD_STATUS:
- host->col_addr = 0;
+ host->buf_start = 0;
host->status_request = true;
break;
case NAND_CMD_READ0:
- host->col_addr = column;
- host->spare_only = false;
+ host->buf_start = column;
useirq = false;
break;
case NAND_CMD_READOOB:
- host->col_addr = column;
- host->spare_only = true;
+ host->buf_start = column + mtd->writesize;
+
useirq = false;
if (host->pagesize_2k)
command = NAND_CMD_READ0; /* only READ0 is valid */
@@ -739,15 +577,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
mxc_nand_command(mtd, NAND_CMD_READ0, 0,
page_addr);
- host->col_addr = column - mtd->writesize;
- host->spare_only = true;
+ host->buf_start = column;
/* Set program pointer to spare region */
if (!host->pagesize_2k)
send_cmd(host, NAND_CMD_READOOB, false);
} else {
- host->spare_only = false;
- host->col_addr = column;
+ host->buf_start = column;
/* Set program pointer to page start */
if (!host->pagesize_2k)
@@ -757,13 +593,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
break;
case NAND_CMD_PAGEPROG:
- send_page(host, 0, host->spare_only, NFC_INPUT);
+ memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize);
+ copy_spare(mtd, false);
+ send_page(host, 0, NFC_INPUT);
if (host->pagesize_2k) {
/* data in 4 areas datas */
- send_page(host, 1, host->spare_only, NFC_INPUT);
- send_page(host, 2, host->spare_only, NFC_INPUT);
- send_page(host, 3, host->spare_only, NFC_INPUT);
+ send_page(host, 1, NFC_INPUT);
+ send_page(host, 2, NFC_INPUT);
+ send_page(host, 3, NFC_INPUT);
}
break;
@@ -789,16 +627,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
/* send read confirm command */
send_cmd(host, NAND_CMD_READSTART, true);
/* read for each AREA */
- send_page(host, 0, host->spare_only, NFC_OUTPUT);
- send_page(host, 1, host->spare_only, NFC_OUTPUT);
- send_page(host, 2, host->spare_only, NFC_OUTPUT);
- send_page(host, 3, host->spare_only, NFC_OUTPUT);
+ send_page(host, 0, NFC_OUTPUT);
+ send_page(host, 1, NFC_OUTPUT);
+ send_page(host, 2, NFC_OUTPUT);
+ send_page(host, 3, NFC_OUTPUT);
} else
- send_page(host, 0, host->spare_only, NFC_OUTPUT);
+ send_page(host, 0, NFC_OUTPUT);
+
+ memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize);
+ copy_spare(mtd, true);
break;
case NAND_CMD_READID:
- host->col_addr = 0;
send_read_id(host);
break;
@@ -824,10 +664,14 @@ static int __init mxcnd_probe(struct platform_device *pdev)
int err = 0, nr_parts = 0;
/* Allocate memory for MTD device structure and private data */
- host = kzalloc(sizeof(struct mxc_nand_host), GFP_KERNEL);
+ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE +
+ NAND_MAX_OOBSIZE, GFP_KERNEL);
if (!host)
return -ENOMEM;
+ host->data_buf = (uint8_t *)(host + 1);
+ host->spare_len = 16;
+
host->dev = &pdev->dev;
/* structures must be linked */
this = &host->nand;