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