Re: [RFC/PATCH 1/4] MMC/SD Controller driver for OMAP2430

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



ext Madhusudhan Chikkature Rajashekar wrote:
> This patch adds MMC controller driver for OMAP2430/3430.
>
> Signed-off-by: Madhusudhan Chikkature<madhu.cr@xxxxxx>
>
> ---
>  drivers/mmc/host/Makefile     |    9 
>  drivers/mmc/host/omap_hsmmc.c |  985 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 993 insertions(+), 1 deletion(-)
>
> Index: linux-omap-2.6/drivers/mmc/host/omap_hsmmc.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-omap-2.6/drivers/mmc/host/omap_hsmmc.c	2007-12-07 15:00:04.724840120 +0530
> @@ -0,0 +1,985 @@
> +/*
> + * drivers/mmc/host/omap_hsmmc.c
> + *
> + * Driver for OMAP2430/3430 MMC controller.
> + *
> + * Copyright (C) 2007 Texas Instruments.
> + *
> + * Authors:
> + *	Syed Mohammed Khasim	<x0khasim@xxxxxx>
> + *	Madhusudhan		<madhu.cr@xxxxxx>
> + *	Mohit Jalori		<mjalori@xxxxxx>
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +#include <linux/workqueue.h>
> +#include <linux/timer.h>
> +#include <linux/clk.h>
> +#include <linux/mmc/host.h>
> +#include <linux/io.h>
> +#include <asm/semaphore.h>
> +#include <asm/dma.h>
> +#include <asm/hardware.h>
> +#include <asm/arch/board.h>
> +#include <asm/arch/mmc.h>
> +#include <asm/arch/cpu.h>
> +
> +/* OMAP HSMMC Host Controller Registers */
> +#define OMAP_HSMMC_SYSCONFIG	0x0010
> +#define OMAP_HSMMC_CON		0x002C
> +#define OMAP_HSMMC_BLK		0x0104
> +#define OMAP_HSMMC_ARG		0x0108
> +#define OMAP_HSMMC_CMD		0x010C
> +#define OMAP_HSMMC_RSP10	0x0110
> +#define OMAP_HSMMC_RSP32	0x0114
> +#define OMAP_HSMMC_RSP54	0x0118
> +#define OMAP_HSMMC_RSP76	0x011C
> +#define OMAP_HSMMC_DATA		0x0120
> +#define OMAP_HSMMC_HCTL		0x0128
> +#define OMAP_HSMMC_SYSCTL	0x012C
> +#define OMAP_HSMMC_STAT		0x0130
> +#define OMAP_HSMMC_IE		0x0134
> +#define OMAP_HSMMC_ISE		0x0138
> +#define OMAP_HSMMC_CAPA		0x0140
> +
> +#define VS18			(1<<26)
> +#define VS30			(1<<25)
> +#define SDVS18			(0x5<<9)
> +#define SDVS30			(0x6<<9)
> +#define SDVSCLR			0xFFFFF1FF
> +#define SDVSDET			0x00000400
> +#define AUTOIDLE		0x1
> +#define SDBP			(1<<8)
> +#define DTO			0xe
> +#define ICE			0x1
> +#define ICS			0x2
> +#define CEN			(1<<2)
> +#define CLKD_MASK		0x0000FFC0
> +#define INT_EN_MASK		0x307F0033
> +#define INIT_STREAM		(1<<1)
> +#define DP_SELECT		(1<<21)
> +#define DDIR			(1<<4)
> +#define DMA_EN			0x1
> +#define MSBS			1<<5
> +#define BCE			1<<1
> +#define FOUR_BIT		1 << 1
> +#define CC			0x1
> +#define TC			0x02
> +#define OD			0x1
> +#define ERR			(1 << 15)
> +#define CMD_TIMEOUT		(1 << 16)
> +#define DATA_TIMEOUT		(1 << 20)
> +#define CMD_CRC			(1 << 17)
> +#define DATA_CRC		(1 << 21)
> +#define CARD_ERR		(1 << 28)
> +#define STAT_CLEAR		0xFFFFFFFF
> +#define INIT_STREAM_CMD		0x00000000
> +#define DUAL_VOLT_OCR_BIT	7
> +
> +#define OMAP_MMC1_DEVID		1
> +#define OMAP_MMC2_DEVID		2
> +#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"
> +/*
> + * slot_id is device id - 1, device id is a static value
> + * of 1 to represent device 1 etc..
> + */
> +#define mmc_slot(host)		(host->pdata->slots[host->slot_id])
> +
> +/*
> + * MMC Host controller read/write API's
> + */
> +#define OMAP_HSMMC_READ(base, reg)	\
> +	__raw_readl((base) + OMAP_HSMMC_##reg)
> +
> +#define OMAP_HSMMC_WRITE(base, reg, val) \
> +	__raw_writel((val), (base) + OMAP_HSMMC_##reg)
> +
> +struct mmc_omap_host {
> +	struct	device		*dev;
> +	struct	mmc_host	*mmc;
> +	struct	mmc_request	*mrq;
> +	struct	mmc_command	*cmd;
> +	struct	mmc_data	*data;
> +	struct	clk		*fclk;
> +	struct	clk		*iclk;
> +	struct	clk		*dbclk;
> +	struct  semaphore       sem;
> +	struct	work_struct	mmc_carddetect_work;
> +	void	__iomem		*base;
> +	resource_size_t		mapbase;
> +	unsigned int		id;
> +	unsigned int		dma_len;
> +	unsigned int		dma_dir;
> +	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;
> +	struct	omap_mmc_platform_data	*pdata;
> +};
> +
> +/*
> + * Stop clock to the card
> + */
> +static void omap_mmc_stop_clock(struct mmc_omap_host *host)
> +{
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
> +	if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
> +		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped");
> +}
> +
> +/*
> + * Send init stream sequence to card
> + * before sending IDLE command
> + */
> +static void send_init_stream(struct mmc_omap_host *host)
> +{
> +	int reg = 0;
> +	unsigned long timeout;
> +
> +	disable_irq(host->irq);
> +	OMAP_HSMMC_WRITE(host->base, CON,
> +		OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
> +	OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
> +
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((reg != CC) && time_before(jiffies, timeout))
> +		reg = OMAP_HSMMC_READ(host->base, STAT) & CC;
> +
> +	OMAP_HSMMC_WRITE(host->base, CON,
> +		OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
> +	enable_irq(host->irq);
> +}
> +
> +/*
> + * Configure the response type and send the cmd.
> + */
> +static void
> +mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
> +	struct mmc_data *data)
> +{
> +	int  cmdreg = 0, resptype = 0, cmdtype = 0;
> +
> +	dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",
> +		mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
> +	host->cmd = cmd;
> +
> +	/*
> +	 * Clear status bits and enable interrupts
> +	 */
> +	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> +	OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
> +	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136)
> +			resptype = 1;
> +		else
> +			resptype = 2;
> +	}
> +
> +	/*
> +	 * Unlike OMAP1 controller, the cmdtype does not seem to be based on
> +	 * ac, bc, adtc, bcr. Only CMD12 needs a val of 0x3, rest 0x0.
> +	 */
> +	if (cmd->opcode == 12)
> +		cmdtype = 0x3;
> +
> +	cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
> +
> +	if (data) {
> +		cmdreg |= DP_SELECT | MSBS | BCE;
> +		if (data->flags & MMC_DATA_READ)
> +			cmdreg |= DDIR;
> +		else
> +			cmdreg &= ~(DDIR);
> +	}
> +
> +	if (host->use_dma)
> +		cmdreg |= DMA_EN;
> +
> +	OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
> +	OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
> +}
> +
> +/*
> + * Notify the transfer complete to MMC core
> + */
> +static void
> +mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
> +{
> +	host->data = NULL;
> +
> +	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;
> +
> +	if (!data->error)
> +		data->bytes_xfered += data->blocks * (data->blksz);
> +	else
> +		data->bytes_xfered = 0;
> +
> +	if (!data->stop) {
> +		host->mrq = NULL;
> +		mmc_request_done(host->mmc, data->mrq);
> +		return;
> +	}
> +	mmc_omap_start_command(host, data->stop, NULL);
> +}
> +
> +/*
> + * Notify the core about command completion
> + */
> +static void
> +mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
> +{
> +	host->cmd = NULL;
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136) {
> +			/* response type 2 */
> +			cmd->resp[3] = OMAP_HSMMC_READ(host->base, RSP10);
> +			cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32);
> +			cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54);
> +			cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76);
> +		} else {
> +			/* response types 1, 1b, 3, 4, 5, 6 */
> +			cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
> +		}
> +	}
> +	if (host->data == NULL || cmd->error) {
> +		host->mrq = NULL;
> +		mmc_request_done(host->mmc, cmd->mrq);
> +	}
> +}
> +
> +/*
> + * DMA clean up for command errors
> + */
> +static void mmc_dma_cleanup(struct mmc_omap_host *host)
> +{
> +	host->data->error = -ETIMEDOUT;
> +
> +	if (host->use_dma && host->dma_ch != -1) {
> +		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
> +			host->dma_dir);
> +		omap_free_dma(host->dma_ch);
> +		host->dma_ch = -1;
> +		up(&host->sem);
> +	}
> +	host->data = NULL;
> +	host->datadir = OMAP_MMC_DATADIR_NONE;
> +}
> +
> +/*
> + * MMC controller IRQ handler
> + */
> +static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
> +{
> +	struct mmc_omap_host *host = dev_id;
> +	int end_cmd = 0, end_trans = 0, status;
> +
> +	if (host->cmd == NULL && host->data == NULL) {
> +		OMAP_HSMMC_WRITE(host->base, STAT,
> +			OMAP_HSMMC_READ(host->base, STAT));
> +		return IRQ_HANDLED;
> +	}
> +
> +	status = OMAP_HSMMC_READ(host->base, STAT);
> +	dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
> +
> +	if (status & ERR) {
> +		if ((status & CMD_TIMEOUT) ||
> +			(status & CMD_CRC)) {
> +			if (host->cmd) {
> +				if (status & CMD_TIMEOUT)
> +					host->cmd->error = -ETIMEDOUT;
> +				else
> +					host->cmd->error = -EILSEQ;
> +				end_cmd = 1;
> +			}
> +			if (host->data)
> +				mmc_dma_cleanup(host);
> +		}
> +		if ((status & DATA_TIMEOUT) ||
> +			(status & DATA_CRC)) {
> +			if (host->data) {
> +				if (status & DATA_TIMEOUT)
> +					mmc_dma_cleanup(host);
> +				else
> +					host->data->error = -EILSEQ;
> +				end_trans = 1;
> +			}
> +		}
> +		if (status & CARD_ERR) {
> +			dev_dbg(mmc_dev(host->mmc),
> +				"Ignoring card err CMD%d\n", host->cmd->opcode);
> +			if (host->cmd)
> +				end_cmd = 1;
> +			if (host->data)
> +				end_trans = 1;
> +		}
> +	}
> +
> +	OMAP_HSMMC_WRITE(host->base, STAT, status);
> +
> +	if (end_cmd || (status & CC))
> +		mmc_omap_cmd_done(host, host->cmd);
> +	if (end_trans || (status & TC))
> +		mmc_omap_xfer_done(host, host->data);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Switch MMC operating voltage
> + */
> +static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
> +{
> +	u32 reg_val = 0;
> +	int ret;
> +
> +	/* Disable the clocks */
> +	clk_disable(host->fclk);
> +	clk_disable(host->iclk);
> +	clk_disable(host->dbclk);
> +
> +	/* Turn the power off */
> +	ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
> +	if (ret != 0)
> +		goto err;
> +
> +	/* Turn the power ON with given VDD 1.8 or 3.0v */
> +	ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
> +	if (ret != 0)
> +		goto err;
> +
> +	clk_enable(host->fclk);
> +	clk_enable(host->iclk);
> +	clk_enable(host->dbclk);
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +		OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
> +	reg_val = OMAP_HSMMC_READ(host->base, HCTL);
> +	/*
> +	 * If a MMC dual voltage card is detected, the set_ios fn calls
> +	 * this fn with VDD bit set for 1.8V. Upon card removal from the
> +	 * slot, mmc_omap_detect fn sets the VDD back to 3V.
> +	 */
> +	if (((1 << vdd) == MMC_VDD_32_33) || ((1 << vdd) == MMC_VDD_33_34))
> +		reg_val |= SDVS30;
> +	if ((1 << vdd) == MMC_VDD_165_195)
> +		reg_val |= SDVS18;
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +		OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
> +
> +	return 0;
> +err:
> +	dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage \n");
> +	return ret;
> +}
> +
> +/*
> + * Work Item to notify the core about card insertion/removal
> + */
> +static void mmc_omap_detect(struct work_struct *work)
> +{
> +	u16 vdd = 0;
> +	struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
> +						mmc_carddetect_work);
> +
> +	if (host->carddetect) {
> +		if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
> +			/*
> +			 * Set the VDD back to 3V when the card is removed
> +			 * before the set_ios fn turns off the power.
> +			 */
> +			vdd = fls(host->mmc->ocr_avail) - 1;
> +			if (omap_mmc_switch_opcond(host, vdd) != 0)
> +				host->mmc->ios.vdd = vdd;
> +		}
> +		mmc_detect_change(host->mmc, (HZ * 200) / 1000);
> +	} else
> +		mmc_detect_change(host->mmc, (HZ * 50) / 1000);
> +}
> +
> +/*
> + * ISR for handling card insertion and removal
> + */
> +void omap_mmc_notify_card_detect(struct device *dev, int slot, int detected)
> +{
> +	struct mmc_omap_host *host = dev_get_drvdata(dev);
> +	host->carddetect = detected;
> +	schedule_work(&host->mmc_carddetect_work);
> +}
> +
> +/*
> + * DMA call back function
> + */
> +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
> +{
> +	struct mmc_omap_host *host = data;
> +
> +	if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
> +		dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
> +
> +	if (host->dma_ch < 0)
> +		return;
> +
> +	omap_free_dma(host->dma_ch);
> +	host->dma_ch = -1;
> +	/*
> +	 * DMA Callback: run in interrupt context.
> +	 * mutex_unlock will through a kernel warning if used.
> +	 */
> +	up(&host->sem);
> +}
> +
> +/*
> + * 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 int
> +mmc_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;
> +	struct mmc_data *data = req->data;
> +
> +	/*
> +	 * If for some reason the DMA transfer is still active,
> +	 * we wait for timeout period and free the dma
> +	 */
> +	if (host->dma_ch != -1) {
> +		set_current_state(TASK_UNINTERRUPTIBLE);
> +		schedule_timeout(100);
> +		if (down_trylock(&host->sem)) {
> +			omap_free_dma(host->dma_ch);
> +			host->dma_ch = -1;
> +			up(&host->sem);
> +			return err;
> +		}
> +	} else {
> +		if (down_trylock(&host->sem))
> +			return err;
> +	}
> +
> +	if (!(data->flags & MMC_DATA_WRITE)) {
> +		host->dma_dir = DMA_FROM_DEVICE;
> +		sync_dev = OMAP24XX_DMA_MMC1_RX;
> +	} else {
> +		host->dma_dir = DMA_TO_DEVICE;
> +		sync_dev = OMAP24XX_DMA_MMC1_TX;
> +	}
> +
> +	ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
> +			host, &dma_ch);
> +	if (ret != 0) {
> +		dev_dbg(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);
> +	host->dma_ch = dma_ch;
> +
> +	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;
> +
> +	omap_start_dma(dma_ch);
> +	return 0;
> +}
> +
> +/*
> + * Configure block length for MMC/SD cards and initiate the transfer.
> + */
> +static int
> +mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
> +{
> +	int ret;
> +	host->data = req->data;
> +
> +	if (req->data == NULL) {
> +		host->datadir = OMAP_MMC_DATADIR_NONE;
> +		OMAP_HSMMC_WRITE(host->base, BLK, 0);
> +		return 0;
> +	}
> +
> +	OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
> +					| (req->data->blocks << 16));
> +
> +	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) {
> +			dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
> +			return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/*
> + * Request function. for read/write operation
> + */
> +static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
> +{
> +	struct mmc_omap_host *host = mmc_priv(mmc);
> +
> +	WARN_ON(host->mrq != NULL);
> +	host->mrq = req;
> +	mmc_omap_prepare_data(host, req);
> +	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)
> +{
> +	struct mmc_omap_host *host = mmc_priv(mmc);
> +	u16 dsor = 0;
> +	unsigned long regval;
> +	unsigned long timeout;
> +
> +	switch (ios->power_mode) {
> +	case MMC_POWER_OFF:
> +		mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
> +		break;
> +	case MMC_POWER_UP:
> +		mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
> +		break;
> +	}
> +
> +	switch (mmc->ios.bus_width) {
> +	case MMC_BUS_WIDTH_4:
> +		OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
> +		break;
> +	case MMC_BUS_WIDTH_1:
> +		OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
> +		break;
> +	}
> +
> +	if (host->id == OMAP_MMC1_DEVID) {
> +		/* Only MMC1 can operate at 3V/1.8V */
> +		if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
> +			(ios->vdd == DUAL_VOLT_OCR_BIT)) {
> +				/*
> +				 * The mmc_select_voltage fn of the core does
> +				 * not seem to set the power_mode to
> +				 * MMC_POWER_UP upon recalculating the voltage.
> +				 * vdd 1.8v.
> +				 */
> +				if (omap_mmc_switch_opcond(host, ios->vdd) != 0)
> +					dev_dbg(mmc_dev(host->mmc),
> +						"Switch operation failed\n");
> +		}
> +	}
> +
> +	if (ios->clock) {
> +		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
> +		if (dsor < 1)
> +			dsor = 1;
> +
> +		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
> +			dsor++;
> +
> +		if (dsor > 250)
> +			dsor = 250;
> +	}
> +	omap_mmc_stop_clock(host);
> +	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
> +	regval = regval & ~(CLKD_MASK);
> +	regval = regval | (dsor << 6) | (DTO << 16);
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
> +
> +	/* Wait till the ICS bit is set */
> +	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> +	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
> +		&& time_before(jiffies, timeout))
> +		msleep(1);
> +
> +	OMAP_HSMMC_WRITE(host->base, SYSCTL,
> +		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
> +
> +	if (ios->power_mode == MMC_POWER_ON)
> +		send_init_stream(host);
> +
> +	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
> +		OMAP_HSMMC_WRITE(host->base, CON,
> +				OMAP_HSMMC_READ(host->base, CON) | OD);
> +}
> +/* NOTE: Read only switch not supported yet */
> +static struct mmc_host_ops mmc_omap_ops = {
> +	.request = omap_mmc_request,
> +	.set_ios = omap_mmc_set_ios,
> +};
> +
> +static int __init omap_mmc_probe(struct platform_device *pdev)
> +{
> +	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
> +	struct mmc_host *mmc;
> +	struct mmc_omap_host *host = NULL;
> +	struct resource *res;
> +	int ret = 0, irq;
> +
> +	if (pdata == NULL) {
> +		dev_err(&pdev->dev, "Platform Data is 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);
> +	if (res == NULL || irq < 0)
> +		return -ENXIO;
> +
> +	res = request_mem_region(res->start, res->end - res->start + 1,
> +							pdev->name);
> +	if (res == NULL)
> +		return -EBUSY;
> +
> +	mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
> +	if (!mmc) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	host		= mmc_priv(mmc);
> +	host->mmc	= mmc;
> +	host->pdata	= pdata;
> +	host->use_dma	= 1;
> +	host->dma_ch	= -1;
> +	host->irq	= irq;
> +	host->id	= pdev->id;
> +	host->slot_id	= pdev->id - 1;
> +	host->mapbase	= res->start;
> +	host->base	= ioremap(host->mapbase, SZ_4K);
> +	mmc->ops	= &mmc_omap_ops;
> +	mmc->f_min	= 400000;
> +	mmc->f_max	= 52000000;
> +
> +	sema_init(&host->sem, 1);
> +
> +	host->iclk = clk_get(&pdev->dev, "mmchs_ick");
> +	if (IS_ERR(host->iclk)) {
> +		ret = PTR_ERR(host->iclk);
> +		host->iclk = NULL;
> +		goto err;
> +	}
> +	host->fclk = clk_get(&pdev->dev, "mmchs_fck");
> +	if (IS_ERR(host->fclk)) {
> +		ret = PTR_ERR(host->fclk);
> +		host->fclk = NULL;
> +		clk_put(host->iclk);
> +		goto err;
> +	}
> +
> +	if (clk_enable(host->fclk) != 0)
> +		goto err;
> +
> +	if (clk_enable(host->iclk) != 0) {
> +		clk_disable(host->fclk);
> +		clk_put(host->fclk);
> +		goto err;
> +	}
> +
> +	host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
> +	/*
> +	 * MMC can still work without debounce clock.
> +	 */
> +	if (IS_ERR(host->dbclk))
> +		dev_dbg(mmc_dev(host->mmc), "Failed to get debounce clock \n");
> +	else
> +		if (clk_enable(host->dbclk) != 0)
> +			dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
> +							"clk failed\n");
> +		else
> +			host->dbclk_enabled = 1;
> +
> +	mmc->ocr_avail = mmc_slot(host).ocr_mask;
> +	mmc->caps |= MMC_CAP_MULTIWRITE | MMC_CAP_MMC_HIGHSPEED |
> +				MMC_CAP_SD_HIGHSPEED;
> +
> +	if (pdata->conf.wire4)
> +		mmc->caps |= MMC_CAP_4_BIT_DATA;
> +
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | SDVS30);
> +
> +	OMAP_HSMMC_WRITE(host->base, CAPA, OMAP_HSMMC_READ(host->base,
> +							CAPA) | VS30 | VS18);
> +
> +	/* Set the controller to AUTO IDLE mode */
> +	OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
> +			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
> +
> +	/* Set SD bus power bit */
> +	OMAP_HSMMC_WRITE(host->base, HCTL,
> +			OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
> +
> +	/* Request IRQ for MMC operations */
> +	ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, pdev->name,
> +			 host);
> +	if (ret) {
> +		dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ");
> +		goto irq_err;
> +	}
> +
> +	INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
> +	if (pdata->init != NULL) {
> +		if (pdata->init(&pdev->dev) != 0) {
> +			free_irq(host->irq, host);
> +			goto irq_err;
> +		}
> +	}
> +
> +	OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
> +	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
> +
> +	platform_set_drvdata(pdev, host);
> +	mmc_add_host(mmc);
> +
> +	return 0;
> +
> +err:
> +	dev_dbg(mmc_dev(host->mmc), "Probe Failed\n");
> +	if (host)
> +		mmc_free_host(mmc);
> +	return ret;
> +
> +irq_err:
> +	dev_dbg(mmc_dev(host->mmc), "Unable to configure MMC IRQs");
> +	clk_disable(host->fclk);
> +	clk_disable(host->iclk);
> +	clk_put(host->fclk);
> +	clk_put(host->iclk);
> +	if (host->dbclk_enabled) {
> +		clk_disable(host->dbclk);
> +		clk_put(host->dbclk);
> +	}
> +
> +	if (host)
> +		mmc_free_host(mmc);
> +	return ret;
> +}
> +
> +static int omap_mmc_remove(struct platform_device *pdev)
> +{
> +	struct mmc_omap_host *host = platform_get_drvdata(pdev);
> +
> +	platform_set_drvdata(pdev, NULL);
> +	if (host) {
> +		host->pdata->cleanup(&pdev->dev);
> +		free_irq(host->irq, host);
> +		flush_scheduled_work();
> +
> +		clk_disable(host->fclk);
> +		clk_disable(host->iclk);
> +		clk_put(host->fclk);
> +		clk_put(host->iclk);
> +		if (host->dbclk_enabled) {
> +			clk_disable(host->dbclk);
> +			clk_put(host->dbclk);
> +		}
> +
> +		mmc_free_host(host->mmc);
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	int ret = 0;
> +	struct mmc_omap_host *host = platform_get_drvdata(pdev);
> +
> +	if (host && host->suspended)
> +		return 0;
> +
> +	if (host) {
> +		ret = mmc_suspend_host(host->mmc, state);
> +		if (ret == 0) {
> +			host->suspended = 1;
> +
> +			OMAP_HSMMC_WRITE(host->base, ISE, 0);
> +			OMAP_HSMMC_WRITE(host->base, IE, 0);
> +
> +			ret = host->pdata->suspend(&pdev->dev, host->slot_id);
> +			if (ret)
> +				dev_dbg(mmc_dev(host->mmc),
> +					"Unable to handle MMC board"
> +					"level suspend\n");
> +
> +			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);
> +			}
> +
> +			clk_disable(host->fclk);
> +			clk_disable(host->iclk);
> +			clk_disable(host->dbclk);
> +		}
> +
> +	}
> +	return ret;
> +}
> +
> +/* Routine to resume the MMC device */
> +static int omap_mmc_resume(struct platform_device *pdev)
> +{
> +	int ret = 0;
> +	struct mmc_omap_host *host = platform_get_drvdata(pdev);
> +
> +	if (host && !host->suspended)
> +		return 0;
> +
> +	if (host) {
> +
> +		ret = clk_enable(host->fclk);
> +		if (ret)
> +			goto clk_en_err;
> +
> +		ret = clk_enable(host->iclk);
> +		if (ret) {
> +			clk_disable(host->fclk);
> +			clk_put(host->fclk);
> +			goto clk_en_err;
> +		}
> +
> +		if (clk_enable(host->dbclk) != 0)
> +			dev_dbg(mmc_dev(host->mmc),
> +					"Enabling debounce clk failed\n");
> +
> +		ret = host->pdata->resume(&pdev->dev, host->slot_id);
> +		if (ret)
> +			dev_dbg(mmc_dev(host->mmc),
> +					"Unmask interrupt failed\n");
> +
> +		/* Notify the core to resume the host */
> +		ret = mmc_resume_host(host->mmc);
> +		if (ret == 0)
> +			host->suspended = 0;
> +	}
> +
> +	return ret;
> +
> +clk_en_err:
> +	dev_dbg(mmc_dev(host->mmc),
> +		"Failed to enable MMC clocks during resume\n");
> +	return ret;
> +}
> +
> +#else
> +#define omap_mmc_suspend	NULL
> +#define omap_mmc_resume		NULL
> +#endif
> +
> +static struct platform_driver omap_mmc_driver = {
> +	.probe		= omap_mmc_probe,
> +	.remove		= omap_mmc_remove,
> +	.suspend	= omap_mmc_suspend,
> +	.resume		= omap_mmc_resume,
> +	.driver		= {
> +		.name = DRIVER_NAME,
> +	},
> +};
> +
> +static int __init omap_mmc_init(void)
> +{
> +	/* Register the MMC driver */
> +	return platform_driver_register(&omap_mmc_driver);
> +}
> +
> +static void __exit omap_mmc_cleanup(void)
> +{
> +	/* Unregister MMC driver */
> +	platform_driver_unregister(&omap_mmc_driver);
> +}
> +
> +module_init(omap_mmc_init);
> +module_exit(omap_mmc_cleanup);
> +
> +MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS(DRIVER_NAME);
> +MODULE_AUTHOR("Texas Instruments Inc");
> Index: linux-omap-2.6/drivers/mmc/host/Makefile
> ===================================================================
> --- linux-omap-2.6.orig/drivers/mmc/host/Makefile	2007-12-07 13:31:28.428705929 +0530
> +++ linux-omap-2.6/drivers/mmc/host/Makefile	2007-12-07 13:34:26.606027304 +0530
> @@ -13,7 +13,14 @@
>  obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o
>  obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
>  obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
> -obj-$(CONFIG_MMC_OMAP)		+= omap.o
> +
> +ifeq ($(CONFIG_MMC_OMAP),y)
> +obj-$(CONFIG_ARCH_OMAP3430)    += omap_hsmmc.o
> +obj-$(CONFIG_ARCH_OMAP2430)    += omap_hsmmc.o
> +obj-$(CONFIG_ARCH_OMAP2420)    += omap.o
> +obj-$(CONFIG_ARCH_OMAP1)       += omap.o
> +endif
> +
>  obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
>  obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
>  obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
>
> _______________________________________________
> Linux-omap-open-source mailing list
> Linux-omap-open-source@xxxxxxxxxxxxxx
> http://linux.omap.com/mailman/listinfo/linux-omap-open-source
>
>   
Hi Madhu,

I'd rather see this code using multislot structures and omap.c instead
of defining a whole new codebase for omap2430 and/or omap3.

BR,

Carlos.


-- 
Carlos Eduardo Aguiar
Nokia Institute of Technology - INdT
Open Source Mobile Research Center - OSMRC - Manaus
Core Team
Phone: +55 92 2126-1079
Mobile: +55 92 8127-1797
E-mail: carlos.aguiar@xxxxxxxxxxx

-
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux