*
* Copyright (C) 2004 Nokia Corporation
* Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
- * Pin multiplexing and Innovator support by Tony Lindgren <tony@atomide.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <asm/arch/mux.h>
#include <asm/arch/fpga.h>
#include <asm/arch/tps65010.h>
+#include <asm/arch/menelaus.h>
#include <asm/hardware/clock.h>
/* Specifies how often in millisecs to poll for card status changes
* when the cover switch is open */
-#define OMAP_MMC_SWITCH_POLL_DELAY 500
+#define OMAP_MMC_SWITCH_POLL_DELAY 500
static int mmc_omap_enable_poll = 1;
struct mmc_omap_host {
- int initialized;
+ int initialized;
int suspended;
struct mmc_request * mrq;
struct mmc_command * cmd;
struct mmc_host * mmc;
struct device * dev;
unsigned char id; /* 16xx chips have 2 MMC blocks */
- struct clk * clk;
+ struct clk * iclk;
+ struct clk * fclk;
void __iomem *base;
int irq;
unsigned char bus_mode;
+ unsigned char hw_bus_mode;
unsigned int sg_len;
int sg_idx;
{
if (host->switch_pin < 0)
return 0;
- return omap_get_gpio_datain(host->switch_pin);
+ return omap_get_gpio_datain(host->switch_pin);
}
static ssize_t
resptype = 0;
cmdtype = 0;
+ /*
+ * On 24xx we may have external MMC transceiver on Menelaus.
+ * In that case we need to manually toggle between open-drain
+ * and push-pull states.
+ */
+ if (omap_has_menelaus() && (host->bus_mode != host->hw_bus_mode)) {
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ menelaus_mmc_opendrain(1);
+ else
+ menelaus_mmc_opendrain(0);
+ host->hw_bus_mode = host->bus_mode;
+ }
+
/* Protocol layer does not provide response type,
* but our hardware needs to know exact type, not just size!
*/
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
cmdreg |= 1 << 15;
- clk_use(host->clk);
+ clk_use(host->fclk);
OMAP_MMC_WRITE(host->base, CTO, 200);
OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff);
}
host->data = NULL;
host->sg_len = 0;
- clk_unuse(host->clk);
+ clk_unuse(host->fclk);
/* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
* dozens of requests until the card finishes writing data.
if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
DBG("MMC%d: End request, err %x\n", host->id, cmd->error);
host->mrq = NULL;
- clk_unuse(host->clk);
+ clk_unuse(host->fclk);
mmc_request_done(host->mmc, cmd->mrq);
}
}
schedule_work(&host->switch_work);
}
+/* FIXME: Handle card insertion and removal properly. Maybe use a mask
+ * for MMC state? */
+static void mmc_omap_switch_callback(unsigned long data, u8 mmc_mask)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ if (machine_is_omap_h4()) {
+ if (mmc_mask & 0x1)
+ printk("XXX card in slot 1\n");
+ if (mmc_mask & 0x2)
+ printk("XXX card in slot 2\n");
+ } else {
+ /* Assume card detect connected to cover switch */
+ if (mmc_mask & 0x2)
+ printk("XXX cover open\n");
+ else
+ printk("XXX cover closed\n");
+ }
+}
+
static void mmc_omap_switch_handler(void *data)
{
struct mmc_omap_host *host = (struct mmc_omap_host *) data;
else if (machine_is_omap_h3())
/* GPIO 4 of TPS65010 sends SD_EN signal */
tps65010_set_gpio_out_value(GPIO4, HIGH);
- else
+ else if (cpu_is_omap24xx()) {
+ u16 reg = OMAP_MMC_READ(host->base, CON);
+ OMAP_MMC_WRITE(host->base, CON, reg | (1 << 11));
+ } else
if (host->power_pin >= 0)
omap_set_gpio_dataout(host->power_pin, 1);
} else {
tps65010_set_gpio_out_value(GPIO3, LOW);
else if (machine_is_omap_h3())
tps65010_set_gpio_out_value(GPIO4, LOW);
- else
+ else if (cpu_is_omap24xx()) {
+ u16 reg = OMAP_MMC_READ(host->base, CON);
+ OMAP_MMC_WRITE(host->base, CON, reg & ~(1 << 11));
+ } else
if (host->power_pin >= 0)
omap_set_gpio_dataout(host->power_pin, 0);
}
host->id, ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd / 100, ios->vdd % 100);
- if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000) {
- /* Fix for broken stack */
- realclock = 400000;
- } else {
+ if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000)
+ realclock = 400000; /* Fix for broken stack */
+ else
realclock = ios->clock;
- }
- if (ios->clock == 0) {
+ if (ios->clock == 0)
dsor = 0;
- } else {
- int func_clk_rate = clk_get_rate(host->clk);
+ else {
+ int func_clk_rate = clk_get_rate(host->fclk);
dsor = func_clk_rate / realclock;
if (dsor < 1)
}
host->bus_mode = ios->bus_mode;
- clk_use(host->clk);
+ if (omap_has_menelaus()) {
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ menelaus_mmc_opendrain(1);
+ else
+ menelaus_mmc_opendrain(0);
+ }
+ host->hw_bus_mode = host->bus_mode;
+
+ clk_use(host->fclk);
+
/* On insanely high arm_per frequencies something sometimes
* goes somehow out of sync, and the POW bit is not being set,
* which results in the while loop below getting stuck.
while (0 == (OMAP_MMC_READ(host->base, STAT) & 1));
OMAP_MMC_WRITE(host->base, STAT, 1);
}
- clk_unuse(host->clk);
+ clk_unuse(host->fclk);
}
static struct mmc_host_ops mmc_omap_ops = {
host->dma_timer.data = (unsigned long) host;
host->id = pdev->id;
- host->clk = clk_get(dev, (host->id == 1) ? "mmc1_ck" : "mmc2_ck");
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
+
+ if (cpu_is_omap24xx()) {
+ host->iclk = clk_get(dev, "mmc_ick");
+ if (IS_ERR(host->iclk))
+ goto out;
+ clk_use(host->iclk);
+ }
+
+ if (!cpu_is_omap24xx())
+ host->fclk = clk_get(dev,
+ (host->id == 1) ? "mmc1_ck" : "mmc2_ck");
+ else
+ host->fclk = clk_get(dev, "mmc_fck");
+
+ if (IS_ERR(host->fclk)) {
+ ret = PTR_ERR(host->fclk);
goto out;
}
host->switch_pin = minfo->switch_pin;
host->wp_pin = minfo->wp_pin;
- host->use_dma = 1;
+ /* FIXME: Remove once DMA works on 24xx */
+ if (cpu_is_omap24xx())
+ host->use_dma = 0;
+ else
+ host->use_dma = 1;
host->dma_ch = -1;
host->irq = pdev->resource[1].start;
if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host))
schedule_work(&host->switch_work);
}
+
+ /* FIXME: Add callback function for card detection */
+ printk("XXX host: 0x%08x\n", &host);
+ if (omap_has_menelaus())
+ menelaus_mmc_register(mmc_omap_switch_callback, &host);
+
no_switch:
return 0;
+
out:
/* FIXME: Free other resources too. */
if (host) {
- if (host->clk && !IS_ERR(host->clk))
- clk_put(host->clk);
+ if (host->iclk && !IS_ERR(host->iclk))
+ clk_put(host->iclk);
+ if (host->fclk && !IS_ERR(host->fclk))
+ clk_put(host->fclk);
mmc_free_host(host->mmc);
}
return ret;
del_timer_sync(&host->switch_timer);
flush_scheduled_work();
}
- if (host->clk && !IS_ERR(host->clk))
- clk_put(host->clk);
+ if (host->iclk && !IS_ERR(host->iclk))
+ clk_put(host->iclk);
+ if (host->fclk && !IS_ERR(host->fclk))
+ clk_put(host->fclk);
mmc_free_host(host->mmc);
}
+ if (omap_has_menelaus())
+ menelaus_mmc_remove();
+
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);