Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mxc_nand: do not depend on disabling the irq in the interrupt handler

This patch reverts the driver to enabling/disabling the NFC interrupt
mask rather than enabling/disabling the system interrupt. This cleans
up the driver so that it doesn't rely on interrupts being disabled
within the interrupt handler.

For i.MX21 we keep the current behaviour, that is calling
enable_irq/disable_irq_nosync to enable/disable interrupts. This patch
is based on earlier work by John Ogness.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: John Ogness <john.ogness@linutronix.de>
Tested-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Sascha Hauer and committed by
Linus Torvalds
63f1474c f68c834b

+83 -9
+83 -9
drivers/mtd/nand/mxc_nand.c
··· 30 30 #include <linux/clk.h> 31 31 #include <linux/err.h> 32 32 #include <linux/io.h> 33 + #include <linux/irq.h> 34 + #include <linux/completion.h> 33 35 34 36 #include <asm/mach/flash.h> 35 37 #include <mach/mxc_nand.h> ··· 153 151 int irq; 154 152 int eccsize; 155 153 156 - wait_queue_head_t irq_waitq; 154 + struct completion op_completion; 157 155 158 156 uint8_t *data_buf; 159 157 unsigned int buf_start; ··· 166 164 void (*send_read_id)(struct mxc_nand_host *); 167 165 uint16_t (*get_dev_status)(struct mxc_nand_host *); 168 166 int (*check_int)(struct mxc_nand_host *); 167 + void (*irq_control)(struct mxc_nand_host *, int); 169 168 }; 170 169 171 170 /* OOB placement block for use with hardware ecc generation */ ··· 219 216 { 220 217 struct mxc_nand_host *host = dev_id; 221 218 222 - disable_irq_nosync(irq); 219 + if (!host->check_int(host)) 220 + return IRQ_NONE; 223 221 224 - wake_up(&host->irq_waitq); 222 + host->irq_control(host, 0); 223 + 224 + complete(&host->op_completion); 225 225 226 226 return IRQ_HANDLED; 227 227 } ··· 251 245 if (!(tmp & NFC_V1_V2_CONFIG2_INT)) 252 246 return 0; 253 247 254 - writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); 248 + if (!cpu_is_mx21()) 249 + writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); 255 250 256 251 return 1; 252 + } 253 + 254 + /* 255 + * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit 256 + * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the 257 + * driver can enable/disable the irq line rather than simply masking the 258 + * interrupts. 259 + */ 260 + static void irq_control_mx21(struct mxc_nand_host *host, int activate) 261 + { 262 + if (activate) 263 + enable_irq(host->irq); 264 + else 265 + disable_irq_nosync(host->irq); 266 + } 267 + 268 + static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) 269 + { 270 + uint16_t tmp; 271 + 272 + tmp = readw(NFC_V1_V2_CONFIG1); 273 + 274 + if (activate) 275 + tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; 276 + else 277 + tmp |= NFC_V1_V2_CONFIG1_INT_MSK; 278 + 279 + writew(tmp, NFC_V1_V2_CONFIG1); 280 + } 281 + 282 + static void irq_control_v3(struct mxc_nand_host *host, int activate) 283 + { 284 + uint32_t tmp; 285 + 286 + tmp = readl(NFC_V3_CONFIG2); 287 + 288 + if (activate) 289 + tmp &= ~NFC_V3_CONFIG2_INT_MSK; 290 + else 291 + tmp |= NFC_V3_CONFIG2_INT_MSK; 292 + 293 + writel(tmp, NFC_V3_CONFIG2); 257 294 } 258 295 259 296 /* This function polls the NANDFC to wait for the basic operation to ··· 308 259 309 260 if (useirq) { 310 261 if (!host->check_int(host)) { 311 - 312 - enable_irq(host->irq); 313 - 314 - wait_event(host->irq_waitq, host->check_int(host)); 262 + INIT_COMPLETION(host->op_completion); 263 + host->irq_control(host, 1); 264 + wait_for_completion(&host->op_completion); 315 265 } 316 266 } else { 317 267 while (max_retries-- > 0) { ··· 847 799 NFC_V3_CONFIG2_2CMD_PHASES | 848 800 NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | 849 801 NFC_V3_CONFIG2_ST_CMD(0x70) | 802 + NFC_V3_CONFIG2_INT_MSK | 850 803 NFC_V3_CONFIG2_NUM_ADDR_PHASE0; 851 804 852 805 if (chip->ecc.mode == NAND_ECC_HW) ··· 1073 1024 host->send_read_id = send_read_id_v1_v2; 1074 1025 host->get_dev_status = get_dev_status_v1_v2; 1075 1026 host->check_int = check_int_v1_v2; 1027 + if (cpu_is_mx21()) 1028 + host->irq_control = irq_control_mx21; 1029 + else 1030 + host->irq_control = irq_control_v1_v2; 1076 1031 } 1077 1032 1078 1033 if (nfc_is_v21()) { ··· 1115 1062 host->send_read_id = send_read_id_v3; 1116 1063 host->check_int = check_int_v3; 1117 1064 host->get_dev_status = get_dev_status_v3; 1065 + host->irq_control = irq_control_v3; 1118 1066 oob_smallpage = &nandv2_hw_eccoob_smallpage; 1119 1067 oob_largepage = &nandv2_hw_eccoob_largepage; 1120 1068 } else ··· 1147 1093 this->options |= NAND_USE_FLASH_BBT; 1148 1094 } 1149 1095 1150 - init_waitqueue_head(&host->irq_waitq); 1096 + init_completion(&host->op_completion); 1151 1097 1152 1098 host->irq = platform_get_irq(pdev, 0); 1099 + 1100 + /* 1101 + * mask the interrupt. For i.MX21 explicitely call 1102 + * irq_control_v1_v2 to use the mask bit. We can't call 1103 + * disable_irq_nosync() for an interrupt we do not own yet. 1104 + */ 1105 + if (cpu_is_mx21()) 1106 + irq_control_v1_v2(host, 0); 1107 + else 1108 + host->irq_control(host, 0); 1153 1109 1154 1110 err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); 1155 1111 if (err) 1156 1112 goto eirq; 1113 + 1114 + host->irq_control(host, 0); 1115 + 1116 + /* 1117 + * Now that the interrupt is disabled make sure the interrupt 1118 + * mask bit is cleared on i.MX21. Otherwise we can't read 1119 + * the interrupt status bit on this machine. 1120 + */ 1121 + if (cpu_is_mx21()) 1122 + irq_control_v1_v2(host, 1); 1157 1123 1158 1124 /* first scan to find the device and get the page size */ 1159 1125 if (nand_scan_ident(mtd, 1, NULL)) {