]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'omap_hsmmc'
authorTony Lindgren <tony@atomide.com>
Tue, 9 Dec 2008 19:57:05 +0000 (11:57 -0800)
committerTony Lindgren <tony@atomide.com>
Tue, 9 Dec 2008 19:57:05 +0000 (11:57 -0800)
1  2 
drivers/mmc/host/omap_hsmmc.c

index 4ab132546eb18b5ae756ae03c07b109f41b7488e,78c5c18d5b95ee36d874113a38ed2a83a26d8d80..1fcc5443cf0dd1d9398e6ea0e20d72833a99145d
  #define OMAP_MMC1_DEVID               0
  #define OMAP_MMC2_DEVID               1
  
- #define OMAP_MMC_DATADIR_NONE 0
- #define OMAP_MMC_DATADIR_READ 1
- #define OMAP_MMC_DATADIR_WRITE        2
  #define MMC_TIMEOUT_MS                20
  #define OMAP_MMC_MASTER_CLOCK 96000000
 -#define DRIVER_NAME           "mmci-omap"
 +#define DRIVER_NAME           "mmci-omap-hs"
  
  /*
   * One controller can have multiple slots, like on some omap boards using
  #define OMAP_HSMMC_WRITE(base, reg, val) \
        __raw_writel((val), (base) + OMAP_HSMMC_##reg)
  
+ enum {OFF = 0, ON};
+ #define IDLE_TIMEOUT (jiffies_to_msecs(10))
  struct mmc_omap_host {
        struct  device          *dev;
        struct  mmc_host        *mmc;
        resource_size_t         mapbase;
        unsigned int            id;
        unsigned int            dma_len;
-       unsigned int            dma_dir;
+       unsigned int            dma_sg_idx;
        unsigned char           bus_mode;
-       unsigned char           datadir;
        u32                     *buffer;
        u32                     bytesleft;
        int                     suspended;
        int                     irq;
        int                     carddetect;
        int                     use_dma, dma_ch;
-       int                     initstr;
        int                     slot_id;
        int                     dbclk_enabled;
+       struct timer_list       idle_timer;
+       spinlock_t              clk_lock;     /* for changing enabled state */
+       unsigned int            fclk_enabled:1;
        struct  omap_mmc_platform_data  *pdata;
  };
  
+ static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
+ {
+       unsigned long flags;
+       int ret = 0;
+       spin_lock_irqsave(&host->clk_lock, flags);
+       del_timer(&host->idle_timer);
+       if (host->fclk_enabled != state) {
+               if (state == ON) {
+                       ret = clk_enable(host->fclk);
+                       if (ret != 0)
+                               goto err_out;
+                       dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+               } else {
+                       clk_disable(host->fclk);
+                       dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+               }
+               host->fclk_enabled = state;
+       }
+ err_out:
+       spin_unlock_irqrestore(&host->clk_lock, flags);
+       return ret;
+ }
+ static void mmc_omap_idle_timer(unsigned long data)
+ {
+       struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+       mmc_omap_fclk_state(host, OFF);
+ }
+ static void mmc_omap_fclk_lazy_disable(struct mmc_omap_host *host)
+ {
+       mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ }
  /*
   * Stop clock to the card
   */
@@@ -270,6 -311,15 +311,15 @@@ mmc_omap_start_command(struct mmc_omap_
        OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
  }
  
+ static int
+ mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+ {
+       if (data->flags & MMC_DATA_WRITE)
+               return DMA_TO_DEVICE;
+       else
+               return DMA_FROM_DEVICE;
+ }
  /*
   * Notify the transfer complete to MMC core
   */
@@@ -280,9 -330,7 +330,7 @@@ mmc_omap_xfer_done(struct mmc_omap_hos
  
        if (host->use_dma && host->dma_ch != -1)
                dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
-                       host->dma_dir);
-       host->datadir = OMAP_MMC_DATADIR_NONE;
+                       mmc_omap_get_dma_dir(host, data));
  
        if (!data->error)
                data->bytes_xfered += data->blocks * (data->blksz);
  
        if (!data->stop) {
                host->mrq = NULL;
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, data->mrq);
                return;
        }
@@@ -319,6 -368,7 +368,7 @@@ mmc_omap_cmd_done(struct mmc_omap_host 
        }
        if (host->data == NULL || cmd->error) {
                host->mrq = NULL;
+               mmc_omap_fclk_lazy_disable(host);
                mmc_request_done(host->mmc, cmd->mrq);
        }
  }
@@@ -332,13 -382,12 +382,12 @@@ static void mmc_dma_cleanup(struct mmc_
  
        if (host->use_dma && host->dma_ch != -1) {
                dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
-                       host->dma_dir);
+                       mmc_omap_get_dma_dir(host, host->data));
                omap_free_dma(host->dma_ch);
                host->dma_ch = -1;
                up(&host->sem);
        }
        host->data = NULL;
-       host->datadir = OMAP_MMC_DATADIR_NONE;
  }
  
  /*
@@@ -457,7 -506,7 +506,7 @@@ static int omap_mmc_switch_opcond(struc
        int ret;
  
        /* Disable the clocks */
-       clk_disable(host->fclk);
+       mmc_omap_fclk_state(host, OFF);
        clk_disable(host->iclk);
        clk_disable(host->dbclk);
  
        if (ret != 0)
                goto err;
  
-       clk_enable(host->fclk);
+       mmc_omap_fclk_state(host, ON);
        clk_enable(host->iclk);
        clk_enable(host->dbclk);
  
         * Only MMC1 supports 3.0V.  MMC2 will not function if SDVS30 is
         * set in HCTL.
         */
-       if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
-                               ((1 << vdd) == MMC_VDD_33_34)))
-               reg_val |= SDVS30;
-       if ((1 << vdd) == MMC_VDD_165_195)
+       if (host->id == OMAP_MMC1_DEVID) {
+               if (((1 << vdd) == MMC_VDD_32_33) ||
+                   ((1 << vdd) == MMC_VDD_33_34))
+                       reg_val |= SDVS30;
+               else if ((1 << vdd) == MMC_VDD_165_195)
+                       reg_val |= SDVS18;
+       } else
                reg_val |= SDVS18;
  
        OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
@@@ -513,6 -565,7 +565,7 @@@ static void mmc_omap_detect(struct work
                                                mmc_carddetect_work);
  
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
+       mmc_omap_fclk_state(host, ON);
        if (host->carddetect) {
                if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
                        /*
                while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
                mmc_detect_change(host->mmc, (HZ * 50) / 1000);
        }
+       mmc_omap_fclk_lazy_disable(host);
  }
  
  /*
@@@ -545,6 -599,55 +599,55 @@@ static irqreturn_t omap_mmc_cd_handler(
        return IRQ_HANDLED;
  }
  
+ static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+                                    struct mmc_data *data)
+ {
+       int sync_dev;
+       if (data->flags & MMC_DATA_WRITE) {
+               if (host->id == OMAP_MMC1_DEVID)
+                       sync_dev = OMAP24XX_DMA_MMC1_TX;
+               else
+                       sync_dev = OMAP24XX_DMA_MMC2_TX;
+       } else {
+               if (host->id == OMAP_MMC1_DEVID)
+                       sync_dev = OMAP24XX_DMA_MMC1_RX;
+               else
+                       sync_dev = OMAP24XX_DMA_MMC2_RX;
+       }
+       return sync_dev;
+ }
+ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+                                      struct mmc_data *data,
+                                      struct scatterlist *sgl)
+ {
+       int blksz, nblk, dma_ch;
+       dma_ch = host->dma_ch;
+       if (data->flags & MMC_DATA_WRITE) {
+               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                       sg_dma_address(sgl), 0, 0);
+       } else {
+               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                       sg_dma_address(sgl), 0, 0);
+       }
+       blksz = host->data->blksz;
+       nblk = sg_dma_len(sgl) / blksz;
+       omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+                       blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
+                       mmc_omap_get_dma_sync_dev(host, data),
+                       !(data->flags & MMC_DATA_WRITE));
+       omap_start_dma(dma_ch);
+ }
  /*
   * DMA call back function
   */
@@@ -558,6 -661,14 +661,14 @@@ static void mmc_omap_dma_cb(int lch, u1
        if (host->dma_ch < 0)
                return;
  
+       host->dma_sg_idx++;
+       if (host->dma_sg_idx < host->dma_len) {
+               /* Fire up the next transfer. */
+               mmc_omap_config_dma_params(host, host->data,
+                                          host->data->sg + host->dma_sg_idx);
+               return;
+       }
        omap_free_dma(host->dma_ch);
        host->dma_ch = -1;
        /*
        up(&host->sem);
  }
  
- /*
-  * Configure dma src and destination parameters
-  */
- static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
-                               struct mmc_data *data)
- {
-       if (sync_dir == 0) {
-               omap_set_dma_dest_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_src_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(&data->sg[0]), 0, 0);
-       } else {
-               omap_set_dma_src_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_dest_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(&data->sg[0]), 0, 0);
-       }
-       return 0;
- }
  /*
   * Routine to configure and start DMA for the MMC card
   */
  static int
  mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
  {
-       int sync_dev, sync_dir = 0;
-       int dma_ch = 0, ret = 0, err = 1;
+       int dma_ch = 0, ret = 0, err = 1, i;
        struct mmc_data *data = req->data;
  
+       /* Sanity check: all the SG entries must be aligned by block size. */
+       for (i = 0; i < host->dma_len; i++) {
+               struct scatterlist *sgl;
+               sgl = data->sg + i;
+               if (sgl->length % data->blksz)
+                       return -EINVAL;
+       }
+       if ((data->blksz % 4) != 0)
+               /* REVISIT: The MMC buffer increments only when MSB is written.
+                * Return error for blksz which is non multiple of four.
+                */
+               return -EINVAL;
        /*
         * If for some reason the DMA transfer is still active,
         * we wait for timeout period and free the dma
                        return err;
        }
  
-       if (!(data->flags & MMC_DATA_WRITE)) {
-               host->dma_dir = DMA_FROM_DEVICE;
-               if (host->id == OMAP_MMC1_DEVID)
-                       sync_dev = OMAP24XX_DMA_MMC1_RX;
-               else
-                       sync_dev = OMAP24XX_DMA_MMC2_RX;
-       } else {
-               host->dma_dir = DMA_TO_DEVICE;
-               if (host->id == OMAP_MMC1_DEVID)
-                       sync_dev = OMAP24XX_DMA_MMC1_TX;
-               else
-                       sync_dev = OMAP24XX_DMA_MMC2_TX;
-       }
-       ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
-                       host, &dma_ch);
+       ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
+                              mmc_omap_dma_cb,host, &dma_ch);
        if (ret != 0) {
-               dev_dbg(mmc_dev(host->mmc),
+               dev_err(mmc_dev(host->mmc),
                        "%s: omap_request_dma() failed with %d\n",
                        mmc_hostname(host->mmc), ret);
                return ret;
        }
  
        host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-                       data->sg_len, host->dma_dir);
+                       data->sg_len, mmc_omap_get_dma_dir(host, data));
        host->dma_ch = dma_ch;
+       host->dma_sg_idx = 0;
  
-       if (!(data->flags & MMC_DATA_WRITE))
-               mmc_omap_config_dma_param(1, host, data);
-       else
-               mmc_omap_config_dma_param(0, host, data);
+       mmc_omap_config_dma_params(host, data, data->sg);
  
-       if ((data->blksz % 4) == 0)
-               omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-                       (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
-                       sync_dev, sync_dir);
-       else
-               /* REVISIT: The MMC buffer increments only when MSB is written.
-                * Return error for blksz which is non multiple of four.
-                */
-               return -EINVAL;
-       omap_start_dma(dma_ch);
        return 0;
  }
  
@@@ -710,7 -784,6 +784,6 @@@ mmc_omap_prepare_data(struct mmc_omap_h
        host->data = req->data;
  
        if (req->data == NULL) {
-               host->datadir = OMAP_MMC_DATADIR_NONE;
                OMAP_HSMMC_WRITE(host->base, BLK, 0);
                return 0;
        }
                                        | (req->data->blocks << 16));
        set_data_timeout(host, req);
  
-       host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
-                       OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
        if (host->use_dma) {
                ret = mmc_omap_start_dma_transfer(host, req);
                if (ret != 0) {
@@@ -741,11 -811,11 +811,11 @@@ static void omap_mmc_request(struct mmc
  
        WARN_ON(host->mrq != NULL);
        host->mrq = req;
+       mmc_omap_fclk_state(host, ON);
        mmc_omap_prepare_data(host, req);
        mmc_omap_start_command(host, req->cmd, req->data);
  }
  
  /* Routine to configure clock values. Exposed API to core */
  static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
  {
        unsigned long regval;
        unsigned long timeout;
  
+       mmc_omap_fclk_state(host, ON);
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
        if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
                OMAP_HSMMC_WRITE(host->base, CON,
                                OMAP_HSMMC_READ(host->base, CON) | OD);
+       mmc_omap_fclk_lazy_disable(host);
  }
  
  static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@@ -925,14 -999,17 +999,17 @@@ static int __init omap_mmc_probe(struc
                goto err1;
        }
  
-       if (clk_enable(host->fclk) != 0) {
+       spin_lock_init(&host->clk_lock);
+       setup_timer(&host->idle_timer, mmc_omap_idle_timer,
+                   (unsigned long) host);
+       if (mmc_omap_fclk_state(host, ON) != 0) {
                clk_put(host->iclk);
                clk_put(host->fclk);
                goto err1;
        }
        if (clk_enable(host->iclk) != 0) {
-               clk_disable(host->fclk);
+               mmc_omap_fclk_state(host, OFF);
                clk_put(host->iclk);
                clk_put(host->fclk);
                goto err1;
                else
                        host->dbclk_enabled = 1;
  
- #ifdef CONFIG_MMC_BLOCK_BOUNCE
-       mmc->max_phys_segs = 1;
-       mmc->max_hw_segs = 1;
- #endif
+       /* Since we do only SG emulation, we can have as many segs
+        * as we want. */
+       mmc->max_phys_segs = 1024;
+       mmc->max_hw_segs = 1024;
        mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
        mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
                if (ret < 0)
                        goto err_cover_switch;
        }
+       mmc_omap_fclk_lazy_disable(host);
  
        return 0;
  
@@@ -1047,7 -1126,7 +1126,7 @@@ err_irq_cd
  err_irq_cd_init:
        free_irq(host->irq, host);
  err_irq:
-       clk_disable(host->fclk);
+       mmc_omap_fclk_state(host, OFF);
        clk_disable(host->iclk);
        clk_put(host->fclk);
        clk_put(host->iclk);
@@@ -1072,14 -1151,17 +1151,17 @@@ static int omap_mmc_remove(struct platf
        struct resource *res;
        u16 vdd = 0;
  
-       if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-       /*
-        * Set the vdd back to 3V,
-        * applicable for dual volt support.
-        */
-               vdd = fls(host->mmc->ocr_avail) - 1;
-               if (omap_mmc_switch_opcond(host, vdd) != 0)
-                       host->mmc->ios.vdd = vdd;
+       if (host) {
+               mmc_omap_fclk_state(host, ON);
+               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+                       /*
+                        * Set the vdd back to 3V,
+                        * applicable for dual volt support.
+                        */
+                       vdd = fls(host->mmc->ocr_avail) - 1;
+                       if (omap_mmc_switch_opcond(host, vdd) != 0)
+                               host->mmc->ios.vdd = vdd;
+               }
        }
  
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
                        free_irq(mmc_slot(host).card_detect_irq, host);
                flush_scheduled_work();
  
-               clk_disable(host->fclk);
+               mmc_omap_fclk_state(host, OFF);
                clk_disable(host->iclk);
                clk_put(host->fclk);
                clk_put(host->iclk);
@@@ -1126,6 -1208,7 +1208,7 @@@ static int omap_mmc_suspend(struct plat
                if (ret == 0) {
                        host->suspended = 1;
  
+                       mmc_omap_fclk_state(host, ON);
                        OMAP_HSMMC_WRITE(host->base, ISE, 0);
                        OMAP_HSMMC_WRITE(host->base, IE, 0);
  
                                                " level suspend\n");
                        }
  
-                       if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       & SDVSCLR);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDVS30);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDBP);
-                       }
-                       clk_disable(host->fclk);
+                       OMAP_HSMMC_WRITE(host->base, HCTL,
+                                        OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+                       mmc_omap_fclk_state(host, OFF);
                        clk_disable(host->iclk);
                        clk_disable(host->dbclk);
                }
@@@ -1168,14 -1241,13 +1241,13 @@@ static int omap_mmc_resume(struct platf
                return 0;
  
        if (host) {
-               ret = clk_enable(host->fclk);
-               if (ret)
+               int i;
+               if (mmc_omap_fclk_state(host, ON) != 0)
                        goto clk_en_err;
  
                ret = clk_enable(host->iclk);
                if (ret) {
-                       clk_disable(host->fclk);
+                       mmc_omap_fclk_state(host, OFF);
                        clk_put(host->fclk);
                        goto clk_en_err;
                }
                        dev_dbg(mmc_dev(host->mmc),
                                        "Enabling debounce clk failed\n");
  
+               OMAP_HSMMC_WRITE(host->base, HCTL,
+                                OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+               for (i = 0; i < 100; i++)
+                       if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+                               break;
                if (host->pdata->resume) {
                        ret = host->pdata->resume(&pdev->dev, host->slot_id);
                        if (ret)
                ret = mmc_resume_host(host->mmc);
                if (ret == 0)
                        host->suspended = 0;
+               mmc_omap_fclk_lazy_disable(host);
        }
  
        return ret;