#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>
#define DRIVER_NAME "glamo-mci"
struct glamo_mci_host {
- struct platform_device *pdev;
struct glamo_mmc_platform_data *pdata;
+ struct platform_device *pdev;
+ struct glamo_core *core;
struct mmc_host *mmc;
struct resource *mmio_mem;
struct resource *data_mem;
struct timer_list disable_timer;
struct work_struct irq_work;
+ struct work_struct read_work;
unsigned clk_enabled : 1;
+
};
static void glamo_mci_send_request(struct mmc_host *mmc, struct mmc_request* mrq);
* for example
*/
-static int sd_max_clk = 50000000;
+static int sd_max_clk = 21000000;
module_param(sd_max_clk, int, 0644);
/*
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->core, GLAMO_ENGINE_MMC);
host->clk_enabled = 0;
}
}
del_timer_sync(&host->disable_timer);
if (!host->clk_enabled) {
- glamo_engine_div_enable(host->pdata->core, GLAMO_ENGINE_MMC);
+ glamo_engine_div_enable(host->core, GLAMO_ENGINE_MMC);
host->clk_enabled = 1;
}
}
if (freq) {
glamo_mci_clock_enable(host);
- real_rate = glamo_engine_reclock(host->pdata->core, GLAMO_ENGINE_MMC, freq);
+ real_rate = glamo_engine_reclock(host->core, GLAMO_ENGINE_MMC, freq);
} else {
glamo_mci_clock_disable(host);
}
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);
}
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);
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);
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;
break;
}
- if (triggers_int)
+ if (cmd->data)
host->mrq = cmd->mrq;
/* always largest timeout */
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;
}
((readw(®_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,
* but something insane like suspend problems can mean
* we spin here forever, so we timeout after a LONG time
*/
- while ((!(readw(host->pdata->core->base +
+ while ((!(readw(host->core->base +
GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC)) &&
(timeout--));
return -ETIMEDOUT;
}
/* ack this interrupt source */
- writew(GLAMO_IRQ_MMC, host->pdata->core->base +
+ writew(GLAMO_IRQ_MMC, host->core->base +
GLAMO_REG_IRQ_CLEAR);
/* yay we are an interrupt controller! -- call the ISR
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;
}
cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
cmd->flags);
- glamo_mci_clock_enable(host);
glamo_mci_send_command(host, cmd);
/*
goto done;
- if (!host->pdata->core->irq_works) {
+ if (!host->core->irq_works) {
if (glamo_mci_irq_poll(host, mrq->cmd))
goto done;
}
break;
case MMC_POWER_OFF:
default:
- glamo_engine_disable(host->pdata->core,
+ glamo_engine_disable(host->core,
GLAMO_ENGINE_MMC);
ret = regulator_disable(host->regulator);
static int glamo_mci_probe(struct platform_device *pdev)
{
- struct mmc_host *mmc;
- struct glamo_mci_host *host;
+ struct mmc_host *mmc;
+ struct glamo_mci_host *host;
+ struct glamo_core *core = dev_get_drvdata(pdev->dev.parent);
int ret;
dev_info(&pdev->dev, "glamo_mci driver (C)2007 Openmoko, Inc\n");
host = mmc_priv(mmc);
host->mmc = mmc;
host->pdev = pdev;
- host->pdata = pdev->dev.platform_data;
+ if (core->pdata)
+ host->pdata = core->pdata->mmc_data;
host->power_mode = MMC_POWER_OFF;
host->clk_enabled = 0;
+ host->core = core;
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) {
host->vdd = 0;
- host->clk_rate = glamo_pll_rate(host->pdata->core, GLAMO_PLL1);
+ host->clk_rate = glamo_pll_rate(host->core, GLAMO_PLL1);
/* explain our host controller capabilities */
mmc->ops = &glamo_mci_ops;
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 */
platform_set_drvdata(pdev, mmc);
- glamo_engine_enable(host->pdata->core, GLAMO_ENGINE_MMC);
- glamo_engine_reset(host->pdata->core, GLAMO_ENGINE_MMC);
+ glamo_engine_enable(host->core, GLAMO_ENGINE_MMC);
+ glamo_engine_reset(host->core, GLAMO_ENGINE_MMC);
glamo_reg_write(host, GLAMO_REG_MMC_WDATADS1,
(u16)(host->data_mem->start));
mmc_free_host(mmc);
- glamo_engine_disable(host->pdata->core, GLAMO_ENGINE_MMC);
+ glamo_engine_disable(host->core, GLAMO_ENGINE_MMC);
return 0;
}
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->core, GLAMO_ENGINE_MMC);
+ glamo_engine_reset(host->core, GLAMO_ENGINE_MMC);
glamo_reg_write(host, GLAMO_REG_MMC_WDATADS1,
(u16)(host->data_mem->start));