diff options
| -rw-r--r-- | drivers/mmc/host/s3cmci.c | 161 | ||||
| -rw-r--r-- | drivers/mmc/host/s3cmci.h | 5 |
2 files changed, 156 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 | ||
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index fc96194a06f4..62fac6602306 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h | |||
| @@ -42,6 +42,11 @@ struct s3cmci_host { | |||
| 42 | int dodma; | 42 | int dodma; |
| 43 | int dmatogo; | 43 | int dmatogo; |
| 44 | 44 | ||
| 45 | bool irq_disabled; | ||
| 46 | bool irq_enabled; | ||
| 47 | bool irq_state; | ||
| 48 | int sdio_irqen; | ||
| 49 | |||
| 45 | struct mmc_request *mrq; | 50 | struct mmc_request *mrq; |
| 46 | int cmd_is_stop; | 51 | int cmd_is_stop; |
| 47 | 52 | ||
