[s3c24xx] glamo-mmc: Limit clock rate.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@17047 3c298f89-4303-0410-b956-a3cf2f4a3e73master
parent
3ae7d9a0f9
commit
7b7f645d26
|
@ -18,7 +18,6 @@
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/crc7.h>
|
#include <linux/crc7.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
@ -53,6 +52,7 @@ struct glamo_mci_host {
|
||||||
struct timer_list disable_timer;
|
struct timer_list disable_timer;
|
||||||
|
|
||||||
struct work_struct irq_work;
|
struct work_struct irq_work;
|
||||||
|
struct work_struct read_work;
|
||||||
|
|
||||||
unsigned clk_enabled : 1;
|
unsigned clk_enabled : 1;
|
||||||
};
|
};
|
||||||
|
@ -74,7 +74,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
|
||||||
* for example
|
* for example
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int sd_max_clk = 50000000;
|
static int sd_max_clk = 21000000;
|
||||||
module_param(sd_max_clk, int, 0644);
|
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
|
static void glamo_mci_request_done(struct glamo_mci_host *host, struct
|
||||||
mmc_request *mrq) {
|
mmc_request *mrq) {
|
||||||
mod_timer(&host->disable_timer, jiffies + HZ / 16);
|
mod_timer(&host->disable_timer, jiffies + HZ / 16);
|
||||||
|
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,12 +226,17 @@ static void glamo_mci_irq_worker(struct work_struct *work)
|
||||||
irq_work);
|
irq_work);
|
||||||
struct mmc_command *cmd;
|
struct mmc_command *cmd;
|
||||||
uint16_t status;
|
uint16_t status;
|
||||||
|
|
||||||
if (!host->mrq || !host->mrq->cmd)
|
if (!host->mrq || !host->mrq->cmd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cmd = host->mrq->cmd;
|
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);
|
status = glamo_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
|
||||||
dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status);
|
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)
|
if (status & GLAMO_STAT1_MMC_RB_DRDY)
|
||||||
status &= ~GLAMO_STAT1_MMC_DTOUT;
|
status &= ~GLAMO_STAT1_MMC_DTOUT;
|
||||||
|
|
||||||
if (status & (GLAMO_STAT1_MMC_RTOUT |
|
if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
|
||||||
GLAMO_STAT1_MMC_DTOUT))
|
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
if (status & (GLAMO_STAT1_MMC_BWERR |
|
if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) {
|
||||||
GLAMO_STAT1_MMC_BRERR))
|
|
||||||
cmd->error = -EILSEQ;
|
cmd->error = -EILSEQ;
|
||||||
|
}
|
||||||
if (cmd->error) {
|
if (cmd->error) {
|
||||||
dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
|
dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* issue STOP if we have been given one to use */
|
/* 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);
|
glamo_mci_send_command(host, host->mrq->stop);
|
||||||
|
}
|
||||||
|
|
||||||
if (cmd->data->flags & MMC_DATA_READ)
|
if (cmd->data->flags & MMC_DATA_READ)
|
||||||
do_pio_read(host, cmd->data);
|
do_pio_read(host, cmd->data);
|
||||||
|
@ -263,10 +267,70 @@ done:
|
||||||
glamo_mci_request_done(host, cmd->mrq);
|
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)
|
static irqreturn_t glamo_mci_irq(int irq, void *devid)
|
||||||
{
|
{
|
||||||
struct glamo_mci_host *host = (struct glamo_mci_host*)devid;
|
struct glamo_mci_host *host = (struct glamo_mci_host*)devid;
|
||||||
|
|
||||||
schedule_work(&host->irq_work);
|
schedule_work(&host->irq_work);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -391,7 +455,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (triggers_int)
|
if (cmd->data)
|
||||||
host->mrq = cmd->mrq;
|
host->mrq = cmd->mrq;
|
||||||
|
|
||||||
/* always largest timeout */
|
/* always largest timeout */
|
||||||
|
@ -430,8 +494,7 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
|
||||||
GLAMO_STAT1_MMC_DTOUT)) ||
|
GLAMO_STAT1_MMC_DTOUT)) ||
|
||||||
(timeout == 0)) {
|
(timeout == 0)) {
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
} else if (status & (GLAMO_STAT1_MMC_BWERR |
|
} else if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) {
|
||||||
GLAMO_STAT1_MMC_BRERR)) {
|
|
||||||
cmd->error = -EILSEQ;
|
cmd->error = -EILSEQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,6 +514,15 @@ static void glamo_mci_send_command(struct glamo_mci_host *host,
|
||||||
((readw(®_resp[2])) << 24);
|
((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,
|
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 glamo_mci_host *host = mmc_priv(mmc);
|
||||||
struct mmc_command *cmd = mrq->cmd;
|
struct mmc_command *cmd = mrq->cmd;
|
||||||
|
|
||||||
|
glamo_mci_clock_enable(host);
|
||||||
host->request_counter++;
|
host->request_counter++;
|
||||||
if (cmd->data) {
|
if (cmd->data) {
|
||||||
if(glamo_mci_prepare_pio(host, cmd->data)) {
|
if(glamo_mci_prepare_pio(host, cmd->data)) {
|
||||||
|
cmd->error = -EIO;
|
||||||
cmd->data->error = -EIO;
|
cmd->data->error = -EIO;
|
||||||
goto done;
|
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->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
|
||||||
cmd->flags);
|
cmd->flags);
|
||||||
|
|
||||||
glamo_mci_clock_enable(host);
|
|
||||||
glamo_mci_send_command(host, cmd);
|
glamo_mci_send_command(host, cmd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -664,6 +737,7 @@ static int glamo_mci_probe(struct platform_device *pdev)
|
||||||
host->clk_enabled = 0;
|
host->clk_enabled = 0;
|
||||||
|
|
||||||
INIT_WORK(&host->irq_work, glamo_mci_irq_worker);
|
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");
|
host->regulator = regulator_get(pdev->dev.parent, "SD_3V3");
|
||||||
if (!host->regulator) {
|
if (!host->regulator) {
|
||||||
|
@ -744,7 +818,7 @@ static int glamo_mci_probe(struct platform_device *pdev)
|
||||||
MMC_CAP_MMC_HIGHSPEED |
|
MMC_CAP_MMC_HIGHSPEED |
|
||||||
MMC_CAP_SD_HIGHSPEED;
|
MMC_CAP_SD_HIGHSPEED;
|
||||||
mmc->f_min = host->clk_rate / 256;
|
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_count = (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */
|
||||||
mmc->max_blk_size = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */
|
mmc->max_blk_size = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */
|
||||||
|
|
Loading…
Reference in New Issue