mmc: dw_mmc: Add the ability to set the ciu clock frequency
[cascardo/linux.git] / drivers / mmc / host / dw_mmc.c
index bc3a1bc..ee5f167 100644 (file)
@@ -39,7 +39,7 @@
 #include "dw_mmc.h"
 
 /* Common flag combinations */
-#define DW_MCI_DATA_ERROR_FLAGS        (SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+#define DW_MCI_DATA_ERROR_FLAGS        (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
                                 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
                                 SDMMC_INT_EBE)
 #define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
 #define DW_MCI_DMA_THRESHOLD   16
 
 #ifdef CONFIG_MMC_DW_IDMAC
+#define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
+                                SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
+                                SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
+                                SDMMC_IDMAC_INT_TI)
+
 struct idmac_desc {
        u32             des0;   /* Control Descriptor */
 #define IDMAC_DES0_DIC BIT(1)
@@ -433,6 +438,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
        mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
 
        /* Mask out interrupts - get Tx & Rx complete only */
+       mci_writel(host, IDSTS, IDMAC_INT_CLR);
        mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
                   SDMMC_IDMAC_INT_TI);
 
@@ -1087,7 +1093,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        status = host->data_status;
 
                        if (status & DW_MCI_DATA_ERROR_FLAGS) {
-                               if (status & SDMMC_INT_DTO) {
+                               if (status & SDMMC_INT_DRTO) {
                                        data->error = -ETIMEDOUT;
                                } else if (status & SDMMC_INT_DCRC) {
                                        data->error = -EILSEQ;
@@ -1985,19 +1991,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 #endif /* CONFIG_MMC_DW_IDMAC */
        }
 
-       host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc");
-       if (IS_ERR(host->vmmc)) {
-               pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
-               host->vmmc = NULL;
-       } else {
-               ret = regulator_enable(host->vmmc);
-               if (ret) {
-                       dev_err(host->dev,
-                               "failed to enable regulator: %d\n", ret);
-                       goto err_setup_bus;
-               }
-       }
-
        if (dw_mci_get_cd(mmc))
                set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
        else
@@ -2124,6 +2117,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
        struct device_node *np = dev->of_node;
        const struct dw_mci_drv_data *drv_data = host->drv_data;
        int idx, ret;
+       u32 clock_frequency;
 
        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata) {
@@ -2150,6 +2144,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
        of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
 
+       if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
+               pdata->bus_hz = clock_frequency;
+
        if (drv_data && drv_data->parse_dt) {
                ret = drv_data->parse_dt(host);
                if (ret)
@@ -2207,18 +2204,23 @@ int dw_mci_probe(struct dw_mci *host)
        host->ciu_clk = devm_clk_get(host->dev, "ciu");
        if (IS_ERR(host->ciu_clk)) {
                dev_dbg(host->dev, "ciu clock not available\n");
+               host->bus_hz = host->pdata->bus_hz;
        } else {
                ret = clk_prepare_enable(host->ciu_clk);
                if (ret) {
                        dev_err(host->dev, "failed to enable ciu clock\n");
                        goto err_clk_biu;
                }
-       }
 
-       if (IS_ERR(host->ciu_clk))
-               host->bus_hz = host->pdata->bus_hz;
-       else
+               if (host->pdata->bus_hz) {
+                       ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
+                       if (ret)
+                               dev_warn(host->dev,
+                                        "Unable to set bus rate to %ul\n",
+                                        host->pdata->bus_hz);
+               }
                host->bus_hz = clk_get_rate(host->ciu_clk);
+       }
 
        if (drv_data && drv_data->setup_clock) {
                ret = drv_data->setup_clock(host);
@@ -2229,11 +2231,29 @@ int dw_mci_probe(struct dw_mci *host)
                }
        }
 
+       host->vmmc = devm_regulator_get(host->dev, "vmmc");
+       if (IS_ERR(host->vmmc)) {
+               ret = PTR_ERR(host->vmmc);
+               if (ret == -EPROBE_DEFER)
+                       goto err_clk_ciu;
+
+               dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
+               host->vmmc = NULL;
+       } else {
+               ret = regulator_enable(host->vmmc);
+               if (ret) {
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(host->dev,
+                                       "regulator_enable fail: %d\n", ret);
+                       goto err_clk_ciu;
+               }
+       }
+
        if (!host->bus_hz) {
                dev_err(host->dev,
                        "Platform data must supply bus speed\n");
                ret = -ENODEV;
-               goto err_clk_ciu;
+               goto err_regulator;
        }
 
        host->quirks = host->pdata->quirks;
@@ -2321,8 +2341,10 @@ int dw_mci_probe(struct dw_mci *host)
        tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
        host->card_workqueue = alloc_workqueue("dw-mci-card",
                        WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
-       if (!host->card_workqueue)
+       if (!host->card_workqueue) {
+               ret = -ENOMEM;
                goto err_dmaunmap;
+       }
        INIT_WORK(&host->card_work, dw_mci_work_routine_card);
        ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
                               host->irq_flags, "dw-mci", host);
@@ -2378,6 +2400,7 @@ err_dmaunmap:
        if (host->use_dma && host->dma_ops->exit)
                host->dma_ops->exit(host);
 
+err_regulator:
        if (host->vmmc)
                regulator_disable(host->vmmc);