[s3c24xx] glamo-mmc: Limit clock rate.
[openwrt.git] / target / linux / s3c24xx / files-2.6.30 / drivers / mfd / glamo / glamo-mci.c
index 37373f5..fdf292f 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/crc7.h>
 #include <linux/scatterlist.h>
@@ -53,6 +52,7 @@ struct glamo_mci_host {
        struct timer_list disable_timer;
 
        struct work_struct irq_work;
+       struct work_struct read_work;
 
        unsigned clk_enabled : 1;
 };
@@ -74,7 +74,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
  * for example
  */
 
-static int sd_max_clk = 50000000;
+static int sd_max_clk = 21000000;
 module_param(sd_max_clk, int, 0644);
 
 /*
@@ -139,9 +139,8 @@ static void glamo_reg_set_bit_mask(struct glamo_mci_host *glamo,
 
 static void glamo_mci_clock_disable(struct glamo_mci_host *host) {
        if (host->clk_enabled) {
-/*             glamo_engine_div_disable(host->pdata->core, GLAMO_ENGINE_MMC);*/
+               glamo_engine_div_disable(host->pdata->core, GLAMO_ENGINE_MMC);
                host->clk_enabled = 0;
-        printk("clk disabled\n");
        }
 }
 
@@ -151,7 +150,6 @@ static void glamo_mci_clock_enable(struct glamo_mci_host *host) {
        if (!host->clk_enabled) {
                glamo_engine_div_enable(host->pdata->core, GLAMO_ENGINE_MMC);
                host->clk_enabled = 1;
-        printk("clk enabled\n");
        }
 }
 
@@ -218,7 +216,6 @@ static int glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq)
 static void glamo_mci_request_done(struct glamo_mci_host *host, struct
 mmc_request *mrq) {
        mod_timer(&host->disable_timer, jiffies + HZ / 16);
-
        mmc_request_done(host->mmc, mrq);
 }
 
@@ -229,12 +226,17 @@ static void glamo_mci_irq_worker(struct work_struct *work)
                                                                                                irq_work);
        struct mmc_command *cmd;
        uint16_t status;
-
        if (!host->mrq || !host->mrq->cmd)
                return;
 
        cmd = host->mrq->cmd;
 
+#if 0
+       if (cmd->data->flags & MMC_DATA_READ) {
+               return;
+       }
+#endif
+
        status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
        dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status);
 
@@ -242,20 +244,20 @@ static void glamo_mci_irq_worker(struct work_struct *work)
        if (status & GLAMO_STAT1_MMC_RB_DRDY)
                status &= ~GLAMO_STAT1_MMC_DTOUT;
 
-       if (status & (GLAMO_STAT1_MMC_RTOUT |
-                         GLAMO_STAT1_MMC_DTOUT))
+       if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
                cmd->error = -ETIMEDOUT;
-       if (status & (GLAMO_STAT1_MMC_BWERR |
-                         GLAMO_STAT1_MMC_BRERR))
+       if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) {
                cmd->error = -EILSEQ;
+       }
        if (cmd->error) {
                dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
                goto done;
        }
 
        /* issue STOP if we have been given one to use */
-       if (host->mrq->stop)
+       if (host->mrq->stop) {
                glamo_mci_send_command(host, host->mrq->stop);
+       }
 
        if (cmd->data->flags & MMC_DATA_READ)
                do_pio_read(host, cmd->data);
@@ -265,10 +267,70 @@ done:
        glamo_mci_request_done(host, cmd->mrq);
 }
 
+static void glamo_mci_read_worker(struct work_struct *work)
+{
+       struct glamo_mci_host *host = container_of(work, struct glamo_mci_host,
+                                                                                               read_work);
+       struct mmc_command *cmd;
+       uint16_t status;
+       uint16_t blocks_ready;
+       size_t data_read = 0;
+       size_t data_ready;
+       struct scatterlist *sg;
+       u16 __iomem *from_ptr = host->data_base;
+       void *sg_pointer;
+
+
+       cmd = host->mrq->cmd;
+       sg = cmd->data->sg;
+       do {
+               status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+
+               if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
+                       cmd->error = -ETIMEDOUT;
+               if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR))
+                       cmd->error = -EILSEQ;
+               if (cmd->error) {
+                       dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
+                       goto done;
+               }
+
+               blocks_ready = glamo_reg_read(host, GLAMO_REG_MMC_RB_BLKCNT);
+               data_ready = blocks_ready * cmd->data->blksz;
+
+               if (data_ready == data_read)
+                       yield();
+
+               while(sg && data_read + sg->length <= data_ready) {
+                       sg_pointer = page_address(sg_page(sg)) + sg->offset;
+                       memcpy(sg_pointer, from_ptr, sg->length);
+                       from_ptr += sg->length >> 1;
+
+                       data_read += sg->length;
+                       sg = sg_next(sg);
+               }
+
+       } while(sg);
+       cmd->data->bytes_xfered = data_read;
+
+       do {
+               status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+       } while (!(status & GLAMO_STAT1_MMC_IDLE));
+
+       if (host->mrq->stop)
+               glamo_mci_send_command(host, host->mrq->stop);
+
+       do {
+               status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+       } while (!(status & GLAMO_STAT1_MMC_IDLE));
+done:
+       host->mrq = NULL;
+       glamo_mci_request_done(host, cmd->mrq);
+}
+
 static irqreturn_t glamo_mci_irq(int irq, void *devid)
 {
        struct glamo_mci_host *host = (struct glamo_mci_host*)devid;
-
        schedule_work(&host->irq_work);
 
        return IRQ_HANDLED;
@@ -393,7 +455,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
                break;
        }
 
-       if (triggers_int)
+       if (cmd->data)
                host->mrq = cmd->mrq;
 
        /* always largest timeout */
@@ -432,8 +494,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
                                   GLAMO_STAT1_MMC_DTOUT)) ||
                (timeout == 0)) {
                cmd->error = -ETIMEDOUT;
-       } else if (status & (GLAMO_STAT1_MMC_BWERR |
-                         GLAMO_STAT1_MMC_BRERR)) {
+       } else if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) {
                cmd->error = -EILSEQ;
        }
 
@@ -453,6 +514,15 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
                                                   ((readw(&reg_resp[2])) << 24);
                }
        }
+
+#if 0
+       /* We'll only get an interrupt when all data has been transfered.
+          By starting to copy data when it's avaiable we can increase throughput by
+          up to 30%. */
+       if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
+               schedule_work(&host->read_work);
+#endif
+
 }
 
 static int glamo_mci_prepare_pio(struct glamo_mci_host *host,
@@ -515,9 +585,11 @@ static void glamo_mci_send_request(struct mmc_host *mmc, struct mmc_request *mrq
        struct glamo_mci_host *host = mmc_priv(mmc);
        struct mmc_command *cmd = mrq->cmd;
 
+       glamo_mci_clock_enable(host);
        host->request_counter++;
        if (cmd->data) {
                if(glamo_mci_prepare_pio(host, cmd->data)) {
+                       cmd->error = -EIO;
                        cmd->data->error = -EIO;
                        goto done;
                }
@@ -528,7 +600,6 @@ static void glamo_mci_send_request(struct mmc_host *mmc, struct mmc_request *mrq
                 cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
                 cmd->flags);
 
-       glamo_mci_clock_enable(host);
        glamo_mci_send_command(host, cmd);
 
        /*
@@ -666,6 +737,7 @@ static int glamo_mci_probe(struct platform_device *pdev)
        host->clk_enabled = 0;
 
        INIT_WORK(&host->irq_work, glamo_mci_irq_worker);
+       INIT_WORK(&host->read_work, glamo_mci_read_worker);
 
        host->regulator = regulator_get(pdev->dev.parent, "SD_3V3");
        if (!host->regulator) {
@@ -746,7 +818,7 @@ static int glamo_mci_probe(struct platform_device *pdev)
                         MMC_CAP_MMC_HIGHSPEED |
                         MMC_CAP_SD_HIGHSPEED;
        mmc->f_min     = host->clk_rate / 256;
-       mmc->f_max     = host->clk_rate;
+       mmc->f_max     = sd_max_clk;
 
        mmc->max_blk_count = (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */
        mmc->max_blk_size  = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */
@@ -829,28 +901,28 @@ static int glamo_mci_remove(struct platform_device *pdev)
 
 #ifdef CONFIG_PM
 
-static int glamo_mci_suspend(struct platform_device *dev, pm_message_t state)
+static int glamo_mci_suspend(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = dev_get_drvdata(dev);
        struct glamo_mci_host *host = mmc_priv(mmc);
        int ret;
 
        cancel_work_sync(&host->irq_work);
 
-       ret = mmc_suspend_host(mmc, state);
+       ret = mmc_suspend_host(mmc, PMSG_SUSPEND);
        glamo_mci_clock_enable(host);
 
        return ret;
 }
 
-static int glamo_mci_resume(struct platform_device *dev)
+static int glamo_mci_resume(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = dev_get_drvdata(dev);
        struct glamo_mci_host *host = mmc_priv(mmc);
        int ret;
 
-    glamo_engine_enable(host->pdata->core, GLAMO_ENGINE_MMC);
-    glamo_engine_reset(host->pdata->core, GLAMO_ENGINE_MMC);
+       glamo_engine_enable(host->pdata->core, GLAMO_ENGINE_MMC);
+       glamo_engine_reset(host->pdata->core, GLAMO_ENGINE_MMC);
 
        glamo_reg_write(host, GLAMO_REG_MMC_WDATADS1,
                        (u16)(host->data_mem->start));
@@ -861,28 +933,34 @@ static int glamo_mci_resume(struct platform_device *dev)
                        (u16)(host->data_mem->start));
        glamo_reg_write(host, GLAMO_REG_MMC_RDATADS2,
                        (u16)(host->data_mem->start >> 16));
-    mdelay(5);
+       mdelay(5);
 
        ret = mmc_resume_host(host->mmc);
 /*     glamo_mci_clock_disable(host);*/
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(glamo_mci_resume);
+
+static struct dev_pm_ops glamo_mci_pm_ops = {
+       .suspend = glamo_mci_suspend,
+       .resume  = glamo_mci_resume,
+};
+#define GLAMO_MCI_PM_OPS (&glamo_mci_pm_ops)
 
 #else /* CONFIG_PM */
-#define glamo_mci_suspend NULL
-#define glamo_mci_resume NULL
+#define GLAMO_MCI_PM_OPS NULL
 #endif /* CONFIG_PM */
 
 
 static struct platform_driver glamo_mci_driver =
 {
-       .driver.name    = "glamo-mci",
-       .probe          = glamo_mci_probe,
-       .remove         = glamo_mci_remove,
-       .suspend        = glamo_mci_suspend,
-       .resume         = glamo_mci_resume,
+       .probe  = glamo_mci_probe,
+       .remove = glamo_mci_remove,
+       .driver = {
+               .name  = "glamo-mci",
+               .owner = THIS_MODULE,
+               .pm    = GLAMO_MCI_PM_OPS,
+       },
 };
 
 static int __init glamo_mci_init(void)
This page took 0.039658 seconds and 4 git commands to generate.