diff options
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 85 |
1 files changed, 46 insertions, 39 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 72dc3cde646d..af40d227bece 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -627,6 +627,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) | |||
627 | { | 627 | { |
628 | struct dw_mci *host = slot->host; | 628 | struct dw_mci *host = slot->host; |
629 | u32 div; | 629 | u32 div; |
630 | u32 clk_en_a; | ||
630 | 631 | ||
631 | if (slot->clock != host->current_speed) { | 632 | if (slot->clock != host->current_speed) { |
632 | div = host->bus_hz / slot->clock; | 633 | div = host->bus_hz / slot->clock; |
@@ -659,9 +660,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) | |||
659 | mci_send_cmd(slot, | 660 | mci_send_cmd(slot, |
660 | SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); | 661 | SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); |
661 | 662 | ||
662 | /* enable clock */ | 663 | /* enable clock; only low power if no SDIO */ |
663 | mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE | | 664 | clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; |
664 | SDMMC_CLKEN_LOW_PWR) << slot->id)); | 665 | if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id))) |
666 | clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; | ||
667 | mci_writel(host, CLKENA, clk_en_a); | ||
665 | 668 | ||
666 | /* inform CIU */ | 669 | /* inform CIU */ |
667 | mci_send_cmd(slot, | 670 | mci_send_cmd(slot, |
@@ -862,6 +865,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc) | |||
862 | return present; | 865 | return present; |
863 | } | 866 | } |
864 | 867 | ||
868 | /* | ||
869 | * Disable lower power mode. | ||
870 | * | ||
871 | * Low power mode will stop the card clock when idle. According to the | ||
872 | * description of the CLKENA register we should disable low power mode | ||
873 | * for SDIO cards if we need SDIO interrupts to work. | ||
874 | * | ||
875 | * This function is fast if low power mode is already disabled. | ||
876 | */ | ||
877 | static void dw_mci_disable_low_power(struct dw_mci_slot *slot) | ||
878 | { | ||
879 | struct dw_mci *host = slot->host; | ||
880 | u32 clk_en_a; | ||
881 | const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; | ||
882 | |||
883 | clk_en_a = mci_readl(host, CLKENA); | ||
884 | |||
885 | if (clk_en_a & clken_low_pwr) { | ||
886 | mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr); | ||
887 | mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | | ||
888 | SDMMC_CMD_PRV_DAT_WAIT, 0); | ||
889 | } | ||
890 | } | ||
891 | |||
865 | static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) | 892 | static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) |
866 | { | 893 | { |
867 | struct dw_mci_slot *slot = mmc_priv(mmc); | 894 | struct dw_mci_slot *slot = mmc_priv(mmc); |
@@ -871,6 +898,14 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) | |||
871 | /* Enable/disable Slot Specific SDIO interrupt */ | 898 | /* Enable/disable Slot Specific SDIO interrupt */ |
872 | int_mask = mci_readl(host, INTMASK); | 899 | int_mask = mci_readl(host, INTMASK); |
873 | if (enb) { | 900 | if (enb) { |
901 | /* | ||
902 | * Turn off low power mode if it was enabled. This is a bit of | ||
903 | * a heavy operation and we disable / enable IRQs a lot, so | ||
904 | * we'll leave low power mode disabled and it will get | ||
905 | * re-enabled again in dw_mci_setup_bus(). | ||
906 | */ | ||
907 | dw_mci_disable_low_power(slot); | ||
908 | |||
874 | mci_writel(host, INTMASK, | 909 | mci_writel(host, INTMASK, |
875 | (int_mask | SDMMC_INT_SDIO(slot->id))); | 910 | (int_mask | SDMMC_INT_SDIO(slot->id))); |
876 | } else { | 911 | } else { |
@@ -1429,22 +1464,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host) | |||
1429 | nbytes += len; | 1464 | nbytes += len; |
1430 | remain -= len; | 1465 | remain -= len; |
1431 | } while (remain); | 1466 | } while (remain); |
1432 | sg_miter->consumed = offset; | ||
1433 | 1467 | ||
1468 | sg_miter->consumed = offset; | ||
1434 | status = mci_readl(host, MINTSTS); | 1469 | status = mci_readl(host, MINTSTS); |
1435 | mci_writel(host, RINTSTS, SDMMC_INT_RXDR); | 1470 | mci_writel(host, RINTSTS, SDMMC_INT_RXDR); |
1436 | if (status & DW_MCI_DATA_ERROR_FLAGS) { | ||
1437 | host->data_status = status; | ||
1438 | data->bytes_xfered += nbytes; | ||
1439 | sg_miter_stop(sg_miter); | ||
1440 | host->sg = NULL; | ||
1441 | smp_wmb(); | ||
1442 | |||
1443 | set_bit(EVENT_DATA_ERROR, &host->pending_events); | ||
1444 | |||
1445 | tasklet_schedule(&host->tasklet); | ||
1446 | return; | ||
1447 | } | ||
1448 | } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ | 1471 | } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ |
1449 | data->bytes_xfered += nbytes; | 1472 | data->bytes_xfered += nbytes; |
1450 | 1473 | ||
@@ -1497,23 +1520,10 @@ static void dw_mci_write_data_pio(struct dw_mci *host) | |||
1497 | nbytes += len; | 1520 | nbytes += len; |
1498 | remain -= len; | 1521 | remain -= len; |
1499 | } while (remain); | 1522 | } while (remain); |
1500 | sg_miter->consumed = offset; | ||
1501 | 1523 | ||
1524 | sg_miter->consumed = offset; | ||
1502 | status = mci_readl(host, MINTSTS); | 1525 | status = mci_readl(host, MINTSTS); |
1503 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR); | 1526 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR); |
1504 | if (status & DW_MCI_DATA_ERROR_FLAGS) { | ||
1505 | host->data_status = status; | ||
1506 | data->bytes_xfered += nbytes; | ||
1507 | sg_miter_stop(sg_miter); | ||
1508 | host->sg = NULL; | ||
1509 | |||
1510 | smp_wmb(); | ||
1511 | |||
1512 | set_bit(EVENT_DATA_ERROR, &host->pending_events); | ||
1513 | |||
1514 | tasklet_schedule(&host->tasklet); | ||
1515 | return; | ||
1516 | } | ||
1517 | } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ | 1527 | } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ |
1518 | data->bytes_xfered += nbytes; | 1528 | data->bytes_xfered += nbytes; |
1519 | 1529 | ||
@@ -1547,12 +1557,11 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) | |||
1547 | static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | 1557 | static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) |
1548 | { | 1558 | { |
1549 | struct dw_mci *host = dev_id; | 1559 | struct dw_mci *host = dev_id; |
1550 | u32 status, pending; | 1560 | u32 pending; |
1551 | unsigned int pass_count = 0; | 1561 | unsigned int pass_count = 0; |
1552 | int i; | 1562 | int i; |
1553 | 1563 | ||
1554 | do { | 1564 | do { |
1555 | status = mci_readl(host, RINTSTS); | ||
1556 | pending = mci_readl(host, MINTSTS); /* read-only mask reg */ | 1565 | pending = mci_readl(host, MINTSTS); /* read-only mask reg */ |
1557 | 1566 | ||
1558 | /* | 1567 | /* |
@@ -1570,7 +1579,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | |||
1570 | 1579 | ||
1571 | if (pending & DW_MCI_CMD_ERROR_FLAGS) { | 1580 | if (pending & DW_MCI_CMD_ERROR_FLAGS) { |
1572 | mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); | 1581 | mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); |
1573 | host->cmd_status = status; | 1582 | host->cmd_status = pending; |
1574 | smp_wmb(); | 1583 | smp_wmb(); |
1575 | set_bit(EVENT_CMD_COMPLETE, &host->pending_events); | 1584 | set_bit(EVENT_CMD_COMPLETE, &host->pending_events); |
1576 | } | 1585 | } |
@@ -1578,18 +1587,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | |||
1578 | if (pending & DW_MCI_DATA_ERROR_FLAGS) { | 1587 | if (pending & DW_MCI_DATA_ERROR_FLAGS) { |
1579 | /* if there is an error report DATA_ERROR */ | 1588 | /* if there is an error report DATA_ERROR */ |
1580 | mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); | 1589 | mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); |
1581 | host->data_status = status; | 1590 | host->data_status = pending; |
1582 | smp_wmb(); | 1591 | smp_wmb(); |
1583 | set_bit(EVENT_DATA_ERROR, &host->pending_events); | 1592 | set_bit(EVENT_DATA_ERROR, &host->pending_events); |
1584 | if (!(pending & (SDMMC_INT_DTO | SDMMC_INT_DCRC | | 1593 | tasklet_schedule(&host->tasklet); |
1585 | SDMMC_INT_SBE | SDMMC_INT_EBE))) | ||
1586 | tasklet_schedule(&host->tasklet); | ||
1587 | } | 1594 | } |
1588 | 1595 | ||
1589 | if (pending & SDMMC_INT_DATA_OVER) { | 1596 | if (pending & SDMMC_INT_DATA_OVER) { |
1590 | mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); | 1597 | mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); |
1591 | if (!host->data_status) | 1598 | if (!host->data_status) |
1592 | host->data_status = status; | 1599 | host->data_status = pending; |
1593 | smp_wmb(); | 1600 | smp_wmb(); |
1594 | if (host->dir_status == DW_MCI_RECV_STATUS) { | 1601 | if (host->dir_status == DW_MCI_RECV_STATUS) { |
1595 | if (host->sg != NULL) | 1602 | if (host->sg != NULL) |
@@ -1613,7 +1620,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | |||
1613 | 1620 | ||
1614 | if (pending & SDMMC_INT_CMD_DONE) { | 1621 | if (pending & SDMMC_INT_CMD_DONE) { |
1615 | mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); | 1622 | mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); |
1616 | dw_mci_cmd_interrupt(host, status); | 1623 | dw_mci_cmd_interrupt(host, pending); |
1617 | } | 1624 | } |
1618 | 1625 | ||
1619 | if (pending & SDMMC_INT_CD) { | 1626 | if (pending & SDMMC_INT_CD) { |