OMAP: HSMMC: Patches
Subsystems (2)
cover OMAP: HSMMC: Patches
Hi
OMAP HSMMC is not ready for mainline, but development is ongoing.
In the meantime these patches are best posted and applied via omap mailing list and tree.
Regards
Adrian
Quoted 14 lines · Adrian Hunter
Hi
OMAP HSMMC is not ready for mainline, but development is ongoing.
In the meantime these patches are best posted and applied via omap mailing list and tree.
Regards
AdrianSignature · 3 lines
To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Just realised I forgot the from lines in all those patches - will resend.
PATCH 1/9 OMAP: HSMMC: Make driver support dynamic idle
From: Amit Kucheria <[email protected]>
Date: Mon, 22 Sep 2008 10:23:09 +0300
Add a timer that is kept active by MMC requests. FCLK is disabled on timeout
---drivers/mmc/host/omap_hsmmc.c | 73 +++++++++++++++++++++++++++++++++-------
1 files changed, 60 insertions(+), 13 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -121,6 +121,9 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \ __raw_writel((val), (base) + OMAP_HSMMC_##reg)
+enum {OFF = 0, ON};
+#define IDLE_TIMEOUT (5*HZ)
+
struct mmc_omap_host {
struct device *dev;
struct mmc_host *mmc;
@@ -148,9 +151,50 @@ struct mmc_omap_host {
int initstr;
int slot_id;
int dbclk_enabled;+
+ struct timer_list idle_timer;
+ spinlock_t clk_lock; /* for changing enabled state */
+ int fclk_enabled:1;
+
struct omap_mmc_platform_data *pdata; };
+int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ if (host->fclk_enabled != state) {
+ if (state == ON) {
+ if (host->fclk_enabled == OFF) {
+ ret = clk_enable(host->fclk);
+ if (ret != 0)
+ return ret;
+ dev_dbg(mmc_dev(host->mmc),
+ "mmc_fclk: enabled\n");
+ }
+ /* Revisit: Change the timer bump based on real
+ MMC usage characteristics */
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ } else {
+ clk_disable(host->fclk);
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+ del_timer(&host->idle_timer);
+ }
+ host->fclk_enabled = state;
+ }
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ return 0;
+}
+
+static void mmc_omap_idle_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ mmc_omap_fclk_state(host, OFF);
+}
+
/*
* Stop clock to the card
*/
@@ -457,7 +501,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
int ret; /* Disable the clocks */ - clk_disable(host->fclk); + mmc_omap_fclk_state(host, OFF); clk_disable(host->iclk); clk_disable(host->dbclk);
@@ -471,7 +515,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
if (ret != 0) goto err;
- clk_enable(host->fclk);
+ mmc_omap_fclk_state(host, ON); clk_enable(host->iclk); clk_enable(host->dbclk);
@@ -742,10 +786,10 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
WARN_ON(host->mrq != NULL); host->mrq = req; mmc_omap_prepare_data(host, req); + mmc_omap_fclk_state(host, ON); mmc_omap_start_command(host, req->cmd, req->data); }
-
/* Routine to configure clock values. Exposed API to core */
static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
@@ -824,6 +868,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) | OD);
+
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -860,7 +905,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
struct mmc_host *mmc;
struct mmc_omap_host *host = NULL;
struct resource *res;
- int ret = 0, irq;
+ int ret = 0, irq, reg;
u32 hctl, capa;
if (pdata == NULL) {
@@ -925,14 +970,17 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err1;
}- if (clk_enable(host->fclk) != 0) {
+ spin_lock_init(&host->clk_lock);
+ setup_timer(&host->idle_timer, mmc_omap_idle_timer,
+ (unsigned long) host);
+
+ if (mmc_omap_fclk_state(host, ON) != 0) {
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
}
-
if (clk_enable(host->iclk) != 0) {
- clk_disable(host->fclk);
+ mmc_omap_fclk_state(host, OFF);
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
@@ -1047,7 +1095,7 @@ err_irq_cd:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
- clk_disable(host->fclk);
+ mmc_omap_fclk_state(host, OFF);
clk_disable(host->iclk);
clk_put(host->fclk);
clk_put(host->iclk);
@@ -1096,7 +1144,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
free_irq(mmc_slot(host).card_detect_irq, host);
flush_scheduled_work();- clk_disable(host->fclk);
+ mmc_omap_fclk_state(host, OFF); clk_disable(host->iclk); clk_put(host->fclk); clk_put(host->iclk); @@ -1149,7 +1197,7 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) | SDBP); }
- clk_disable(host->fclk);
+ mmc_omap_fclk_state(host, OFF); clk_disable(host->iclk); clk_disable(host->dbclk); }
@@ -1169,13 +1217,12 @@ static int omap_mmc_resume(struct platform_device *pdev)
if (host) {- ret = clk_enable(host->fclk);
- if (ret)
+ if (mmc_omap_fclk_state(host, ON) != 0)
goto clk_en_err;
ret = clk_enable(host->iclk);
if (ret) {
- clk_disable(host->fclk);
+ mmc_omap_fclk_state(host, OFF);
clk_put(host->fclk);
goto clk_en_err;
}Signature · 1 line
1.5.4.3
PATCH 2/9 OMAP: HSMMC: Ensure fclk is running before accessing HSMMC registers
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Fri, 24 Oct 2008 16:03:23 +0300
---drivers/mmc/host/omap_hsmmc.c | 32 ++++++++++++++++++++------------
1 files changed, 20 insertions(+), 12 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -154,26 +154,24 @@ struct mmc_omap_host {
struct timer_list idle_timer; spinlock_t clk_lock; /* for changing enabled state */
- int fclk_enabled:1;
+ unsigned int fclk_enabled:1;
struct omap_mmc_platform_data *pdata; };
-int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state) +static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
{
unsigned long flags;- int ret;
+ int ret = 0;
spin_lock_irqsave(&host->clk_lock, flags);
if (host->fclk_enabled != state) {
if (state == ON) {- if (host->fclk_enabled == OFF) {
- ret = clk_enable(host->fclk);
- if (ret != 0)
- return ret;
- dev_dbg(mmc_dev(host->mmc),
- "mmc_fclk: enabled\n");
- }
+ ret = clk_enable(host->fclk);
+ if (ret != 0)
+ goto err_out;
+
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); /* Revisit: Change the timer bump based on real MMC usage characteristics */ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT); @@ -184,8 +182,10 @@ int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state) } host->fclk_enabled = state; } + +err_out: spin_unlock_irqrestore(&host->clk_lock, flags);
- return 0;
+ return ret;
}
static void mmc_omap_idle_timer(unsigned long data)
@@ -557,6 +557,7 @@ static void mmc_omap_detect(struct work_struct *work)
mmc_carddetect_work);
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
+ mmc_omap_fclk_state(host, ON);
if (host->carddetect) {
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
/*@@ -785,8 +786,8 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
WARN_ON(host->mrq != NULL); host->mrq = req; - mmc_omap_prepare_data(host, req); mmc_omap_fclk_state(host, ON); + mmc_omap_prepare_data(host, req); mmc_omap_start_command(host, req->cmd, req->data); }
@@ -798,6 +799,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
unsigned long regval; unsigned long timeout;
+ mmc_omap_fclk_state(host, ON);
+
switch (ios->power_mode) {
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
@@ -869,6 +872,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) | OD);+ if (ios->power_mode == MMC_POWER_OFF)
+ mmc_omap_fclk_state(host, OFF);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1120,6 +1125,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
struct resource *res; u16 vdd = 0;
+ mmc_omap_fclk_state(host, ON);
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
/*
* Set the vdd back to 3V,
@@ -1170,6 +1176,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
if (host) {
+ mmc_omap_fclk_state(host, ON);
+
ret = mmc_suspend_host(host->mmc, state);
if (ret == 0) {
host->suspended = 1;Signature · 1 line
1.5.4.3
PATCH 3/9 OMAP: HSMMC: Make fclk disable request driven.
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Fri, 24 Oct 2008 16:30:26 +0300
Start idle timer only at the end of request or request like operation such card detect, set_ios, probe, or resume. This will ensure the fclk is disable quickly after the operation is done, but never too early.
---drivers/mmc/host/omap_hsmmc.c | 15 ++++++++++-----
1 files changed, 10 insertions(+), 5 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -122,7 +122,7 @@
__raw_writel((val), (base) + OMAP_HSMMC_##reg)
enum {OFF = 0, ON};
-#define IDLE_TIMEOUT (5*HZ)
+#define IDLE_TIMEOUT (jiffies_to_msecs(10))
struct mmc_omap_host {
struct device *dev;
@@ -165,6 +165,7 @@ static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
int ret = 0;
spin_lock_irqsave(&host->clk_lock, flags);
+ del_timer(&host->idle_timer);
if (host->fclk_enabled != state) {
if (state == ON) {
ret = clk_enable(host->fclk);
@@ -172,13 +173,9 @@ static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
goto err_out;
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
- /* Revisit: Change the timer bump based on real
- MMC usage characteristics */
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
} else {
clk_disable(host->fclk);
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
- del_timer(&host->idle_timer);
}
host->fclk_enabled = state;
}@@ -335,6 +332,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
if (!data->stop) {
host->mrq = NULL;
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
mmc_request_done(host->mmc, data->mrq);
return;
}
@@ -363,6 +361,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
}
if (host->data == NULL || cmd->error) {
host->mrq = NULL;
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
mmc_request_done(host->mmc, cmd->mrq);
}
}
@@ -575,6 +574,7 @@ static void mmc_omap_detect(struct work_struct *work)
while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
}+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
}
/*
@@ -874,6 +874,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode == MMC_POWER_OFF) mmc_omap_fclk_state(host, OFF);
+ else
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1088,6 +1090,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
if (ret < 0) goto err_cover_switch; }
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
return 0;
@@ -1250,6 +1253,8 @@ static int omap_mmc_resume(struct platform_device *pdev)
ret = mmc_resume_host(host->mmc); if (ret == 0) host->suspended = 0; + + mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT); } return ret;
Signature · 1 line
1.5.4.3
PATCH 4/9 OMAP: HSMMC: Fix suspend/resume for MMCHS2
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Tue, 28 Oct 2008 11:12:28 +0200
For HSMMC2 and HSMMC3 set SDVSS to 1.8V in HCTL before enabling the SDBP bit.
Fixes bug 84194.
---drivers/mmc/host/omap_hsmmc.c | 19 ++++++++++---------
1 files changed, 10 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -1197,15 +1197,16 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
}
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- & SDVSCLR);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDVS30);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDBP);
+ u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
+ SDVSCLR;
+
+ if (host->id == OMAP_MMC1_DEVID)
+ hctl |= SDVS30;
+ else
+ hctl |= SDVS18;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl);
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP); } mmc_omap_fclk_state(host, OFF);
Signature · 1 line
1.5.4.3
PATCH 5/9 OMAP: HSMMC: Fix oops in omap_mmc_remove
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:29 +0200
Do not access host in omap_mmc_remove() if it is null. This occurs when module is removed before its platform data is set.
This fixes the oops part in bug 86677.
---drivers/mmc/host/omap_hsmmc.c | 20 +++++++++++---------
1 files changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device *pdev)
struct resource *res; u16 vdd = 0;
- mmc_omap_fclk_state(host, ON);
- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- /*
- * Set the vdd back to 3V,
- * applicable for dual volt support.
- */
- vdd = fls(host->mmc->ocr_avail) - 1;
- if (omap_mmc_switch_opcond(host, vdd) != 0)
- host->mmc->ios.vdd = vdd;
+ if (host) {
+ mmc_omap_fclk_state(host, ON);
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the vdd back to 3V,
+ * applicable for dual volt support.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }
} res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Signature · 1 line
1.5.4.3
PATCH 1/1 OMAP: HSMMC: Fix SDVS values in omap_mmc_switch_opcond
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:28 +0200
Set only 1.8V as SDVS value in HCTL register for HSMMC2 and HSMMC3.
---drivers/mmc/host/omap_hsmmc.c | 11 +++++++----
1 files changed, 7 insertions(+), 4 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -529,10 +529,13 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
* Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is
* set in HCTL.
*/
- if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
- ((1 << vdd) == MMC_VDD_33_34)))
- reg_val |= SDVS30;
- if ((1 << vdd) == MMC_VDD_165_195)
+ if (host->id == OMAP_MMC1_DEVID) {
+ if (((1 << vdd) == MMC_VDD_32_33) ||
+ ((1 << vdd) == MMC_VDD_33_34))
+ reg_val |= SDVS30;
+ else if ((1 << vdd) == MMC_VDD_165_195)
+ reg_val |= SDVS18;
+ } else
reg_val |= SDVS18; OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
Signature · 1 line
1.5.4.3
PATCH 7/9 OMAP: HSMMC: Use mod_timer from a single point for readability.
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:30 +0200
---drivers/mmc/host/omap_hsmmc.c | 18 +++++++++++-------
1 files changed, 11 insertions(+), 7 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -192,6 +192,11 @@ static void mmc_omap_idle_timer(unsigned long data)
mmc_omap_fclk_state(host, OFF); }
+static void mmc_omap_fclk_lazy_disable(struct mmc_omap_host *host)
+{
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+}
+
/*
* Stop clock to the card
*/
@@ -332,7 +337,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
if (!data->stop) {
host->mrq = NULL;
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
mmc_request_done(host->mmc, data->mrq);
return;
}
@@ -361,7 +366,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
}
if (host->data == NULL || cmd->error) {
host->mrq = NULL;
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
mmc_request_done(host->mmc, cmd->mrq);
}
}
@@ -577,7 +582,7 @@ static void mmc_omap_detect(struct work_struct *work)
while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
}- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
}
/*
@@ -878,7 +883,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode == MMC_POWER_OFF) mmc_omap_fclk_state(host, OFF); else
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1093,7 +1098,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
if (ret < 0) goto err_cover_switch; }
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
return 0;
@@ -1233,7 +1238,6 @@ static int omap_mmc_resume(struct platform_device *pdev)
return 0;
if (host) {
-
if (mmc_omap_fclk_state(host, ON) != 0)
goto clk_en_err;@@ -1260,7 +1264,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
if (ret == 0) host->suspended = 0;
- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host); } return ret;
Signature · 1 line
1.5.4.3
PATCH 8/9 OMAP: HSMMC: Implement scatter-gather emulation
In reply to Adrian HunterFrom: Juha Yrjola <[email protected]>
Date: Fri, 14 Nov 2008 15:22:00 +0200
Instead of using the bounce buffer, using scatter-gather emulation (as in the OMAP1/2 MMC driver) removes the need of one extra memory copy and improves performance.
---drivers/mmc/host/omap_hsmmc.c | 174 ++++++++++++++++++++++------------------
1 files changed, 96 insertions(+), 78 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -98,9 +98,6 @@
#define OMAP_MMC1_DEVID 0
#define OMAP_MMC2_DEVID 1
-#define OMAP_MMC_DATADIR_NONE 0
-#define OMAP_MMC_DATADIR_READ 1
-#define OMAP_MMC_DATADIR_WRITE 2
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "mmci-omap"
@@ -139,16 +136,14 @@ struct mmc_omap_host {
resource_size_t mapbase;
unsigned int id;
unsigned int dma_len;
- unsigned int dma_dir;
+ unsigned int dma_sg_idx;
unsigned char bus_mode;
- unsigned char datadir;
u32 *buffer;
u32 bytesleft;
int suspended;
int irq;
int carddetect;
int use_dma, dma_ch;
- int initstr;
int slot_id;
int dbclk_enabled;@@ -316,6 +311,15 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); }
+static int
+mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ if (data->flags & MMC_DATA_WRITE)
+ return DMA_TO_DEVICE;
+ else
+ return DMA_FROM_DEVICE;
+}
+
/*
* Notify the transfer complete to MMC core
*/
@@ -326,9 +330,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
if (host->use_dma && host->dma_ch != -1) dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
- host->dma_dir);
-
- host->datadir = OMAP_MMC_DATADIR_NONE;
+ mmc_omap_get_dma_dir(host, data));
if (!data->error) data->bytes_xfered += data->blocks * (data->blksz);
@@ -380,13 +382,12 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host)
if (host->use_dma && host->dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
- host->dma_dir);
+ mmc_omap_get_dma_dir(host, host->data));
omap_free_dma(host->dma_ch);
host->dma_ch = -1;
up(&host->sem);
}
host->data = NULL;- host->datadir = OMAP_MMC_DATADIR_NONE;
}
/*
@@ -598,6 +599,55 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
return IRQ_HANDLED; }
+static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+ struct mmc_data *data)
+{
+ int sync_dev;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_TX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_TX;
+ } else {
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_RX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_RX;
+ }
+ return sync_dev;
+}
+
+static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+ struct mmc_data *data,
+ struct scatterlist *sgl)
+{
+ int blksz, nblk, dma_ch;
+
+ dma_ch = host->dma_ch;
+ if (data->flags & MMC_DATA_WRITE) {
+ omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sgl), 0, 0);
+ } else {
+ omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sgl), 0, 0);
+ }
+
+ blksz = host->data->blksz;
+ nblk = sg_dma_len(sgl) / blksz;
+
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+ blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
+ mmc_omap_get_dma_sync_dev(host, data),
+ !(data->flags & MMC_DATA_WRITE));
+
+ omap_start_dma(dma_ch);
+}
+
/*
* DMA call back function
*/
@@ -611,6 +661,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
if (host->dma_ch < 0) return;
+ host->dma_sg_idx++;
+ if (host->dma_sg_idx < host->dma_len) {
+ /* Fire up the next transfer. */
+ mmc_omap_config_dma_params(host, host->data,
+ host->data->sg + host->dma_sg_idx);
+ return;
+ }
+
omap_free_dma(host->dma_ch); host->dma_ch = -1; /*
@@ -621,38 +679,28 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) }
/*
- * Configure dma src and destination parameters
- */
-static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
- struct mmc_data *data)
-{
- if (sync_dir == 0) {
- omap_set_dma_dest_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_src_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(&data->sg[0]), 0, 0);
- } else {
- omap_set_dma_src_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_dest_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(&data->sg[0]), 0, 0);
- }
- return 0;
-}
-/*
* Routine to configure and start DMA for the MMC card
*/
static intmmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) {
- int sync_dev, sync_dir = 0;
- int dma_ch = 0, ret = 0, err = 1;
+ int dma_ch = 0, ret = 0, err = 1, i; struct mmc_data *data = req->data;
+ /* Sanity check: all the SG entries must be aligned by block size. */
+ for (i = 0; i < host->dma_len; i++) {
+ struct scatterlist *sgl;
+
+ sgl = data->sg + i;
+ if (sgl->length % data->blksz)
+ return -EINVAL;
+ }
+ if ((data->blksz % 4) != 0)
+ /* REVISIT: The MMC buffer increments only when MSB is written.
+ * Return error for blksz which is non multiple of four.
+ */
+ return -EINVAL;
+
/*
* If for some reason the DMA transfer is still active,
* we wait for timeout period and free the dma
@@ -671,49 +719,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
return err; }
- if (!(data->flags & MMC_DATA_WRITE)) {
- host->dma_dir = DMA_FROM_DEVICE;
- if (host->id == OMAP_MMC1_DEVID)
- sync_dev = OMAP24XX_DMA_MMC1_RX;
- else
- sync_dev = OMAP24XX_DMA_MMC2_RX;
- } else {
- host->dma_dir = DMA_TO_DEVICE;
- if (host->id == OMAP_MMC1_DEVID)
- sync_dev = OMAP24XX_DMA_MMC1_TX;
- else
- sync_dev = OMAP24XX_DMA_MMC2_TX;
- }
-
- ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
- host, &dma_ch);
+ ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
+ mmc_omap_dma_cb,host, &dma_ch);
if (ret != 0) {
- dev_dbg(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"%s: omap_request_dma() failed with %d\n",
mmc_hostname(host->mmc), ret);
return ret;
}
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
+ data->sg_len, mmc_omap_get_dma_dir(host, data));
host->dma_ch = dma_ch;+ host->dma_sg_idx = 0;
- if (!(data->flags & MMC_DATA_WRITE))
- mmc_omap_config_dma_param(1, host, data);
- else
- mmc_omap_config_dma_param(0, host, data);
-
- if ((data->blksz % 4) == 0)
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
- (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
- sync_dev, sync_dir);
- else
- /* REVISIT: The MMC buffer increments only when MSB is written.
- * Return error for blksz which is non multiple of four.
- */
- return -EINVAL;
+ mmc_omap_config_dma_params(host, data, data->sg);
- omap_start_dma(dma_ch); return 0; }
@@ -763,7 +784,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
host->data = req->data;
if (req->data == NULL) {
- host->datadir = OMAP_MMC_DATADIR_NONE;
OMAP_HSMMC_WRITE(host->base, BLK, 0);
return 0;
}
@@ -772,9 +792,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
| (req->data->blocks << 16));
set_data_timeout(host, req);- host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
- OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
-
if (host->use_dma) {
ret = mmc_omap_start_dma_transfer(host, req);
if (ret != 0) {
@@ -920,7 +937,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
struct mmc_host *mmc;
struct mmc_omap_host *host = NULL;
struct resource *res;
- int ret = 0, irq, reg;
+ int ret = 0, irq;
u32 hctl, capa;
if (pdata == NULL) {
@@ -1014,10 +1031,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else
host->dbclk_enabled = 1;-#ifdef CONFIG_MMC_BLOCK_BOUNCE
- mmc->max_phys_segs = 1;
- mmc->max_hw_segs = 1;
-#endif
+ /* Since we do only SG emulation, we can have as many segs
+ * as we want. */
+ mmc->max_phys_segs = 1024;
+ mmc->max_hw_segs = 1024;
+
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
Signature · 1 line
1.5.4.3
PATCH 9/9 OMAP: HSMMC: SDBP and missing clock enabled fixes
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Wed, 19 Nov 2008 15:49:52 +0200
Disable SDBP at suspend and enable it resume. Make sure fclk is running at suspend before touching HCTL.
---drivers/mmc/host/omap_hsmmc.c | 31 ++++++++++++-------------------
1 files changed, 12 insertions(+), 19 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -897,10 +897,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) | OD);
- if (ios->power_mode == MMC_POWER_OFF)
- mmc_omap_fclk_state(host, OFF);
- else
- mmc_omap_fclk_lazy_disable(host);
+ mmc_omap_fclk_lazy_disable(host);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1207,12 +1204,11 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
if (host) {
- mmc_omap_fclk_state(host, ON);
-
ret = mmc_suspend_host(host->mmc, state);
if (ret == 0) {
host->suspended = 1;+ mmc_omap_fclk_state(host, ON); OMAP_HSMMC_WRITE(host->base, ISE, 0); OMAP_HSMMC_WRITE(host->base, IE, 0);
@@ -1224,19 +1220,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
" level suspend\n"); }
- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
- SDVSCLR;
-
- if (host->id == OMAP_MMC1_DEVID)
- hctl |= SDVS30;
- else
- hctl |= SDVS18;
-
- OMAP_HSMMC_WRITE(host->base, HCTL, hctl);
- OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP);
- }
-
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_omap_fclk_state(host, OFF);
clk_disable(host->iclk);
clk_disable(host->dbclk);
@@ -1256,6 +1241,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
return 0;
if (host) {
+ int i;
if (mmc_omap_fclk_state(host, ON) != 0)
goto clk_en_err;@@ -1270,6 +1256,13 @@ static int omap_mmc_resume(struct platform_device *pdev)
dev_dbg(mmc_dev(host->mmc), "Enabling debounce clk failed\n");
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ for (i = 0; i < 100; i++)
+ if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+ break;
+
if (host->pdata->resume) {
ret = host->pdata->resume(&pdev->dev, host->slot_id);
if (ret)Signature · 1 line
1.5.4.3
Quoted 8 lines · Adrian Hunter
Quoted 4 lines · Adrian Hunter
OMAP HSMMC is not ready for mainline, but development is ongoing.
In the meantime these patches are best posted and applied via omap mailing list and tree.
Just realised I forgot the from lines in all those patches - will resend.
And now 4 and 5 again without the spurious bug references...
PATCH 4/9 OMAP: HSMMC: Fix suspend/resume for MMCHS2
From: Jarkko Lavinen <[email protected]>
Date: Tue, 28 Oct 2008 11:12:28 +0200
For HSMMC2 and HSMMC3 set SDVSS to 1.8V in HCTL before enabling the SDBP bit.
---drivers/mmc/host/omap_hsmmc.c | 19 ++++++++++---------
1 files changed, 10 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -1197,15 +1197,16 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
}
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- & SDVSCLR);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDVS30);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDBP);
+ u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
+ SDVSCLR;
+
+ if (host->id == OMAP_MMC1_DEVID)
+ hctl |= SDVS30;
+ else
+ hctl |= SDVS18;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl);
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP); } mmc_omap_fclk_state(host, OFF);
Signature · 1 line
1.5.4.3
PATCH 5/9 OMAP: HSMMC: Fix oops in omap_mmc_remove
In reply to Adrian HunterFrom: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:29 +0200
Do not access host in omap_mmc_remove() if it is null. This occurs when module is removed before its platform data is set.
---drivers/mmc/host/omap_hsmmc.c | 20 +++++++++++---------
1 files changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device *pdev)
struct resource *res; u16 vdd = 0;
- mmc_omap_fclk_state(host, ON);
- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- /*
- * Set the vdd back to 3V,
- * applicable for dual volt support.
- */
- vdd = fls(host->mmc->ocr_avail) - 1;
- if (omap_mmc_switch_opcond(host, vdd) != 0)
- host->mmc->ios.vdd = vdd;
+ if (host) {
+ mmc_omap_fclk_state(host, ON);
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the vdd back to 3V,
+ * applicable for dual volt support.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }
} res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Signature · 1 line
1.5.4.3
Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove
On Fri, Nov 21, 2008 at 11:58 AM, Adrian Hunter
<[email protected]> wrote:
<snip>
Quoted 1 line
(quoted diff) +0
-0@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device
<snip>
Review
Quoted 10 lines
(quoted diff) +10
-0+ mmc_omap_fclk_state(host, ON); + if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { + /* + * Set the vdd back to 3V, + * applicable for dual volt support. + */ + vdd = fls(host->mmc->ocr_avail) - 1; + if (omap_mmc_switch_opcond(host, vdd) != 0) + host->mmc->ios.vdd = vdd; + }
I wonder what this block is for? Wouldn't simple
host->mmc->ios.vdd = vdd;
be enough? It calls omap_mmc_switch_opcond() which toggles clocks and power without good reason (I think). Clocks will be turned off later in omap_mmc_remove() anyway, and .set_ios will turn off power. Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned omap_mmc_switch_opcond() to be called unconditionally here.
The same block can be found in mmc_omap_detect() function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(
Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove
On Fri, Nov 21, 2008 at 04:03:25PM +0200, ext Grazvydas Ignotas wrote:
...
Quoted 6 lines
Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned
omap_mmc_switch_opcond()to be called unconditionally here.The same block can be found in
mmc_omap_detect()function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(
TI TRM saus HSMMC2 and HSMMC3 works only with SDVS set to 1.8V in HCTL.
If the voltage is set to 3.0V and then SDBP (bus power bit) is set, it is rejected and SDBP remains zero, the controller does nothing and is seemingly frozen.
The wrong voltage for MMC2 SDVS is set in omap_mmc_switch_opcond() if vdd different from 1.8V.
Also omap_mmc_suspend() set SDVS to 3.0V even for MMC2 and MMC3. When suspending, everything seemed to work and at resume MMC2 was hung and failed to detect the card since not even CMD0 was sent.
Regards
Jarkko Lavinen
Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove
Quoted 15 lines · Jarkko Lavinen
On Fri, Nov 21, 2008 at 04:03:25PM +0200, ext Grazvydas Ignotas wrote:
...Quoted 6 lines
Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned
omap_mmc_switch_opcond()to be called unconditionally here.The same block can be found in
mmc_omap_detect()function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(TI TRM saus HSMMC2 and HSMMC3 works only with SDVS set to 1.8V in HCTL.
If the voltage is set to 3.0V and then SDBP (bus power bit) is set, it is rejected and SDBP remains zero, the controller does nothing and is seemingly frozen.
Quoted 6 lines
The wrong voltage for MMC2 SDVS is set in
omap_mmc_switch_opcond()if vdd different from 1.8V.Also
omap_mmc_suspend()set SDVS to 3.0V even for MMC2 and MMC3. When suspending, everything seemed to work and at resume MMC2 was hung and failed to detect the card since not even CMD0 was sent.
ok, but why there are calls to omap_mmc_switch_opcond() from both omap_mmc_remove() and mmc_omap_detect() at all? This basically causes power and clocks to be turned off, then on, then again off whenever card is or host driver removed. This is probably no big deal, but still.