diff options
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
| -rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 1903 | 
1 files changed, 855 insertions, 1048 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 82a1079bbdc..6b7b7558592 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -17,32 +17,35 @@  #include <linux/module.h>  #include <linux/init.h> +#include <linux/kernel.h>  #include <linux/debugfs.h> +#include <linux/dmaengine.h>  #include <linux/seq_file.h> +#include <linux/sizes.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/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#include <linux/omap-dmaengine.h>  #include <linux/mmc/host.h>  #include <linux/mmc/core.h>  #include <linux/mmc/mmc.h>  #include <linux/io.h> -#include <linux/semaphore.h>  #include <linux/gpio.h>  #include <linux/regulator/consumer.h> -#include <plat/dma.h> -#include <mach/hardware.h> -#include <plat/board.h> -#include <plat/mmc.h> -#include <plat/cpu.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/platform_data/mmc-omap.h>  /* OMAP HSMMC Host Controller Registers */ -#define OMAP_HSMMC_SYSCONFIG	0x0010  #define OMAP_HSMMC_SYSSTATUS	0x0014  #define OMAP_HSMMC_CON		0x002C +#define OMAP_HSMMC_SDMASA	0x0100  #define OMAP_HSMMC_BLK		0x0104  #define OMAP_HSMMC_ARG		0x0108  #define OMAP_HSMMC_CMD		0x010C @@ -56,10 +59,12 @@  #define OMAP_HSMMC_STAT		0x0130  #define OMAP_HSMMC_IE		0x0134  #define OMAP_HSMMC_ISE		0x0138 +#define OMAP_HSMMC_AC12		0x013C  #define OMAP_HSMMC_CAPA		0x0140  #define VS18			(1 << 26)  #define VS30			(1 << 25) +#define HSS			(1 << 21)  #define SDVS18			(0x5 << 9)  #define SDVS30			(0x6 << 9)  #define SDVS33			(0x7 << 9) @@ -72,59 +77,70 @@  #define ICE			0x1  #define ICS			0x2  #define CEN			(1 << 2) +#define CLKD_MAX		0x3FF		/* max clock divisor: 1023 */  #define CLKD_MASK		0x0000FFC0  #define CLKD_SHIFT		6  #define DTO_MASK		0x000F0000  #define DTO_SHIFT		16 -#define INT_EN_MASK		0x307F0033 -#define BWR_ENABLE		(1 << 4) -#define BRR_ENABLE		(1 << 5) -#define DTO_ENABLE		(1 << 20)  #define INIT_STREAM		(1 << 1) +#define ACEN_ACMD23		(2 << 2)  #define DP_SELECT		(1 << 21)  #define DDIR			(1 << 4) -#define DMA_EN			0x1 +#define DMAE			0x1  #define MSBS			(1 << 5)  #define BCE			(1 << 1)  #define FOUR_BIT		(1 << 1) +#define HSPE			(1 << 2) +#define DDR			(1 << 19)  #define DW8			(1 << 5) -#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 SRC			(1 << 25)  #define SRD			(1 << 26)  #define SOFTRESET		(1 << 1) -#define RESETDONE		(1 << 0) - -/* - * FIXME: Most likely all the data using these _DEVID defines should come - * from the platform_data, or implemented in controller and slot specific - * functions. - */ -#define OMAP_MMC1_DEVID		0 -#define OMAP_MMC2_DEVID		1 -#define OMAP_MMC3_DEVID		2 -#define OMAP_MMC4_DEVID		3 -#define OMAP_MMC5_DEVID		4 - -#define MMC_TIMEOUT_MS		20 -#define OMAP_MMC_MASTER_CLOCK	96000000 -#define DRIVER_NAME		"mmci-omap-hs" - -/* Timeouts for entering power saving states on inactivity, msec */ -#define OMAP_MMC_DISABLED_TIMEOUT	100 -#define OMAP_MMC_SLEEP_TIMEOUT		1000 -#define OMAP_MMC_OFF_TIMEOUT		8000 +/* Interrupt masks for IE and ISE register */ +#define CC_EN			(1 << 0) +#define TC_EN			(1 << 1) +#define BWR_EN			(1 << 4) +#define BRR_EN			(1 << 5) +#define ERR_EN			(1 << 15) +#define CTO_EN			(1 << 16) +#define CCRC_EN			(1 << 17) +#define CEB_EN			(1 << 18) +#define CIE_EN			(1 << 19) +#define DTO_EN			(1 << 20) +#define DCRC_EN			(1 << 21) +#define DEB_EN			(1 << 22) +#define ACE_EN			(1 << 24) +#define CERR_EN			(1 << 28) +#define BADA_EN			(1 << 29) + +#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\ +		DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \ +		BRR_EN | BWR_EN | TC_EN | CC_EN) + +#define CNI	(1 << 7) +#define ACIE	(1 << 4) +#define ACEB	(1 << 3) +#define ACCE	(1 << 2) +#define ACTO	(1 << 1) +#define ACNE	(1 << 0) + +#define MMC_AUTOSUSPEND_DELAY	100 +#define MMC_TIMEOUT_MS		20		/* 20 mSec */ +#define MMC_TIMEOUT_US		20000		/* 20000 micro Sec */ +#define OMAP_MMC_MIN_CLOCK	400000 +#define OMAP_MMC_MAX_CLOCK	52000000 +#define DRIVER_NAME		"omap_hsmmc" + +#define VDD_1V8			1800000		/* 180000 uV */ +#define VDD_3V0			3000000		/* 300000 uV */ +#define VDD_165_195		(ffs(MMC_VDD_165_195) - 1) + +#define AUTO_CMD23		(1 << 1)	/* Auto CMD23 support */  /*   * One controller can have multiple slots, like on some omap boards using   * omap.c controller driver. Luckily this is not currently done on any known @@ -141,6 +157,11 @@  #define OMAP_HSMMC_WRITE(base, reg, val) \  	__raw_writel((val), (base) + OMAP_HSMMC_##reg) +struct omap_hsmmc_next { +	unsigned int	dma_len; +	s32		cookie; +}; +  struct omap_hsmmc_host {  	struct	device		*dev;  	struct	mmc_host	*mmc; @@ -148,7 +169,6 @@ struct omap_hsmmc_host {  	struct	mmc_command	*cmd;  	struct	mmc_data	*data;  	struct	clk		*fclk; -	struct	clk		*iclk;  	struct	clk		*dbclk;  	/*  	 * vcc == configured supply @@ -159,38 +179,48 @@ struct omap_hsmmc_host {  	 */  	struct	regulator	*vcc;  	struct	regulator	*vcc_aux; -	struct	work_struct	mmc_carddetect_work; +	struct	regulator	*pbias; +	bool			pbias_enabled;  	void	__iomem		*base;  	resource_size_t		mapbase;  	spinlock_t		irq_lock; /* Prevent races with irq handler */ -	unsigned int		id;  	unsigned int		dma_len;  	unsigned int		dma_sg_idx;  	unsigned char		bus_mode;  	unsigned char		power_mode; -	u32			*buffer; -	u32			bytesleft;  	int			suspended; +	u32			con; +	u32			hctl; +	u32			sysctl; +	u32			capa;  	int			irq;  	int			use_dma, dma_ch; -	int			dma_line_tx, dma_line_rx; +	struct dma_chan		*tx_chan; +	struct dma_chan		*rx_chan;  	int			slot_id; -	int			got_dbclk;  	int			response_busy;  	int			context_loss; -	int			dpm_state; -	int			vdd;  	int			protect_card;  	int			reqs_blocked;  	int			use_reg;  	int			req_in_progress; - +	unsigned long		clk_rate; +	unsigned int		flags; +	struct omap_hsmmc_next	next_data;  	struct	omap_mmc_platform_data	*pdata;  }; +struct omap_mmc_of_data { +	u32 reg_offset; +	u8 controller_flags; +}; + +static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); +  static int omap_hsmmc_card_detect(struct device *dev, int slot)  { -	struct omap_mmc_platform_data *mmc = dev->platform_data; +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); +	struct omap_mmc_platform_data *mmc = host->pdata;  	/* NOTE: assumes card detect signal is active-low */  	return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); @@ -198,7 +228,8 @@ static int omap_hsmmc_card_detect(struct device *dev, int slot)  static int omap_hsmmc_get_wp(struct device *dev, int slot)  { -	struct omap_mmc_platform_data *mmc = dev->platform_data; +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); +	struct omap_mmc_platform_data *mmc = host->pdata;  	/* NOTE: assumes write protect signal is active-high */  	return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); @@ -206,7 +237,8 @@ static int omap_hsmmc_get_wp(struct device *dev, int slot)  static int omap_hsmmc_get_cover_state(struct device *dev, int slot)  { -	struct omap_mmc_platform_data *mmc = dev->platform_data; +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); +	struct omap_mmc_platform_data *mmc = host->pdata;  	/* NOTE: assumes card detect signal is active-low */  	return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); @@ -216,7 +248,8 @@ static int omap_hsmmc_get_cover_state(struct device *dev, int slot)  static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)  { -	struct omap_mmc_platform_data *mmc = dev->platform_data; +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); +	struct omap_mmc_platform_data *mmc = host->pdata;  	disable_irq(mmc->slots[0].card_detect_irq);  	return 0; @@ -224,7 +257,8 @@ static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)  static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)  { -	struct omap_mmc_platform_data *mmc = dev->platform_data; +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); +	struct omap_mmc_platform_data *mmc = host->pdata;  	enable_irq(mmc->slots[0].card_detect_irq);  	return 0; @@ -239,28 +273,7 @@ static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)  #ifdef CONFIG_REGULATOR -static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, -				  int vdd) -{ -	struct omap_hsmmc_host *host = -		platform_get_drvdata(to_platform_device(dev)); -	int ret; - -	if (mmc_slot(host).before_set_reg) -		mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); - -	if (power_on) -		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); -	else -		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); - -	if (mmc_slot(host).after_set_reg) -		mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); - -	return ret; -} - -static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, +static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,  				   int vdd)  {  	struct omap_hsmmc_host *host = @@ -277,6 +290,15 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,  	if (mmc_slot(host).before_set_reg)  		mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); +	if (host->pbias) { +		if (host->pbias_enabled == 1) { +			ret = regulator_disable(host->pbias); +			if (!ret) +				host->pbias_enabled = 0; +		} +		regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0); +	} +  	/*  	 * Assume Vcc regulator is used only to power the card ... OMAP  	 * VDDS is used to power the pins, optionally with a transceiver to @@ -291,11 +313,12 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,  	 * chips/cards need an interface voltage rail too.  	 */  	if (power_on) { -		ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); +		if (host->vcc) +			ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);  		/* Enable interface voltage rail, if needed */  		if (ret == 0 && host->vcc_aux) {  			ret = regulator_enable(host->vcc_aux); -			if (ret < 0) +			if (ret < 0 && host->vcc)  				ret = mmc_regulator_set_ocr(host->mmc,  							host->vcc, 0);  		} @@ -303,103 +326,47 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,  		/* Shut down the rail */  		if (host->vcc_aux)  			ret = regulator_disable(host->vcc_aux); -		if (!ret) { +		if (host->vcc) {  			/* Then proceed to shut down the local regulator */  			ret = mmc_regulator_set_ocr(host->mmc,  						host->vcc, 0);  		}  	} +	if (host->pbias) { +		if (vdd <= VDD_165_195) +			ret = regulator_set_voltage(host->pbias, VDD_1V8, +								VDD_1V8); +		else +			ret = regulator_set_voltage(host->pbias, VDD_3V0, +								VDD_3V0); +		if (ret < 0) +			goto error_set_power; + +		if (host->pbias_enabled == 0) { +			ret = regulator_enable(host->pbias); +			if (!ret) +				host->pbias_enabled = 1; +		} +	} +  	if (mmc_slot(host).after_set_reg)  		mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); +error_set_power:  	return ret;  } -static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep, -				  int vdd, int cardsleep) -{ -	struct omap_hsmmc_host *host = -		platform_get_drvdata(to_platform_device(dev)); -	int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; - -	return regulator_set_mode(host->vcc, mode); -} - -static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, -				   int vdd, int cardsleep) -{ -	struct omap_hsmmc_host *host = -		platform_get_drvdata(to_platform_device(dev)); -	int err, mode; - -	/* -	 * If we don't see a Vcc regulator, assume it's a fixed -	 * voltage always-on regulator. -	 */ -	if (!host->vcc) -		return 0; - -	mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; - -	if (!host->vcc_aux) -		return regulator_set_mode(host->vcc, mode); - -	if (cardsleep) { -		/* VCC can be turned off if card is asleep */ -		if (sleep) -			err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); -		else -			err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); -	} else -		err = regulator_set_mode(host->vcc, mode); -	if (err) -		return err; - -	if (!mmc_slot(host).vcc_aux_disable_is_sleep) -		return regulator_set_mode(host->vcc_aux, mode); - -	if (sleep) -		return regulator_disable(host->vcc_aux); -	else -		return regulator_enable(host->vcc_aux); -} -  static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)  {  	struct regulator *reg; -	int ret = 0;  	int ocr_value = 0; -	switch (host->id) { -	case OMAP_MMC1_DEVID: -		/* On-chip level shifting via PBIAS0/PBIAS1 */ -		mmc_slot(host).set_power = omap_hsmmc_1_set_power; -		mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep; -		break; -	case OMAP_MMC2_DEVID: -	case OMAP_MMC3_DEVID: -		/* Off-chip level shifting, or none */ -		mmc_slot(host).set_power = omap_hsmmc_23_set_power; -		mmc_slot(host).set_sleep = omap_hsmmc_23_set_sleep; -		break; -	default: -		pr_err("MMC%d configuration not supported!\n", host->id); -		return -EINVAL; -	} - -	reg = regulator_get(host->dev, "vmmc"); +	reg = devm_regulator_get(host->dev, "vmmc");  	if (IS_ERR(reg)) { -		dev_dbg(host->dev, "vmmc regulator missing\n"); -		/* -		* HACK: until fixed.c regulator is usable, -		* we don't require a main regulator -		* for MMC2 or MMC3 -		*/ -		if (host->id == OMAP_MMC1_DEVID) { -			ret = PTR_ERR(reg); -			goto err; -		} +		dev_err(host->dev, "unable to get vmmc regulator %ld\n", +			PTR_ERR(reg)); +		return PTR_ERR(reg);  	} else {  		host->vcc = reg;  		ocr_value = mmc_regulator_get_ocrmask(reg); @@ -407,52 +374,43 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)  			mmc_slot(host).ocr_mask = ocr_value;  		} else {  			if (!(mmc_slot(host).ocr_mask & ocr_value)) { -				pr_err("MMC%d ocrmask %x is not supported\n", -					host->id, mmc_slot(host).ocr_mask); +				dev_err(host->dev, "ocrmask %x is not supported\n", +					mmc_slot(host).ocr_mask);  				mmc_slot(host).ocr_mask = 0;  				return -EINVAL;  			}  		} -		mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg); +	} +	mmc_slot(host).set_power = omap_hsmmc_set_power; -		/* Allow an aux regulator */ -		reg = regulator_get(host->dev, "vmmc_aux"); -		host->vcc_aux = IS_ERR(reg) ? NULL : reg; +	/* Allow an aux regulator */ +	reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); +	host->vcc_aux = IS_ERR(reg) ? NULL : reg; -		/* -		* UGLY HACK:  workaround regulator framework bugs. -		* When the bootloader leaves a supply active, it's -		* initialized with zero usecount ... and we can't -		* disable it without first enabling it.  Until the -		* framework is fixed, we need a workaround like this -		* (which is safe for MMC, but not in general). -		*/ -		if (regulator_is_enabled(host->vcc) > 0) { -			regulator_enable(host->vcc); -			regulator_disable(host->vcc); -		} -		if (host->vcc_aux) { -			if (regulator_is_enabled(reg) > 0) { -				regulator_enable(reg); -				regulator_disable(reg); -			} -		} +	reg = devm_regulator_get_optional(host->dev, "pbias"); +	host->pbias = IS_ERR(reg) ? NULL : reg; + +	/* For eMMC do not power off when not in sleep state */ +	if (mmc_slot(host).no_regulator_off_init) +		return 0; +	/* +	 * To disable boot_on regulator, enable regulator +	 * to increase usecount and then disable it. +	 */ +	if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || +	    (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { +		int vdd = ffs(mmc_slot(host).ocr_mask) - 1; + +		mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); +		mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);  	}  	return 0; - -err: -	mmc_slot(host).set_power = NULL; -	mmc_slot(host).set_sleep = NULL; -	return ret;  }  static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)  { -	regulator_put(host->vcc); -	regulator_put(host->vcc_aux);  	mmc_slot(host).set_power = NULL; -	mmc_slot(host).set_sleep = NULL;  }  static inline int omap_hsmmc_have_reg(void) @@ -530,6 +488,15 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)  }  /* + * Start clock to the card + */ +static void omap_hsmmc_start_clock(struct omap_hsmmc_host *host) +{ +	OMAP_HSMMC_WRITE(host->base, SYSCTL, +		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); +} + +/*   * Stop clock to the card   */  static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) @@ -537,7 +504,7 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_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\n"); +		dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stopped\n");  }  static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, @@ -546,13 +513,13 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,  	unsigned int irq_mask;  	if (host->use_dma) -		irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE); +		irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);  	else  		irq_mask = INT_EN_MASK;  	/* Disable timeout for erases */  	if (cmd->opcode == MMC_ERASE) -		irq_mask &= ~DTO_ENABLE; +		irq_mask &= ~DTO_EN;  	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);  	OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); @@ -566,6 +533,108 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)  	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);  } +/* Calculate divisor for the given clock frequency */ +static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios) +{ +	u16 dsor = 0; + +	if (ios->clock) { +		dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); +		if (dsor > CLKD_MAX) +			dsor = CLKD_MAX; +	} + +	return dsor; +} + +static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) +{ +	struct mmc_ios *ios = &host->mmc->ios; +	unsigned long regval; +	unsigned long timeout; +	unsigned long clkdiv; + +	dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock); + +	omap_hsmmc_stop_clock(host); + +	regval = OMAP_HSMMC_READ(host->base, SYSCTL); +	regval = regval & ~(CLKD_MASK | DTO_MASK); +	clkdiv = calc_divisor(host, ios); +	regval = regval | (clkdiv << 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) != ICS +		&& time_before(jiffies, timeout)) +		cpu_relax(); + +	/* +	 * Enable High-Speed Support +	 * Pre-Requisites +	 *	- Controller should support High-Speed-Enable Bit +	 *	- Controller should not be using DDR Mode +	 *	- Controller should advertise that it supports High Speed +	 *	  in capabilities register +	 *	- MMC/SD clock coming out of controller > 25MHz +	 */ +	if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && +	    (ios->timing != MMC_TIMING_MMC_DDR52) && +	    ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { +		regval = OMAP_HSMMC_READ(host->base, HCTL); +		if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) +			regval |= HSPE; +		else +			regval &= ~HSPE; + +		OMAP_HSMMC_WRITE(host->base, HCTL, regval); +	} + +	omap_hsmmc_start_clock(host); +} + +static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host) +{ +	struct mmc_ios *ios = &host->mmc->ios; +	u32 con; + +	con = OMAP_HSMMC_READ(host->base, CON); +	if (ios->timing == MMC_TIMING_MMC_DDR52) +		con |= DDR;	/* configure in DDR mode */ +	else +		con &= ~DDR; +	switch (ios->bus_width) { +	case MMC_BUS_WIDTH_8: +		OMAP_HSMMC_WRITE(host->base, CON, con | DW8); +		break; +	case MMC_BUS_WIDTH_4: +		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); +		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, CON, con & ~DW8); +		OMAP_HSMMC_WRITE(host->base, HCTL, +			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); +		break; +	} +} + +static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host) +{ +	struct mmc_ios *ios = &host->mmc->ios; +	u32 con; + +	con = OMAP_HSMMC_READ(host->base, CON); +	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) +		OMAP_HSMMC_WRITE(host->base, CON, con | OD); +	else +		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); +} +  #ifdef CONFIG_PM  /* @@ -575,40 +644,18 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)  static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)  {  	struct mmc_ios *ios = &host->mmc->ios; -	struct omap_mmc_platform_data *pdata = host->pdata; -	int context_loss = 0; -	u32 hctl, capa, con; -	u16 dsor = 0; +	u32 hctl, capa;  	unsigned long timeout; -	if (pdata->get_context_loss_count) { -		context_loss = pdata->get_context_loss_count(host->dev); -		if (context_loss < 0) -			return 1; -	} - -	dev_dbg(mmc_dev(host->mmc), "context was %slost\n", -		context_loss == host->context_loss ? "not " : ""); -	if (host->context_loss == context_loss) -		return 1; - -	/* Wait for hardware reset */ -	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); -	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE -		&& time_before(jiffies, timeout)) -		; - -	/* Do software reset */ -	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET); -	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); -	while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE -		&& time_before(jiffies, timeout)) -		; +	if (host->con == OMAP_HSMMC_READ(host->base, CON) && +	    host->hctl == OMAP_HSMMC_READ(host->base, HCTL) && +	    host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) && +	    host->capa == OMAP_HSMMC_READ(host->base, CAPA)) +		return 0; -	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, -			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); +	host->context_loss++; -	if (host->id == OMAP_MMC1_DEVID) { +	if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {  		if (host->power_mode != MMC_POWER_OFF &&  		    (1 << ios->vdd) <= MMC_VDD_23_24)  			hctl = SDVS18; @@ -640,58 +687,15 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)  	if (host->power_mode == MMC_POWER_OFF)  		goto out; -	con = OMAP_HSMMC_READ(host->base, CON); -	switch (ios->bus_width) { -	case MMC_BUS_WIDTH_8: -		OMAP_HSMMC_WRITE(host->base, CON, con | DW8); -		break; -	case MMC_BUS_WIDTH_4: -		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); -		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, CON, con & ~DW8); -		OMAP_HSMMC_WRITE(host->base, HCTL, -			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); -		break; -	} - -	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_HSMMC_WRITE(host->base, SYSCTL, -		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); -	OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16)); -	OMAP_HSMMC_WRITE(host->base, SYSCTL, -		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); +	omap_hsmmc_set_bus_width(host); -	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); -	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS -		&& time_before(jiffies, timeout)) -		; +	omap_hsmmc_set_clock(host); -	OMAP_HSMMC_WRITE(host->base, SYSCTL, -		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); +	omap_hsmmc_set_bus_mode(host); -	con = OMAP_HSMMC_READ(host->base, CON); -	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) -		OMAP_HSMMC_WRITE(host->base, CON, con | OD); -	else -		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);  out: -	host->context_loss = context_loss; - -	dev_dbg(mmc_dev(host->mmc), "context is restored\n"); +	dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n", +		host->context_loss);  	return 0;  } @@ -700,15 +704,10 @@ out:   */  static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)  { -	struct omap_mmc_platform_data *pdata = host->pdata; -	int context_loss; - -	if (pdata->get_context_loss_count) { -		context_loss = pdata->get_context_loss_count(host->dev); -		if (context_loss < 0) -			return; -		host->context_loss = context_loss; -	} +	host->con =  OMAP_HSMMC_READ(host->base, CON); +	host->hctl = OMAP_HSMMC_READ(host->base, HCTL); +	host->sysctl =  OMAP_HSMMC_READ(host->base, SYSCTL); +	host->capa = OMAP_HSMMC_READ(host->base, CAPA);  }  #else @@ -744,8 +743,8 @@ static void send_init_stream(struct omap_hsmmc_host *host)  	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; +	while ((reg != CC_EN) && time_before(jiffies, timeout)) +		reg = OMAP_HSMMC_READ(host->base, STAT) & CC_EN;  	OMAP_HSMMC_WRITE(host->base, CON,  		OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); @@ -800,7 +799,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,  {  	int cmdreg = 0, resptype = 0, cmdtype = 0; -	dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n", +	dev_vdbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",  		mmc_hostname(host->mmc), cmd->opcode, cmd->arg);  	host->cmd = cmd; @@ -827,6 +826,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,  	cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); +	if ((host->flags & AUTO_CMD23) && mmc_op_multi(cmd->opcode) && +	    host->mrq->sbc) { +		cmdreg |= ACEN_ACMD23; +		OMAP_HSMMC_WRITE(host->base, SDMASA, host->mrq->sbc->arg); +	}  	if (data) {  		cmdreg |= DP_SELECT | MSBS | BCE;  		if (data->flags & MMC_DATA_READ) @@ -836,7 +840,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,  	}  	if (host->use_dma) -		cmdreg |= DMA_EN; +		cmdreg |= DMAE;  	host->req_in_progress = 1; @@ -853,14 +857,21 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)  		return DMA_FROM_DEVICE;  } +static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host, +	struct mmc_data *data) +{ +	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan; +} +  static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)  {  	int dma_ch; +	unsigned long flags; -	spin_lock(&host->irq_lock); +	spin_lock_irqsave(&host->irq_lock, flags);  	host->req_in_progress = 0;  	dma_ch = host->dma_ch; -	spin_unlock(&host->irq_lock); +	spin_unlock_irqrestore(&host->irq_lock, flags);  	omap_hsmmc_disable_irq(host);  	/* Do not complete the request if DMA is still in progress */ @@ -897,11 +908,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)  	else  		data->bytes_xfered = 0; -	if (!data->stop) { +	if (data->stop && (data->error || !host->mrq->sbc)) +		omap_hsmmc_start_command(host, data->stop, NULL); +	else  		omap_hsmmc_request_done(host, data->mrq); -		return; -	} -	omap_hsmmc_start_command(host, data->stop, NULL);  }  /* @@ -910,6 +920,15 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)  static void  omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)  { +	if (host->mrq->sbc && (host->cmd == host->mrq->sbc) && +	    !host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) { +		host->cmd = NULL; +		omap_hsmmc_start_dma_transfer(host); +		omap_hsmmc_start_command(host, host->mrq->cmd, +						host->mrq->data); +		return; +	} +  	host->cmd = NULL;  	if (cmd->flags & MMC_RSP_PRESENT) { @@ -925,7 +944,7 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)  		}  	}  	if ((host->data == NULL && !host->response_busy) || cmd->error) -		omap_hsmmc_request_done(host, cmd->mrq); +		omap_hsmmc_request_done(host, host->mrq);  }  /* @@ -934,18 +953,24 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)  static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)  {  	int dma_ch; +	unsigned long flags;  	host->data->error = errno; -	spin_lock(&host->irq_lock); +	spin_lock_irqsave(&host->irq_lock, flags);  	dma_ch = host->dma_ch;  	host->dma_ch = -1; -	spin_unlock(&host->irq_lock); +	spin_unlock_irqrestore(&host->irq_lock, flags);  	if (host->use_dma && dma_ch != -1) { -		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, +		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data); + +		dmaengine_terminate_all(chan); +		dma_unmap_sg(chan->device->dev, +			host->data->sg, host->data->sg_len,  			omap_hsmmc_get_dma_dir(host, host->data)); -		omap_free_dma(dma_ch); + +		host->data->host_cookie = 0;  	}  	host->data = NULL;  } @@ -954,14 +979,14 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)   * Readable error output   */  #ifdef CONFIG_MMC_DEBUG -static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status) +static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)  {  	/* --- means reserved bit without definition at documentation */  	static const char *omap_hsmmc_status_bits[] = { -		"CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ", -		"OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC", -		"CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---", -		"---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---" +		"CC"  , "TC"  , "BGE", "---", "BWR" , "BRR" , "---" , "---" , +		"CIRQ",	"OBI" , "---", "---", "---" , "---" , "---" , "ERRI", +		"CTO" , "CCRC", "CEB", "CIE", "DTO" , "DCRC", "DEB" , "---" , +		"ACE" , "---" , "---", "---", "CERR", "BADA", "---" , "---"  	};  	char res[256];  	char *buf = res; @@ -976,7 +1001,12 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)  			buf += len;  		} -	dev_dbg(mmc_dev(host->mmc), "%s\n", res); +	dev_vdbg(mmc_dev(host->mmc), "%s\n", res); +} +#else +static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, +					     u32 status) +{  }  #endif  /* CONFIG_MMC_DEBUG */ @@ -991,8 +1021,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,  						   unsigned long bit)  {  	unsigned long i = 0; -	unsigned long limit = (loops_per_jiffy * -				msecs_to_jiffies(MMC_TIMEOUT_MS)); +	unsigned long limit = MMC_TIMEOUT_US;  	OMAP_HSMMC_WRITE(host->base, SYSCTL,  			 OMAP_HSMMC_READ(host->base, SYSCTL) | bit); @@ -1002,15 +1031,15 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,  	 * Monitor a 0->1 transition first  	 */  	if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { -		while ((!(OMAP_HSMMC_READ(host, SYSCTL) & bit)) +		while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))  					&& (i++ < limit)) -			cpu_relax(); +			udelay(1);  	}  	i = 0;  	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&  		(i++ < limit)) -		cpu_relax(); +		udelay(1);  	if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)  		dev_err(mmc_dev(host->mmc), @@ -1018,77 +1047,65 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,  			__func__);  } +static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, +					int err, int end_cmd) +{ +	if (end_cmd) { +		omap_hsmmc_reset_controller_fsm(host, SRC); +		if (host->cmd) +			host->cmd->error = err; +	} + +	if (host->data) { +		omap_hsmmc_reset_controller_fsm(host, SRD); +		omap_hsmmc_dma_cleanup(host, err); +	} else if (host->mrq && host->mrq->cmd) +		host->mrq->cmd->error = err; +} +  static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)  {  	struct mmc_data *data;  	int end_cmd = 0, end_trans = 0; - -	if (!host->req_in_progress) { -		do { -			OMAP_HSMMC_WRITE(host->base, STAT, status); -			/* Flush posted write */ -			status = OMAP_HSMMC_READ(host->base, STAT); -		} while (status & INT_EN_MASK); -		return; -	} +	int error = 0;  	data = host->data; -	dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); - -	if (status & ERR) { -#ifdef CONFIG_MMC_DEBUG -		omap_hsmmc_report_irq(host, status); -#endif -		if ((status & CMD_TIMEOUT) || -			(status & CMD_CRC)) { -			if (host->cmd) { -				if (status & CMD_TIMEOUT) { -					omap_hsmmc_reset_controller_fsm(host, -									SRC); -					host->cmd->error = -ETIMEDOUT; -				} else { -					host->cmd->error = -EILSEQ; -				} +	dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); + +	if (status & ERR_EN) { +		omap_hsmmc_dbg_report_irq(host, status); + +		if (status & (CTO_EN | CCRC_EN)) +			end_cmd = 1; +		if (status & (CTO_EN | DTO_EN)) +			hsmmc_command_incomplete(host, -ETIMEDOUT, end_cmd); +		else if (status & (CCRC_EN | DCRC_EN)) +			hsmmc_command_incomplete(host, -EILSEQ, end_cmd); + +		if (status & ACE_EN) { +			u32 ac12; +			ac12 = OMAP_HSMMC_READ(host->base, AC12); +			if (!(ac12 & ACNE) && host->mrq->sbc) {  				end_cmd = 1; +				if (ac12 & ACTO) +					error =  -ETIMEDOUT; +				else if (ac12 & (ACCE | ACEB | ACIE)) +					error = -EILSEQ; +				host->mrq->sbc->error = error; +				hsmmc_command_incomplete(host, error, end_cmd);  			} -			if (host->data || host->response_busy) { -				if (host->data) -					omap_hsmmc_dma_cleanup(host, -								-ETIMEDOUT); -				host->response_busy = 0; -				omap_hsmmc_reset_controller_fsm(host, SRD); -			} +			dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);  		} -		if ((status & DATA_TIMEOUT) || -			(status & DATA_CRC)) { -			if (host->data || host->response_busy) { -				int err = (status & DATA_TIMEOUT) ? -						-ETIMEDOUT : -EILSEQ; - -				if (host->data) -					omap_hsmmc_dma_cleanup(host, err); -				else -					host->mrq->cmd->error = err; -				host->response_busy = 0; -				omap_hsmmc_reset_controller_fsm(host, SRD); -				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; +		if (host->data || host->response_busy) { +			end_trans = !end_cmd; +			host->response_busy = 0;  		}  	}  	OMAP_HSMMC_WRITE(host->base, STAT, status); - -	if (end_cmd || ((status & CC) && host->cmd)) +	if (end_cmd || ((status & CC_EN) && host->cmd))  		omap_hsmmc_cmd_done(host, host->cmd); -	if ((end_trans || (status & TC)) && host->mrq) +	if ((end_trans || (status & TC_EN)) && host->mrq)  		omap_hsmmc_xfer_done(host, data);  } @@ -1101,11 +1118,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)  	int status;  	status = OMAP_HSMMC_READ(host->base, STAT); -	do { +	while (status & INT_EN_MASK && host->req_in_progress) {  		omap_hsmmc_do_irq(host, status); +  		/* Flush posted write */  		status = OMAP_HSMMC_READ(host->base, STAT); -	} while (status & INT_EN_MASK); +	}  	return IRQ_HANDLED;  } @@ -1136,10 +1154,9 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)  	int ret;  	/* Disable the clocks */ -	clk_disable(host->fclk); -	clk_disable(host->iclk); -	if (host->got_dbclk) -		clk_disable(host->dbclk); +	pm_runtime_put_sync(host->dev); +	if (host->dbclk) +		clk_disable_unprepare(host->dbclk);  	/* Turn the power off */  	ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); @@ -1148,10 +1165,9 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)  	if (!ret)  		ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,  					       vdd); -	clk_enable(host->iclk); -	clk_enable(host->fclk); -	if (host->got_dbclk) -		clk_enable(host->dbclk); +	pm_runtime_get_sync(host->dev); +	if (host->dbclk) +		clk_prepare_enable(host->dbclk);  	if (ret != 0)  		goto err; @@ -1185,7 +1201,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)  	return 0;  err: -	dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); +	dev_err(mmc_dev(host->mmc), "Unable to switch operating voltage\n");  	return ret;  } @@ -1198,14 +1214,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)  	host->reqs_blocked = 0;  	if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {  		if (host->protect_card) { -			printk(KERN_INFO "%s: cover is closed, " +			dev_info(host->dev, "%s: cover is closed, "  					 "card is now accessible\n",  					 mmc_hostname(host->mmc));  			host->protect_card = 0;  		}  	} else {  		if (!host->protect_card) { -			printk(KERN_INFO "%s: cover is open, " +			dev_info(host->dev, "%s: cover is open, "  					 "card is now inaccessible\n",  					 mmc_hostname(host->mmc));  			host->protect_card = 1; @@ -1214,18 +1230,14 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)  }  /* - * Work Item to notify the core about card insertion/removal + * irq handler to notify the core about card insertion/removal   */ -static void omap_hsmmc_detect(struct work_struct *work) +static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)  { -	struct omap_hsmmc_host *host = -		container_of(work, struct omap_hsmmc_host, mmc_carddetect_work); +	struct omap_hsmmc_host *host = dev_id;  	struct omap_mmc_slot_data *slot = &mmc_slot(host);  	int carddetect; -	if (host->suspended) -		return; -  	sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");  	if (slot->card_detect) @@ -1239,121 +1251,91 @@ static void omap_hsmmc_detect(struct work_struct *work)  		mmc_detect_change(host->mmc, (HZ * 200) / 1000);  	else  		mmc_detect_change(host->mmc, (HZ * 50) / 1000); -} - -/* - * ISR for handling card insertion and removal - */ -static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id) -{ -	struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id; - -	if (host->suspended) -		return IRQ_HANDLED; -	schedule_work(&host->mmc_carddetect_work); -  	return IRQ_HANDLED;  } -static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, -				     struct mmc_data *data) +static void omap_hsmmc_dma_callback(void *param)  { -	int sync_dev; - -	if (data->flags & MMC_DATA_WRITE) -		sync_dev = host->dma_line_tx; -	else -		sync_dev = host->dma_line_rx; -	return sync_dev; -} - -static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, -				       struct mmc_data *data, -				       struct scatterlist *sgl) -{ -	int blksz, nblk, dma_ch; +	struct omap_hsmmc_host *host = param; +	struct dma_chan *chan; +	struct mmc_data *data; +	int req_in_progress; -	dma_ch = host->dma_ch; -	if (data->flags & MMC_DATA_WRITE) { -		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, -			(host->mapbase + OMAP_HSMMC_DATA), 0, 0); -		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, -			sg_dma_address(sgl), 0, 0); -	} else { -		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, -			(host->mapbase + OMAP_HSMMC_DATA), 0, 0); -		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, -			sg_dma_address(sgl), 0, 0); +	spin_lock_irq(&host->irq_lock); +	if (host->dma_ch < 0) { +		spin_unlock_irq(&host->irq_lock); +		return;  	} -	blksz = host->data->blksz; -	nblk = sg_dma_len(sgl) / blksz; +	data = host->mrq->data; +	chan = omap_hsmmc_get_dma_chan(host, data); +	if (!data->host_cookie) +		dma_unmap_sg(chan->device->dev, +			     data->sg, data->sg_len, +			     omap_hsmmc_get_dma_dir(host, data)); + +	req_in_progress = host->req_in_progress; +	host->dma_ch = -1; +	spin_unlock_irq(&host->irq_lock); -	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, -			blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, -			omap_hsmmc_get_dma_sync_dev(host, data), -			!(data->flags & MMC_DATA_WRITE)); +	/* If DMA has finished after TC, complete the request */ +	if (!req_in_progress) { +		struct mmc_request *mrq = host->mrq; -	omap_start_dma(dma_ch); +		host->mrq = NULL; +		mmc_request_done(host->mmc, mrq); +	}  } -/* - * DMA call back function - */ -static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) +static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, +				       struct mmc_data *data, +				       struct omap_hsmmc_next *next, +				       struct dma_chan *chan)  { -	struct omap_hsmmc_host *host = cb_data; -	struct mmc_data *data = host->mrq->data; -	int dma_ch, req_in_progress; +	int dma_len; -	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { -		dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", -			ch_status); -		return; +	if (!next && data->host_cookie && +	    data->host_cookie != host->next_data.cookie) { +		dev_warn(host->dev, "[%s] invalid cookie: data->host_cookie %d" +		       " host->next_data.cookie %d\n", +		       __func__, data->host_cookie, host->next_data.cookie); +		data->host_cookie = 0;  	} -	spin_lock(&host->irq_lock); -	if (host->dma_ch < 0) { -		spin_unlock(&host->irq_lock); -		return; -	} +	/* Check if next job is already prepared */ +	if (next || data->host_cookie != host->next_data.cookie) { +		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len, +				     omap_hsmmc_get_dma_dir(host, data)); -	host->dma_sg_idx++; -	if (host->dma_sg_idx < host->dma_len) { -		/* Fire up the next transfer. */ -		omap_hsmmc_config_dma_params(host, data, -					   data->sg + host->dma_sg_idx); -		spin_unlock(&host->irq_lock); -		return; +	} else { +		dma_len = host->next_data.dma_len; +		host->next_data.dma_len = 0;  	} -	dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, -		omap_hsmmc_get_dma_dir(host, data)); -	req_in_progress = host->req_in_progress; -	dma_ch = host->dma_ch; -	host->dma_ch = -1; -	spin_unlock(&host->irq_lock); - -	omap_free_dma(dma_ch); +	if (dma_len == 0) +		return -EINVAL; -	/* If DMA has finished after TC, complete the request */ -	if (!req_in_progress) { -		struct mmc_request *mrq = host->mrq; +	if (next) { +		next->dma_len = dma_len; +		data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; +	} else +		host->dma_len = dma_len; -		host->mrq = NULL; -		mmc_request_done(host->mmc, mrq); -	} +	return 0;  }  /*   * Routine to configure and start DMA for the MMC card   */ -static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host, +static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,  					struct mmc_request *req)  { -	int dma_ch = 0, ret = 0, i; +	struct dma_slave_config cfg; +	struct dma_async_tx_descriptor *tx; +	int ret = 0, i;  	struct mmc_data *data = req->data; +	struct dma_chan *chan;  	/* Sanity check: all the SG entries must be aligned by block size. */  	for (i = 0; i < data->sg_len; i++) { @@ -1371,21 +1353,39 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,  	BUG_ON(host->dma_ch != -1); -	ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), -			       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); -	if (ret != 0) { -		dev_err(mmc_dev(host->mmc), -			"%s: omap_request_dma() failed with %d\n", -			mmc_hostname(host->mmc), ret); +	chan = omap_hsmmc_get_dma_chan(host, data); + +	cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA; +	cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA; +	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +	cfg.src_maxburst = data->blksz / 4; +	cfg.dst_maxburst = data->blksz / 4; + +	ret = dmaengine_slave_config(chan, &cfg); +	if (ret) +		return ret; + +	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan); +	if (ret)  		return ret; + +	tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len, +		data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, +		DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +	if (!tx) { +		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n"); +		/* FIXME: cleanup */ +		return -1;  	} -	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, -			data->sg_len, omap_hsmmc_get_dma_dir(host, data)); -	host->dma_ch = dma_ch; -	host->dma_sg_idx = 0; +	tx->callback = omap_hsmmc_dma_callback; +	tx->callback_param = host; + +	/* Does not fail */ +	dmaengine_submit(tx); -	omap_hsmmc_config_dma_params(host, data, data->sg); +	host->dma_ch = 1;  	return 0;  } @@ -1402,7 +1402,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,  	if (clkd == 0)  		clkd = 1; -	cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); +	cycle_ns = 1000000000 / (host->clk_rate / clkd);  	timeout = timeout_ns / cycle_ns;  	timeout += timeout_clks;  	if (timeout) { @@ -1427,6 +1427,21 @@ static void set_data_timeout(struct omap_hsmmc_host *host,  	OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);  } +static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host) +{ +	struct mmc_request *req = host->mrq; +	struct dma_chan *chan; + +	if (!req->data) +		return; +	OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) +				| (req->data->blocks << 16)); +	set_data_timeout(host, req->data->timeout_ns, +				req->data->timeout_clks); +	chan = omap_hsmmc_get_dma_chan(host, req->data); +	dma_async_issue_pending(chan); +} +  /*   * Configure block length for MMC/SD cards and initiate the transfer.   */ @@ -1447,20 +1462,50 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)  		return 0;  	} -	OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) -					| (req->data->blocks << 16)); -	set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks); -  	if (host->use_dma) { -		ret = omap_hsmmc_start_dma_transfer(host, req); +		ret = omap_hsmmc_setup_dma_transfer(host, req);  		if (ret != 0) { -			dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); +			dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");  			return ret;  		}  	}  	return 0;  } +static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, +				int err) +{ +	struct omap_hsmmc_host *host = mmc_priv(mmc); +	struct mmc_data *data = mrq->data; + +	if (host->use_dma && data->host_cookie) { +		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data); + +		dma_unmap_sg(c->device->dev, data->sg, data->sg_len, +			     omap_hsmmc_get_dma_dir(host, data)); +		data->host_cookie = 0; +	} +} + +static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, +			       bool is_first_req) +{ +	struct omap_hsmmc_host *host = mmc_priv(mmc); + +	if (mrq->data->host_cookie) { +		mrq->data->host_cookie = 0; +		return ; +	} + +	if (host->use_dma) { +		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data); + +		if (omap_hsmmc_pre_dma_transfer(host, mrq->data, +						&host->next_data, c)) +			mrq->data->host_cookie = 0; +	} +} +  /*   * Request function. for read/write operation   */ @@ -1492,6 +1537,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)  		host->reqs_blocked = 0;  	WARN_ON(host->mrq != NULL);  	host->mrq = req; +	host->clk_rate = clk_get_rate(host->fclk);  	err = omap_hsmmc_prepare_data(host, req);  	if (err) {  		req->cmd->error = err; @@ -1501,7 +1547,12 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)  		mmc_request_done(mmc, req);  		return;  	} +	if (req->sbc && !(host->flags & AUTO_CMD23)) { +		omap_hsmmc_start_command(host, req->sbc, NULL); +		return; +	} +	omap_hsmmc_start_dma_transfer(host);  	omap_hsmmc_start_command(host, req->cmd, req->data);  } @@ -1509,25 +1560,19 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)  static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  {  	struct omap_hsmmc_host *host = mmc_priv(mmc); -	u16 dsor = 0; -	unsigned long regval; -	unsigned long timeout; -	u32 con;  	int do_send_init_stream = 0; -	mmc_host_enable(host->mmc); +	pm_runtime_get_sync(host->dev);  	if (ios->power_mode != host->power_mode) {  		switch (ios->power_mode) {  		case MMC_POWER_OFF:  			mmc_slot(host).set_power(host->dev, host->slot_id,  						 0, 0); -			host->vdd = 0;  			break;  		case MMC_POWER_UP:  			mmc_slot(host).set_power(host->dev, host->slot_id,  						 1, ios->vdd); -			host->vdd = ios->vdd;  			break;  		case MMC_POWER_ON:  			do_send_init_stream = 1; @@ -1538,24 +1583,9 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  	/* FIXME: set registers based only on changes to ios */ -	con = OMAP_HSMMC_READ(host->base, CON); -	switch (mmc->ios.bus_width) { -	case MMC_BUS_WIDTH_8: -		OMAP_HSMMC_WRITE(host->base, CON, con | DW8); -		break; -	case MMC_BUS_WIDTH_4: -		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); -		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, CON, con & ~DW8); -		OMAP_HSMMC_WRITE(host->base, HCTL, -			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); -		break; -	} +	omap_hsmmc_set_bus_width(host); -	if (host->id == OMAP_MMC1_DEVID) { +	if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {  		/* Only MMC1 can interface at 3V without some flavor  		 * of external transceiver; but they all handle 1.8V.  		 */ @@ -1573,47 +1603,14 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		}  	} -	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_hsmmc_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) != ICS -		&& time_before(jiffies, timeout)) -		msleep(1); - -	OMAP_HSMMC_WRITE(host->base, SYSCTL, -		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); +	omap_hsmmc_set_clock(host);  	if (do_send_init_stream)  		send_init_stream(host); -	con = OMAP_HSMMC_READ(host->base, CON); -	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) -		OMAP_HSMMC_WRITE(host->base, CON, con | OD); -	else -		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); +	omap_hsmmc_set_bus_mode(host); -	if (host->power_mode == MMC_POWER_OFF) -		mmc_host_disable(host->mmc); -	else -		mmc_host_lazy_disable(host->mmc); +	pm_runtime_put_autosuspend(host->dev);  }  static int omap_hsmmc_get_cd(struct mmc_host *mmc) @@ -1647,7 +1644,7 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)  	u32 hctl, capa, value;  	/* Only MMC1 supports 3.0V */ -	if (host->id == OMAP_MMC1_DEVID) { +	if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {  		hctl = SDVS30;  		capa = VS30 | VS18;  	} else { @@ -1661,265 +1658,34 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)  	value = OMAP_HSMMC_READ(host->base, CAPA);  	OMAP_HSMMC_WRITE(host->base, CAPA, value | capa); -	/* Set the controller to AUTO IDLE mode */ -	value = OMAP_HSMMC_READ(host->base, SYSCONFIG); -	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE); -  	/* Set SD bus power bit */  	set_sd_bus_power(host);  } -/* - * Dynamic power saving handling, FSM: - *   ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF - *     ^___________|          |                      | - *     |______________________|______________________| - * - * ENABLED:   mmc host is fully functional - * DISABLED:  fclk is off - * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep - * REGSLEEP:  fclk is off, voltage regulator is asleep - * OFF:       fclk is off, voltage regulator is off - * - * Transition handlers return the timeout for the next state transition - * or negative error. - */ - -enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF}; - -/* Handler for [ENABLED -> DISABLED] transition */ -static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host) -{ -	omap_hsmmc_context_save(host); -	clk_disable(host->fclk); -	host->dpm_state = DISABLED; - -	dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n"); - -	if (host->power_mode == MMC_POWER_OFF) -		return 0; - -	return OMAP_MMC_SLEEP_TIMEOUT; -} - -/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */ -static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host) -{ -	int err, new_state; - -	if (!mmc_try_claim_host(host->mmc)) -		return 0; - -	clk_enable(host->fclk); -	omap_hsmmc_context_restore(host); -	if (mmc_card_can_sleep(host->mmc)) { -		err = mmc_card_sleep(host->mmc); -		if (err < 0) { -			clk_disable(host->fclk); -			mmc_release_host(host->mmc); -			return err; -		} -		new_state = CARDSLEEP; -	} else { -		new_state = REGSLEEP; -	} -	if (mmc_slot(host).set_sleep) -		mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0, -					 new_state == CARDSLEEP); -	/* FIXME: turn off bus power and perhaps interrupts too */ -	clk_disable(host->fclk); -	host->dpm_state = new_state; - -	mmc_release_host(host->mmc); - -	dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n", -		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); - -	if (mmc_slot(host).no_off) -		return 0; - -	if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || -	    mmc_slot(host).card_detect || -	    (mmc_slot(host).get_cover_state && -	     mmc_slot(host).get_cover_state(host->dev, host->slot_id))) -		return OMAP_MMC_OFF_TIMEOUT; - -	return 0; -} - -/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */ -static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host) -{ -	if (!mmc_try_claim_host(host->mmc)) -		return 0; - -	if (mmc_slot(host).no_off) -		return 0; - -	if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) || -	      mmc_slot(host).card_detect || -	      (mmc_slot(host).get_cover_state && -	       mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) { -		mmc_release_host(host->mmc); -		return 0; -	} - -	mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); -	host->vdd = 0; -	host->power_mode = MMC_POWER_OFF; - -	dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n", -		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); - -	host->dpm_state = OFF; - -	mmc_release_host(host->mmc); - -	return 0; -} - -/* Handler for [DISABLED -> ENABLED] transition */ -static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host) -{ -	int err; - -	err = clk_enable(host->fclk); -	if (err < 0) -		return err; - -	omap_hsmmc_context_restore(host); -	host->dpm_state = ENABLED; - -	dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n"); - -	return 0; -} - -/* Handler for [SLEEP -> ENABLED] transition */ -static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host) -{ -	if (!mmc_try_claim_host(host->mmc)) -		return 0; - -	clk_enable(host->fclk); -	omap_hsmmc_context_restore(host); -	if (mmc_slot(host).set_sleep) -		mmc_slot(host).set_sleep(host->dev, host->slot_id, 0, -			 host->vdd, host->dpm_state == CARDSLEEP); -	if (mmc_card_can_sleep(host->mmc)) -		mmc_card_awake(host->mmc); - -	dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n", -		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP"); - -	host->dpm_state = ENABLED; - -	mmc_release_host(host->mmc); - -	return 0; -} - -/* Handler for [OFF -> ENABLED] transition */ -static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host) -{ -	clk_enable(host->fclk); - -	omap_hsmmc_context_restore(host); -	omap_hsmmc_conf_bus_power(host); -	mmc_power_restore_host(host->mmc); - -	host->dpm_state = ENABLED; - -	dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n"); - -	return 0; -} - -/* - * Bring MMC host to ENABLED from any other PM state. - */ -static int omap_hsmmc_enable(struct mmc_host *mmc) -{ -	struct omap_hsmmc_host *host = mmc_priv(mmc); - -	switch (host->dpm_state) { -	case DISABLED: -		return omap_hsmmc_disabled_to_enabled(host); -	case CARDSLEEP: -	case REGSLEEP: -		return omap_hsmmc_sleep_to_enabled(host); -	case OFF: -		return omap_hsmmc_off_to_enabled(host); -	default: -		dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); -		return -EINVAL; -	} -} - -/* - * Bring MMC host in PM state (one level deeper). - */ -static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy) -{ -	struct omap_hsmmc_host *host = mmc_priv(mmc); - -	switch (host->dpm_state) { -	case ENABLED: { -		int delay; - -		delay = omap_hsmmc_enabled_to_disabled(host); -		if (lazy || delay < 0) -			return delay; -		return 0; -	} -	case DISABLED: -		return omap_hsmmc_disabled_to_sleep(host); -	case CARDSLEEP: -	case REGSLEEP: -		return omap_hsmmc_sleep_to_off(host); -	default: -		dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n"); -		return -EINVAL; -	} -} -  static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)  {  	struct omap_hsmmc_host *host = mmc_priv(mmc); -	int err; -	err = clk_enable(host->fclk); -	if (err) -		return err; -	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); -	omap_hsmmc_context_restore(host); +	pm_runtime_get_sync(host->dev); +  	return 0;  } -static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy) +static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)  {  	struct omap_hsmmc_host *host = mmc_priv(mmc); -	omap_hsmmc_context_save(host); -	clk_disable(host->fclk); -	dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); +	pm_runtime_mark_last_busy(host->dev); +	pm_runtime_put_autosuspend(host->dev); +  	return 0;  }  static const struct mmc_host_ops omap_hsmmc_ops = {  	.enable = omap_hsmmc_enable_fclk,  	.disable = omap_hsmmc_disable_fclk, -	.request = omap_hsmmc_request, -	.set_ios = omap_hsmmc_set_ios, -	.get_cd = omap_hsmmc_get_cd, -	.get_ro = omap_hsmmc_get_ro, -	.init_card = omap_hsmmc_init_card, -	/* NYET -- enable_sdio_irq */ -}; - -static const struct mmc_host_ops omap_hsmmc_ps_ops = { -	.enable = omap_hsmmc_enable, -	.disable = omap_hsmmc_disable, +	.post_req = omap_hsmmc_post_req, +	.pre_req = omap_hsmmc_pre_req,  	.request = omap_hsmmc_request,  	.set_ios = omap_hsmmc_set_ios,  	.get_cd = omap_hsmmc_get_cd, @@ -1934,33 +1700,12 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)  {  	struct mmc_host *mmc = s->private;  	struct omap_hsmmc_host *host = mmc_priv(mmc); -	int context_loss = 0; - -	if (host->pdata->get_context_loss_count) -		context_loss = host->pdata->get_context_loss_count(host->dev); - -	seq_printf(s, "mmc%d:\n" -			" enabled:\t%d\n" -			" dpm_state:\t%d\n" -			" nesting_cnt:\t%d\n" -			" ctx_loss:\t%d:%d\n" -			"\nregs:\n", -			mmc->index, mmc->enabled ? 1 : 0, -			host->dpm_state, mmc->nesting_cnt, -			host->context_loss, context_loss); - -	if (host->suspended || host->dpm_state == OFF) { -		seq_printf(s, "host suspended, can't read registers\n"); -		return 0; -	} -	if (clk_enable(host->fclk) != 0) { -		seq_printf(s, "can't read the regs\n"); -		return 0; -	} +	seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", +			mmc->index, host->context_loss); + +	pm_runtime_get_sync(host->dev); -	seq_printf(s, "SYSCONFIG:\t0x%08x\n", -			OMAP_HSMMC_READ(host->base, SYSCONFIG));  	seq_printf(s, "CON:\t\t0x%08x\n",  			OMAP_HSMMC_READ(host->base, CON));  	seq_printf(s, "HCTL:\t\t0x%08x\n", @@ -1974,7 +1719,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)  	seq_printf(s, "CAPA:\t\t0x%08x\n",  			OMAP_HSMMC_READ(host->base, CAPA)); -	clk_disable(host->fclk); +	pm_runtime_mark_last_busy(host->dev); +	pm_runtime_put_autosuspend(host->dev);  	return 0;  } @@ -2006,13 +1752,121 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)  #endif -static int __init omap_hsmmc_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = { +	/* See 35xx errata 2.1.1.128 in SPRZ278F */ +	.controller_flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ, +}; + +static const struct omap_mmc_of_data omap4_mmc_of_data = { +	.reg_offset = 0x100, +}; + +static const struct of_device_id omap_mmc_of_match[] = { +	{ +		.compatible = "ti,omap2-hsmmc", +	}, +	{ +		.compatible = "ti,omap3-pre-es3-hsmmc", +		.data = &omap3_pre_es3_mmc_of_data, +	}, +	{ +		.compatible = "ti,omap3-hsmmc", +	}, +	{ +		.compatible = "ti,omap4-hsmmc", +		.data = &omap4_mmc_of_data, +	}, +	{}, +}; +MODULE_DEVICE_TABLE(of, omap_mmc_of_match); + +static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) +{ +	struct omap_mmc_platform_data *pdata; +	struct device_node *np = dev->of_node; +	u32 bus_width, max_freq; +	int cd_gpio, wp_gpio; + +	cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); +	wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); +	if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER) +		return ERR_PTR(-EPROBE_DEFER); + +	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); /* out of memory */ + +	if (of_find_property(np, "ti,dual-volt", NULL)) +		pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; + +	/* This driver only supports 1 slot */ +	pdata->nr_slots = 1; +	pdata->slots[0].switch_pin = cd_gpio; +	pdata->slots[0].gpio_wp = wp_gpio; + +	if (of_find_property(np, "ti,non-removable", NULL)) { +		pdata->slots[0].nonremovable = true; +		pdata->slots[0].no_regulator_off_init = true; +	} +	of_property_read_u32(np, "bus-width", &bus_width); +	if (bus_width == 4) +		pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA; +	else if (bus_width == 8) +		pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA; + +	if (of_find_property(np, "ti,needs-special-reset", NULL)) +		pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + +	if (!of_property_read_u32(np, "max-frequency", &max_freq)) +		pdata->max_freq = max_freq; + +	if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) +		pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + +	if (of_find_property(np, "keep-power-in-suspend", NULL)) +		pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER; + +	if (of_find_property(np, "enable-sdio-wakeup", NULL)) +		pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + +	return pdata; +} +#else +static inline struct omap_mmc_platform_data +			*of_get_hsmmc_pdata(struct device *dev) +{ +	return ERR_PTR(-EINVAL); +} +#endif + +static int omap_hsmmc_probe(struct platform_device *pdev)  {  	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;  	struct mmc_host *mmc;  	struct omap_hsmmc_host *host = NULL;  	struct resource *res;  	int ret, irq; +	const struct of_device_id *match; +	dma_cap_mask_t mask; +	unsigned tx_req, rx_req; +	struct pinctrl *pinctrl; +	const struct omap_mmc_of_data *data; +	void __iomem *base; + +	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); +	if (match) { +		pdata = of_get_hsmmc_pdata(&pdev->dev); + +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata); + +		if (match->data) { +			data = match->data; +			pdata->reg_offset = data->reg_offset; +			pdata->controller_flags |= data->controller_flags; +		} +	}  	if (pdata == NULL) {  		dev_err(&pdev->dev, "Platform Data is missing\n"); @@ -2029,12 +1883,9 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	if (res == NULL || irq < 0)  		return -ENXIO; -	res->start += pdata->reg_offset; -	res->end += pdata->reg_offset; -	res = request_mem_region(res->start, res->end - res->start + 1, -							pdev->name); -	if (res == NULL) -		return -EBUSY; +	base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(base)) +		return PTR_ERR(base);  	ret = omap_hsmmc_gpio_init(pdata);  	if (ret) @@ -2051,84 +1902,56 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	host->pdata	= pdata;  	host->dev	= &pdev->dev;  	host->use_dma	= 1; -	host->dev->dma_mask = &pdata->dma_mask;  	host->dma_ch	= -1;  	host->irq	= irq; -	host->id	= pdev->id;  	host->slot_id	= 0; -	host->mapbase	= res->start; -	host->base	= ioremap(host->mapbase, SZ_4K); +	host->mapbase	= res->start + pdata->reg_offset; +	host->base	= base + pdata->reg_offset;  	host->power_mode = MMC_POWER_OFF; +	host->next_data.cookie = 1; +	host->pbias_enabled = 0;  	platform_set_drvdata(pdev, host); -	INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); -	if (mmc_slot(host).power_saving) -		mmc->ops	= &omap_hsmmc_ps_ops; -	else -		mmc->ops	= &omap_hsmmc_ops; +	mmc->ops	= &omap_hsmmc_ops; -	/* -	 * If regulator_disable can only put vcc_aux to sleep then there is -	 * no off state. -	 */ -	if (mmc_slot(host).vcc_aux_disable_is_sleep) -		mmc_slot(host).no_off = 1; +	mmc->f_min = OMAP_MMC_MIN_CLOCK; -	mmc->f_min	= 400000; -	mmc->f_max	= 52000000; +	if (pdata->max_freq > 0) +		mmc->f_max = pdata->max_freq; +	else +		mmc->f_max = OMAP_MMC_MAX_CLOCK;  	spin_lock_init(&host->irq_lock); -	host->iclk = clk_get(&pdev->dev, "ick"); -	if (IS_ERR(host->iclk)) { -		ret = PTR_ERR(host->iclk); -		host->iclk = NULL; -		goto err1; -	} -	host->fclk = clk_get(&pdev->dev, "fck"); +	host->fclk = devm_clk_get(&pdev->dev, "fck");  	if (IS_ERR(host->fclk)) {  		ret = PTR_ERR(host->fclk);  		host->fclk = NULL; -		clk_put(host->iclk);  		goto err1;  	} -	omap_hsmmc_context_save(host); - -	mmc->caps |= MMC_CAP_DISABLE; -	mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT); -	/* we start off in DISABLED state */ -	host->dpm_state = DISABLED; - -	if (mmc_host_enable(host->mmc) != 0) { -		clk_put(host->iclk); -		clk_put(host->fclk); -		goto err1; +	if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { +		dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); +		mmc->caps2 |= MMC_CAP2_NO_MULTI_READ;  	} -	if (clk_enable(host->iclk) != 0) { -		mmc_host_disable(host->mmc); -		clk_put(host->iclk); -		clk_put(host->fclk); -		goto err1; -	} +	pm_runtime_enable(host->dev); +	pm_runtime_get_sync(host->dev); +	pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY); +	pm_runtime_use_autosuspend(host->dev); -	if (cpu_is_omap2430()) { -		host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); -		/* -		 * MMC can still work without debounce clock. -		 */ -		if (IS_ERR(host->dbclk)) -			dev_warn(mmc_dev(host->mmc), -				"Failed to get debounce clock\n"); -		else -			host->got_dbclk = 1; +	omap_hsmmc_context_save(host); -		if (host->got_dbclk) -			if (clk_enable(host->dbclk) != 0) -				dev_dbg(mmc_dev(host->mmc), "Enabling debounce" -							" clk failed\n"); +	host->dbclk = devm_clk_get(&pdev->dev, "mmchsdb_fck"); +	/* +	 * MMC can still work without debounce clock. +	 */ +	if (IS_ERR(host->dbclk)) { +		host->dbclk = NULL; +	} else if (clk_prepare_enable(host->dbclk) != 0) { +		dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); +		host->dbclk = NULL;  	}  	/* Since we do only SG emulation, we can have as many segs @@ -2150,48 +1973,64 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	if (mmc_slot(host).nonremovable)  		mmc->caps |= MMC_CAP_NONREMOVABLE; +	mmc->pm_caps = mmc_slot(host).pm_caps; +  	omap_hsmmc_conf_bus_power(host); -	/* Select DMA lines */ -	switch (host->id) { -	case OMAP_MMC1_DEVID: -		host->dma_line_tx = OMAP24XX_DMA_MMC1_TX; -		host->dma_line_rx = OMAP24XX_DMA_MMC1_RX; -		break; -	case OMAP_MMC2_DEVID: -		host->dma_line_tx = OMAP24XX_DMA_MMC2_TX; -		host->dma_line_rx = OMAP24XX_DMA_MMC2_RX; -		break; -	case OMAP_MMC3_DEVID: -		host->dma_line_tx = OMAP34XX_DMA_MMC3_TX; -		host->dma_line_rx = OMAP34XX_DMA_MMC3_RX; -		break; -	case OMAP_MMC4_DEVID: -		host->dma_line_tx = OMAP44XX_DMA_MMC4_TX; -		host->dma_line_rx = OMAP44XX_DMA_MMC4_RX; -		break; -	case OMAP_MMC5_DEVID: -		host->dma_line_tx = OMAP44XX_DMA_MMC5_TX; -		host->dma_line_rx = OMAP44XX_DMA_MMC5_RX; -		break; -	default: -		dev_err(mmc_dev(host->mmc), "Invalid MMC id\n"); +	if (!pdev->dev.of_node) { +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); +		if (!res) { +			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); +			ret = -ENXIO; +			goto err_irq; +		} +		tx_req = res->start; + +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); +		if (!res) { +			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); +			ret = -ENXIO; +			goto err_irq; +		} +		rx_req = res->start; +	} + +	dma_cap_zero(mask); +	dma_cap_set(DMA_SLAVE, mask); + +	host->rx_chan = +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn, +						 &rx_req, &pdev->dev, "rx"); + +	if (!host->rx_chan) { +		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req); +		ret = -ENXIO; +		goto err_irq; +	} + +	host->tx_chan = +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn, +						 &tx_req, &pdev->dev, "tx"); + +	if (!host->tx_chan) { +		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req); +		ret = -ENXIO;  		goto err_irq;  	}  	/* Request IRQ for MMC operations */ -	ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED, +	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,  			mmc_hostname(mmc), host);  	if (ret) { -		dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); +		dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");  		goto err_irq;  	}  	if (pdata->init != NULL) {  		if (pdata->init(&pdev->dev) != 0) { -			dev_dbg(mmc_dev(host->mmc), +			dev_err(mmc_dev(host->mmc),  				"Unable to configure MMC IRQs\n"); -			goto err_irq_cd_init; +			goto err_irq;  		}  	} @@ -2206,13 +2045,13 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	/* Request IRQ for card detect */  	if ((mmc_slot(host).card_detect_irq)) { -		ret = request_irq(mmc_slot(host).card_detect_irq, -				  omap_hsmmc_cd_handler, -				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING -					  | IRQF_DISABLED, -				  mmc_hostname(mmc), host); +		ret = devm_request_threaded_irq(&pdev->dev, +						mmc_slot(host).card_detect_irq, +						NULL, omap_hsmmc_detect, +					   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					   mmc_hostname(mmc), host);  		if (ret) { -			dev_dbg(mmc_dev(host->mmc), +			dev_err(mmc_dev(host->mmc),  				"Unable to grab MMC CD IRQ\n");  			goto err_irq_cd;  		} @@ -2222,7 +2061,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	omap_hsmmc_disable_irq(host); -	mmc_host_lazy_disable(host->mmc); +	pinctrl = devm_pinctrl_get_select_default(&pdev->dev); +	if (IS_ERR(pinctrl)) +		dev_warn(&pdev->dev, +			"pins are not configured from the driver\n");  	omap_hsmmc_protect_card(host); @@ -2241,213 +2083,178 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)  	}  	omap_hsmmc_debugfs(mmc); +	pm_runtime_mark_last_busy(host->dev); +	pm_runtime_put_autosuspend(host->dev);  	return 0;  err_slot_name:  	mmc_remove_host(mmc); -	free_irq(mmc_slot(host).card_detect_irq, host);  err_irq_cd:  	if (host->use_reg)  		omap_hsmmc_reg_put(host);  err_reg:  	if (host->pdata->cleanup)  		host->pdata->cleanup(&pdev->dev); -err_irq_cd_init: -	free_irq(host->irq, host);  err_irq: -	mmc_host_disable(host->mmc); -	clk_disable(host->iclk); -	clk_put(host->fclk); -	clk_put(host->iclk); -	if (host->got_dbclk) { -		clk_disable(host->dbclk); -		clk_put(host->dbclk); -	} +	if (host->tx_chan) +		dma_release_channel(host->tx_chan); +	if (host->rx_chan) +		dma_release_channel(host->rx_chan); +	pm_runtime_put_sync(host->dev); +	pm_runtime_disable(host->dev); +	if (host->dbclk) +		clk_disable_unprepare(host->dbclk);  err1: -	iounmap(host->base); -	platform_set_drvdata(pdev, NULL);  	mmc_free_host(mmc);  err_alloc:  	omap_hsmmc_gpio_free(pdata);  err: -	release_mem_region(res->start, res->end - res->start + 1);  	return ret;  }  static int omap_hsmmc_remove(struct platform_device *pdev)  {  	struct omap_hsmmc_host *host = platform_get_drvdata(pdev); -	struct resource *res; -	if (host) { -		mmc_host_enable(host->mmc); -		mmc_remove_host(host->mmc); -		if (host->use_reg) -			omap_hsmmc_reg_put(host); -		if (host->pdata->cleanup) -			host->pdata->cleanup(&pdev->dev); -		free_irq(host->irq, host); -		if (mmc_slot(host).card_detect_irq) -			free_irq(mmc_slot(host).card_detect_irq, host); -		flush_scheduled_work(); - -		mmc_host_disable(host->mmc); -		clk_disable(host->iclk); -		clk_put(host->fclk); -		clk_put(host->iclk); -		if (host->got_dbclk) { -			clk_disable(host->dbclk); -			clk_put(host->dbclk); -		} +	pm_runtime_get_sync(host->dev); +	mmc_remove_host(host->mmc); +	if (host->use_reg) +		omap_hsmmc_reg_put(host); +	if (host->pdata->cleanup) +		host->pdata->cleanup(&pdev->dev); -		mmc_free_host(host->mmc); -		iounmap(host->base); -		omap_hsmmc_gpio_free(pdev->dev.platform_data); -	} +	if (host->tx_chan) +		dma_release_channel(host->tx_chan); +	if (host->rx_chan) +		dma_release_channel(host->rx_chan); -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res) -		release_mem_region(res->start, res->end - res->start + 1); -	platform_set_drvdata(pdev, NULL); +	pm_runtime_put_sync(host->dev); +	pm_runtime_disable(host->dev); +	if (host->dbclk) +		clk_disable_unprepare(host->dbclk); + +	omap_hsmmc_gpio_free(host->pdata); +	mmc_free_host(host->mmc);  	return 0;  }  #ifdef CONFIG_PM +static int omap_hsmmc_prepare(struct device *dev) +{ +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); + +	if (host->pdata->suspend) +		return host->pdata->suspend(dev, host->slot_id); + +	return 0; +} + +static void omap_hsmmc_complete(struct device *dev) +{ +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); + +	if (host->pdata->resume) +		host->pdata->resume(dev, host->slot_id); + +} +  static int omap_hsmmc_suspend(struct device *dev)  { -	int ret = 0; -	struct platform_device *pdev = to_platform_device(dev); -	struct omap_hsmmc_host *host = platform_get_drvdata(pdev); +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); -	if (host && host->suspended) +	if (!host)  		return 0; -	if (host) { -		host->suspended = 1; -		if (host->pdata->suspend) { -			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"); -				host->suspended = 0; -				return ret; -			} -		} -		cancel_work_sync(&host->mmc_carddetect_work); -		ret = mmc_suspend_host(host->mmc); -		mmc_host_enable(host->mmc); -		if (ret == 0) { -			omap_hsmmc_disable_irq(host); -			OMAP_HSMMC_WRITE(host->base, HCTL, -				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); -			mmc_host_disable(host->mmc); -			clk_disable(host->iclk); -			if (host->got_dbclk) -				clk_disable(host->dbclk); -		} else { -			host->suspended = 0; -			if (host->pdata->resume) { -				ret = host->pdata->resume(&pdev->dev, -							  host->slot_id); -				if (ret) -					dev_dbg(mmc_dev(host->mmc), -						"Unmask interrupt failed\n"); -			} -			mmc_host_disable(host->mmc); -		} +	pm_runtime_get_sync(host->dev); +	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { +		omap_hsmmc_disable_irq(host); +		OMAP_HSMMC_WRITE(host->base, HCTL, +				OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);  	} -	return ret; + +	if (host->dbclk) +		clk_disable_unprepare(host->dbclk); + +	pm_runtime_put_sync(host->dev); +	return 0;  }  /* Routine to resume the MMC device */  static int omap_hsmmc_resume(struct device *dev)  { -	int ret = 0; -	struct platform_device *pdev = to_platform_device(dev); -	struct omap_hsmmc_host *host = platform_get_drvdata(pdev); +	struct omap_hsmmc_host *host = dev_get_drvdata(dev); -	if (host && !host->suspended) +	if (!host)  		return 0; -	if (host) { -		ret = clk_enable(host->iclk); -		if (ret) -			goto clk_en_err; - -		if (mmc_host_enable(host->mmc) != 0) { -			clk_disable(host->iclk); -			goto clk_en_err; -		} +	pm_runtime_get_sync(host->dev); -		if (host->got_dbclk) -			clk_enable(host->dbclk); +	if (host->dbclk) +		clk_prepare_enable(host->dbclk); +	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))  		omap_hsmmc_conf_bus_power(host); -		if (host->pdata->resume) { -			ret = host->pdata->resume(&pdev->dev, host->slot_id); -			if (ret) -				dev_dbg(mmc_dev(host->mmc), -					"Unmask interrupt failed\n"); -		} +	omap_hsmmc_protect_card(host); -		omap_hsmmc_protect_card(host); +	pm_runtime_mark_last_busy(host->dev); +	pm_runtime_put_autosuspend(host->dev); +	return 0; +} -		/* Notify the core to resume the host */ -		ret = mmc_resume_host(host->mmc); -		if (ret == 0) -			host->suspended = 0; +#else +#define omap_hsmmc_prepare	NULL +#define omap_hsmmc_complete	NULL +#define omap_hsmmc_suspend	NULL +#define omap_hsmmc_resume	NULL +#endif -		mmc_host_lazy_disable(host->mmc); -	} +static int omap_hsmmc_runtime_suspend(struct device *dev) +{ +	struct omap_hsmmc_host *host; -	return ret; +	host = platform_get_drvdata(to_platform_device(dev)); +	omap_hsmmc_context_save(host); +	dev_dbg(dev, "disabled\n"); -clk_en_err: -	dev_dbg(mmc_dev(host->mmc), -		"Failed to enable MMC clocks during resume\n"); -	return ret; +	return 0;  } -#else -#define omap_hsmmc_suspend	NULL -#define omap_hsmmc_resume		NULL -#endif +static int omap_hsmmc_runtime_resume(struct device *dev) +{ +	struct omap_hsmmc_host *host; + +	host = platform_get_drvdata(to_platform_device(dev)); +	omap_hsmmc_context_restore(host); +	dev_dbg(dev, "enabled\n"); + +	return 0; +}  static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {  	.suspend	= omap_hsmmc_suspend,  	.resume		= omap_hsmmc_resume, +	.prepare	= omap_hsmmc_prepare, +	.complete	= omap_hsmmc_complete, +	.runtime_suspend = omap_hsmmc_runtime_suspend, +	.runtime_resume = omap_hsmmc_runtime_resume,  };  static struct platform_driver omap_hsmmc_driver = { +	.probe		= omap_hsmmc_probe,  	.remove		= omap_hsmmc_remove,  	.driver		= {  		.name = DRIVER_NAME,  		.owner = THIS_MODULE,  		.pm = &omap_hsmmc_dev_pm_ops, +		.of_match_table = of_match_ptr(omap_mmc_of_match),  	},  }; -static int __init omap_hsmmc_init(void) -{ -	/* Register the MMC driver */ -	return platform_driver_probe(&omap_hsmmc_driver, omap_hsmmc_probe); -} - -static void __exit omap_hsmmc_cleanup(void) -{ -	/* Unregister MMC driver */ -	platform_driver_unregister(&omap_hsmmc_driver); -} - -module_init(omap_hsmmc_init); -module_exit(omap_hsmmc_cleanup); - +module_platform_driver(omap_hsmmc_driver);  MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");  MODULE_LICENSE("GPL");  MODULE_ALIAS("platform:" DRIVER_NAME);  | 
