All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Juha Yrjölä" <juha.yrjola@solidboot.com>
To: linux-omap-open-source@linux.omap.com
Subject: [PATCH] ARM: OMAP: Implement support for two MMC slots
Date: Mon, 19 Jun 2006 19:52:02 +0300	[thread overview]
Message-ID: <20060619165202.GA30055@mail.solidboot.com> (raw)

Hi,

Here's a patch for review.  It is by no means complete yet, but
at least supporting two slots connected to Menelaus does work.

I'd also appreciate patches implementing support for
omap_mmc_platform_data for the various board-*.c files 

Cheers,
Juha

diff-tree 464698d02cea1aae47d88a4fd99fa45f090473f9 (from 6ec81c783e6fe46d02ab2b6d9924ab0788a55e26)
Author: Juha Yrjola <juha.yrjola@solidboot.com>
Date:   Mon Jun 19 19:52:52 2006 +0300

    ARM: OMAP: Implement support for two MMC slots
    
    On OMAP2 boards that use Menelaus, two MMC/SD cards can be connected
    through one set of pins from OMAP to two different slots behind Menelaus.
    Switching which slot is to be accessed is done using a GPIO line.
    
    The MMC driver is now also closer to the way other ARM platforms (e.g. PXA)
    handles board-specific MMC data.  It uses function pointers for powering
    the card and checking the state of the SD read-only switch, among other
    things.
    
    Signed-off-by: Juha Yrjola <juha.yrjola@solidboot.com>

diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c
index 8bff566..882203e 100644
--- a/arch/arm/plat-omap/devices.c
+++ b/arch/arm/plat-omap/devices.c
@@ -25,6 +25,7 @@ #include <asm/arch/board.h>
 #include <asm/arch/mux.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/menelaus.h>
+#include <asm/arch/mmc.h>
 
 #if 	defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE)
 
@@ -156,7 +157,7 @@ #define OMAP_MMC1_INT		INT_MMC
 #endif
 #define	OMAP_MMC2_BASE		0xfffb7c00	/* omap16xx only */
 
-static struct omap_mmc_conf mmc1_conf;
+static struct omap_mmc_platform_data mmc1_conf;
 
 static u64 mmc1_dmamask = 0xffffffff;
 
@@ -185,7 +186,7 @@ static struct platform_device mmc_omap_d
 
 #ifdef	CONFIG_ARCH_OMAP16XX
 
-static struct omap_mmc_conf mmc2_conf;
+static struct omap_mmc_platform_data mmc2_conf;
 
 static u64 mmc2_dmamask = 0xffffffff;
 
@@ -211,21 +212,16 @@ static struct platform_device mmc_omap_d
 	.num_resources	= ARRAY_SIZE(mmc2_resources),
 	.resource	= mmc2_resources,
 };
+
 #endif
 
 static void __init omap_init_mmc(void)
 {
-	const struct omap_mmc_config	*mmc_conf;
-	const struct omap_mmc_conf	*mmc;
-
-	/* NOTE:  assumes MMC was never (wrongly) enabled */
-	mmc_conf = omap_get_config(OMAP_TAG_MMC, struct omap_mmc_config);
-	if (!mmc_conf)
-		return;
+	struct omap_mmc_platform_data *conf;
 
 	/* block 1 is always available and has just one pinout option */
-	mmc = &mmc_conf->mmc[0];
-	if (mmc->enabled) {
+	conf = &mmc1_conf;
+	if (conf->enabled) {
 		if (!cpu_is_omap24xx()) {
 			omap_cfg_reg(MMC_CMD);
 			omap_cfg_reg(MMC_CLK);
@@ -236,29 +232,28 @@ static void __init omap_init_mmc(void)
 				omap_cfg_reg(P20_1710_MMC_DATDIR0);
 			}
 		}
-		if (mmc->wire4) {
+		if (conf->wire4) {
 			if (!cpu_is_omap24xx()) {
 				omap_cfg_reg(MMC_DAT1);
 				/* NOTE:  DAT2 can be on W10 (here) or M15 */
-				if (!mmc->nomux)
+				if (!conf->nomux)
 					omap_cfg_reg(MMC_DAT2);
 				omap_cfg_reg(MMC_DAT3);
 			}
 		}
-		mmc1_conf = *mmc;
 		(void) platform_device_register(&mmc_omap_device1);
 	}
 
 #ifdef	CONFIG_ARCH_OMAP16XX
 	/* block 2 is on newer chips, and has many pinout options */
-	mmc = &mmc_conf->mmc[1];
-	if (mmc->enabled) {
-		if (!mmc->nomux) {
+	conf = &mmc2_conf;
+	if (conf->enabled) {
+		if (!conf->nomux) {
 			omap_cfg_reg(Y8_1610_MMC2_CMD);
 			omap_cfg_reg(Y10_1610_MMC2_CLK);
 			omap_cfg_reg(R18_1610_MMC2_CLKIN);
 			omap_cfg_reg(W8_1610_MMC2_DAT0);
-			if (mmc->wire4) {
+			if (conf->wire4) {
 				omap_cfg_reg(V8_1610_MMC2_DAT1);
 				omap_cfg_reg(W15_1610_MMC2_DAT2);
 				omap_cfg_reg(R10_1610_MMC2_DAT3);
@@ -274,14 +269,33 @@ #ifdef	CONFIG_ARCH_OMAP16XX
 		if (cpu_is_omap1710())
 			omap_writel(omap_readl(MOD_CONF_CTRL_1) | (1 << 24),
 				     MOD_CONF_CTRL_1);
-		mmc2_conf = *mmc;
 		(void) platform_device_register(&mmc_omap_device2);
 	}
 #endif
 	return;
 }
+
+void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info)
+{
+	switch (host) {
+	case 1:
+		mmc1_conf = *info;
+		break;
+#ifdef	CONFIG_ARCH_OMAP16XX
+	case 2:
+		mmc2_conf = *info;
+		break;
+#endif
+	default:
+		BUG();
+	}
+}
+
 #else
 static inline void omap_init_mmc(void) {}
+void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info)
+{
+}
 #endif
 
 /*-------------------------------------------------------------------------*/
diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c
index ab2ab45..10d0b32 100644
--- a/drivers/mmc/omap.c
+++ b/drivers/mmc/omap.c
@@ -32,7 +32,7 @@ #include <asm/irq.h>
 #include <asm/scatterlist.h>
 #include <asm/mach-types.h>
 
-#include <asm/arch/board.h>
+#include <asm/arch/mmc.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/dma.h>
 #include <asm/arch/mux.h>
@@ -98,7 +98,25 @@ #define RSP_TYPE(x)	((x) & ~(MMC_RSP_BUS
  * when the cover switch is open */
 #define OMAP_MMC_SWITCH_POLL_DELAY	500
 
-static int mmc_omap_enable_poll = 1;
+struct mmc_omap_host;
+
+struct mmc_omap_slot {
+	int			id;
+	unsigned int		vdd;
+	u16			saved_con;
+	u16			bus_mode;
+	unsigned		powered:1;
+
+	struct work_struct	cover_work;
+	struct timer_list	cover_timer;
+	int			cover_last_state;
+	int			enable_poll;
+
+	struct mmc_request *	mrq;
+	struct mmc_omap_host *  host;
+	struct mmc_host *	mmc;
+	struct omap_mmc_slot_data *pdata;
+};
 
 struct mmc_omap_host {
 	int			initialized;
@@ -111,12 +129,10 @@ struct mmc_omap_host {
 	unsigned char		id; /* 16xx chips have 2 MMC blocks */
 	struct clk *		iclk;
 	struct clk *		fclk;
-	struct resource		*mem_res;
-	void __iomem		*virt_base;
+	struct resource *	mem_res;
+	void __iomem *		virt_base;
 	unsigned int		phys_base;
 	int			irq;
-	unsigned char		bus_mode;
-	unsigned char		hw_bus_mode;
 
 	unsigned int		sg_len;
 	int			sg_idx;
@@ -133,23 +149,87 @@ struct mmc_omap_host {
 	struct timer_list	dma_timer;
 	unsigned		dma_len;
 
-	short			power_pin;
-	short			wp_pin;
+	struct mmc_omap_slot *	slots[OMAP_MMC_MAX_SLOTS];
+	struct mmc_omap_slot *	current_slot;
+	spinlock_t		slot_lock;
+	wait_queue_head_t	slot_wq;
+	int			nr_slots;
 
-	int			switch_pin;
-	struct work_struct	switch_work;
-	struct timer_list	switch_timer;
-	int			switch_last_state;
+        struct omap_mmc_platform_data *pdata;
 };
 
+static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
+{
+	struct mmc_omap_host *host = slot->host;
+	unsigned long flags;
+
+	if (claimed)
+		goto no_claim;
+	spin_lock_irqsave(&host->slot_lock, flags);
+	while (host->mmc != NULL) {
+		spin_unlock_irqrestore(&host->slot_lock, flags);
+		wait_event(host->slot_wq, host->mmc == NULL);
+		spin_lock_irqsave(&host->slot_lock, flags);
+	}
+	host->mmc = slot->mmc;
+	spin_unlock_irqrestore(&host->slot_lock, flags);
+no_claim:
+	clk_enable(host->fclk);
+	if (host->current_slot != slot) {
+		if (host->pdata->switch_slot != NULL)
+			host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id);
+		host->current_slot = slot;
+	}
+	OMAP_MMC_WRITE(host, CON, slot->saved_con);
+}
+
+static void mmc_omap_start_request(struct mmc_omap_host *host,
+				   struct mmc_request *req);
+
+static void mmc_omap_release_slot(struct mmc_omap_slot *slot)
+{
+	struct mmc_omap_host *host = slot->host;
+	unsigned long flags;
+	int i;
+
+	BUG_ON(slot == NULL || host->mmc == NULL);
+	clk_disable(host->fclk);
+
+	spin_lock_irqsave(&host->slot_lock, flags);
+	/* Check for any pending requests */
+	for (i = 0; i < host->nr_slots; i++) {
+		struct mmc_omap_slot *new_slot;
+		struct mmc_request *rq;
+
+		if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
+			continue;
+
+		new_slot = host->slots[i];
+		/* The current slot should not have a request in queue */
+		BUG_ON(new_slot == host->current_slot);
+
+		host->mmc = new_slot->mmc;
+		spin_unlock_irqrestore(&host->slot_lock, flags);
+		mmc_omap_select_slot(new_slot, 1);
+		rq = new_slot->mrq;
+		new_slot->mrq = NULL;
+		mmc_omap_start_request(host, rq);
+
+		return;
+	}
+
+	host->mmc = NULL;
+	wake_up(&host->slot_wq);
+	spin_unlock_irqrestore(&host->slot_lock, flags);
+}
+
 static inline int
-mmc_omap_cover_is_open(struct mmc_omap_host *host)
+mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
 {
-	if (host->switch_pin < 0)
-		return 0;
-	return omap_get_gpio_datain(host->switch_pin);
+	return slot->pdata->get_cover_state(mmc_dev(slot->mmc), slot->id);
 }
 
+#if 0
 static ssize_t
 mmc_omap_show_cover_switch(struct device *dev,
 	struct device_attribute *attr, char *buf)
@@ -191,6 +271,7 @@ mmc_omap_store_enable_poll(struct device
 
 static DEVICE_ATTR(enable_poll, 0664,
 		   mmc_omap_show_enable_poll, mmc_omap_store_enable_poll);
+#endif
 
 static void
 mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
@@ -232,7 +313,7 @@ mmc_omap_start_command(struct mmc_omap_h
 
 	cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
 
-	if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+	if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN)
 		cmdreg |= 1 << 6;
 
 	if (cmd->flags & MMC_RSP_BUSY)
@@ -241,8 +322,6 @@ mmc_omap_start_command(struct mmc_omap_h
 	if (host->data && !(host->data->flags & MMC_DATA_WRITE))
 		cmdreg |= 1 << 15;
 
-	clk_enable(host->fclk);
-
 	OMAP_MMC_WRITE(host, CTO, 200);
 	OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
 	OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
@@ -275,7 +354,6 @@ mmc_omap_xfer_done(struct mmc_omap_host 
 	}
 	host->data = NULL;
 	host->sg_len = 0;
-	clk_disable(host->fclk);
 
 	/* NOTE:  MMC layer will sometimes poll-wait CMD13 next, issuing
 	 * dozens of requests until the card finishes writing data.
@@ -283,8 +361,12 @@ mmc_omap_xfer_done(struct mmc_omap_host 
 	 */
 
 	if (!data->stop) {
+		struct mmc_host *mmc;
+
 		host->mrq = NULL;
-		mmc_request_done(host->mmc, data->mrq);
+		mmc = host->mmc;
+		mmc_omap_release_slot(host->current_slot);
+		mmc_request_done(mmc, data->mrq);
 		return;
 	}
 
@@ -368,9 +450,12 @@ mmc_omap_cmd_done(struct mmc_omap_host *
 	}
 
 	if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
+		struct mmc_host *mmc;
+
 		host->mrq = NULL;
-		clk_disable(host->fclk);
-		mmc_request_done(host->mmc, cmd->mrq);
+		mmc = host->mmc;
+		mmc_omap_release_slot(host->current_slot);
+		mmc_request_done(mmc, cmd->mrq);
 	}
 }
 
@@ -495,11 +580,8 @@ #endif
 			/* Timeouts are routine with some commands */
 			if (host->cmd) {
 				if (host->cmd->opcode != MMC_ALL_SEND_CID &&
-						host->cmd->opcode !=
-						MMC_SEND_OP_COND &&
-						host->cmd->opcode !=
-						MMC_APP_CMD &&
-						!mmc_omap_cover_is_open(host))
+				    host->cmd->opcode != MMC_SEND_OP_COND &&
+				    host->cmd->opcode != MMC_APP_CMD)
 					dev_err(mmc_dev(host->mmc),
 						"command timeout, CMD %d\n",
 						host->cmd->opcode);
@@ -566,6 +648,7 @@ #endif
 	return IRQ_HANDLED;
 }
 
+#ifdef XXX
 static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id;
@@ -620,6 +703,8 @@ static void mmc_omap_switch_handler(void
 	}
 }
 
+#endif
+
 /* Prepare to transfer the next segment of a scatterlist */
 static void
 mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
@@ -879,115 +964,109 @@ mmc_omap_prepare_data(struct mmc_omap_ho
 	}
 }
 
-static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
+static void mmc_omap_start_request(struct mmc_omap_host *host,
+				   struct mmc_request *req)
 {
-	struct mmc_omap_host *host = mmc_priv(mmc);
-
-	WARN_ON(host->mrq != NULL);
+	BUG_ON(host->mrq != NULL);
 
 	host->mrq = req;
-
 	/* only touch fifo AFTER the controller readies it */
 	mmc_omap_prepare_data(host, req);
 	mmc_omap_start_command(host, req->cmd);
 	if (host->dma_in_use)
 		omap_start_dma(host->dma_ch);
+	BUG_ON(irqs_disabled());
 }
 
-static void innovator_fpga_socket_power(int on)
+static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
 {
-#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX)
-	if (on) {
-		fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3),
-		     OMAP1510_FPGA_POWER);
-	} else {
-		fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3),
-		     OMAP1510_FPGA_POWER);
-	}
-#endif
+	struct mmc_omap_slot *slot = mmc_priv(mmc);
+	struct mmc_omap_host *host = slot->host;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->slot_lock, flags);
+	if (host->mmc != NULL) {
+		BUG_ON(slot->mrq != NULL);
+		slot->mrq = req;
+		spin_unlock_irqrestore(&host->slot_lock, flags);
+		return;
+	} else
+		host->mmc = mmc;
+	spin_unlock_irqrestore(&host->slot_lock, flags);
+	mmc_omap_select_slot(slot, 1);
+	mmc_omap_start_request(host, req);
 }
 
-/*
- * Turn the socket power on/off. Innovator uses FPGA, most boards
- * probably use GPIO.
- */
-static void mmc_omap_power(struct mmc_omap_host *host, int on)
+static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on, int vdd)
 {
-	if (on) {
-		if (machine_is_omap_innovator())
-			innovator_fpga_socket_power(1);
-		else if (machine_is_omap_h2())
-			tps65010_set_gpio_out_value(GPIO3, HIGH);
-		else if (machine_is_omap_h3())
-			/* GPIO 4 of TPS65010 sends SD_EN signal */
-			tps65010_set_gpio_out_value(GPIO4, HIGH);
-		else if (cpu_is_omap24xx()) {
-			u16 reg = OMAP_MMC_READ(host, CON);
-			OMAP_MMC_WRITE(host, CON, reg | (1 << 11));
-		} else
-			if (host->power_pin >= 0)
-				omap_set_gpio_dataout(host->power_pin, 1);
-		msleep(1);
-	} else {
-		if (machine_is_omap_innovator())
-			innovator_fpga_socket_power(0);
-		else if (machine_is_omap_h2())
-			tps65010_set_gpio_out_value(GPIO3, LOW);
-		else if (machine_is_omap_h3())
-			tps65010_set_gpio_out_value(GPIO4, LOW);
-		else if (cpu_is_omap24xx()) {
-			u16 reg = OMAP_MMC_READ(host, CON);
-			OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11));
-		} else
-			if (host->power_pin >= 0)
-				omap_set_gpio_dataout(host->power_pin, 0);
+	struct mmc_omap_host *host;
+
+	host = slot->host;
+
+	if (slot->pdata->set_power != NULL)
+		slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on, vdd);
+	if (cpu_is_omap24xx()) {
+		u16 w;
+
+		if (power_on) {
+			w = OMAP_MMC_READ(host, CON);
+			OMAP_MMC_WRITE(host, CON, w | (1 << 11));
+		} else {
+			w = OMAP_MMC_READ(host, CON);
+			OMAP_MMC_WRITE(host, CON, w & ~(1 << 11));
+		}
 	}
 }
 
 static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
-	struct mmc_omap_host *host = mmc_priv(mmc);
-	int fclk_rate;
-	int dsor;
-	int freq, i;
+	struct mmc_omap_slot *slot = mmc_priv(mmc);
+	struct mmc_omap_host *host = slot->host;
+	int dsor, power_on;
+	int i;
 
-	freq = ios->clock;
+	if (ios->clock != 0) {
+		int freq, fclk_rate;
 
-	/* At least on OMAP2420, the divisor must be != 0 for the
-	 * initialization sequence to complete successfully. */
-	if (freq == 0)
-		freq = 4000000;
+		freq = ios->clock;
 
-	fclk_rate = clk_get_rate(host->fclk);
-	dsor = fclk_rate / freq;
-	if (dsor < 1)
-		dsor = 1;
+		fclk_rate = clk_get_rate(host->fclk);
+		dsor = fclk_rate / freq;
+		if (dsor < 1)
+			dsor = 1;
 
-	if (fclk_rate / dsor > freq)
-		dsor++;
+		if (fclk_rate / dsor > freq)
+			dsor++;
 
-	if (dsor > 250)
-		dsor = 250;
-	dsor++;
+		if (dsor > 250)
+			dsor = 250;
+	} else
+		dsor = 0;
 
 	if (ios->bus_width == MMC_BUS_WIDTH_4)
 		dsor |= 1 << 15;
 
-	switch (ios->power_mode) {
-	case MMC_POWER_OFF:
-		mmc_omap_power(host, 0);
-		break;
-	case MMC_POWER_UP:
-	case MMC_POWER_ON:
-		mmc_omap_power(host, 1);
-		dsor |= 1 << 11;
-		break;
+	if (ios->power_mode != MMC_POWER_OFF)
+		power_on = 1;
+	else
+		power_on = 0;
+
+	mmc_omap_select_slot(slot, 0);
+	if (slot->powered != power_on || ios->vdd != slot->vdd) {
+		mmc_omap_set_power(slot, power_on, ios->vdd);
+		slot->vdd = ios->vdd;
+		slot->powered = power_on;
 	}
 
-	host->bus_mode = ios->bus_mode;
-	host->hw_bus_mode = host->bus_mode;
+	if (power_on)
+		dsor |= 1 << 11;
 
-	clk_enable(host->fclk);
+	slot->bus_mode = ios->bus_mode;
+	if (slot->bus_mode != ios->bus_mode) {
+		if (slot->pdata->set_bus_mode != NULL)
+			slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id, ios->bus_mode);
+		slot->bus_mode = ios->bus_mode;
+	}
 
 	/* On insanely high arm_per frequencies something sometimes
 	 * goes somehow out of sync, and the POW bit is not being set,
@@ -995,23 +1074,25 @@ static void mmc_omap_set_ios(struct mmc_
 	 * Writing to the CON register twice seems to do the trick. */
 	for (i = 0; i < 2; i++)
 		OMAP_MMC_WRITE(host, CON, dsor);
-	if (ios->power_mode == MMC_POWER_UP) {
+	slot->saved_con = dsor;
+	if (ios->power_mode == MMC_POWER_ON) {
 		/* Send clock cycles, poll completion */
 		OMAP_MMC_WRITE(host, IE, 0);
 		OMAP_MMC_WRITE(host, STAT, 0xffff);
 		OMAP_MMC_WRITE(host, CMD, 1 << 7);
-		printk("CMD %04x\n", OMAP_MMC_READ(host, CMD));
 		while ((OMAP_MMC_READ(host, STAT) & 1) == 0);
 		OMAP_MMC_WRITE(host, STAT, 1);
 	}
-	clk_disable(host->fclk);
+	mmc_omap_release_slot(slot);
 }
 
 static int mmc_omap_get_ro(struct mmc_host *mmc)
 {
-	struct mmc_omap_host *host = mmc_priv(mmc);
+	struct mmc_omap_slot *slot = mmc_priv(mmc);
 
-	return host->wp_pin && omap_get_gpio_datain(host->wp_pin);
+	if (slot->pdata->get_ro != NULL)
+		return slot->pdata->get_ro(mmc_dev(mmc), slot->id);
+	return 0;
 }
 
 static struct mmc_host_ops mmc_omap_ops = {
@@ -1020,19 +1101,62 @@ static struct mmc_host_ops mmc_omap_ops 
 	.get_ro		= mmc_omap_get_ro,
 };
 
-static int __init mmc_omap_probe(struct platform_device *pdev)
+static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 {
-	struct omap_mmc_conf *minfo = pdev->dev.platform_data;
+	struct mmc_omap_slot *slot;
 	struct mmc_host *mmc;
+
+	mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev);
+	if (mmc == NULL)
+		return -ENOMEM;
+	slot = mmc_priv(mmc);
+
+	slot->host = host;
+	slot->mmc = mmc;
+	slot->id = id;
+	slot->pdata = &host->pdata->slots[id];
+
+	host->slots[id] = slot;
+
+	if (host->pdata->wire4)
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	mmc->ops = &mmc_omap_ops;
+	mmc->f_min = 400000;
+	if (cpu_class_is_omap2())
+		mmc->f_max = 48000000;
+	else
+		mmc->f_max = 24000000;
+	mmc->ocr_avail = slot->pdata->ocr_mask;
+
+	/* Use scatterlist DMA to reduce per-transfer costs.
+	 * NOTE max_seg_size assumption that small blocks aren't
+	 * normally used (except e.g. for reading SD registers).
+	 */
+	mmc->max_phys_segs = 32;
+	mmc->max_hw_segs = 32;
+	mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */
+	mmc->max_seg_size = mmc->max_sectors * 512;
+
+	return mmc_add_host(mmc);
+}
+
+static int __init mmc_omap_probe(struct platform_device *pdev)
+{
+	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
-	int ret = 0;
+	int ret = 0, i;
 	int irq;
 
-	if (minfo == NULL) {
+	if (pdata == NULL) {
 		dev_err(&pdev->dev, "platform data missing\n");
 		return -ENXIO;
 	}
+	if (pdata->nr_slots == 0) {
+		dev_err(&pdev->dev, "no slots\n");
+		return -ENXIO;
+	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	irq = platform_get_irq(pdev, 0);
@@ -1044,29 +1168,39 @@ static int __init mmc_omap_probe(struct 
 	if (res == NULL)
 		return -EBUSY;
 
-	mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
-	if (mmc == NULL) {
+	host = kzalloc(sizeof(*host), GFP_KERNEL);
+	if (host == NULL) {
 		ret = -ENOMEM;
 		goto err_free_mem_region;
 	}
 
-	host = mmc_priv(mmc);
-	host->mmc = mmc;
-
 	spin_lock_init(&host->dma_lock);
 	init_timer(&host->dma_timer);
+	spin_lock_init(&host->slot_lock);
+	init_waitqueue_head(&host->slot_wq);
 
 	host->dma_timer.function = mmc_omap_dma_timer;
 	host->dma_timer.data = (unsigned long) host;
 
+	host->pdata = pdata;
+	host->dev = &pdev->dev;
+	platform_set_drvdata(pdev, host);
+
 	host->id = pdev->id;
 	host->mem_res = res;
 	host->irq = irq;
 
+	host->use_dma = 1;
+	host->dma_ch = -1;
+
+	host->irq = irq;
+	host->phys_base = host->mem_res->start;
+	host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base);
+
 	if (cpu_is_omap24xx()) {
 		host->iclk = clk_get(&pdev->dev, "mmc_ick");
 		if (IS_ERR(host->iclk))
-			goto err_free_mmc_host;
+			goto err_free_host;
 		clk_enable(host->iclk);
 	}
 
@@ -1079,106 +1213,34 @@ static int __init mmc_omap_probe(struct 
 		goto err_free_iclk;
 	}
 
-	/* REVISIT:
-	 * Also, use minfo->cover to decide how to manage
-	 * the card detect sensing.
-	 */
-	host->power_pin = minfo->power_pin;
-	host->switch_pin = minfo->switch_pin;
-	host->wp_pin = minfo->wp_pin;
-	host->use_dma = 1;
-	host->dma_ch = -1;
-
-	host->irq = irq;
-	host->phys_base = host->mem_res->start;
-	host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base);
-
-	if (minfo->wire4)
-		 mmc->caps |= MMC_CAP_4_BIT_DATA;
-
-	mmc->ops = &mmc_omap_ops;
-	mmc->f_min = 400000;
-	mmc->f_max = 24000000;
-	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-
-	/* Use scatterlist DMA to reduce per-transfer costs.
-	 * NOTE max_seg_size assumption that small blocks aren't
-	 * normally used (except e.g. for reading SD registers).
-	 */
-	mmc->max_phys_segs = 32;
-	mmc->max_hw_segs = 32;
-	mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */
-	mmc->max_seg_size = mmc->max_sectors * 512;
-
-	if (host->power_pin >= 0) {
-		if ((ret = omap_request_gpio(host->power_pin)) != 0) {
-			dev_err(mmc_dev(host->mmc),
-				"Unable to get GPIO pin for MMC power\n");
-			goto err_free_fclk;
-		}
-		omap_set_gpio_direction(host->power_pin, 0);
-	}
-
 	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
 	if (ret)
-		goto err_free_power_gpio;
-
-	host->dev = &pdev->dev;
-	platform_set_drvdata(pdev, host);
+		goto err_free_fclk;
 
-	if (host->switch_pin >= 0) {
-		INIT_WORK(&host->switch_work, mmc_omap_switch_handler, host);
-		init_timer(&host->switch_timer);
-		host->switch_timer.function = mmc_omap_switch_timer;
-		host->switch_timer.data = (unsigned long) host;
-		if (omap_request_gpio(host->switch_pin) != 0) {
-			dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n");
-			host->switch_pin = -1;
-			goto no_switch;
-		}
+	if (pdata->init != NULL) {
+		ret = pdata->init(&pdev->dev);
+		if (ret < 0)
+			goto err_free_irq;
+	}
 
-		omap_set_gpio_direction(host->switch_pin, 1);
-		ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin),
-				  mmc_omap_switch_irq, SA_TRIGGER_RISING, DRIVER_NAME, host);
-		if (ret) {
-			dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n");
-			omap_free_gpio(host->switch_pin);
-			host->switch_pin = -1;
-			goto no_switch;
-		}
-		ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
-		if (ret == 0) {
-			ret = device_create_file(&pdev->dev, &dev_attr_enable_poll);
-			if (ret != 0)
-				device_remove_file(&pdev->dev, &dev_attr_cover_switch);
-		}
-		if (ret) {
-			dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n");
-			free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
-			omap_free_gpio(host->switch_pin);
-			host->switch_pin = -1;
-			goto no_switch;
+	host->nr_slots = pdata->nr_slots;
+	for (i = 0; i < pdata->nr_slots; i++) {
+		ret = mmc_omap_new_slot(host, i);
+		if (ret < 0) {
+			while (--i >= 0) {
+				mmc_remove_host(host->slots[i]->mmc);
+				mmc_free_host(host->slots[i]->mmc);
+			}
+			goto err_plat_cleanup;
 		}
-		if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host))
-			schedule_work(&host->switch_work);
 	}
-
-	mmc_add_host(mmc);
-
-no_switch:
 	return 0;
-	/* FIXME: Free other resources too. */
-	if (host) {
-		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);
-	}
 
-err_free_power_gpio:
-	if (host->power_pin >= 0)
-		omap_free_gpio(host->power_pin);
+err_plat_cleanup:
+	if (pdata->cleanup)
+		pdata->cleanup(&pdev->dev);
+err_free_irq:
+	free_irq(host->irq, host);
 err_free_fclk:
 	clk_put(host->fclk);
 err_free_iclk:
@@ -1186,35 +1248,30 @@ err_free_iclk:
 		clk_disable(host->iclk);
 		clk_put(host->iclk);
 	}
-err_free_mmc_host:
-	mmc_free_host(host->mmc);
+err_free_host:
+	kfree(host);
 err_free_mem_region:
 	release_mem_region(res->start, res->end - res->start + 1);
+
 	return ret;
 }
 
 static int mmc_omap_remove(struct platform_device *pdev)
 {
 	struct mmc_omap_host *host = platform_get_drvdata(pdev);
+	int i;
 
 	platform_set_drvdata(pdev, NULL);
-
 	BUG_ON(host == NULL);
 
-	mmc_remove_host(host->mmc);
-	free_irq(host->irq, host);
-
-	if (host->power_pin >= 0)
-		omap_free_gpio(host->power_pin);
-	if (host->switch_pin >= 0) {
-		device_remove_file(&pdev->dev, &dev_attr_enable_poll);
-		device_remove_file(&pdev->dev, &dev_attr_cover_switch);
-		free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
-		omap_free_gpio(host->switch_pin);
-		host->switch_pin = -1;
-		del_timer_sync(&host->switch_timer);
-		flush_scheduled_work();
+	for (i = 0; i < host->nr_slots; i++) {
+		mmc_remove_host(host->slots[i]->mmc);
+		mmc_free_host(host->slots[i]->mmc);
 	}
+
+	if (host->pdata->cleanup)
+		host->pdata->cleanup(&pdev->dev);
+
 	if (host->iclk && !IS_ERR(host->iclk))
 		clk_put(host->iclk);
 	if (host->fclk && !IS_ERR(host->fclk))
@@ -1223,7 +1280,7 @@ static int mmc_omap_remove(struct platfo
 	release_mem_region(pdev->resource[0].start,
 			   pdev->resource[0].end - pdev->resource[0].start + 1);
 
-	mmc_free_host(host->mmc);
+	kfree(host);
 
 	return 0;
 }
diff --git a/include/asm-arm/arch-omap/mmc.h b/include/asm-arm/arch-omap/mmc.h
new file mode 100644
index 0000000..780659a
--- /dev/null
+++ b/include/asm-arm/arch-omap/mmc.h
@@ -0,0 +1,65 @@
+/*
+ * MMC definitions for OMAP2
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ *
+ * 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __OMAP2_MMC_H
+#define __OMAP2_MMC_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
+
+#define OMAP_MMC_MAX_SLOTS	2
+
+struct omap_mmc_platform_data {
+	unsigned enabled:1;
+	/* number of slots on board */
+	unsigned nr_slots:2;
+	/* nomux means "standard" muxing is wrong on this board, and that
+	 * board-specific code handled it before common init logic.
+	 */
+	unsigned nomux:1;
+	/* 4 wire signaling is optional, and is only used for SD/SDIO and
+	 * MMCv4 */
+	unsigned wire4:1;
+
+	/* switch the bus to a new slot */
+	int (* switch_slot)(struct device *dev, int slot);
+	/* initialize board-specific MMC functionality, can be NULL if
+	 * not supported */
+	int (* init)(struct device *dev);
+	void (* cleanup)(struct device *dev);
+
+	struct omap_mmc_slot_data {
+		int (* set_bus_mode)(struct device *dev, int slot, int bus_mode);
+		int (* set_power)(struct device *dev, int slot, int power_on, int vdd);
+		int (* get_ro)(struct device *dev, int slot);
+
+		/* return MMC cover switch state, can be NULL if not supported.
+		 *
+		 * possible return values:
+		 *  -1 - no cover
+		 *   0 - open
+		 *   1 - closed
+		 */
+		int (* get_cover_state)(struct device *dev, int slot);
+
+		u32 ocr_mask;
+	} slots[OMAP_MMC_MAX_SLOTS];
+};
+
+extern void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info);
+
+/* called from board-specific card detection service routine */
+extern void omap_notify_mmc_card_detect(struct device *dev, int slot, int detected);
+
+extern void omap_notify_cover_event(struct device *dev, int slot, int is_closed);
+
+#endif

             reply	other threads:[~2006-06-19 16:52 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-06-19 16:52 Juha Yrjölä [this message]
2006-06-20 16:39 ` [PATCH] ARM: OMAP: Implement support for two MMC slots Tony Lindgren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20060619165202.GA30055@mail.solidboot.com \
    --to=juha.yrjola@solidboot.com \
    --cc=linux-omap-open-source@linux.omap.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.