📄 pxa3xx_nand.c
字号:
/* * drivers/mtd/nand/pxa3xx_nand.c * * Copyright © 2005 Intel Corporation * Copyright © 2006 Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/clk.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/partitions.h>#include <linux/io.h>#include <linux/irq.h>#include <asm/dma.h>#include <mach/pxa-regs.h>#include <mach/pxa3xx_nand.h>#define CHIP_DELAY_TIMEOUT (2 * HZ/10)/* registers and bit definitions */#define NDCR (0x00) /* Control register */#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */#define NDSR (0x14) /* Status Register */#define NDPCR (0x18) /* Page Count Register */#define NDBDR0 (0x1C) /* Bad Block Register 0 */#define NDBDR1 (0x20) /* Bad Block Register 1 */#define NDDB (0x40) /* Data Buffer */#define NDCB0 (0x48) /* Command Buffer0 */#define NDCB1 (0x4C) /* Command Buffer1 */#define NDCB2 (0x50) /* Command Buffer2 */#define NDCR_SPARE_EN (0x1 << 31)#define NDCR_ECC_EN (0x1 << 30)#define NDCR_DMA_EN (0x1 << 29)#define NDCR_ND_RUN (0x1 << 28)#define NDCR_DWIDTH_C (0x1 << 27)#define NDCR_DWIDTH_M (0x1 << 26)#define NDCR_PAGE_SZ (0x1 << 24)#define NDCR_NCSX (0x1 << 23)#define NDCR_ND_MODE (0x3 << 21)#define NDCR_NAND_MODE (0x0)#define NDCR_CLR_PG_CNT (0x1 << 20)#define NDCR_CLR_ECC (0x1 << 19)#define NDCR_RD_ID_CNT_MASK (0x7 << 16)#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)#define NDCR_RA_START (0x1 << 15)#define NDCR_PG_PER_BLK (0x1 << 14)#define NDCR_ND_ARB_EN (0x1 << 12)#define NDSR_MASK (0xfff)#define NDSR_RDY (0x1 << 11)#define NDSR_CS0_PAGED (0x1 << 10)#define NDSR_CS1_PAGED (0x1 << 9)#define NDSR_CS0_CMDD (0x1 << 8)#define NDSR_CS1_CMDD (0x1 << 7)#define NDSR_CS0_BBD (0x1 << 6)#define NDSR_CS1_BBD (0x1 << 5)#define NDSR_DBERR (0x1 << 4)#define NDSR_SBERR (0x1 << 3)#define NDSR_WRDREQ (0x1 << 2)#define NDSR_RDDREQ (0x1 << 1)#define NDSR_WRCMDREQ (0x1)#define NDCB0_AUTO_RS (0x1 << 25)#define NDCB0_CSEL (0x1 << 24)#define NDCB0_CMD_TYPE_MASK (0x7 << 21)#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)#define NDCB0_NC (0x1 << 20)#define NDCB0_DBC (0x1 << 19)#define NDCB0_ADDR_CYC_MASK (0x7 << 16)#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)#define NDCB0_CMD2_MASK (0xff << 8)#define NDCB0_CMD1_MASK (0xff)#define NDCB0_ADDR_CYC_SHIFT (16)/* dma-able I/O address for the NAND data and commands */#define NDCB0_DMA_ADDR (0x43100048)#define NDDB_DMA_ADDR (0x43100040)/* macros for registers read/write */#define nand_writel(info, off, val) \ __raw_writel((val), (info)->mmio_base + (off))#define nand_readl(info, off) \ __raw_readl((info)->mmio_base + (off))/* error code and state */enum { ERR_NONE = 0, ERR_DMABUSERR = -1, ERR_SENDCMD = -2, ERR_DBERR = -3, ERR_BBERR = -4,};enum { STATE_READY = 0, STATE_CMD_HANDLE, STATE_DMA_READING, STATE_DMA_WRITING, STATE_DMA_DONE, STATE_PIO_READING, STATE_PIO_WRITING,};struct pxa3xx_nand_info { struct nand_chip nand_chip; struct platform_device *pdev; const struct pxa3xx_nand_flash *flash_info; struct clk *clk; void __iomem *mmio_base; unsigned int buf_start; unsigned int buf_count; /* DMA information */ int drcmr_dat; int drcmr_cmd; unsigned char *data_buff; dma_addr_t data_buff_phys; size_t data_buff_size; int data_dma_ch; struct pxa_dma_desc *data_desc; dma_addr_t data_desc_addr; uint32_t reg_ndcr; /* saved column/page_addr during CMD_SEQIN */ int seqin_column; int seqin_page_addr; /* relate to the command */ unsigned int state; int use_ecc; /* use HW ECC ? */ int use_dma; /* use DMA ? */ size_t data_size; /* data size in FIFO */ int retcode; struct completion cmd_complete; /* generated NDCBx register values */ uint32_t ndcb0; uint32_t ndcb1; uint32_t ndcb2; /* calculated from pxa3xx_nand_flash data */ size_t oob_size; size_t read_id_bytes; unsigned int col_addr_cycles; unsigned int row_addr_cycles;};static int use_dma = 1;module_param(use_dma, bool, 0444);MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTINstatic struct pxa3xx_nand_cmdset smallpage_cmdset = { .read1 = 0x0000, .read2 = 0x0050, .program = 0x1080, .read_status = 0x0070, .read_id = 0x0090, .erase = 0xD060, .reset = 0x00FF, .lock = 0x002A, .unlock = 0x2423, .lock_status = 0x007A,};static struct pxa3xx_nand_cmdset largepage_cmdset = { .read1 = 0x3000, .read2 = 0x0050, .program = 0x1080, .read_status = 0x0070, .read_id = 0x0090, .erase = 0xD060, .reset = 0x00FF, .lock = 0x002A, .unlock = 0x2423, .lock_status = 0x007A,};static struct pxa3xx_nand_timing samsung512MbX16_timing = { .tCH = 10, .tCS = 0, .tWH = 20, .tWP = 40, .tRH = 30, .tRP = 40, .tR = 11123, .tWHR = 110, .tAR = 10,};static struct pxa3xx_nand_flash samsung512MbX16 = { .timing = &samsung512MbX16_timing, .cmdset = &smallpage_cmdset, .page_per_block = 32, .page_size = 512, .flash_width = 16, .dfc_width = 16, .num_blocks = 4096, .chip_id = 0x46ec,};static struct pxa3xx_nand_timing micron_timing = { .tCH = 10, .tCS = 25, .tWH = 15, .tWP = 25, .tRH = 15, .tRP = 25, .tR = 25000, .tWHR = 60, .tAR = 10,};static struct pxa3xx_nand_flash micron1GbX8 = { .timing = µn_timing, .cmdset = &largepage_cmdset, .page_per_block = 64, .page_size = 2048, .flash_width = 8, .dfc_width = 8, .num_blocks = 1024, .chip_id = 0xa12c,};static struct pxa3xx_nand_flash micron1GbX16 = { .timing = µn_timing, .cmdset = &largepage_cmdset, .page_per_block = 64, .page_size = 2048, .flash_width = 16, .dfc_width = 16, .num_blocks = 1024, .chip_id = 0xb12c,};static struct pxa3xx_nand_timing stm2GbX16_timing = { .tCH = 10, .tCS = 35, .tWH = 15, .tWP = 25, .tRH = 15, .tRP = 25, .tR = 25000, .tWHR = 60, .tAR = 10,};static struct pxa3xx_nand_flash stm2GbX16 = { .timing = &stm2GbX16_timing, .cmdset = &largepage_cmdset, .page_per_block = 64, .page_size = 2048, .flash_width = 16, .dfc_width = 16, .num_blocks = 2048, .chip_id = 0xba20,};static struct pxa3xx_nand_flash *builtin_flash_types[] = { &samsung512MbX16, µn1GbX8, µn1GbX16, &stm2GbX16,};#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */#define NDTR0_tCH(c) (min((c), 7) << 19)#define NDTR0_tCS(c) (min((c), 7) << 16)#define NDTR0_tWH(c) (min((c), 7) << 11)#define NDTR0_tWP(c) (min((c), 7) << 8)#define NDTR0_tRH(c) (min((c), 7) << 3)#define NDTR0_tRP(c) (min((c), 7) << 0)#define NDTR1_tR(c) (min((c), 65535) << 16)#define NDTR1_tWHR(c) (min((c), 15) << 4)#define NDTR1_tAR(c) (min((c), 15) << 0)/* convert nano-seconds to nand flash controller clock cycles */#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1)static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_timing *t){ unsigned long nand_clk = clk_get_rate(info->clk); uint32_t ndtr0, ndtr1; ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); nand_writel(info, NDTR0CS0, ndtr0); nand_writel(info, NDTR1CS0, ndtr1);}#define WAIT_EVENT_TIMEOUT 10static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event){ int timeout = WAIT_EVENT_TIMEOUT; uint32_t ndsr; while (timeout--) { ndsr = nand_readl(info, NDSR) & NDSR_MASK; if (ndsr & event) { nand_writel(info, NDSR, ndsr); return 0; } udelay(10); } return -ETIMEDOUT;}static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, uint16_t cmd, int column, int page_addr){ const struct pxa3xx_nand_flash *f = info->flash_info; const struct pxa3xx_nand_cmdset *cmdset = f->cmdset; /* calculate data size */ switch (f->page_size) { case 2048: info->data_size = (info->use_ecc) ? 2088 : 2112; break; case 512: info->data_size = (info->use_ecc) ? 520 : 528; break; default: return -EINVAL; } /* generate values for NDCBx registers */ info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb1 = 0; info->ndcb2 = 0; info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles); if (info->col_addr_cycles == 2) { /* large block, 2 cycles for column address * row address starts from 3rd cycle */ info->ndcb1 |= (page_addr << 16) | (column & 0xffff); if (info->row_addr_cycles == 3) info->ndcb2 = (page_addr >> 16) & 0xff; } else /* small block, 1 cycles for column address * row address starts from 2nd cycle */ info->ndcb1 = (page_addr << 8) | (column & 0xff); if (cmd == cmdset->program) info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; return 0;}static int prepare_erase_cmd(struct pxa3xx_nand_info *info, uint16_t cmd, int page_addr){ info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); info->ndcb1 = page_addr; info->ndcb2 = 0; return 0;}static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd){ const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb1 = 0; info->ndcb2 = 0; if (cmd == cmdset->read_id) { info->ndcb0 |= NDCB0_CMD_TYPE(3); info->data_size = 8; } else if (cmd == cmdset->read_status) { info->ndcb0 |= NDCB0_CMD_TYPE(4); info->data_size = 8; } else if (cmd == cmdset->reset || cmd == cmdset->lock || cmd == cmdset->unlock) { info->ndcb0 |= NDCB0_CMD_TYPE(5); } else return -EINVAL; return 0;}static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -