aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/s3cmci.c
diff options
context:
space:
mode:
authorBen Dooks <ben@simtec.co.uk>2009-10-01 18:44:18 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-10-01 19:11:15 -0400
commitc225889375fea2a542f1c9dedffec4c7b8ebc9ab (patch)
tree8ade1de61fa0286cfc32ef4e5c62ebaa821059c0 /drivers/mmc/host/s3cmci.c
parent9bdd203b4dc82e9047486f0fed1977eef8185c6d (diff)
s3cmci: add SDIO IRQ support
The controller supports SDIO IRQ detection so add support for hardware assisted SDIO interrupt detection for the SDIO core. This improves the response time for SDIO interrupts and thus the transfer rate from devices such as the Marvel 8686. As a note, it does seem that the controller will miss an IRQ than is held asserted, so there are some manual checks to see if the SDIO interrupt is active after a transfer. Major testing on the S3C2440. Signed-off-by: Ben Dooks <ben@simtec.co.uk> Cc: <linux-mmc@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc/host/s3cmci.c')
-rw-r--r--drivers/mmc/host/s3cmci.c161
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
191static inline void clear_imask(struct s3cmci_host *host) 191static 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*/
212static 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
196static inline int get_data_buffer(struct s3cmci_host *host, 222static 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 */
280static 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 */
307static 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
241static void do_pio_read(struct s3cmci_host *host) 325static 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)
776request_done: 870request_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
1043static int s3cmci_card_present(struct mmc_host *mmc) 1139static 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
1277static 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
1181static struct mmc_host_ops s3cmci_ops = { 1317static 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
1188static struct s3c24xx_mci_pdata s3cmci_def_pdata = { 1325static 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