[s3c24xx] glamo-mmc: Limit clock rate.
[openwrt/svn-archive/archive.git] / target / linux / s3c24xx / files-2.6.30 / drivers / mfd / glamo / glamo-mci.c
index 812e03797bb90a7b8f909e9d6ae35eea251c740e..fdf292f8ea16c13cb68acd326283e79744cf54d1 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);
 
 /*
@@ -216,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);
 }
 
@@ -227,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);
 
@@ -240,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);
@@ -263,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;
@@ -391,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 */
@@ -430,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;
        }
 
@@ -451,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,
@@ -513,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;
                }
@@ -526,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);
 
        /*
@@ -664,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) {
@@ -744,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 */