mirror of https://gitee.com/openkylin/linux.git
mtd: pxa3xx_nand: mtd scan id process could be defined by driver itself
Different NAND driver may require its unique detection. For pxa3xx_nand, it use its self id database to get the necessary info. Signed-off-by: Lei Wen <leiwen@marvell.com> Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
4eb2da8994
commit
401e67e225
|
@ -123,6 +123,7 @@ enum {
|
||||||
struct pxa3xx_nand_info {
|
struct pxa3xx_nand_info {
|
||||||
struct nand_chip nand_chip;
|
struct nand_chip nand_chip;
|
||||||
|
|
||||||
|
struct nand_hw_control controller;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct pxa3xx_nand_cmdset *cmdset;
|
struct pxa3xx_nand_cmdset *cmdset;
|
||||||
|
|
||||||
|
@ -157,6 +158,7 @@ struct pxa3xx_nand_info {
|
||||||
|
|
||||||
int use_ecc; /* use HW ECC ? */
|
int use_ecc; /* use HW ECC ? */
|
||||||
int use_dma; /* use DMA ? */
|
int use_dma; /* use DMA ? */
|
||||||
|
int is_ready;
|
||||||
|
|
||||||
unsigned int page_size; /* page size of attached chip */
|
unsigned int page_size; /* page size of attached chip */
|
||||||
unsigned int data_size; /* data size in FIFO */
|
unsigned int data_size; /* data size in FIFO */
|
||||||
|
@ -223,6 +225,8 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
|
||||||
/* Define a default flash type setting serve as flash detecting only */
|
/* Define a default flash type setting serve as flash detecting only */
|
||||||
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
|
||||||
|
|
||||||
|
const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
|
||||||
|
|
||||||
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
#define NDTR0_tCH(c) (min((c), 7) << 19)
|
||||||
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
#define NDTR0_tCS(c) (min((c), 7) << 16)
|
||||||
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
#define NDTR0_tWH(c) (min((c), 7) << 11)
|
||||||
|
@ -437,8 +441,10 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
|
||||||
info->state = STATE_CMD_DONE;
|
info->state = STATE_CMD_DONE;
|
||||||
is_completed = 1;
|
is_completed = 1;
|
||||||
}
|
}
|
||||||
if (status & NDSR_FLASH_RDY)
|
if (status & NDSR_FLASH_RDY) {
|
||||||
|
info->is_ready = 1;
|
||||||
info->state = STATE_READY;
|
info->state = STATE_READY;
|
||||||
|
}
|
||||||
|
|
||||||
if (status & NDSR_WRCMDREQ) {
|
if (status & NDSR_WRCMDREQ) {
|
||||||
nand_writel(info, NDSR, NDSR_WRCMDREQ);
|
nand_writel(info, NDSR, NDSR_WRCMDREQ);
|
||||||
|
@ -483,10 +489,11 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
||||||
exec_cmd = 1;
|
exec_cmd = 1;
|
||||||
|
|
||||||
/* reset data and oob column point to handle data */
|
/* reset data and oob column point to handle data */
|
||||||
info->buf_start = 0;
|
info->buf_start = 0;
|
||||||
info->buf_count = 0;
|
info->buf_count = 0;
|
||||||
info->oob_size = 0;
|
info->oob_size = 0;
|
||||||
info->use_ecc = 0;
|
info->use_ecc = 0;
|
||||||
|
info->is_ready = 0;
|
||||||
info->retcode = ERR_NONE;
|
info->retcode = ERR_NONE;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
@ -849,42 +856,6 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
|
|
||||||
const struct pxa3xx_nand_platform_data *pdata)
|
|
||||||
{
|
|
||||||
const struct pxa3xx_nand_flash *f;
|
|
||||||
uint32_t id = -1;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (pdata->keep_config)
|
|
||||||
if (pxa3xx_nand_detect_config(info) == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* we use default timing to detect id */
|
|
||||||
f = DEFAULT_FLASH_TYPE;
|
|
||||||
pxa3xx_nand_config_flash(info, f);
|
|
||||||
pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0);
|
|
||||||
id = *((uint16_t *)(info->data_buff));
|
|
||||||
|
|
||||||
for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) {
|
|
||||||
/* we first choose the flash definition from platfrom */
|
|
||||||
if (i < pdata->num_flash)
|
|
||||||
f = pdata->flash + i;
|
|
||||||
else
|
|
||||||
f = &builtin_flash_types[i - pdata->num_flash + 1];
|
|
||||||
if (f->chip_id == id) {
|
|
||||||
dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id);
|
|
||||||
pxa3xx_nand_config_flash(info, f);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_warn(&info->pdev->dev,
|
|
||||||
"failed to detect configured nand flash; found %04x instead of\n",
|
|
||||||
id);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the maximum possible buffer size for large page with OOB data
|
/* the maximum possible buffer size for large page with OOB data
|
||||||
* is: 2048 + 64 = 2112 bytes, allocate a page here for both the
|
* is: 2048 + 64 = 2112 bytes, allocate a page here for both the
|
||||||
* data buffer and the DMA descriptor
|
* data buffer and the DMA descriptor
|
||||||
|
@ -926,57 +897,110 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nand_ecclayout hw_smallpage_ecclayout = {
|
static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
|
||||||
.eccbytes = 6,
|
|
||||||
.eccpos = {8, 9, 10, 11, 12, 13 },
|
|
||||||
.oobfree = { {2, 6} }
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct nand_ecclayout hw_largepage_ecclayout = {
|
|
||||||
.eccbytes = 24,
|
|
||||||
.eccpos = {
|
|
||||||
40, 41, 42, 43, 44, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 52, 53, 54, 55,
|
|
||||||
56, 57, 58, 59, 60, 61, 62, 63},
|
|
||||||
.oobfree = { {2, 38} }
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
|
|
||||||
struct pxa3xx_nand_info *info)
|
|
||||||
{
|
{
|
||||||
struct nand_chip *this = &info->nand_chip;
|
struct mtd_info *mtd = info->mtd;
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
|
||||||
this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
|
/* use the common timing to make a try */
|
||||||
this->options |= NAND_NO_AUTOINCR;
|
pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
|
||||||
this->waitfunc = pxa3xx_nand_waitfunc;
|
if (info->is_ready)
|
||||||
this->select_chip = pxa3xx_nand_select_chip;
|
return 1;
|
||||||
this->dev_ready = pxa3xx_nand_dev_ready;
|
|
||||||
this->cmdfunc = pxa3xx_nand_cmdfunc;
|
|
||||||
this->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
|
||||||
this->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
|
||||||
this->read_word = pxa3xx_nand_read_word;
|
|
||||||
this->read_byte = pxa3xx_nand_read_byte;
|
|
||||||
this->read_buf = pxa3xx_nand_read_buf;
|
|
||||||
this->write_buf = pxa3xx_nand_write_buf;
|
|
||||||
this->verify_buf = pxa3xx_nand_verify_buf;
|
|
||||||
|
|
||||||
this->ecc.mode = NAND_ECC_HW;
|
|
||||||
this->ecc.size = info->page_size;
|
|
||||||
|
|
||||||
if (info->page_size == 2048)
|
|
||||||
this->ecc.layout = &hw_largepage_ecclayout;
|
|
||||||
else
|
else
|
||||||
this->ecc.layout = &hw_smallpage_ecclayout;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
this->chip_delay = 25;
|
static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct pxa3xx_nand_info *info = mtd->priv;
|
||||||
|
struct platform_device *pdev = info->pdev;
|
||||||
|
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
const struct pxa3xx_nand_flash *f = NULL;
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
uint32_t id = -1;
|
||||||
|
int i, ret, num;
|
||||||
|
|
||||||
|
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = pxa3xx_nand_sensing(info);
|
||||||
|
if (!ret) {
|
||||||
|
kfree(mtd);
|
||||||
|
info->mtd = NULL;
|
||||||
|
printk(KERN_INFO "There is no nand chip on cs 0!\n");
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
|
||||||
|
id = *((uint16_t *)(info->data_buff));
|
||||||
|
if (id != 0)
|
||||||
|
printk(KERN_INFO "Detect a flash id %x\n", id);
|
||||||
|
else {
|
||||||
|
kfree(mtd);
|
||||||
|
info->mtd = NULL;
|
||||||
|
printk(KERN_WARNING "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) {
|
||||||
|
pxa3xx_nand_config_flash(info, f);
|
||||||
|
mtd->writesize = f->page_size;
|
||||||
|
mtd->writesize_shift = ffs(mtd->writesize) - 1;
|
||||||
|
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
|
||||||
|
mtd->oobsize = mtd->writesize / 32;
|
||||||
|
mtd->erasesize = f->page_size * f->page_per_block;
|
||||||
|
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
|
||||||
|
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
|
||||||
|
|
||||||
|
mtd->name = mtd_names[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash)) {
|
||||||
|
kfree(mtd);
|
||||||
|
info->mtd = NULL;
|
||||||
|
printk(KERN_ERR "ERROR!! flash not defined!!!\n");
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
chip->ecc.size = f->page_size;
|
||||||
|
chip->chipsize = (uint64_t)f->num_blocks * f->page_per_block
|
||||||
|
* f->page_size;
|
||||||
|
mtd->size = chip->chipsize;
|
||||||
|
|
||||||
|
/* Calculate the address shift from the page size */
|
||||||
|
chip->page_shift = ffs(mtd->writesize) - 1;
|
||||||
|
chip->pagemask = mtd_div_by_ws(chip->chipsize, mtd) - 1;
|
||||||
|
chip->numchips = 1;
|
||||||
|
chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
|
||||||
|
chip->bbt_erase_shift = chip->phys_erase_shift;
|
||||||
|
|
||||||
|
chip->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0;
|
||||||
|
chip->options |= NAND_NO_AUTOINCR;
|
||||||
|
chip->options |= NAND_NO_READRDY;
|
||||||
|
|
||||||
|
return nand_scan_tail(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
|
struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
struct pxa3xx_nand_info *info;
|
struct pxa3xx_nand_info *info;
|
||||||
|
struct nand_chip *chip;
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *mtd;
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
|
@ -989,12 +1013,27 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
info = (struct pxa3xx_nand_info *)(&mtd[1]);
|
info = (struct pxa3xx_nand_info *)(&mtd[1]);
|
||||||
|
chip = (struct nand_chip *)(&mtd[1]);
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
|
|
||||||
mtd->priv = info;
|
|
||||||
info->mtd = mtd;
|
info->mtd = mtd;
|
||||||
|
mtd->priv = info;
|
||||||
mtd->owner = THIS_MODULE;
|
mtd->owner = THIS_MODULE;
|
||||||
|
|
||||||
|
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
|
||||||
|
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
|
||||||
|
chip->controller = &info->controller;
|
||||||
|
chip->waitfunc = pxa3xx_nand_waitfunc;
|
||||||
|
chip->select_chip = pxa3xx_nand_select_chip;
|
||||||
|
chip->dev_ready = pxa3xx_nand_dev_ready;
|
||||||
|
chip->cmdfunc = pxa3xx_nand_cmdfunc;
|
||||||
|
chip->read_word = pxa3xx_nand_read_word;
|
||||||
|
chip->read_byte = pxa3xx_nand_read_byte;
|
||||||
|
chip->read_buf = pxa3xx_nand_read_buf;
|
||||||
|
chip->write_buf = pxa3xx_nand_write_buf;
|
||||||
|
chip->verify_buf = pxa3xx_nand_verify_buf;
|
||||||
|
|
||||||
|
spin_lock_init(&chip->controller->lock);
|
||||||
|
init_waitqueue_head(&chip->controller->wq);
|
||||||
info->clk = clk_get(&pdev->dev, NULL);
|
info->clk = clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(info->clk)) {
|
if (IS_ERR(info->clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get nand clock\n");
|
dev_err(&pdev->dev, "failed to get nand clock\n");
|
||||||
|
@ -1062,21 +1101,12 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
|
||||||
goto fail_free_buf;
|
goto fail_free_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pxa3xx_nand_detect_flash(info, pdata);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "failed to detect flash\n");
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto fail_free_irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxa3xx_nand_init_mtd(mtd, info);
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
|
||||||
fail_free_irq:
|
|
||||||
free_irq(irq, info);
|
|
||||||
fail_free_buf:
|
fail_free_buf:
|
||||||
|
free_irq(irq, info);
|
||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
pxa_free_dma(info->data_dma_ch);
|
pxa_free_dma(info->data_dma_ch);
|
||||||
dma_free_coherent(&pdev->dev, info->data_buff_size,
|
dma_free_coherent(&pdev->dev, info->data_buff_size,
|
||||||
|
@ -1146,7 +1176,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (nand_scan(info->mtd, 1)) {
|
if (pxa3xx_nand_scan(info->mtd)) {
|
||||||
dev_err(&pdev->dev, "failed to scan nand\n");
|
dev_err(&pdev->dev, "failed to scan nand\n");
|
||||||
pxa3xx_nand_remove(pdev);
|
pxa3xx_nand_remove(pdev);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
Loading…
Reference in New Issue