diff options
Diffstat (limited to 'drivers/mmc/host/s3cmci.c')
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 161 |
1 files changed, 151 insertions, 10 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8c2c8b456087..7660ac4572e5 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c | |||
@@ -190,7 +190,33 @@ static inline u32 disable_imask(struct s3cmci_host *host, u32 imask) | |||
190 | 190 | ||
191 | static inline void clear_imask(struct s3cmci_host *host) | 191 | static inline void clear_imask(struct s3cmci_host *host) |
192 | { | 192 | { |
193 | writel(0, host->base + host->sdiimsk); | 193 | u32 mask = readl(host->base + host->sdiimsk); |
194 | |||
195 | /* preserve the SDIO IRQ mask state */ | ||
196 | mask &= S3C2410_SDIIMSK_SDIOIRQ; | ||
197 | writel(mask, host->base + host->sdiimsk); | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled | ||
202 | * @host: The host to check. | ||
203 | * | ||
204 | * Test to see if the SDIO interrupt is being signalled in case the | ||
205 | * controller has failed to re-detect a card interrupt. Read GPE8 and | ||
206 | * see if it is low and if so, signal a SDIO interrupt. | ||
207 | * | ||
208 | * This is currently called if a request is finished (we assume that the | ||
209 | * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is | ||
210 | * already being indicated. | ||
211 | */ | ||
212 | static void s3cmci_check_sdio_irq(struct s3cmci_host *host) | ||
213 | { | ||
214 | if (host->sdio_irqen) { | ||
215 | if (gpio_get_value(S3C2410_GPE(8)) == 0) { | ||
216 | printk(KERN_DEBUG "%s: signalling irq\n", __func__); | ||
217 | mmc_signal_sdio_irq(host->mmc); | ||
218 | } | ||
219 | } | ||
194 | } | 220 | } |
195 | 221 | ||
196 | static inline int get_data_buffer(struct s3cmci_host *host, | 222 | static inline int get_data_buffer(struct s3cmci_host *host, |
@@ -238,6 +264,64 @@ static inline u32 fifo_free(struct s3cmci_host *host) | |||
238 | return 63 - fifostat; | 264 | return 63 - fifostat; |
239 | } | 265 | } |
240 | 266 | ||
267 | /** | ||
268 | * s3cmci_enable_irq - enable IRQ, after having disabled it. | ||
269 | * @host: The device state. | ||
270 | * @more: True if more IRQs are expected from transfer. | ||
271 | * | ||
272 | * Enable the main IRQ if needed after it has been disabled. | ||
273 | * | ||
274 | * The IRQ can be one of the following states: | ||
275 | * - disabled during IDLE | ||
276 | * - disabled whilst processing data | ||
277 | * - enabled during transfer | ||
278 | * - enabled whilst awaiting SDIO interrupt detection | ||
279 | */ | ||
280 | static void s3cmci_enable_irq(struct s3cmci_host *host, bool more) | ||
281 | { | ||
282 | unsigned long flags; | ||
283 | bool enable = false; | ||
284 | |||
285 | local_irq_save(flags); | ||
286 | |||
287 | host->irq_enabled = more; | ||
288 | host->irq_disabled = false; | ||
289 | |||
290 | enable = more | host->sdio_irqen; | ||
291 | |||
292 | if (host->irq_state != enable) { | ||
293 | host->irq_state = enable; | ||
294 | |||
295 | if (enable) | ||
296 | enable_irq(host->irq); | ||
297 | else | ||
298 | disable_irq(host->irq); | ||
299 | } | ||
300 | |||
301 | local_irq_restore(flags); | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * | ||
306 | */ | ||
307 | static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer) | ||
308 | { | ||
309 | unsigned long flags; | ||
310 | |||
311 | local_irq_save(flags); | ||
312 | |||
313 | //printk(KERN_DEBUG "%s: transfer %d\n", __func__, transfer); | ||
314 | |||
315 | host->irq_disabled = transfer; | ||
316 | |||
317 | if (transfer && host->irq_state) { | ||
318 | host->irq_state = false; | ||
319 | disable_irq(host->irq); | ||
320 | } | ||
321 | |||
322 | local_irq_restore(flags); | ||
323 | } | ||
324 | |||
241 | static void do_pio_read(struct s3cmci_host *host) | 325 | static void do_pio_read(struct s3cmci_host *host) |
242 | { | 326 | { |
243 | int res; | 327 | int res; |
@@ -374,8 +458,7 @@ static void pio_tasklet(unsigned long data) | |||
374 | { | 458 | { |
375 | struct s3cmci_host *host = (struct s3cmci_host *) data; | 459 | struct s3cmci_host *host = (struct s3cmci_host *) data; |
376 | 460 | ||
377 | 461 | s3cmci_disable_irq(host, true); | |
378 | disable_irq(host->irq); | ||
379 | 462 | ||
380 | if (host->pio_active == XFER_WRITE) | 463 | if (host->pio_active == XFER_WRITE) |
381 | do_pio_write(host); | 464 | do_pio_write(host); |
@@ -395,9 +478,10 @@ static void pio_tasklet(unsigned long data) | |||
395 | host->mrq->data->error = -EINVAL; | 478 | host->mrq->data->error = -EINVAL; |
396 | } | 479 | } |
397 | 480 | ||
481 | s3cmci_enable_irq(host, false); | ||
398 | finalize_request(host); | 482 | finalize_request(host); |
399 | } else | 483 | } else |
400 | enable_irq(host->irq); | 484 | s3cmci_enable_irq(host, true); |
401 | } | 485 | } |
402 | 486 | ||
403 | /* | 487 | /* |
@@ -432,17 +516,27 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id) | |||
432 | struct s3cmci_host *host = dev_id; | 516 | struct s3cmci_host *host = dev_id; |
433 | struct mmc_command *cmd; | 517 | struct mmc_command *cmd; |
434 | u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; | 518 | u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; |
435 | u32 mci_cclear, mci_dclear; | 519 | u32 mci_cclear = 0, mci_dclear; |
436 | unsigned long iflags; | 520 | unsigned long iflags; |
437 | 521 | ||
522 | mci_dsta = readl(host->base + S3C2410_SDIDSTA); | ||
523 | mci_imsk = readl(host->base + host->sdiimsk); | ||
524 | |||
525 | if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) { | ||
526 | if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) { | ||
527 | mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT; | ||
528 | writel(mci_dclear, host->base + S3C2410_SDIDSTA); | ||
529 | |||
530 | mmc_signal_sdio_irq(host->mmc); | ||
531 | return IRQ_HANDLED; | ||
532 | } | ||
533 | } | ||
534 | |||
438 | spin_lock_irqsave(&host->complete_lock, iflags); | 535 | spin_lock_irqsave(&host->complete_lock, iflags); |
439 | 536 | ||
440 | mci_csta = readl(host->base + S3C2410_SDICMDSTAT); | 537 | mci_csta = readl(host->base + S3C2410_SDICMDSTAT); |
441 | mci_dsta = readl(host->base + S3C2410_SDIDSTA); | ||
442 | mci_dcnt = readl(host->base + S3C2410_SDIDCNT); | 538 | mci_dcnt = readl(host->base + S3C2410_SDIDCNT); |
443 | mci_fsta = readl(host->base + S3C2410_SDIFSTA); | 539 | mci_fsta = readl(host->base + S3C2410_SDIFSTA); |
444 | mci_imsk = readl(host->base + host->sdiimsk); | ||
445 | mci_cclear = 0; | ||
446 | mci_dclear = 0; | 540 | mci_dclear = 0; |
447 | 541 | ||
448 | if ((host->complete_what == COMPLETION_NONE) || | 542 | if ((host->complete_what == COMPLETION_NONE) || |
@@ -776,6 +870,8 @@ static void finalize_request(struct s3cmci_host *host) | |||
776 | request_done: | 870 | request_done: |
777 | host->complete_what = COMPLETION_NONE; | 871 | host->complete_what = COMPLETION_NONE; |
778 | host->mrq = NULL; | 872 | host->mrq = NULL; |
873 | |||
874 | s3cmci_check_sdio_irq(host); | ||
779 | mmc_request_done(host->mmc, mrq); | 875 | mmc_request_done(host->mmc, mrq); |
780 | } | 876 | } |
781 | 877 | ||
@@ -1037,7 +1133,7 @@ static void s3cmci_send_request(struct mmc_host *mmc) | |||
1037 | s3cmci_send_command(host, cmd); | 1133 | s3cmci_send_command(host, cmd); |
1038 | 1134 | ||
1039 | /* Enable Interrupt */ | 1135 | /* Enable Interrupt */ |
1040 | enable_irq(host->irq); | 1136 | s3cmci_enable_irq(host, true); |
1041 | } | 1137 | } |
1042 | 1138 | ||
1043 | static int s3cmci_card_present(struct mmc_host *mmc) | 1139 | static int s3cmci_card_present(struct mmc_host *mmc) |
@@ -1178,11 +1274,52 @@ static int s3cmci_get_ro(struct mmc_host *mmc) | |||
1178 | return ret; | 1274 | return ret; |
1179 | } | 1275 | } |
1180 | 1276 | ||
1277 | static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable) | ||
1278 | { | ||
1279 | struct s3cmci_host *host = mmc_priv(mmc); | ||
1280 | unsigned long flags; | ||
1281 | u32 con; | ||
1282 | |||
1283 | local_irq_save(flags); | ||
1284 | |||
1285 | con = readl(host->base + S3C2410_SDICON); | ||
1286 | host->sdio_irqen = enable; | ||
1287 | |||
1288 | if (enable == host->sdio_irqen) | ||
1289 | goto same_state; | ||
1290 | |||
1291 | if (enable) { | ||
1292 | con |= S3C2410_SDICON_SDIOIRQ; | ||
1293 | enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); | ||
1294 | |||
1295 | if (!host->irq_state && !host->irq_disabled) { | ||
1296 | host->irq_state = true; | ||
1297 | enable_irq(host->irq); | ||
1298 | } | ||
1299 | } else { | ||
1300 | disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); | ||
1301 | con &= ~S3C2410_SDICON_SDIOIRQ; | ||
1302 | |||
1303 | if (!host->irq_enabled && host->irq_state) { | ||
1304 | disable_irq_nosync(host->irq); | ||
1305 | host->irq_state = false; | ||
1306 | } | ||
1307 | } | ||
1308 | |||
1309 | writel(con, host->base + S3C2410_SDICON); | ||
1310 | |||
1311 | same_state: | ||
1312 | local_irq_restore(flags); | ||
1313 | |||
1314 | s3cmci_check_sdio_irq(host); | ||
1315 | } | ||
1316 | |||
1181 | static struct mmc_host_ops s3cmci_ops = { | 1317 | static struct mmc_host_ops s3cmci_ops = { |
1182 | .request = s3cmci_request, | 1318 | .request = s3cmci_request, |
1183 | .set_ios = s3cmci_set_ios, | 1319 | .set_ios = s3cmci_set_ios, |
1184 | .get_ro = s3cmci_get_ro, | 1320 | .get_ro = s3cmci_get_ro, |
1185 | .get_cd = s3cmci_card_present, | 1321 | .get_cd = s3cmci_card_present, |
1322 | .enable_sdio_irq = s3cmci_enable_sdio_irq, | ||
1186 | }; | 1323 | }; |
1187 | 1324 | ||
1188 | static struct s3c24xx_mci_pdata s3cmci_def_pdata = { | 1325 | static struct s3c24xx_mci_pdata s3cmci_def_pdata = { |
@@ -1257,6 +1394,9 @@ static int s3cmci_state_show(struct seq_file *seq, void *v) | |||
1257 | seq_printf(seq, "Prescale = %d\n", host->prescaler); | 1394 | seq_printf(seq, "Prescale = %d\n", host->prescaler); |
1258 | seq_printf(seq, "is2440 = %d\n", host->is2440); | 1395 | seq_printf(seq, "is2440 = %d\n", host->is2440); |
1259 | seq_printf(seq, "IRQ = %d\n", host->irq); | 1396 | seq_printf(seq, "IRQ = %d\n", host->irq); |
1397 | seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled); | ||
1398 | seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled); | ||
1399 | seq_printf(seq, "IRQ state = %d\n", host->irq_state); | ||
1260 | seq_printf(seq, "CD IRQ = %d\n", host->irq_cd); | 1400 | seq_printf(seq, "CD IRQ = %d\n", host->irq_cd); |
1261 | seq_printf(seq, "Do DMA = %d\n", host->dodma); | 1401 | seq_printf(seq, "Do DMA = %d\n", host->dodma); |
1262 | seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk); | 1402 | seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk); |
@@ -1468,6 +1608,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) | |||
1468 | * ensure we don't lock the system with un-serviceable requests. */ | 1608 | * ensure we don't lock the system with un-serviceable requests. */ |
1469 | 1609 | ||
1470 | disable_irq(host->irq); | 1610 | disable_irq(host->irq); |
1611 | host->irq_state = false; | ||
1471 | 1612 | ||
1472 | if (host->pdata->gpio_detect) { | 1613 | if (host->pdata->gpio_detect) { |
1473 | ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect"); | 1614 | ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect"); |
@@ -1526,7 +1667,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) | |||
1526 | 1667 | ||
1527 | mmc->ops = &s3cmci_ops; | 1668 | mmc->ops = &s3cmci_ops; |
1528 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | 1669 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; |
1529 | mmc->caps = MMC_CAP_4_BIT_DATA; | 1670 | mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; |
1530 | mmc->f_min = host->clk_rate / (host->clk_div * 256); | 1671 | mmc->f_min = host->clk_rate / (host->clk_div * 256); |
1531 | mmc->f_max = host->clk_rate / host->clk_div; | 1672 | mmc->f_max = host->clk_rate / host->clk_div; |
1532 | 1673 | ||