|
@@ -15,7 +15,9 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
+#include <linux/dmaengine.h>
|
|
|
#include <linux/dma-mapping.h>
|
|
|
+#include <linux/dma/pxa-dma.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/mtd/mtd.h>
|
|
@@ -33,10 +35,6 @@
|
|
|
#define ARCH_HAS_DMA
|
|
|
#endif
|
|
|
|
|
|
-#ifdef ARCH_HAS_DMA
|
|
|
-#include <mach/dma.h>
|
|
|
-#endif
|
|
|
-
|
|
|
#include <linux/platform_data/mtd-nand-pxa3xx.h>
|
|
|
|
|
|
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
|
|
@@ -78,7 +76,8 @@
|
|
|
#define NDCR_ND_MODE (0x3 << 21)
|
|
|
#define NDCR_NAND_MODE (0x0)
|
|
|
#define NDCR_CLR_PG_CNT (0x1 << 20)
|
|
|
-#define NDCR_STOP_ON_UNCOR (0x1 << 19)
|
|
|
+#define NFCV1_NDCR_ARB_CNTL (0x1 << 19)
|
|
|
+#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19)
|
|
|
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
|
|
|
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
|
|
|
|
|
@@ -201,6 +200,10 @@ struct pxa3xx_nand_info {
|
|
|
unsigned int oob_buff_pos;
|
|
|
|
|
|
/* DMA information */
|
|
|
+ struct scatterlist sg;
|
|
|
+ enum dma_data_direction dma_dir;
|
|
|
+ struct dma_chan *dma_chan;
|
|
|
+ dma_cookie_t dma_cookie;
|
|
|
int drcmr_dat;
|
|
|
int drcmr_cmd;
|
|
|
|
|
@@ -208,8 +211,6 @@ struct pxa3xx_nand_info {
|
|
|
unsigned char *oob_buff;
|
|
|
dma_addr_t data_buff_phys;
|
|
|
int data_dma_ch;
|
|
|
- struct pxa_dma_desc *data_desc;
|
|
|
- dma_addr_t data_desc_addr;
|
|
|
|
|
|
struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
|
|
|
unsigned int state;
|
|
@@ -252,6 +253,25 @@ static bool use_dma = 1;
|
|
|
module_param(use_dma, bool, 0444);
|
|
|
MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
|
|
|
|
|
|
+struct pxa3xx_nand_timing {
|
|
|
+ unsigned int tCH; /* Enable signal hold time */
|
|
|
+ unsigned int tCS; /* Enable signal setup time */
|
|
|
+ unsigned int tWH; /* ND_nWE high duration */
|
|
|
+ unsigned int tWP; /* ND_nWE pulse time */
|
|
|
+ unsigned int tRH; /* ND_nRE high duration */
|
|
|
+ unsigned int tRP; /* ND_nRE pulse width */
|
|
|
+ unsigned int tR; /* ND_nWE high to ND_nRE low for read */
|
|
|
+ unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
|
|
|
+ unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
|
|
+};
|
|
|
+
|
|
|
+struct pxa3xx_nand_flash {
|
|
|
+ uint32_t chip_id;
|
|
|
+ unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
|
|
|
+ unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
|
|
|
+ struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
|
|
+};
|
|
|
+
|
|
|
static struct pxa3xx_nand_timing timing[] = {
|
|
|
{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
|
|
|
{ 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
|
|
@@ -260,15 +280,14 @@ static struct pxa3xx_nand_timing timing[] = {
|
|
|
};
|
|
|
|
|
|
static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
|
|
-{ "DEFAULT FLASH", 0, 0, 2048, 8, 8, 0, &timing[0] },
|
|
|
-{ "64MiB 16-bit", 0x46ec, 32, 512, 16, 16, 4096, &timing[1] },
|
|
|
-{ "256MiB 8-bit", 0xdaec, 64, 2048, 8, 8, 2048, &timing[1] },
|
|
|
-{ "4GiB 8-bit", 0xd7ec, 128, 4096, 8, 8, 8192, &timing[1] },
|
|
|
-{ "128MiB 8-bit", 0xa12c, 64, 2048, 8, 8, 1024, &timing[2] },
|
|
|
-{ "128MiB 16-bit", 0xb12c, 64, 2048, 16, 16, 1024, &timing[2] },
|
|
|
-{ "512MiB 8-bit", 0xdc2c, 64, 2048, 8, 8, 4096, &timing[2] },
|
|
|
-{ "512MiB 16-bit", 0xcc2c, 64, 2048, 16, 16, 4096, &timing[2] },
|
|
|
-{ "256MiB 16-bit", 0xba20, 64, 2048, 16, 16, 2048, &timing[3] },
|
|
|
+ { 0x46ec, 16, 16, &timing[1] },
|
|
|
+ { 0xdaec, 8, 8, &timing[1] },
|
|
|
+ { 0xd7ec, 8, 8, &timing[1] },
|
|
|
+ { 0xa12c, 8, 8, &timing[2] },
|
|
|
+ { 0xb12c, 16, 16, &timing[2] },
|
|
|
+ { 0xdc2c, 8, 8, &timing[2] },
|
|
|
+ { 0xcc2c, 16, 16, &timing[2] },
|
|
|
+ { 0xba20, 16, 16, &timing[3] },
|
|
|
};
|
|
|
|
|
|
static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
|
|
@@ -329,9 +348,6 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
|
|
|
.oobfree = { }
|
|
|
};
|
|
|
|
|
|
-/* Define a default flash type setting serve as flash detecting only */
|
|
|
-#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
|
|
-
|
|
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
|
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
|
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
|
@@ -393,6 +409,128 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
|
|
|
nand_writel(info, NDTR1CS0, ndtr1);
|
|
|
}
|
|
|
|
|
|
+static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
|
|
|
+ const struct nand_sdr_timings *t)
|
|
|
+{
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ unsigned long nand_clk = clk_get_rate(info->clk);
|
|
|
+ uint32_t ndtr0, ndtr1;
|
|
|
+
|
|
|
+ u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
|
|
|
+ u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
|
|
|
+ u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
|
|
|
+ u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
|
|
|
+ u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
|
|
|
+ u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
|
|
|
+ u32 tR = chip->chip_delay * 1000;
|
|
|
+ u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
|
|
|
+ u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
|
|
|
+
|
|
|
+ /* fallback to a default value if tR = 0 */
|
|
|
+ if (!tR)
|
|
|
+ tR = 20000;
|
|
|
+
|
|
|
+ ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) |
|
|
|
+ NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) |
|
|
|
+ NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) |
|
|
|
+ NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) |
|
|
|
+ NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) |
|
|
|
+ NDTR0_tRP(ns2cycle(tRP_min, nand_clk));
|
|
|
+
|
|
|
+ ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) |
|
|
|
+ NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) |
|
|
|
+ NDTR1_tAR(ns2cycle(tAR_min, nand_clk));
|
|
|
+
|
|
|
+ info->ndtr0cs0 = ndtr0;
|
|
|
+ info->ndtr1cs0 = ndtr1;
|
|
|
+ nand_writel(info, NDTR0CS0, ndtr0);
|
|
|
+ nand_writel(info, NDTR1CS0, ndtr1);
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,
|
|
|
+ unsigned int *flash_width,
|
|
|
+ unsigned int *dfc_width)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ const struct pxa3xx_nand_flash *f = NULL;
|
|
|
+ int i, id, ntypes;
|
|
|
+
|
|
|
+ ntypes = ARRAY_SIZE(builtin_flash_types);
|
|
|
+
|
|
|
+ chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
+
|
|
|
+ id = chip->read_byte(host->mtd);
|
|
|
+ id |= chip->read_byte(host->mtd) << 0x8;
|
|
|
+
|
|
|
+ for (i = 0; i < ntypes; i++) {
|
|
|
+ f = &builtin_flash_types[i];
|
|
|
+
|
|
|
+ if (f->chip_id == id)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == ntypes) {
|
|
|
+ dev_err(&info->pdev->dev, "Error: timings not found\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pxa3xx_nand_set_timing(host, f->timing);
|
|
|
+
|
|
|
+ *flash_width = f->flash_width;
|
|
|
+ *dfc_width = f->dfc_width;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host,
|
|
|
+ int mode)
|
|
|
+{
|
|
|
+ const struct nand_sdr_timings *timings;
|
|
|
+
|
|
|
+ mode = fls(mode) - 1;
|
|
|
+ if (mode < 0)
|
|
|
+ mode = 0;
|
|
|
+
|
|
|
+ timings = onfi_async_timing_mode_to_sdr_timings(mode);
|
|
|
+ if (IS_ERR(timings))
|
|
|
+ return PTR_ERR(timings);
|
|
|
+
|
|
|
+ pxa3xx_nand_set_sdr_timing(host, timings);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int pxa3xx_nand_init(struct pxa3xx_nand_host *host)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
+ unsigned int flash_width = 0, dfc_width = 0;
|
|
|
+ int mode, err;
|
|
|
+
|
|
|
+ mode = onfi_get_async_timing_mode(chip);
|
|
|
+ if (mode == ONFI_TIMING_MODE_UNKNOWN) {
|
|
|
+ err = pxa3xx_nand_init_timings_compat(host, &flash_width,
|
|
|
+ &dfc_width);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (flash_width == 16) {
|
|
|
+ info->reg_ndcr |= NDCR_DWIDTH_M;
|
|
|
+ chip->options |= NAND_BUSWIDTH_16;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0;
|
|
|
+ } else {
|
|
|
+ err = pxa3xx_nand_init_timings_onfi(host, mode);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Set the data and OOB size, depending on the selected
|
|
|
* spare and ECC configuration.
|
|
@@ -468,6 +606,9 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
|
|
|
ndcr &= ~NDCR_ND_RUN;
|
|
|
nand_writel(info, NDCR, ndcr);
|
|
|
}
|
|
|
+ if (info->dma_chan)
|
|
|
+ dmaengine_terminate_all(info->dma_chan);
|
|
|
+
|
|
|
/* clear status bits */
|
|
|
nand_writel(info, NDSR, NDSR_MASK);
|
|
|
}
|
|
@@ -504,7 +645,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
|
|
* the polling on the last read.
|
|
|
*/
|
|
|
while (len > 8) {
|
|
|
- readsl(info->mmio_base + NDDB, data, 8);
|
|
|
+ ioread32_rep(info->mmio_base + NDDB, data, 8);
|
|
|
|
|
|
ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
|
|
|
val & NDSR_RDDREQ, 1000, 5000);
|
|
@@ -519,7 +660,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- readsl(info->mmio_base + NDDB, data, len);
|
|
|
+ ioread32_rep(info->mmio_base + NDDB, data, len);
|
|
|
}
|
|
|
|
|
|
static void handle_data_pio(struct pxa3xx_nand_info *info)
|
|
@@ -559,57 +700,61 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
|
|
|
info->data_size -= do_bytes;
|
|
|
}
|
|
|
|
|
|
-#ifdef ARCH_HAS_DMA
|
|
|
-static void start_data_dma(struct pxa3xx_nand_info *info)
|
|
|
+static void pxa3xx_nand_data_dma_irq(void *data)
|
|
|
{
|
|
|
- struct pxa_dma_desc *desc = info->data_desc;
|
|
|
- int dma_len = ALIGN(info->data_size + info->oob_size, 32);
|
|
|
+ struct pxa3xx_nand_info *info = data;
|
|
|
+ struct dma_tx_state state;
|
|
|
+ enum dma_status status;
|
|
|
|
|
|
- desc->ddadr = DDADR_STOP;
|
|
|
- desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
|
|
|
+ status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state);
|
|
|
+ if (likely(status == DMA_COMPLETE)) {
|
|
|
+ info->state = STATE_DMA_DONE;
|
|
|
+ } else {
|
|
|
+ dev_err(&info->pdev->dev, "DMA error on data channel\n");
|
|
|
+ info->retcode = ERR_DMABUSERR;
|
|
|
+ }
|
|
|
+ dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
|
|
|
+
|
|
|
+ nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
|
|
+ enable_int(info, NDCR_INT_MASK);
|
|
|
+}
|
|
|
+
|
|
|
+static void start_data_dma(struct pxa3xx_nand_info *info)
|
|
|
+{
|
|
|
+ enum dma_transfer_direction direction;
|
|
|
+ struct dma_async_tx_descriptor *tx;
|
|
|
|
|
|
switch (info->state) {
|
|
|
case STATE_DMA_WRITING:
|
|
|
- desc->dsadr = info->data_buff_phys;
|
|
|
- desc->dtadr = info->mmio_phys + NDDB;
|
|
|
- desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
|
|
|
+ info->dma_dir = DMA_TO_DEVICE;
|
|
|
+ direction = DMA_MEM_TO_DEV;
|
|
|
break;
|
|
|
case STATE_DMA_READING:
|
|
|
- desc->dtadr = info->data_buff_phys;
|
|
|
- desc->dsadr = info->mmio_phys + NDDB;
|
|
|
- desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
|
|
|
+ info->dma_dir = DMA_FROM_DEVICE;
|
|
|
+ direction = DMA_DEV_TO_MEM;
|
|
|
break;
|
|
|
default:
|
|
|
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
|
|
|
info->state);
|
|
|
BUG();
|
|
|
}
|
|
|
-
|
|
|
- DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
|
|
|
- DDADR(info->data_dma_ch) = info->data_desc_addr;
|
|
|
- DCSR(info->data_dma_ch) |= DCSR_RUN;
|
|
|
-}
|
|
|
-
|
|
|
-static void pxa3xx_nand_data_dma_irq(int channel, void *data)
|
|
|
-{
|
|
|
- struct pxa3xx_nand_info *info = data;
|
|
|
- uint32_t dcsr;
|
|
|
-
|
|
|
- dcsr = DCSR(channel);
|
|
|
- DCSR(channel) = dcsr;
|
|
|
-
|
|
|
- if (dcsr & DCSR_BUSERR) {
|
|
|
- info->retcode = ERR_DMABUSERR;
|
|
|
+ info->sg.length = info->data_size +
|
|
|
+ (info->oob_size ? info->spare_size + info->ecc_size : 0);
|
|
|
+ dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
|
|
|
+
|
|
|
+ tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
|
|
|
+ DMA_PREP_INTERRUPT);
|
|
|
+ if (!tx) {
|
|
|
+ dev_err(&info->pdev->dev, "prep_slave_sg() failed\n");
|
|
|
+ return;
|
|
|
}
|
|
|
-
|
|
|
- info->state = STATE_DMA_DONE;
|
|
|
- enable_int(info, NDCR_INT_MASK);
|
|
|
- nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
|
|
|
+ tx->callback = pxa3xx_nand_data_dma_irq;
|
|
|
+ tx->callback_param = info;
|
|
|
+ info->dma_cookie = dmaengine_submit(tx);
|
|
|
+ dma_async_issue_pending(info->dma_chan);
|
|
|
+ dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n",
|
|
|
+ __func__, direction, info->dma_cookie, info->sg.length);
|
|
|
}
|
|
|
-#else
|
|
|
-static void start_data_dma(struct pxa3xx_nand_info *info)
|
|
|
-{}
|
|
|
-#endif
|
|
|
|
|
|
static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
|
|
|
{
|
|
@@ -1128,7 +1273,8 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
|
|
|
}
|
|
|
|
|
|
static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
|
|
|
- struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
|
|
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
|
|
+ int page)
|
|
|
{
|
|
|
chip->write_buf(mtd, buf, mtd->writesize);
|
|
|
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
@@ -1241,45 +1387,23 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
|
|
|
return NAND_STATUS_READY;
|
|
|
}
|
|
|
|
|
|
-static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
|
|
|
- const struct pxa3xx_nand_flash *f)
|
|
|
+static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info)
|
|
|
{
|
|
|
struct platform_device *pdev = info->pdev;
|
|
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
|
struct pxa3xx_nand_host *host = info->host[info->cs];
|
|
|
- uint32_t ndcr = 0x0; /* enable all interrupts */
|
|
|
-
|
|
|
- if (f->page_size != 2048 && f->page_size != 512) {
|
|
|
- dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- if (f->flash_width != 16 && f->flash_width != 8) {
|
|
|
- dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- /* calculate addressing information */
|
|
|
- host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
|
|
|
-
|
|
|
- if (f->num_blocks * f->page_per_block > 65536)
|
|
|
- host->row_addr_cycles = 3;
|
|
|
- else
|
|
|
- host->row_addr_cycles = 2;
|
|
|
-
|
|
|
- ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
|
- ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
|
|
- ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;
|
|
|
- ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;
|
|
|
- ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
|
|
|
- ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
|
|
|
-
|
|
|
- ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
|
|
- ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
|
|
+ struct mtd_info *mtd = host->mtd;
|
|
|
+ struct nand_chip *chip = mtd->priv;
|
|
|
|
|
|
- info->reg_ndcr = ndcr;
|
|
|
+ /* configure default flash values */
|
|
|
+ info->reg_ndcr = 0x0; /* enable all interrupts */
|
|
|
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
|
+ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
|
|
|
+ info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
|
|
|
+ info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
|
|
|
+ info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
|
|
|
+ info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
|
|
|
|
|
|
- pxa3xx_nand_set_timing(host, f->timing);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1289,42 +1413,57 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
|
|
|
|
|
/* Set an initial chunk size */
|
|
|
info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
|
|
|
- info->reg_ndcr = ndcr & ~NDCR_INT_MASK;
|
|
|
+ info->reg_ndcr = ndcr &
|
|
|
+ ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
|
|
|
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
|
|
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#ifdef ARCH_HAS_DMA
|
|
|
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
|
{
|
|
|
struct platform_device *pdev = info->pdev;
|
|
|
- int data_desc_offset = info->buf_size - sizeof(struct pxa_dma_desc);
|
|
|
+ struct dma_slave_config config;
|
|
|
+ dma_cap_mask_t mask;
|
|
|
+ struct pxad_param param;
|
|
|
+ int ret;
|
|
|
|
|
|
- if (use_dma == 0) {
|
|
|
- info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
|
|
- if (info->data_buff == NULL)
|
|
|
- return -ENOMEM;
|
|
|
+ info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
|
|
+ if (info->data_buff == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+ if (use_dma == 0)
|
|
|
return 0;
|
|
|
- }
|
|
|
|
|
|
- info->data_buff = dma_alloc_coherent(&pdev->dev, info->buf_size,
|
|
|
- &info->data_buff_phys, GFP_KERNEL);
|
|
|
- if (info->data_buff == NULL) {
|
|
|
- dev_err(&pdev->dev, "failed to allocate dma buffer\n");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- info->data_desc = (void *)info->data_buff + data_desc_offset;
|
|
|
- info->data_desc_addr = info->data_buff_phys + data_desc_offset;
|
|
|
+ sg_init_one(&info->sg, info->data_buff, info->buf_size);
|
|
|
+ dma_cap_zero(mask);
|
|
|
+ dma_cap_set(DMA_SLAVE, mask);
|
|
|
+ param.prio = PXAD_PRIO_LOWEST;
|
|
|
+ param.drcmr = info->drcmr_dat;
|
|
|
+ info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn,
|
|
|
+ ¶m, &pdev->dev,
|
|
|
+ "data");
|
|
|
+ if (!info->dma_chan) {
|
|
|
+ dev_err(&pdev->dev, "unable to request data dma channel\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
- info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
|
|
|
- pxa3xx_nand_data_dma_irq, info);
|
|
|
- if (info->data_dma_ch < 0) {
|
|
|
- dev_err(&pdev->dev, "failed to request data dma\n");
|
|
|
- dma_free_coherent(&pdev->dev, info->buf_size,
|
|
|
- info->data_buff, info->data_buff_phys);
|
|
|
- return info->data_dma_ch;
|
|
|
+ memset(&config, 0, sizeof(config));
|
|
|
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ config.src_addr = info->mmio_phys + NDDB;
|
|
|
+ config.dst_addr = info->mmio_phys + NDDB;
|
|
|
+ config.src_maxburst = 32;
|
|
|
+ config.dst_maxburst = 32;
|
|
|
+ ret = dmaengine_slave_config(info->dma_chan, &config);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&info->pdev->dev,
|
|
|
+ "dma channel configuration failed: %d\n",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1337,43 +1476,30 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
|
|
|
|
static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
|
|
{
|
|
|
- struct platform_device *pdev = info->pdev;
|
|
|
if (info->use_dma) {
|
|
|
- pxa_free_dma(info->data_dma_ch);
|
|
|
- dma_free_coherent(&pdev->dev, info->buf_size,
|
|
|
- info->data_buff, info->data_buff_phys);
|
|
|
- } else {
|
|
|
- kfree(info->data_buff);
|
|
|
+ dmaengine_terminate_all(info->dma_chan);
|
|
|
+ dma_release_channel(info->dma_chan);
|
|
|
}
|
|
|
-}
|
|
|
-#else
|
|
|
-static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
|
|
-{
|
|
|
- info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
|
|
|
- if (info->data_buff == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
|
|
|
-{
|
|
|
kfree(info->data_buff);
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
-static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
|
|
|
+static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host)
|
|
|
{
|
|
|
+ struct pxa3xx_nand_info *info = host->info_data;
|
|
|
struct mtd_info *mtd;
|
|
|
struct nand_chip *chip;
|
|
|
+ const struct nand_sdr_timings *timings;
|
|
|
int ret;
|
|
|
|
|
|
mtd = info->host[info->cs]->mtd;
|
|
|
chip = mtd->priv;
|
|
|
|
|
|
/* use the common timing to make a try */
|
|
|
- ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ timings = onfi_async_timing_mode_to_sdr_timings(0);
|
|
|
+ if (IS_ERR(timings))
|
|
|
+ return PTR_ERR(timings);
|
|
|
+
|
|
|
+ pxa3xx_nand_set_sdr_timing(host, timings);
|
|
|
|
|
|
chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
|
|
|
ret = chip->waitfunc(mtd, chip);
|
|
@@ -1458,12 +1584,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|
|
struct pxa3xx_nand_info *info = host->info_data;
|
|
|
struct platform_device *pdev = info->pdev;
|
|
|
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
|
- struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;
|
|
|
- const struct pxa3xx_nand_flash *f = NULL;
|
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
- uint32_t id = -1;
|
|
|
- uint64_t chipsize;
|
|
|
- int i, ret, num;
|
|
|
+ int ret;
|
|
|
uint16_t ecc_strength, ecc_step;
|
|
|
|
|
|
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
|
@@ -1472,7 +1594,11 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|
|
/* Set a default chunk size */
|
|
|
info->chunk_size = 512;
|
|
|
|
|
|
- ret = pxa3xx_nand_sensing(info);
|
|
|
+ ret = pxa3xx_nand_config_flash(info);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = pxa3xx_nand_sensing(host);
|
|
|
if (ret) {
|
|
|
dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
|
|
|
info->cs);
|
|
@@ -1480,54 +1606,8 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
|
|
|
- id = *((uint16_t *)(info->data_buff));
|
|
|
- if (id != 0)
|
|
|
- dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);
|
|
|
- else {
|
|
|
- dev_warn(&info->pdev->dev,
|
|
|
- "Read out ID 0, potential timing set wrong!!\n");
|
|
|
-
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- num = ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1;
|
|
|
- for (i = 0; i < num; i++) {
|
|
|
- if (i < pdata->num_flash)
|
|
|
- f = pdata->flash + i;
|
|
|
- else
|
|
|
- f = &builtin_flash_types[i - pdata->num_flash + 1];
|
|
|
-
|
|
|
- /* find the chip in default list */
|
|
|
- if (f->chip_id == id)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
|
|
|
- dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");
|
|
|
-
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- ret = pxa3xx_nand_config_flash(info, f);
|
|
|
- if (ret) {
|
|
|
- dev_err(&info->pdev->dev, "ERROR! Configure failed\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- memset(pxa3xx_flash_ids, 0, sizeof(pxa3xx_flash_ids));
|
|
|
-
|
|
|
- pxa3xx_flash_ids[0].name = f->name;
|
|
|
- pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff;
|
|
|
- pxa3xx_flash_ids[0].pagesize = f->page_size;
|
|
|
- chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size;
|
|
|
- pxa3xx_flash_ids[0].chipsize = chipsize >> 20;
|
|
|
- pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block;
|
|
|
- if (f->flash_width == 16)
|
|
|
- pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16;
|
|
|
- pxa3xx_flash_ids[1].name = NULL;
|
|
|
- def = pxa3xx_flash_ids;
|
|
|
KEEP_CONFIG:
|
|
|
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
|
|
|
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
|
|
chip->options |= NAND_BUSWIDTH_16;
|
|
|
|
|
@@ -1535,9 +1615,18 @@ KEEP_CONFIG:
|
|
|
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
|
|
|
nand_writel(info, NDECCCTRL, 0x0);
|
|
|
|
|
|
- if (nand_scan_ident(mtd, 1, def))
|
|
|
+ if (nand_scan_ident(mtd, 1, NULL))
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ if (!pdata->keep_config) {
|
|
|
+ ret = pxa3xx_nand_init(host);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(&info->pdev->dev, "Failed to init nand: %d\n",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (pdata->flash_bbt) {
|
|
|
/*
|
|
|
* We'll use a bad block table stored in-flash and don't
|
|
@@ -1635,7 +1724,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
|
|
host->cs = cs;
|
|
|
host->info_data = info;
|
|
|
mtd->priv = host;
|
|
|
- mtd->owner = THIS_MODULE;
|
|
|
+ mtd->dev.parent = &pdev->dev;
|
|
|
|
|
|
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
|
|
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
|
@@ -1662,34 +1751,23 @@ static int alloc_nand_resource(struct platform_device *pdev)
|
|
|
return ret;
|
|
|
|
|
|
if (use_dma) {
|
|
|
- /*
|
|
|
- * This is a dirty hack to make this driver work from
|
|
|
- * devicetree bindings. It can be removed once we have
|
|
|
- * a prober DMA controller framework for DT.
|
|
|
- */
|
|
|
- if (pdev->dev.of_node &&
|
|
|
- of_machine_is_compatible("marvell,pxa3xx")) {
|
|
|
- info->drcmr_dat = 97;
|
|
|
- info->drcmr_cmd = 99;
|
|
|
- } else {
|
|
|
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
|
- if (r == NULL) {
|
|
|
- dev_err(&pdev->dev,
|
|
|
- "no resource defined for data DMA\n");
|
|
|
- ret = -ENXIO;
|
|
|
- goto fail_disable_clk;
|
|
|
- }
|
|
|
- info->drcmr_dat = r->start;
|
|
|
-
|
|
|
- r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
|
|
- if (r == NULL) {
|
|
|
- dev_err(&pdev->dev,
|
|
|
- "no resource defined for cmd DMA\n");
|
|
|
- ret = -ENXIO;
|
|
|
- goto fail_disable_clk;
|
|
|
- }
|
|
|
- info->drcmr_cmd = r->start;
|
|
|
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
|
+ if (r == NULL) {
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "no resource defined for data DMA\n");
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto fail_disable_clk;
|
|
|
+ }
|
|
|
+ info->drcmr_dat = r->start;
|
|
|
+
|
|
|
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
|
|
+ if (r == NULL) {
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "no resource defined for cmd DMA\n");
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto fail_disable_clk;
|
|
|
}
|
|
|
+ info->drcmr_cmd = r->start;
|
|
|
}
|
|
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
@@ -1754,6 +1832,16 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
|
|
|
free_irq(irq, info);
|
|
|
pxa3xx_nand_free_buff(info);
|
|
|
|
|
|
+ /*
|
|
|
+ * In the pxa3xx case, the DFI bus is shared between the SMC and NFC.
|
|
|
+ * In order to prevent a lockup of the system bus, the DFI bus
|
|
|
+ * arbitration is granted to SMC upon driver removal. This is done by
|
|
|
+ * setting the x_ARB_CNTL bit, which also prevents the NAND to have
|
|
|
+ * access to the bus anymore.
|
|
|
+ */
|
|
|
+ nand_writel(info, NDCR,
|
|
|
+ (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) |
|
|
|
+ NFCV1_NDCR_ARB_CNTL);
|
|
|
clk_disable_unprepare(info->clk);
|
|
|
|
|
|
for (cs = 0; cs < pdata->num_cs; cs++)
|
|
@@ -1800,15 +1888,16 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
|
|
struct pxa3xx_nand_platform_data *pdata;
|
|
|
struct mtd_part_parser_data ppdata = {};
|
|
|
struct pxa3xx_nand_info *info;
|
|
|
- int ret, cs, probe_success;
|
|
|
+ int ret, cs, probe_success, dma_available;
|
|
|
|
|
|
-#ifndef ARCH_HAS_DMA
|
|
|
- if (use_dma) {
|
|
|
+ dma_available = IS_ENABLED(CONFIG_ARM) &&
|
|
|
+ (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP));
|
|
|
+ if (use_dma && !dma_available) {
|
|
|
use_dma = 0;
|
|
|
dev_warn(&pdev->dev,
|
|
|
"This platform can't do DMA on this device\n");
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
ret = pxa3xx_nand_probe_dt(pdev);
|
|
|
if (ret)
|
|
|
return ret;
|
|
@@ -1861,35 +1950,22 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
-static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
|
|
|
+static int pxa3xx_nand_suspend(struct device *dev)
|
|
|
{
|
|
|
- struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
|
|
- struct pxa3xx_nand_platform_data *pdata;
|
|
|
- struct mtd_info *mtd;
|
|
|
- int cs;
|
|
|
+ struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
|
|
|
|
|
|
- pdata = dev_get_platdata(&pdev->dev);
|
|
|
if (info->state) {
|
|
|
- dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
|
|
|
+ dev_err(dev, "driver busy, state = %d\n", info->state);
|
|
|
return -EAGAIN;
|
|
|
}
|
|
|
|
|
|
- for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
|
- mtd = info->host[cs]->mtd;
|
|
|
- mtd_suspend(mtd);
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int pxa3xx_nand_resume(struct platform_device *pdev)
|
|
|
+static int pxa3xx_nand_resume(struct device *dev)
|
|
|
{
|
|
|
- struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
|
|
|
- struct pxa3xx_nand_platform_data *pdata;
|
|
|
- struct mtd_info *mtd;
|
|
|
- int cs;
|
|
|
+ struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
|
|
|
|
|
|
- pdata = dev_get_platdata(&pdev->dev);
|
|
|
/* We don't want to handle interrupt without calling mtd routine */
|
|
|
disable_int(info, NDCR_INT_MASK);
|
|
|
|
|
@@ -1907,10 +1983,6 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
|
|
* all status before resume
|
|
|
*/
|
|
|
nand_writel(info, NDSR, NDSR_MASK);
|
|
|
- for (cs = 0; cs < pdata->num_cs; cs++) {
|
|
|
- mtd = info->host[cs]->mtd;
|
|
|
- mtd_resume(mtd);
|
|
|
- }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1919,15 +1991,19 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
|
|
|
#define pxa3xx_nand_resume NULL
|
|
|
#endif
|
|
|
|
|
|
+static const struct dev_pm_ops pxa3xx_nand_pm_ops = {
|
|
|
+ .suspend = pxa3xx_nand_suspend,
|
|
|
+ .resume = pxa3xx_nand_resume,
|
|
|
+};
|
|
|
+
|
|
|
static struct platform_driver pxa3xx_nand_driver = {
|
|
|
.driver = {
|
|
|
.name = "pxa3xx-nand",
|
|
|
.of_match_table = pxa3xx_nand_dt_ids,
|
|
|
+ .pm = &pxa3xx_nand_pm_ops,
|
|
|
},
|
|
|
.probe = pxa3xx_nand_probe,
|
|
|
.remove = pxa3xx_nand_remove,
|
|
|
- .suspend = pxa3xx_nand_suspend,
|
|
|
- .resume = pxa3xx_nand_resume,
|
|
|
};
|
|
|
|
|
|
module_platform_driver(pxa3xx_nand_driver);
|