Commit bcbd78d5 authored by Russell King's avatar Russell King

[MMC] MMCI updates.

Calculate data timeout correctly.
Obtain MCLK rate from clock subsystem.
Limit max clock rate properly.
Use clock bypass mode for fast data rates.
parent ca78bf35
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/protocol.h> #include <linux/mmc/protocol.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/hardware/amba.h> #include <asm/hardware/amba.h>
#include <asm/hardware/clock.h>
#include <asm/mach/mmc.h> #include <asm/mach/mmc.h>
#include "mmci.h" #include "mmci.h"
...@@ -59,7 +61,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *req) ...@@ -59,7 +61,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *req)
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
{ {
unsigned int datactrl; unsigned int datactrl, timeout;
DBG("%s: data: blksz %04x blks %04x flags %08x\n", DBG("%s: data: blksz %04x blks %04x flags %08x\n",
host->mmc->host_name, 1 << data->blksz_bits, data->blocks, host->mmc->host_name, 1 << data->blksz_bits, data->blocks,
...@@ -75,7 +77,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) ...@@ -75,7 +77,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
host->size = data->blocks << data->blksz_bits; host->size = data->blocks << data->blksz_bits;
host->data_xfered = 0; host->data_xfered = 0;
writel(0x800000, host->base + MMCIDATATIMER); timeout = data->timeout_clks +
((unsigned long long)data->timeout_ns * host->cclk) /
1000000000ULL;
writel(timeout, host->base + MMCIDATATIMER);
writel(host->size, host->base + MMCIDATALENGTH); writel(host->size, host->base + MMCIDATALENGTH);
writel(datactrl, host->base + MMCIDATACTRL); writel(datactrl, host->base + MMCIDATACTRL);
} }
...@@ -279,14 +285,20 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -279,14 +285,20 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct mmci_host *host = mmc_priv(mmc); struct mmci_host *host = mmc_priv(mmc);
u32 clk = 0, pwr = 0; u32 clk = 0, pwr = 0;
DBG("%s: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n", DBG("%s: set_ios: clock %uHz busmode %u powermode %u Vdd %u\n",
mmc->host_name, ios->clock, ios->bus_mode, ios->power_mode, mmc->host_name, ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd / 100, ios->vdd % 100); ios->vdd);
if (ios->clock) { if (ios->clock) {
clk = host->mclk / (2 * ios->clock) - 1; if (ios->clock >= host->mclk) {
if (clk > 256) clk = MCI_CLK_BYPASS;
clk = 255; host->cclk = host->mclk;
} else {
clk = host->mclk / (2 * ios->clock) - 1;
if (clk > 256)
clk = 255;
host->cclk = host->mclk / (2 * (clk + 1));
}
clk |= MCI_CLK_ENABLE; clk |= MCI_CLK_ENABLE;
} }
...@@ -336,39 +348,56 @@ static void mmci_check_status(unsigned long data) ...@@ -336,39 +348,56 @@ static void mmci_check_status(unsigned long data)
static int mmci_probe(struct amba_device *dev, void *id) static int mmci_probe(struct amba_device *dev, void *id)
{ {
struct mmc_platform_data *plat = dev->dev.platform_data; struct mmc_platform_data *plat = dev->dev.platform_data;
struct mmci_host *host = NULL; struct mmci_host *host;
struct mmc_host *mmc; struct mmc_host *mmc;
int ret; int ret;
/* must have platform data */ /* must have platform data */
if (!plat) if (!plat) {
return -EINVAL; ret = -EINVAL;
goto out;
}
ret = amba_request_regions(dev, DRIVER_NAME); ret = amba_request_regions(dev, DRIVER_NAME);
if (ret) if (ret)
return ret; goto out;
mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
if (!mmc) { if (!mmc) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto rel_regions;
} }
mmc->ops = &mmci_ops;
mmc->f_min = (plat->mclk + 511) / 512;
mmc->f_max = max(plat->mclk / 2, fmax);
mmc->ocr_avail = plat->ocr_mask;
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->clk = clk_get(&dev->dev, "MCLK");
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
host->clk = NULL;
goto host_free;
}
ret = clk_use(host->clk);
if (ret)
goto clk_free;
ret = clk_enable(host->clk);
if (ret)
goto clk_unuse;
host->plat = plat; host->plat = plat;
host->mclk = plat->mclk; host->mclk = clk_get_rate(host->clk);
host->mmc = mmc; host->mmc = mmc;
host->base = ioremap(dev->res.start, SZ_4K); host->base = ioremap(dev->res.start, SZ_4K);
if (!host->base) { if (!host->base) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto clk_disable;
} }
mmc->ops = &mmci_ops;
mmc->f_min = (host->mclk + 511) / 512;
mmc->f_max = min(host->mclk / 2, fmax);
mmc->ocr_avail = plat->ocr_mask;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK0);
...@@ -377,13 +406,11 @@ static int mmci_probe(struct amba_device *dev, void *id) ...@@ -377,13 +406,11 @@ static int mmci_probe(struct amba_device *dev, void *id)
ret = request_irq(dev->irq[0], mmci_irq, SA_SHIRQ, DRIVER_NAME " (cmd)", host); ret = request_irq(dev->irq[0], mmci_irq, SA_SHIRQ, DRIVER_NAME " (cmd)", host);
if (ret) if (ret)
goto out; goto unmap;
ret = request_irq(dev->irq[1], mmci_pio_irq, SA_SHIRQ, DRIVER_NAME " (pio)", host); ret = request_irq(dev->irq[1], mmci_pio_irq, SA_SHIRQ, DRIVER_NAME " (pio)", host);
if (ret) { if (ret)
free_irq(dev->irq[0], host); goto irq0_free;
goto out;
}
writel(MCI_IRQENABLE, host->base + MMCIMASK0); writel(MCI_IRQENABLE, host->base + MMCIMASK0);
writel(MCI_TXFIFOHALFEMPTYMASK|MCI_RXFIFOHALFFULLMASK, host->base + MMCIMASK1); writel(MCI_TXFIFOHALFEMPTYMASK|MCI_RXFIFOHALFFULLMASK, host->base + MMCIMASK1);
...@@ -404,12 +431,21 @@ static int mmci_probe(struct amba_device *dev, void *id) ...@@ -404,12 +431,21 @@ static int mmci_probe(struct amba_device *dev, void *id)
return 0; return 0;
out: irq0_free:
if (host && host->base) free_irq(dev->irq[0], host);
iounmap(host->base); unmap:
if (mmc) iounmap(host->base);
mmc_free_host(mmc); clk_disable:
clk_disable(host->clk);
clk_unuse:
clk_unuse(host->clk);
clk_free:
clk_put(host->clk);
host_free:
mmc_free_host(mmc);
rel_regions:
amba_release_regions(dev); amba_release_regions(dev);
out:
return ret; return ret;
} }
...@@ -436,6 +472,9 @@ static int mmci_remove(struct amba_device *dev) ...@@ -436,6 +472,9 @@ static int mmci_remove(struct amba_device *dev)
free_irq(dev->irq[1], host); free_irq(dev->irq[1], host);
iounmap(host->base); iounmap(host->base);
clk_disable(host->clk);
clk_unuse(host->clk);
clk_put(host->clk);
mmc_free_host(mmc); mmc_free_host(mmc);
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
#define MMCICLOCK 0x004 #define MMCICLOCK 0x004
#define MCI_CLK_ENABLE (1 << 8) #define MCI_CLK_ENABLE (1 << 8)
#define MCI_PWRSAVE (1 << 9) #define MCI_CLK_PWRSAVE (1 << 9)
#define MCI_BYPASS (1 << 10) #define MCI_CLK_BYPASS (1 << 10)
#define MMCIARGUMENT 0x008 #define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c #define MMCICOMMAND 0x00c
...@@ -118,18 +118,22 @@ ...@@ -118,18 +118,22 @@
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) #define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
struct clk;
struct mmci_host { struct mmci_host {
void *base; void *base;
struct mmc_request *req; struct mmc_request *req;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_data *data; struct mmc_data *data;
struct mmc_host *mmc; struct mmc_host *mmc;
struct clk *clk;
unsigned int data_xfered; unsigned int data_xfered;
spinlock_t lock; spinlock_t lock;
unsigned int mclk; unsigned int mclk;
unsigned int cclk;
u32 pwr; u32 pwr;
struct mmc_platform_data *plat; struct mmc_platform_data *plat;
...@@ -139,11 +143,6 @@ struct mmci_host { ...@@ -139,11 +143,6 @@ struct mmci_host {
/* pio stuff */ /* pio stuff */
void *buffer; void *buffer;
unsigned int size; unsigned int size;
/* dma stuff */
// struct scatterlist *sg_list;
// int sg_len;
// int sg_dir;
}; };
#define to_mmci_host(mmc) container_of(mmc, struct mmci_host, mmc) #define to_mmci_host(mmc) container_of(mmc, struct mmci_host, mmc)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment