diff options
| -rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 92 |
1 files changed, 83 insertions, 9 deletions
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b2828e84d243..214b03afdd48 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
| @@ -30,6 +30,8 @@ | |||
| 30 | #include <linux/clk.h> | 30 | #include <linux/clk.h> |
| 31 | #include <linux/err.h> | 31 | #include <linux/err.h> |
| 32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
| 33 | #include <linux/irq.h> | ||
| 34 | #include <linux/completion.h> | ||
| 33 | 35 | ||
| 34 | #include <asm/mach/flash.h> | 36 | #include <asm/mach/flash.h> |
| 35 | #include <mach/mxc_nand.h> | 37 | #include <mach/mxc_nand.h> |
| @@ -151,7 +153,7 @@ struct mxc_nand_host { | |||
| 151 | int irq; | 153 | int irq; |
| 152 | int eccsize; | 154 | int eccsize; |
| 153 | 155 | ||
| 154 | wait_queue_head_t irq_waitq; | 156 | struct completion op_completion; |
| 155 | 157 | ||
| 156 | uint8_t *data_buf; | 158 | uint8_t *data_buf; |
| 157 | unsigned int buf_start; | 159 | unsigned int buf_start; |
| @@ -164,6 +166,7 @@ struct mxc_nand_host { | |||
| 164 | void (*send_read_id)(struct mxc_nand_host *); | 166 | void (*send_read_id)(struct mxc_nand_host *); |
| 165 | uint16_t (*get_dev_status)(struct mxc_nand_host *); | 167 | uint16_t (*get_dev_status)(struct mxc_nand_host *); |
| 166 | int (*check_int)(struct mxc_nand_host *); | 168 | int (*check_int)(struct mxc_nand_host *); |
| 169 | void (*irq_control)(struct mxc_nand_host *, int); | ||
| 167 | }; | 170 | }; |
| 168 | 171 | ||
| 169 | /* OOB placement block for use with hardware ecc generation */ | 172 | /* OOB placement block for use with hardware ecc generation */ |
| @@ -216,9 +219,12 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) | |||
| 216 | { | 219 | { |
| 217 | struct mxc_nand_host *host = dev_id; | 220 | struct mxc_nand_host *host = dev_id; |
| 218 | 221 | ||
| 219 | disable_irq_nosync(irq); | 222 | if (!host->check_int(host)) |
| 223 | return IRQ_NONE; | ||
| 220 | 224 | ||
| 221 | wake_up(&host->irq_waitq); | 225 | host->irq_control(host, 0); |
| 226 | |||
| 227 | complete(&host->op_completion); | ||
| 222 | 228 | ||
| 223 | return IRQ_HANDLED; | 229 | return IRQ_HANDLED; |
| 224 | } | 230 | } |
| @@ -245,11 +251,54 @@ static int check_int_v1_v2(struct mxc_nand_host *host) | |||
| 245 | if (!(tmp & NFC_V1_V2_CONFIG2_INT)) | 251 | if (!(tmp & NFC_V1_V2_CONFIG2_INT)) |
| 246 | return 0; | 252 | return 0; |
| 247 | 253 | ||
| 248 | writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); | 254 | if (!cpu_is_mx21()) |
| 255 | writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); | ||
| 249 | 256 | ||
| 250 | return 1; | 257 | return 1; |
| 251 | } | 258 | } |
| 252 | 259 | ||
| 260 | /* | ||
| 261 | * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit | ||
| 262 | * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the | ||
| 263 | * driver can enable/disable the irq line rather than simply masking the | ||
| 264 | * interrupts. | ||
| 265 | */ | ||
| 266 | static void irq_control_mx21(struct mxc_nand_host *host, int activate) | ||
| 267 | { | ||
| 268 | if (activate) | ||
| 269 | enable_irq(host->irq); | ||
| 270 | else | ||
| 271 | disable_irq_nosync(host->irq); | ||
| 272 | } | ||
| 273 | |||
| 274 | static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) | ||
| 275 | { | ||
| 276 | uint16_t tmp; | ||
| 277 | |||
| 278 | tmp = readw(NFC_V1_V2_CONFIG1); | ||
| 279 | |||
| 280 | if (activate) | ||
| 281 | tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; | ||
| 282 | else | ||
| 283 | tmp |= NFC_V1_V2_CONFIG1_INT_MSK; | ||
| 284 | |||
| 285 | writew(tmp, NFC_V1_V2_CONFIG1); | ||
| 286 | } | ||
| 287 | |||
| 288 | static void irq_control_v3(struct mxc_nand_host *host, int activate) | ||
| 289 | { | ||
| 290 | uint32_t tmp; | ||
| 291 | |||
| 292 | tmp = readl(NFC_V3_CONFIG2); | ||
| 293 | |||
| 294 | if (activate) | ||
| 295 | tmp &= ~NFC_V3_CONFIG2_INT_MSK; | ||
| 296 | else | ||
| 297 | tmp |= NFC_V3_CONFIG2_INT_MSK; | ||
| 298 | |||
| 299 | writel(tmp, NFC_V3_CONFIG2); | ||
| 300 | } | ||
| 301 | |||
| 253 | /* This function polls the NANDFC to wait for the basic operation to | 302 | /* This function polls the NANDFC to wait for the basic operation to |
| 254 | * complete by checking the INT bit of config2 register. | 303 | * complete by checking the INT bit of config2 register. |
| 255 | */ | 304 | */ |
| @@ -259,10 +308,9 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) | |||
| 259 | 308 | ||
| 260 | if (useirq) { | 309 | if (useirq) { |
| 261 | if (!host->check_int(host)) { | 310 | if (!host->check_int(host)) { |
| 262 | 311 | INIT_COMPLETION(host->op_completion); | |
| 263 | enable_irq(host->irq); | 312 | host->irq_control(host, 1); |
| 264 | 313 | wait_for_completion(&host->op_completion); | |
| 265 | wait_event(host->irq_waitq, host->check_int(host)); | ||
| 266 | } | 314 | } |
| 267 | } else { | 315 | } else { |
| 268 | while (max_retries-- > 0) { | 316 | while (max_retries-- > 0) { |
| @@ -799,6 +847,7 @@ static void preset_v3(struct mtd_info *mtd) | |||
| 799 | NFC_V3_CONFIG2_2CMD_PHASES | | 847 | NFC_V3_CONFIG2_2CMD_PHASES | |
| 800 | NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | | 848 | NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | |
| 801 | NFC_V3_CONFIG2_ST_CMD(0x70) | | 849 | NFC_V3_CONFIG2_ST_CMD(0x70) | |
| 850 | NFC_V3_CONFIG2_INT_MSK | | ||
| 802 | NFC_V3_CONFIG2_NUM_ADDR_PHASE0; | 851 | NFC_V3_CONFIG2_NUM_ADDR_PHASE0; |
| 803 | 852 | ||
| 804 | if (chip->ecc.mode == NAND_ECC_HW) | 853 | if (chip->ecc.mode == NAND_ECC_HW) |
| @@ -1024,6 +1073,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 1024 | host->send_read_id = send_read_id_v1_v2; | 1073 | host->send_read_id = send_read_id_v1_v2; |
| 1025 | host->get_dev_status = get_dev_status_v1_v2; | 1074 | host->get_dev_status = get_dev_status_v1_v2; |
| 1026 | host->check_int = check_int_v1_v2; | 1075 | host->check_int = check_int_v1_v2; |
| 1076 | if (cpu_is_mx21()) | ||
| 1077 | host->irq_control = irq_control_mx21; | ||
| 1078 | else | ||
| 1079 | host->irq_control = irq_control_v1_v2; | ||
| 1027 | } | 1080 | } |
| 1028 | 1081 | ||
| 1029 | if (nfc_is_v21()) { | 1082 | if (nfc_is_v21()) { |
| @@ -1062,6 +1115,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 1062 | host->send_read_id = send_read_id_v3; | 1115 | host->send_read_id = send_read_id_v3; |
| 1063 | host->check_int = check_int_v3; | 1116 | host->check_int = check_int_v3; |
| 1064 | host->get_dev_status = get_dev_status_v3; | 1117 | host->get_dev_status = get_dev_status_v3; |
| 1118 | host->irq_control = irq_control_v3; | ||
| 1065 | oob_smallpage = &nandv2_hw_eccoob_smallpage; | 1119 | oob_smallpage = &nandv2_hw_eccoob_smallpage; |
| 1066 | oob_largepage = &nandv2_hw_eccoob_largepage; | 1120 | oob_largepage = &nandv2_hw_eccoob_largepage; |
| 1067 | } else | 1121 | } else |
| @@ -1093,14 +1147,34 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 1093 | this->options |= NAND_USE_FLASH_BBT; | 1147 | this->options |= NAND_USE_FLASH_BBT; |
| 1094 | } | 1148 | } |
| 1095 | 1149 | ||
| 1096 | init_waitqueue_head(&host->irq_waitq); | 1150 | init_completion(&host->op_completion); |
| 1097 | 1151 | ||
| 1098 | host->irq = platform_get_irq(pdev, 0); | 1152 | host->irq = platform_get_irq(pdev, 0); |
| 1099 | 1153 | ||
| 1154 | /* | ||
| 1155 | * mask the interrupt. For i.MX21 explicitely call | ||
| 1156 | * irq_control_v1_v2 to use the mask bit. We can't call | ||
| 1157 | * disable_irq_nosync() for an interrupt we do not own yet. | ||
| 1158 | */ | ||
| 1159 | if (cpu_is_mx21()) | ||
| 1160 | irq_control_v1_v2(host, 0); | ||
| 1161 | else | ||
| 1162 | host->irq_control(host, 0); | ||
| 1163 | |||
| 1100 | err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); | 1164 | err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); |
| 1101 | if (err) | 1165 | if (err) |
| 1102 | goto eirq; | 1166 | goto eirq; |
| 1103 | 1167 | ||
| 1168 | host->irq_control(host, 0); | ||
| 1169 | |||
| 1170 | /* | ||
| 1171 | * Now that the interrupt is disabled make sure the interrupt | ||
| 1172 | * mask bit is cleared on i.MX21. Otherwise we can't read | ||
| 1173 | * the interrupt status bit on this machine. | ||
| 1174 | */ | ||
| 1175 | if (cpu_is_mx21()) | ||
| 1176 | irq_control_v1_v2(host, 1); | ||
| 1177 | |||
| 1104 | /* first scan to find the device and get the page size */ | 1178 | /* first scan to find the device and get the page size */ |
| 1105 | if (nand_scan_ident(mtd, 1, NULL)) { | 1179 | if (nand_scan_ident(mtd, 1, NULL)) { |
| 1106 | err = -ENXIO; | 1180 | err = -ENXIO; |
