mmc: mmci: Add Qualcomm Id to amba id table
[deliverable/linux.git] / drivers / mmc / host / mmci.c
index 5228c5de334d9ca9c300fe83bd636b8ce41b2a6f..6483c5cc67355ef0fed4bd2ca79ba2d7e15fa64a 100644 (file)
@@ -59,6 +59,7 @@ static unsigned int fmax = 515633;
  *           is asserted (likewise for RX)
  * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
  *               is asserted (likewise for RX)
+ * @data_cmd_enable: enable value for data commands.
  * @sdio: variant supports SDIO
  * @st_clkdiv: true if using a ST-specific clock divider algorithm
  * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
@@ -66,10 +67,13 @@ static unsigned int fmax = 515633;
  * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
  *                  register
  * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
  * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
  * @busy_detect: true if busy detection on dat0 is supported
  * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
+ * @explicit_mclk_control: enable explicit mclk control in driver.
+ * @qcom_fifo: enables qcom specific fifo pio read logic.
  */
 struct variant_data {
        unsigned int            clkreg;
@@ -79,16 +83,20 @@ struct variant_data {
        unsigned int            datalength_bits;
        unsigned int            fifosize;
        unsigned int            fifohalfsize;
+       unsigned int            data_cmd_enable;
        unsigned int            datactrl_mask_ddrmode;
        bool                    sdio;
        bool                    st_clkdiv;
        bool                    blksz_datactrl16;
        bool                    blksz_datactrl4;
        u32                     pwrreg_powerup;
+       u32                     f_max;
        bool                    signal_direction;
        bool                    pwrreg_clkgate;
        bool                    busy_detect;
        bool                    pwrreg_nopower;
+       bool                    explicit_mclk_control;
+       bool                    qcom_fifo;
 };
 
 static struct variant_data variant_arm = {
@@ -96,6 +104,7 @@ static struct variant_data variant_arm = {
        .fifohalfsize           = 8 * 4,
        .datalength_bits        = 16,
        .pwrreg_powerup         = MCI_PWR_UP,
+       .f_max                  = 100000000,
 };
 
 static struct variant_data variant_arm_extended_fifo = {
@@ -103,6 +112,7 @@ static struct variant_data variant_arm_extended_fifo = {
        .fifohalfsize           = 64 * 4,
        .datalength_bits        = 16,
        .pwrreg_powerup         = MCI_PWR_UP,
+       .f_max                  = 100000000,
 };
 
 static struct variant_data variant_arm_extended_fifo_hwfc = {
@@ -111,6 +121,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
        .clkreg_enable          = MCI_ARM_HWFCEN,
        .datalength_bits        = 16,
        .pwrreg_powerup         = MCI_PWR_UP,
+       .f_max                  = 100000000,
 };
 
 static struct variant_data variant_u300 = {
@@ -121,6 +132,7 @@ static struct variant_data variant_u300 = {
        .datalength_bits        = 16,
        .sdio                   = true,
        .pwrreg_powerup         = MCI_PWR_ON,
+       .f_max                  = 100000000,
        .signal_direction       = true,
        .pwrreg_clkgate         = true,
        .pwrreg_nopower         = true,
@@ -134,6 +146,7 @@ static struct variant_data variant_nomadik = {
        .sdio                   = true,
        .st_clkdiv              = true,
        .pwrreg_powerup         = MCI_PWR_ON,
+       .f_max                  = 100000000,
        .signal_direction       = true,
        .pwrreg_clkgate         = true,
        .pwrreg_nopower         = true,
@@ -150,6 +163,7 @@ static struct variant_data variant_ux500 = {
        .sdio                   = true,
        .st_clkdiv              = true,
        .pwrreg_powerup         = MCI_PWR_ON,
+       .f_max                  = 100000000,
        .signal_direction       = true,
        .pwrreg_clkgate         = true,
        .busy_detect            = true,
@@ -169,12 +183,30 @@ static struct variant_data variant_ux500v2 = {
        .st_clkdiv              = true,
        .blksz_datactrl16       = true,
        .pwrreg_powerup         = MCI_PWR_ON,
+       .f_max                  = 100000000,
        .signal_direction       = true,
        .pwrreg_clkgate         = true,
        .busy_detect            = true,
        .pwrreg_nopower         = true,
 };
 
+static struct variant_data variant_qcom = {
+       .fifosize               = 16 * 4,
+       .fifohalfsize           = 8 * 4,
+       .clkreg                 = MCI_CLK_ENABLE,
+       .clkreg_enable          = MCI_QCOM_CLK_FLOWENA |
+                                 MCI_QCOM_CLK_SELECT_IN_FBCLK,
+       .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
+       .datactrl_mask_ddrmode  = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
+       .data_cmd_enable        = MCI_QCOM_CSPM_DATCMD,
+       .blksz_datactrl4        = true,
+       .datalength_bits        = 24,
+       .pwrreg_powerup         = MCI_PWR_UP,
+       .f_max                  = 208000000,
+       .explicit_mclk_control  = true,
+       .qcom_fifo              = true,
+};
+
 static int mmci_card_busy(struct mmc_host *mmc)
 {
        struct mmci_host *host = mmc_priv(mmc);
@@ -275,7 +307,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
        host->cclk = 0;
 
        if (desired) {
-               if (desired >= host->mclk) {
+               if (variant->explicit_mclk_control) {
+                       host->cclk = host->mclk;
+               } else if (desired >= host->mclk) {
                        clk = MCI_CLK_BYPASS;
                        if (variant->st_clkdiv)
                                clk |= MCI_ST_UX500_NEG_EDGE;
@@ -841,6 +875,9 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
        if (/*interrupt*/0)
                c |= MCI_CPSM_INTERRUPT;
 
+       if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+               c |= host->variant->data_cmd_enable;
+
        host->cmd = cmd;
 
        writel(cmd->arg, base + MMCIARGUMENT);
@@ -974,15 +1011,34 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
        }
 }
 
+static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain)
+{
+       return remain - (readl(host->base + MMCIFIFOCNT) << 2);
+}
+
+static int mmci_qcom_get_rx_fifocnt(struct mmci_host *host, u32 status, int r)
+{
+       /*
+        * on qcom SDCC4 only 8 words are used in each burst so only 8 addresses
+        * from the fifo range should be used
+        */
+       if (status & MCI_RXFIFOHALFFULL)
+               return host->variant->fifohalfsize;
+       else if (status & MCI_RXDATAAVLBL)
+               return 4;
+
+       return 0;
+}
+
 static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
 {
        void __iomem *base = host->base;
        char *ptr = buffer;
-       u32 status;
+       u32 status = readl(host->base + MMCISTATUS);
        int host_remain = host->size;
 
        do {
-               int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
+               int count = host->get_rx_fifocnt(host, status, host_remain);
 
                if (count > remain)
                        count = remain;
@@ -1313,6 +1369,17 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (!ios->clock && variant->pwrreg_clkgate)
                pwr &= ~MCI_PWR_ON;
 
+       if (host->variant->explicit_mclk_control &&
+           ios->clock != host->clock_cache) {
+               ret = clk_set_rate(host->clk, ios->clock);
+               if (ret < 0)
+                       dev_err(mmc_dev(host->mmc),
+                               "Error setting clock rate (%d)\n", ret);
+               else
+                       host->mclk = clk_get_rate(host->clk);
+       }
+       host->clock_cache = ios->clock;
+
        spin_lock_irqsave(&host->lock, flags);
 
        mmci_set_clkreg(host, ios->clock);
@@ -1460,6 +1527,11 @@ static int mmci_probe(struct amba_device *dev,
        if (ret)
                goto host_free;
 
+       if (variant->qcom_fifo)
+               host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt;
+       else
+               host->get_rx_fifocnt = mmci_get_rx_fifocnt;
+
        host->plat = plat;
        host->variant = variant;
        host->mclk = clk_get_rate(host->clk);
@@ -1468,8 +1540,8 @@ static int mmci_probe(struct amba_device *dev,
         * so we try to adjust the clock down to this,
         * (if possible).
         */
-       if (host->mclk > 100000000) {
-               ret = clk_set_rate(host->clk, 100000000);
+       if (host->mclk > variant->f_max) {
+               ret = clk_set_rate(host->clk, variant->f_max);
                if (ret < 0)
                        goto clk_disable;
                host->mclk = clk_get_rate(host->clk);
@@ -1488,9 +1560,12 @@ static int mmci_probe(struct amba_device *dev,
         * The ARM and ST versions of the block have slightly different
         * clock divider equations which means that the minimum divider
         * differs too.
+        * on Qualcomm like controllers get the nearest minimum clock to 100Khz
         */
        if (variant->st_clkdiv)
                mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
+       else if (variant->explicit_mclk_control)
+               mmc->f_min = clk_round_rate(host->clk, 100000);
        else
                mmc->f_min = DIV_ROUND_UP(host->mclk, 512);
        /*
@@ -1500,9 +1575,14 @@ static int mmci_probe(struct amba_device *dev,
         * the block, of course.
         */
        if (mmc->f_max)
-               mmc->f_max = min(host->mclk, mmc->f_max);
+               mmc->f_max = variant->explicit_mclk_control ?
+                               min(variant->f_max, mmc->f_max) :
+                               min(host->mclk, mmc->f_max);
        else
-               mmc->f_max = min(host->mclk, fmax);
+               mmc->f_max = variant->explicit_mclk_control ?
+                               fmax : min(host->mclk, fmax);
+
+
        dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
 
        /* Get regulators and the supported OCR mask */
@@ -1769,6 +1849,12 @@ static struct amba_id mmci_ids[] = {
                .mask   = 0xf0ffffff,
                .data   = &variant_ux500v2,
        },
+       /* Qualcomm variants */
+       {
+               .id     = 0x00051180,
+               .mask   = 0x000fffff,
+               .data   = &variant_qcom,
+       },
        { 0, 0 },
 };
 
This page took 0.041303 seconds and 5 git commands to generate.