|
|
|
@ -140,6 +140,19 @@ |
|
|
|
|
|
|
|
|
|
#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); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct mxc_nand_host { |
|
|
|
|
struct mtd_info mtd; |
|
|
|
|
struct nand_chip nand; |
|
|
|
@ -165,14 +178,7 @@ struct mxc_nand_host { |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked |
|
|
|
@ -315,7 +321,7 @@ static void irq_control(struct mxc_nand_host *host, int activate) |
|
|
|
|
else |
|
|
|
|
disable_irq_nosync(host->irq); |
|
|
|
|
} else { |
|
|
|
|
host->irq_control(host, activate); |
|
|
|
|
host->devtype_data->irq_control(host, activate); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -323,7 +329,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) |
|
|
|
|
{ |
|
|
|
|
struct mxc_nand_host *host = dev_id; |
|
|
|
|
|
|
|
|
|
if (!host->check_int(host)) |
|
|
|
|
if (!host->devtype_data->check_int(host)) |
|
|
|
|
return IRQ_NONE; |
|
|
|
|
|
|
|
|
|
irq_control(host, 0); |
|
|
|
@ -341,14 +347,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); |
|
|
|
|
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); |
|
|
|
@ -621,7 +627,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++; |
|
|
|
@ -756,34 +762,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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -942,15 +958,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; |
|
|
|
|
|
|
|
|
@ -963,13 +979,14 @@ 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); |
|
|
|
|
copy_spare(mtd, true); |
|
|
|
@ -982,28 +999,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); |
|
|
|
|
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; |
|
|
|
@ -1037,6 +1054,42 @@ static struct nand_bbt_descr bbt_mirror_descr = { |
|
|
|
|
.pattern = mirror_pattern, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* v1: i.MX21, i.MX27, i.MX31 */ |
|
|
|
|
static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { |
|
|
|
|
.preset = preset_v1_v2, |
|
|
|
|
.send_cmd = send_cmd_v1_v2, |
|
|
|
|
.send_addr = send_addr_v1_v2, |
|
|
|
|
.send_page = send_page_v1_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, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* v21: i.MX25, i.MX35 */ |
|
|
|
|
static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { |
|
|
|
|
.preset = preset_v1_v2, |
|
|
|
|
.send_cmd = send_cmd_v1_v2, |
|
|
|
|
.send_addr = send_addr_v1_v2, |
|
|
|
|
.send_page = send_page_v1_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, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* 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, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int __init mxcnd_probe(struct platform_device *pdev) |
|
|
|
|
{ |
|
|
|
|
struct nand_chip *this; |
|
|
|
@ -1100,27 +1153,10 @@ 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; |
|
|
|
|
host->irq_control = irq_control_v1_v2; |
|
|
|
|
if (nfc_is_v1()) { |
|
|
|
|
host->devtype_data = &imx21_nand_devtype_data; |
|
|
|
|
if (cpu_is_mx21()) |
|
|
|
|
host->irqpending_quirk = 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
@ -1128,7 +1164,16 @@ static int __init mxcnd_probe(struct platform_device *pdev) |
|
|
|
|
oob_largepage = &nandv1_hw_eccoob_largepage; |
|
|
|
|
this->ecc.bytes = 3; |
|
|
|
|
host->eccsize = 1; |
|
|
|
|
} else if (nfc_is_v21()) { |
|
|
|
|
host->devtype_data = &imx25_nand_devtype_data; |
|
|
|
|
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_v3_2()) { |
|
|
|
|
host->devtype_data = &imx51_nand_devtype_data; |
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
|
|
|
|
if (!res) { |
|
|
|
|
err = -ENODEV; |
|
|
|
@ -1142,14 +1187,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) |
|
|
|
|
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 |
|
|
|
@ -1186,10 +1223,11 @@ static int __init mxcnd_probe(struct platform_device *pdev) |
|
|
|
|
host->irq = platform_get_irq(pdev, 0); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Use host->irq_control here instead of irq_control because we must not |
|
|
|
|
* disable_irq_nosync without having requested the irq |
|
|
|
|
* Use host->devtype_data->irq_control() here instead of irq_control() |
|
|
|
|
* because we must not disable_irq_nosync without having requested the |
|
|
|
|
* irq. |
|
|
|
|
*/ |
|
|
|
|
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) |
|
|
|
@ -1202,7 +1240,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) |
|
|
|
|
*/ |
|
|
|
|
if (host->irqpending_quirk) { |
|
|
|
|
disable_irq_nosync(host->irq); |
|
|
|
|
host->irq_control(host, 1); |
|
|
|
|
host->devtype_data->irq_control(host, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* first scan to find the device and get the page size */ |
|
|
|
@ -1212,7 +1250,7 @@ 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; |
|
|
|
|