xref: /openbmc/linux/drivers/mmc/host/sdhci-omap.c (revision 7d33c358)
17d326930SKishon Vijay Abraham I /**
27d326930SKishon Vijay Abraham I  * SDHCI Controller driver for TI's OMAP SoCs
37d326930SKishon Vijay Abraham I  *
47d326930SKishon Vijay Abraham I  * Copyright (C) 2017 Texas Instruments
57d326930SKishon Vijay Abraham I  * Author: Kishon Vijay Abraham I <kishon@ti.com>
67d326930SKishon Vijay Abraham I  *
77d326930SKishon Vijay Abraham I  * This program is free software: you can redistribute it and/or modify
87d326930SKishon Vijay Abraham I  * it under the terms of the GNU General Public License version 2 of
97d326930SKishon Vijay Abraham I  * the License as published by the Free Software Foundation.
107d326930SKishon Vijay Abraham I  *
117d326930SKishon Vijay Abraham I  * This program is distributed in the hope that it will be useful,
127d326930SKishon Vijay Abraham I  * but WITHOUT ANY WARRANTY; without even the implied warranty of
137d326930SKishon Vijay Abraham I  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147d326930SKishon Vijay Abraham I  * GNU General Public License for more details.
157d326930SKishon Vijay Abraham I  *
167d326930SKishon Vijay Abraham I  * You should have received a copy of the GNU General Public License
177d326930SKishon Vijay Abraham I  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
187d326930SKishon Vijay Abraham I  */
197d326930SKishon Vijay Abraham I 
207d326930SKishon Vijay Abraham I #include <linux/delay.h>
217d326930SKishon Vijay Abraham I #include <linux/mmc/slot-gpio.h>
227d326930SKishon Vijay Abraham I #include <linux/module.h>
237d326930SKishon Vijay Abraham I #include <linux/of.h>
247d326930SKishon Vijay Abraham I #include <linux/of_device.h>
257d326930SKishon Vijay Abraham I #include <linux/platform_device.h>
267d326930SKishon Vijay Abraham I #include <linux/pm_runtime.h>
277d326930SKishon Vijay Abraham I #include <linux/regulator/consumer.h>
287d326930SKishon Vijay Abraham I 
297d326930SKishon Vijay Abraham I #include "sdhci-pltfm.h"
307d326930SKishon Vijay Abraham I 
317d326930SKishon Vijay Abraham I #define SDHCI_OMAP_CON		0x12c
327d326930SKishon Vijay Abraham I #define CON_DW8			BIT(5)
337d326930SKishon Vijay Abraham I #define CON_DMA_MASTER		BIT(20)
3427ceb7e0SKishon Vijay Abraham I #define CON_DDR			BIT(19)
3520ea26a1SKishon Vijay Abraham I #define CON_CLKEXTFREE		BIT(16)
3620ea26a1SKishon Vijay Abraham I #define CON_PADEN		BIT(15)
377d326930SKishon Vijay Abraham I #define CON_INIT		BIT(1)
387d326930SKishon Vijay Abraham I #define CON_OD			BIT(0)
397d326930SKishon Vijay Abraham I 
409fc2cd76SKishon Vijay Abraham I #define SDHCI_OMAP_DLL		0x0134
419fc2cd76SKishon Vijay Abraham I #define DLL_SWT			BIT(20)
429fc2cd76SKishon Vijay Abraham I #define DLL_FORCE_SR_C_SHIFT	13
439fc2cd76SKishon Vijay Abraham I #define DLL_FORCE_SR_C_MASK	(0x7f << DLL_FORCE_SR_C_SHIFT)
449fc2cd76SKishon Vijay Abraham I #define DLL_FORCE_VALUE		BIT(12)
459fc2cd76SKishon Vijay Abraham I #define DLL_CALIB		BIT(1)
469fc2cd76SKishon Vijay Abraham I 
477d326930SKishon Vijay Abraham I #define SDHCI_OMAP_CMD		0x20c
487d326930SKishon Vijay Abraham I 
4920ea26a1SKishon Vijay Abraham I #define SDHCI_OMAP_PSTATE	0x0224
5020ea26a1SKishon Vijay Abraham I #define PSTATE_DLEV_DAT0	BIT(20)
5120ea26a1SKishon Vijay Abraham I #define PSTATE_DATI		BIT(1)
5220ea26a1SKishon Vijay Abraham I 
537d326930SKishon Vijay Abraham I #define SDHCI_OMAP_HCTL		0x228
547d326930SKishon Vijay Abraham I #define HCTL_SDBP		BIT(8)
557d326930SKishon Vijay Abraham I #define HCTL_SDVS_SHIFT		9
567d326930SKishon Vijay Abraham I #define HCTL_SDVS_MASK		(0x7 << HCTL_SDVS_SHIFT)
577d326930SKishon Vijay Abraham I #define HCTL_SDVS_33		(0x7 << HCTL_SDVS_SHIFT)
587d326930SKishon Vijay Abraham I #define HCTL_SDVS_30		(0x6 << HCTL_SDVS_SHIFT)
597d326930SKishon Vijay Abraham I #define HCTL_SDVS_18		(0x5 << HCTL_SDVS_SHIFT)
607d326930SKishon Vijay Abraham I 
617d326930SKishon Vijay Abraham I #define SDHCI_OMAP_SYSCTL	0x22c
627d326930SKishon Vijay Abraham I #define SYSCTL_CEN		BIT(2)
637d326930SKishon Vijay Abraham I #define SYSCTL_CLKD_SHIFT	6
647d326930SKishon Vijay Abraham I #define SYSCTL_CLKD_MASK	0x3ff
657d326930SKishon Vijay Abraham I 
667d326930SKishon Vijay Abraham I #define SDHCI_OMAP_STAT		0x230
677d326930SKishon Vijay Abraham I 
687d326930SKishon Vijay Abraham I #define SDHCI_OMAP_IE		0x234
697d326930SKishon Vijay Abraham I #define INT_CC_EN		BIT(0)
707d326930SKishon Vijay Abraham I 
717d326930SKishon Vijay Abraham I #define SDHCI_OMAP_AC12		0x23c
727d326930SKishon Vijay Abraham I #define AC12_V1V8_SIGEN		BIT(19)
739fc2cd76SKishon Vijay Abraham I #define AC12_SCLK_SEL		BIT(23)
747d326930SKishon Vijay Abraham I 
757d326930SKishon Vijay Abraham I #define SDHCI_OMAP_CAPA		0x240
767d326930SKishon Vijay Abraham I #define CAPA_VS33		BIT(24)
777d326930SKishon Vijay Abraham I #define CAPA_VS30		BIT(25)
787d326930SKishon Vijay Abraham I #define CAPA_VS18		BIT(26)
797d326930SKishon Vijay Abraham I 
809fc2cd76SKishon Vijay Abraham I #define SDHCI_OMAP_CAPA2	0x0244
819fc2cd76SKishon Vijay Abraham I #define CAPA2_TSDR50		BIT(13)
829fc2cd76SKishon Vijay Abraham I 
837d326930SKishon Vijay Abraham I #define SDHCI_OMAP_TIMEOUT	1		/* 1 msec */
847d326930SKishon Vijay Abraham I 
857d326930SKishon Vijay Abraham I #define SYSCTL_CLKD_MAX		0x3FF
867d326930SKishon Vijay Abraham I 
877d326930SKishon Vijay Abraham I #define IOV_1V8			1800000		/* 180000 uV */
887d326930SKishon Vijay Abraham I #define IOV_3V0			3000000		/* 300000 uV */
897d326930SKishon Vijay Abraham I #define IOV_3V3			3300000		/* 330000 uV */
907d326930SKishon Vijay Abraham I 
919fc2cd76SKishon Vijay Abraham I #define MAX_PHASE_DELAY		0x7C
929fc2cd76SKishon Vijay Abraham I 
937d326930SKishon Vijay Abraham I struct sdhci_omap_data {
947d326930SKishon Vijay Abraham I 	u32 offset;
957d326930SKishon Vijay Abraham I };
967d326930SKishon Vijay Abraham I 
977d326930SKishon Vijay Abraham I struct sdhci_omap_host {
987d326930SKishon Vijay Abraham I 	void __iomem		*base;
997d326930SKishon Vijay Abraham I 	struct device		*dev;
1007d326930SKishon Vijay Abraham I 	struct	regulator	*pbias;
1017d326930SKishon Vijay Abraham I 	bool			pbias_enabled;
1027d326930SKishon Vijay Abraham I 	struct sdhci_host	*host;
1037d326930SKishon Vijay Abraham I 	u8			bus_mode;
1047d326930SKishon Vijay Abraham I 	u8			power_mode;
1057d326930SKishon Vijay Abraham I };
1067d326930SKishon Vijay Abraham I 
1077d326930SKishon Vijay Abraham I static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
1087d326930SKishon Vijay Abraham I 				   unsigned int offset)
1097d326930SKishon Vijay Abraham I {
1107d326930SKishon Vijay Abraham I 	return readl(host->base + offset);
1117d326930SKishon Vijay Abraham I }
1127d326930SKishon Vijay Abraham I 
1137d326930SKishon Vijay Abraham I static inline void sdhci_omap_writel(struct sdhci_omap_host *host,
1147d326930SKishon Vijay Abraham I 				     unsigned int offset, u32 data)
1157d326930SKishon Vijay Abraham I {
1167d326930SKishon Vijay Abraham I 	writel(data, host->base + offset);
1177d326930SKishon Vijay Abraham I }
1187d326930SKishon Vijay Abraham I 
1197d326930SKishon Vijay Abraham I static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
1207d326930SKishon Vijay Abraham I 				bool power_on, unsigned int iov)
1217d326930SKishon Vijay Abraham I {
1227d326930SKishon Vijay Abraham I 	int ret;
1237d326930SKishon Vijay Abraham I 	struct device *dev = omap_host->dev;
1247d326930SKishon Vijay Abraham I 
1257d326930SKishon Vijay Abraham I 	if (IS_ERR(omap_host->pbias))
1267d326930SKishon Vijay Abraham I 		return 0;
1277d326930SKishon Vijay Abraham I 
1287d326930SKishon Vijay Abraham I 	if (power_on) {
1297d326930SKishon Vijay Abraham I 		ret = regulator_set_voltage(omap_host->pbias, iov, iov);
1307d326930SKishon Vijay Abraham I 		if (ret) {
1317d326930SKishon Vijay Abraham I 			dev_err(dev, "pbias set voltage failed\n");
1327d326930SKishon Vijay Abraham I 			return ret;
1337d326930SKishon Vijay Abraham I 		}
1347d326930SKishon Vijay Abraham I 
1357d326930SKishon Vijay Abraham I 		if (omap_host->pbias_enabled)
1367d326930SKishon Vijay Abraham I 			return 0;
1377d326930SKishon Vijay Abraham I 
1387d326930SKishon Vijay Abraham I 		ret = regulator_enable(omap_host->pbias);
1397d326930SKishon Vijay Abraham I 		if (ret) {
1407d326930SKishon Vijay Abraham I 			dev_err(dev, "pbias reg enable fail\n");
1417d326930SKishon Vijay Abraham I 			return ret;
1427d326930SKishon Vijay Abraham I 		}
1437d326930SKishon Vijay Abraham I 
1447d326930SKishon Vijay Abraham I 		omap_host->pbias_enabled = true;
1457d326930SKishon Vijay Abraham I 	} else {
1467d326930SKishon Vijay Abraham I 		if (!omap_host->pbias_enabled)
1477d326930SKishon Vijay Abraham I 			return 0;
1487d326930SKishon Vijay Abraham I 
1497d326930SKishon Vijay Abraham I 		ret = regulator_disable(omap_host->pbias);
1507d326930SKishon Vijay Abraham I 		if (ret) {
1517d326930SKishon Vijay Abraham I 			dev_err(dev, "pbias reg disable fail\n");
1527d326930SKishon Vijay Abraham I 			return ret;
1537d326930SKishon Vijay Abraham I 		}
1547d326930SKishon Vijay Abraham I 		omap_host->pbias_enabled = false;
1557d326930SKishon Vijay Abraham I 	}
1567d326930SKishon Vijay Abraham I 
1577d326930SKishon Vijay Abraham I 	return 0;
1587d326930SKishon Vijay Abraham I }
1597d326930SKishon Vijay Abraham I 
1607d326930SKishon Vijay Abraham I static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
1617d326930SKishon Vijay Abraham I 				 unsigned int iov)
1627d326930SKishon Vijay Abraham I {
1637d326930SKishon Vijay Abraham I 	int ret;
1647d326930SKishon Vijay Abraham I 	struct sdhci_host *host = omap_host->host;
1657d326930SKishon Vijay Abraham I 	struct mmc_host *mmc = host->mmc;
1667d326930SKishon Vijay Abraham I 
1677d326930SKishon Vijay Abraham I 	ret = sdhci_omap_set_pbias(omap_host, false, 0);
1687d326930SKishon Vijay Abraham I 	if (ret)
1697d326930SKishon Vijay Abraham I 		return ret;
1707d326930SKishon Vijay Abraham I 
1717d326930SKishon Vijay Abraham I 	if (!IS_ERR(mmc->supply.vqmmc)) {
1727d326930SKishon Vijay Abraham I 		ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov);
1737d326930SKishon Vijay Abraham I 		if (ret) {
1747d326930SKishon Vijay Abraham I 			dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
1757d326930SKishon Vijay Abraham I 			return ret;
1767d326930SKishon Vijay Abraham I 		}
1777d326930SKishon Vijay Abraham I 	}
1787d326930SKishon Vijay Abraham I 
1797d326930SKishon Vijay Abraham I 	ret = sdhci_omap_set_pbias(omap_host, true, iov);
1807d326930SKishon Vijay Abraham I 	if (ret)
1817d326930SKishon Vijay Abraham I 		return ret;
1827d326930SKishon Vijay Abraham I 
1837d326930SKishon Vijay Abraham I 	return 0;
1847d326930SKishon Vijay Abraham I }
1857d326930SKishon Vijay Abraham I 
1867d326930SKishon Vijay Abraham I static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
1877d326930SKishon Vijay Abraham I 				      unsigned char signal_voltage)
1887d326930SKishon Vijay Abraham I {
1897d326930SKishon Vijay Abraham I 	u32 reg;
1907d326930SKishon Vijay Abraham I 	ktime_t timeout;
1917d326930SKishon Vijay Abraham I 
1927d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
1937d326930SKishon Vijay Abraham I 	reg &= ~HCTL_SDVS_MASK;
1947d326930SKishon Vijay Abraham I 
1957d326930SKishon Vijay Abraham I 	if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
1967d326930SKishon Vijay Abraham I 		reg |= HCTL_SDVS_33;
1977d326930SKishon Vijay Abraham I 	else
1987d326930SKishon Vijay Abraham I 		reg |= HCTL_SDVS_18;
1997d326930SKishon Vijay Abraham I 
2007d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
2017d326930SKishon Vijay Abraham I 
2027d326930SKishon Vijay Abraham I 	reg |= HCTL_SDBP;
2037d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
2047d326930SKishon Vijay Abraham I 
2057d326930SKishon Vijay Abraham I 	/* wait 1ms */
2067d326930SKishon Vijay Abraham I 	timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
2077d326930SKishon Vijay Abraham I 	while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)) {
2087d326930SKishon Vijay Abraham I 		if (WARN_ON(ktime_after(ktime_get(), timeout)))
2097d326930SKishon Vijay Abraham I 			return;
2107d326930SKishon Vijay Abraham I 		usleep_range(5, 10);
2117d326930SKishon Vijay Abraham I 	}
2127d326930SKishon Vijay Abraham I }
2137d326930SKishon Vijay Abraham I 
2149fc2cd76SKishon Vijay Abraham I static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
2159fc2cd76SKishon Vijay Abraham I 				      int count)
2169fc2cd76SKishon Vijay Abraham I {
2179fc2cd76SKishon Vijay Abraham I 	int i;
2189fc2cd76SKishon Vijay Abraham I 	u32 reg;
2199fc2cd76SKishon Vijay Abraham I 
2209fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
2219fc2cd76SKishon Vijay Abraham I 	reg |= DLL_FORCE_VALUE;
2229fc2cd76SKishon Vijay Abraham I 	reg &= ~DLL_FORCE_SR_C_MASK;
2239fc2cd76SKishon Vijay Abraham I 	reg |= (count << DLL_FORCE_SR_C_SHIFT);
2249fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
2259fc2cd76SKishon Vijay Abraham I 
2269fc2cd76SKishon Vijay Abraham I 	reg |= DLL_CALIB;
2279fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
2289fc2cd76SKishon Vijay Abraham I 	for (i = 0; i < 1000; i++) {
2299fc2cd76SKishon Vijay Abraham I 		reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
2309fc2cd76SKishon Vijay Abraham I 		if (reg & DLL_CALIB)
2319fc2cd76SKishon Vijay Abraham I 			break;
2329fc2cd76SKishon Vijay Abraham I 	}
2339fc2cd76SKishon Vijay Abraham I 	reg &= ~DLL_CALIB;
2349fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
2359fc2cd76SKishon Vijay Abraham I }
2369fc2cd76SKishon Vijay Abraham I 
2379fc2cd76SKishon Vijay Abraham I static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
2389fc2cd76SKishon Vijay Abraham I {
2399fc2cd76SKishon Vijay Abraham I 	u32 reg;
2409fc2cd76SKishon Vijay Abraham I 
2419fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
2429fc2cd76SKishon Vijay Abraham I 	reg &= ~AC12_SCLK_SEL;
2439fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
2449fc2cd76SKishon Vijay Abraham I 
2459fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
2469fc2cd76SKishon Vijay Abraham I 	reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
2479fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
2489fc2cd76SKishon Vijay Abraham I }
2499fc2cd76SKishon Vijay Abraham I 
2509fc2cd76SKishon Vijay Abraham I static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
2519fc2cd76SKishon Vijay Abraham I {
2529fc2cd76SKishon Vijay Abraham I 	struct sdhci_host *host = mmc_priv(mmc);
2539fc2cd76SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2549fc2cd76SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
2559fc2cd76SKishon Vijay Abraham I 	struct device *dev = omap_host->dev;
2569fc2cd76SKishon Vijay Abraham I 	struct mmc_ios *ios = &mmc->ios;
2579fc2cd76SKishon Vijay Abraham I 	u32 start_window = 0, max_window = 0;
2589fc2cd76SKishon Vijay Abraham I 	u8 cur_match, prev_match = 0;
2599fc2cd76SKishon Vijay Abraham I 	u32 length = 0, max_len = 0;
2607d33c358SKishon Vijay Abraham I 	u32 ier = host->ier;
2619fc2cd76SKishon Vijay Abraham I 	u32 phase_delay = 0;
2629fc2cd76SKishon Vijay Abraham I 	int ret = 0;
2639fc2cd76SKishon Vijay Abraham I 	u32 reg;
2649fc2cd76SKishon Vijay Abraham I 
2659fc2cd76SKishon Vijay Abraham I 	pltfm_host = sdhci_priv(host);
2669fc2cd76SKishon Vijay Abraham I 	omap_host = sdhci_pltfm_priv(pltfm_host);
2679fc2cd76SKishon Vijay Abraham I 	dev = omap_host->dev;
2689fc2cd76SKishon Vijay Abraham I 
2699fc2cd76SKishon Vijay Abraham I 	/* clock tuning is not needed for upto 52MHz */
2709fc2cd76SKishon Vijay Abraham I 	if (ios->clock <= 52000000)
2719fc2cd76SKishon Vijay Abraham I 		return 0;
2729fc2cd76SKishon Vijay Abraham I 
2739fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
2749fc2cd76SKishon Vijay Abraham I 	if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
2759fc2cd76SKishon Vijay Abraham I 		return 0;
2769fc2cd76SKishon Vijay Abraham I 
2779fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
2789fc2cd76SKishon Vijay Abraham I 	reg |= DLL_SWT;
2799fc2cd76SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
2809fc2cd76SKishon Vijay Abraham I 
2817d33c358SKishon Vijay Abraham I 	/*
2827d33c358SKishon Vijay Abraham I 	 * OMAP5/DRA74X/DRA72x Errata i802:
2837d33c358SKishon Vijay Abraham I 	 * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
2847d33c358SKishon Vijay Abraham I 	 * during the tuning procedure. So disable it during the
2857d33c358SKishon Vijay Abraham I 	 * tuning procedure.
2867d33c358SKishon Vijay Abraham I 	 */
2877d33c358SKishon Vijay Abraham I 	ier &= ~SDHCI_INT_DATA_CRC;
2887d33c358SKishon Vijay Abraham I 	sdhci_writel(host, ier, SDHCI_INT_ENABLE);
2897d33c358SKishon Vijay Abraham I 	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
2907d33c358SKishon Vijay Abraham I 
2919fc2cd76SKishon Vijay Abraham I 	while (phase_delay <= MAX_PHASE_DELAY) {
2929fc2cd76SKishon Vijay Abraham I 		sdhci_omap_set_dll(omap_host, phase_delay);
2939fc2cd76SKishon Vijay Abraham I 
2949fc2cd76SKishon Vijay Abraham I 		cur_match = !mmc_send_tuning(mmc, opcode, NULL);
2959fc2cd76SKishon Vijay Abraham I 		if (cur_match) {
2969fc2cd76SKishon Vijay Abraham I 			if (prev_match) {
2979fc2cd76SKishon Vijay Abraham I 				length++;
2989fc2cd76SKishon Vijay Abraham I 			} else {
2999fc2cd76SKishon Vijay Abraham I 				start_window = phase_delay;
3009fc2cd76SKishon Vijay Abraham I 				length = 1;
3019fc2cd76SKishon Vijay Abraham I 			}
3029fc2cd76SKishon Vijay Abraham I 		}
3039fc2cd76SKishon Vijay Abraham I 
3049fc2cd76SKishon Vijay Abraham I 		if (length > max_len) {
3059fc2cd76SKishon Vijay Abraham I 			max_window = start_window;
3069fc2cd76SKishon Vijay Abraham I 			max_len = length;
3079fc2cd76SKishon Vijay Abraham I 		}
3089fc2cd76SKishon Vijay Abraham I 
3099fc2cd76SKishon Vijay Abraham I 		prev_match = cur_match;
3109fc2cd76SKishon Vijay Abraham I 		phase_delay += 4;
3119fc2cd76SKishon Vijay Abraham I 	}
3129fc2cd76SKishon Vijay Abraham I 
3139fc2cd76SKishon Vijay Abraham I 	if (!max_len) {
3149fc2cd76SKishon Vijay Abraham I 		dev_err(dev, "Unable to find match\n");
3159fc2cd76SKishon Vijay Abraham I 		ret = -EIO;
3169fc2cd76SKishon Vijay Abraham I 		goto tuning_error;
3179fc2cd76SKishon Vijay Abraham I 	}
3189fc2cd76SKishon Vijay Abraham I 
3199fc2cd76SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
3209fc2cd76SKishon Vijay Abraham I 	if (!(reg & AC12_SCLK_SEL)) {
3219fc2cd76SKishon Vijay Abraham I 		ret = -EIO;
3229fc2cd76SKishon Vijay Abraham I 		goto tuning_error;
3239fc2cd76SKishon Vijay Abraham I 	}
3249fc2cd76SKishon Vijay Abraham I 
3259fc2cd76SKishon Vijay Abraham I 	phase_delay = max_window + 4 * (max_len >> 1);
3269fc2cd76SKishon Vijay Abraham I 	sdhci_omap_set_dll(omap_host, phase_delay);
3279fc2cd76SKishon Vijay Abraham I 
3289fc2cd76SKishon Vijay Abraham I 	goto ret;
3299fc2cd76SKishon Vijay Abraham I 
3309fc2cd76SKishon Vijay Abraham I tuning_error:
3319fc2cd76SKishon Vijay Abraham I 	dev_err(dev, "Tuning failed\n");
3329fc2cd76SKishon Vijay Abraham I 	sdhci_omap_disable_tuning(omap_host);
3339fc2cd76SKishon Vijay Abraham I 
3349fc2cd76SKishon Vijay Abraham I ret:
3359fc2cd76SKishon Vijay Abraham I 	sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
3367d33c358SKishon Vijay Abraham I 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
3377d33c358SKishon Vijay Abraham I 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
3389fc2cd76SKishon Vijay Abraham I 	return ret;
3399fc2cd76SKishon Vijay Abraham I }
3409fc2cd76SKishon Vijay Abraham I 
34120ea26a1SKishon Vijay Abraham I static int sdhci_omap_card_busy(struct mmc_host *mmc)
34220ea26a1SKishon Vijay Abraham I {
34320ea26a1SKishon Vijay Abraham I 	u32 reg, ac12;
34420ea26a1SKishon Vijay Abraham I 	int ret = false;
34520ea26a1SKishon Vijay Abraham I 	struct sdhci_host *host = mmc_priv(mmc);
34620ea26a1SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host;
34720ea26a1SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host;
34820ea26a1SKishon Vijay Abraham I 	u32 ier = host->ier;
34920ea26a1SKishon Vijay Abraham I 
35020ea26a1SKishon Vijay Abraham I 	pltfm_host = sdhci_priv(host);
35120ea26a1SKishon Vijay Abraham I 	omap_host = sdhci_pltfm_priv(pltfm_host);
35220ea26a1SKishon Vijay Abraham I 
35320ea26a1SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
35420ea26a1SKishon Vijay Abraham I 	ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
35520ea26a1SKishon Vijay Abraham I 	reg &= ~CON_CLKEXTFREE;
35620ea26a1SKishon Vijay Abraham I 	if (ac12 & AC12_V1V8_SIGEN)
35720ea26a1SKishon Vijay Abraham I 		reg |= CON_CLKEXTFREE;
35820ea26a1SKishon Vijay Abraham I 	reg |= CON_PADEN;
35920ea26a1SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
36020ea26a1SKishon Vijay Abraham I 
36120ea26a1SKishon Vijay Abraham I 	disable_irq(host->irq);
36220ea26a1SKishon Vijay Abraham I 	ier |= SDHCI_INT_CARD_INT;
36320ea26a1SKishon Vijay Abraham I 	sdhci_writel(host, ier, SDHCI_INT_ENABLE);
36420ea26a1SKishon Vijay Abraham I 	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
36520ea26a1SKishon Vijay Abraham I 
36620ea26a1SKishon Vijay Abraham I 	/*
36720ea26a1SKishon Vijay Abraham I 	 * Delay is required for PSTATE to correctly reflect
36820ea26a1SKishon Vijay Abraham I 	 * DLEV/CLEV values after PADEN is set.
36920ea26a1SKishon Vijay Abraham I 	 */
37020ea26a1SKishon Vijay Abraham I 	usleep_range(50, 100);
37120ea26a1SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
37220ea26a1SKishon Vijay Abraham I 	if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
37320ea26a1SKishon Vijay Abraham I 		ret = true;
37420ea26a1SKishon Vijay Abraham I 
37520ea26a1SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
37620ea26a1SKishon Vijay Abraham I 	reg &= ~(CON_CLKEXTFREE | CON_PADEN);
37720ea26a1SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
37820ea26a1SKishon Vijay Abraham I 
37920ea26a1SKishon Vijay Abraham I 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
38020ea26a1SKishon Vijay Abraham I 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
38120ea26a1SKishon Vijay Abraham I 	enable_irq(host->irq);
38220ea26a1SKishon Vijay Abraham I 
38320ea26a1SKishon Vijay Abraham I 	return ret;
38420ea26a1SKishon Vijay Abraham I }
38520ea26a1SKishon Vijay Abraham I 
3867d326930SKishon Vijay Abraham I static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
3877d326930SKishon Vijay Abraham I 						  struct mmc_ios *ios)
3887d326930SKishon Vijay Abraham I {
3897d326930SKishon Vijay Abraham I 	u32 reg;
3907d326930SKishon Vijay Abraham I 	int ret;
3917d326930SKishon Vijay Abraham I 	unsigned int iov;
3927d326930SKishon Vijay Abraham I 	struct sdhci_host *host = mmc_priv(mmc);
3937d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host;
3947d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host;
3957d326930SKishon Vijay Abraham I 	struct device *dev;
3967d326930SKishon Vijay Abraham I 
3977d326930SKishon Vijay Abraham I 	pltfm_host = sdhci_priv(host);
3987d326930SKishon Vijay Abraham I 	omap_host = sdhci_pltfm_priv(pltfm_host);
3997d326930SKishon Vijay Abraham I 	dev = omap_host->dev;
4007d326930SKishon Vijay Abraham I 
4017d326930SKishon Vijay Abraham I 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
4027d326930SKishon Vijay Abraham I 		reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
4037d326930SKishon Vijay Abraham I 		if (!(reg & CAPA_VS33))
4047d326930SKishon Vijay Abraham I 			return -EOPNOTSUPP;
4057d326930SKishon Vijay Abraham I 
4067d326930SKishon Vijay Abraham I 		sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
4077d326930SKishon Vijay Abraham I 
4087d326930SKishon Vijay Abraham I 		reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
4097d326930SKishon Vijay Abraham I 		reg &= ~AC12_V1V8_SIGEN;
4107d326930SKishon Vijay Abraham I 		sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
4117d326930SKishon Vijay Abraham I 
4127d326930SKishon Vijay Abraham I 		iov = IOV_3V3;
4137d326930SKishon Vijay Abraham I 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
4147d326930SKishon Vijay Abraham I 		reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
4157d326930SKishon Vijay Abraham I 		if (!(reg & CAPA_VS18))
4167d326930SKishon Vijay Abraham I 			return -EOPNOTSUPP;
4177d326930SKishon Vijay Abraham I 
4187d326930SKishon Vijay Abraham I 		sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
4197d326930SKishon Vijay Abraham I 
4207d326930SKishon Vijay Abraham I 		reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
4217d326930SKishon Vijay Abraham I 		reg |= AC12_V1V8_SIGEN;
4227d326930SKishon Vijay Abraham I 		sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
4237d326930SKishon Vijay Abraham I 
4247d326930SKishon Vijay Abraham I 		iov = IOV_1V8;
4257d326930SKishon Vijay Abraham I 	} else {
4267d326930SKishon Vijay Abraham I 		return -EOPNOTSUPP;
4277d326930SKishon Vijay Abraham I 	}
4287d326930SKishon Vijay Abraham I 
4297d326930SKishon Vijay Abraham I 	ret = sdhci_omap_enable_iov(omap_host, iov);
4307d326930SKishon Vijay Abraham I 	if (ret) {
4317d326930SKishon Vijay Abraham I 		dev_err(dev, "failed to switch IO voltage to %dmV\n", iov);
4327d326930SKishon Vijay Abraham I 		return ret;
4337d326930SKishon Vijay Abraham I 	}
4347d326930SKishon Vijay Abraham I 
4357d326930SKishon Vijay Abraham I 	dev_dbg(dev, "IO voltage switched to %dmV\n", iov);
4367d326930SKishon Vijay Abraham I 	return 0;
4377d326930SKishon Vijay Abraham I }
4387d326930SKishon Vijay Abraham I 
439300df508SKishon Vijay Abraham I static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
440300df508SKishon Vijay Abraham I 				      u8 power_mode)
441300df508SKishon Vijay Abraham I {
4429fc2cd76SKishon Vijay Abraham I 	if (omap_host->bus_mode == MMC_POWER_OFF)
4439fc2cd76SKishon Vijay Abraham I 		sdhci_omap_disable_tuning(omap_host);
444300df508SKishon Vijay Abraham I 	omap_host->power_mode = power_mode;
445300df508SKishon Vijay Abraham I }
446300df508SKishon Vijay Abraham I 
4477d326930SKishon Vijay Abraham I static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
4487d326930SKishon Vijay Abraham I 				    unsigned int mode)
4497d326930SKishon Vijay Abraham I {
4507d326930SKishon Vijay Abraham I 	u32 reg;
4517d326930SKishon Vijay Abraham I 
4527d326930SKishon Vijay Abraham I 	if (omap_host->bus_mode == mode)
4537d326930SKishon Vijay Abraham I 		return;
4547d326930SKishon Vijay Abraham I 
4557d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
4567d326930SKishon Vijay Abraham I 	if (mode == MMC_BUSMODE_OPENDRAIN)
4577d326930SKishon Vijay Abraham I 		reg |= CON_OD;
4587d326930SKishon Vijay Abraham I 	else
4597d326930SKishon Vijay Abraham I 		reg &= ~CON_OD;
4607d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
4617d326930SKishon Vijay Abraham I 
4627d326930SKishon Vijay Abraham I 	omap_host->bus_mode = mode;
4637d326930SKishon Vijay Abraham I }
4647d326930SKishon Vijay Abraham I 
465ddde0e7dSColin Ian King static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
4667d326930SKishon Vijay Abraham I {
4677d326930SKishon Vijay Abraham I 	struct sdhci_host *host = mmc_priv(mmc);
4687d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host;
4697d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host;
4707d326930SKishon Vijay Abraham I 
4717d326930SKishon Vijay Abraham I 	pltfm_host = sdhci_priv(host);
4727d326930SKishon Vijay Abraham I 	omap_host = sdhci_pltfm_priv(pltfm_host);
4737d326930SKishon Vijay Abraham I 
4747d326930SKishon Vijay Abraham I 	sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
4757d326930SKishon Vijay Abraham I 	sdhci_set_ios(mmc, ios);
476300df508SKishon Vijay Abraham I 	sdhci_omap_set_power_mode(omap_host, ios->power_mode);
4777d326930SKishon Vijay Abraham I }
4787d326930SKishon Vijay Abraham I 
4797d326930SKishon Vijay Abraham I static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
4807d326930SKishon Vijay Abraham I 				   unsigned int clock)
4817d326930SKishon Vijay Abraham I {
4827d326930SKishon Vijay Abraham I 	u16 dsor;
4837d326930SKishon Vijay Abraham I 
4847d326930SKishon Vijay Abraham I 	dsor = DIV_ROUND_UP(clk_get_rate(host->clk), clock);
4857d326930SKishon Vijay Abraham I 	if (dsor > SYSCTL_CLKD_MAX)
4867d326930SKishon Vijay Abraham I 		dsor = SYSCTL_CLKD_MAX;
4877d326930SKishon Vijay Abraham I 
4887d326930SKishon Vijay Abraham I 	return dsor;
4897d326930SKishon Vijay Abraham I }
4907d326930SKishon Vijay Abraham I 
4917d326930SKishon Vijay Abraham I static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host)
4927d326930SKishon Vijay Abraham I {
4937d326930SKishon Vijay Abraham I 	u32 reg;
4947d326930SKishon Vijay Abraham I 
4957d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
4967d326930SKishon Vijay Abraham I 	reg |= SYSCTL_CEN;
4977d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
4987d326930SKishon Vijay Abraham I }
4997d326930SKishon Vijay Abraham I 
5007d326930SKishon Vijay Abraham I static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host)
5017d326930SKishon Vijay Abraham I {
5027d326930SKishon Vijay Abraham I 	u32 reg;
5037d326930SKishon Vijay Abraham I 
5047d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
5057d326930SKishon Vijay Abraham I 	reg &= ~SYSCTL_CEN;
5067d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
5077d326930SKishon Vijay Abraham I }
5087d326930SKishon Vijay Abraham I 
5097d326930SKishon Vijay Abraham I static void sdhci_omap_set_clock(struct sdhci_host *host, unsigned int clock)
5107d326930SKishon Vijay Abraham I {
5117d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5127d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
5137d326930SKishon Vijay Abraham I 	unsigned long clkdiv;
5147d326930SKishon Vijay Abraham I 
5157d326930SKishon Vijay Abraham I 	sdhci_omap_stop_clock(omap_host);
5167d326930SKishon Vijay Abraham I 
5177d326930SKishon Vijay Abraham I 	if (!clock)
5187d326930SKishon Vijay Abraham I 		return;
5197d326930SKishon Vijay Abraham I 
5207d326930SKishon Vijay Abraham I 	clkdiv = sdhci_omap_calc_divisor(pltfm_host, clock);
5217d326930SKishon Vijay Abraham I 	clkdiv = (clkdiv & SYSCTL_CLKD_MASK) << SYSCTL_CLKD_SHIFT;
5227d326930SKishon Vijay Abraham I 	sdhci_enable_clk(host, clkdiv);
5237d326930SKishon Vijay Abraham I 
5247d326930SKishon Vijay Abraham I 	sdhci_omap_start_clock(omap_host);
5257d326930SKishon Vijay Abraham I }
5267d326930SKishon Vijay Abraham I 
527ddde0e7dSColin Ian King static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode,
5287d326930SKishon Vijay Abraham I 			  unsigned short vdd)
5297d326930SKishon Vijay Abraham I {
5307d326930SKishon Vijay Abraham I 	struct mmc_host *mmc = host->mmc;
5317d326930SKishon Vijay Abraham I 
5327d326930SKishon Vijay Abraham I 	mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
5337d326930SKishon Vijay Abraham I }
5347d326930SKishon Vijay Abraham I 
5357d326930SKishon Vijay Abraham I static int sdhci_omap_enable_dma(struct sdhci_host *host)
5367d326930SKishon Vijay Abraham I {
5377d326930SKishon Vijay Abraham I 	u32 reg;
5387d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5397d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
5407d326930SKishon Vijay Abraham I 
5417d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
5427d326930SKishon Vijay Abraham I 	reg |= CON_DMA_MASTER;
5437d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
5447d326930SKishon Vijay Abraham I 
5457d326930SKishon Vijay Abraham I 	return 0;
5467d326930SKishon Vijay Abraham I }
5477d326930SKishon Vijay Abraham I 
548ddde0e7dSColin Ian King static unsigned int sdhci_omap_get_min_clock(struct sdhci_host *host)
5497d326930SKishon Vijay Abraham I {
5507d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5517d326930SKishon Vijay Abraham I 
5527d326930SKishon Vijay Abraham I 	return clk_get_rate(pltfm_host->clk) / SYSCTL_CLKD_MAX;
5537d326930SKishon Vijay Abraham I }
5547d326930SKishon Vijay Abraham I 
5557d326930SKishon Vijay Abraham I static void sdhci_omap_set_bus_width(struct sdhci_host *host, int width)
5567d326930SKishon Vijay Abraham I {
5577d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5587d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
5597d326930SKishon Vijay Abraham I 	u32 reg;
5607d326930SKishon Vijay Abraham I 
5617d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
5627d326930SKishon Vijay Abraham I 	if (width == MMC_BUS_WIDTH_8)
5637d326930SKishon Vijay Abraham I 		reg |= CON_DW8;
5647d326930SKishon Vijay Abraham I 	else
5657d326930SKishon Vijay Abraham I 		reg &= ~CON_DW8;
5667d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
5677d326930SKishon Vijay Abraham I 
5687d326930SKishon Vijay Abraham I 	sdhci_set_bus_width(host, width);
5697d326930SKishon Vijay Abraham I }
5707d326930SKishon Vijay Abraham I 
5717d326930SKishon Vijay Abraham I static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
5727d326930SKishon Vijay Abraham I {
5737d326930SKishon Vijay Abraham I 	u32 reg;
5747d326930SKishon Vijay Abraham I 	ktime_t timeout;
5757d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5767d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
5777d326930SKishon Vijay Abraham I 
5787d326930SKishon Vijay Abraham I 	if (omap_host->power_mode == power_mode)
5797d326930SKishon Vijay Abraham I 		return;
5807d326930SKishon Vijay Abraham I 
5817d326930SKishon Vijay Abraham I 	if (power_mode != MMC_POWER_ON)
5827d326930SKishon Vijay Abraham I 		return;
5837d326930SKishon Vijay Abraham I 
5847d326930SKishon Vijay Abraham I 	disable_irq(host->irq);
5857d326930SKishon Vijay Abraham I 
5867d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
5877d326930SKishon Vijay Abraham I 	reg |= CON_INIT;
5887d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
5897d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CMD, 0x0);
5907d326930SKishon Vijay Abraham I 
5917d326930SKishon Vijay Abraham I 	/* wait 1ms */
5927d326930SKishon Vijay Abraham I 	timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
5937d326930SKishon Vijay Abraham I 	while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)) {
5947d326930SKishon Vijay Abraham I 		if (WARN_ON(ktime_after(ktime_get(), timeout)))
5957d326930SKishon Vijay Abraham I 			return;
5967d326930SKishon Vijay Abraham I 		usleep_range(5, 10);
5977d326930SKishon Vijay Abraham I 	}
5987d326930SKishon Vijay Abraham I 
5997d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
6007d326930SKishon Vijay Abraham I 	reg &= ~CON_INIT;
6017d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
6027d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
6037d326930SKishon Vijay Abraham I 
6047d326930SKishon Vijay Abraham I 	enable_irq(host->irq);
6057d326930SKishon Vijay Abraham I }
6067d326930SKishon Vijay Abraham I 
60727ceb7e0SKishon Vijay Abraham I static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
60827ceb7e0SKishon Vijay Abraham I 					 unsigned int timing)
60927ceb7e0SKishon Vijay Abraham I {
61027ceb7e0SKishon Vijay Abraham I 	u32 reg;
61127ceb7e0SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
61227ceb7e0SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
61327ceb7e0SKishon Vijay Abraham I 
61427ceb7e0SKishon Vijay Abraham I 	sdhci_omap_stop_clock(omap_host);
61527ceb7e0SKishon Vijay Abraham I 
61627ceb7e0SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
61727ceb7e0SKishon Vijay Abraham I 	if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
61827ceb7e0SKishon Vijay Abraham I 		reg |= CON_DDR;
61927ceb7e0SKishon Vijay Abraham I 	else
62027ceb7e0SKishon Vijay Abraham I 		reg &= ~CON_DDR;
62127ceb7e0SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
62227ceb7e0SKishon Vijay Abraham I 
62327ceb7e0SKishon Vijay Abraham I 	sdhci_set_uhs_signaling(host, timing);
62427ceb7e0SKishon Vijay Abraham I 	sdhci_omap_start_clock(omap_host);
62527ceb7e0SKishon Vijay Abraham I }
62627ceb7e0SKishon Vijay Abraham I 
6277d326930SKishon Vijay Abraham I static struct sdhci_ops sdhci_omap_ops = {
6287d326930SKishon Vijay Abraham I 	.set_clock = sdhci_omap_set_clock,
6297d326930SKishon Vijay Abraham I 	.set_power = sdhci_omap_set_power,
6307d326930SKishon Vijay Abraham I 	.enable_dma = sdhci_omap_enable_dma,
6317d326930SKishon Vijay Abraham I 	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
6327d326930SKishon Vijay Abraham I 	.get_min_clock = sdhci_omap_get_min_clock,
6337d326930SKishon Vijay Abraham I 	.set_bus_width = sdhci_omap_set_bus_width,
6347d326930SKishon Vijay Abraham I 	.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
6357d326930SKishon Vijay Abraham I 	.reset = sdhci_reset,
63627ceb7e0SKishon Vijay Abraham I 	.set_uhs_signaling = sdhci_omap_set_uhs_signaling,
6377d326930SKishon Vijay Abraham I };
6387d326930SKishon Vijay Abraham I 
6397d326930SKishon Vijay Abraham I static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
6407d326930SKishon Vijay Abraham I {
6417d326930SKishon Vijay Abraham I 	u32 reg;
6427d326930SKishon Vijay Abraham I 	int ret = 0;
6437d326930SKishon Vijay Abraham I 	struct device *dev = omap_host->dev;
6447d326930SKishon Vijay Abraham I 	struct regulator *vqmmc;
6457d326930SKishon Vijay Abraham I 
6467d326930SKishon Vijay Abraham I 	vqmmc = regulator_get(dev, "vqmmc");
6477d326930SKishon Vijay Abraham I 	if (IS_ERR(vqmmc)) {
6487d326930SKishon Vijay Abraham I 		ret = PTR_ERR(vqmmc);
6497d326930SKishon Vijay Abraham I 		goto reg_put;
6507d326930SKishon Vijay Abraham I 	}
6517d326930SKishon Vijay Abraham I 
6527d326930SKishon Vijay Abraham I 	/* voltage capabilities might be set by boot loader, clear it */
6537d326930SKishon Vijay Abraham I 	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
6547d326930SKishon Vijay Abraham I 	reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
6557d326930SKishon Vijay Abraham I 
6567d326930SKishon Vijay Abraham I 	if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3))
6577d326930SKishon Vijay Abraham I 		reg |= CAPA_VS33;
6587d326930SKishon Vijay Abraham I 	if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8))
6597d326930SKishon Vijay Abraham I 		reg |= CAPA_VS18;
6607d326930SKishon Vijay Abraham I 
6617d326930SKishon Vijay Abraham I 	sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg);
6627d326930SKishon Vijay Abraham I 
6637d326930SKishon Vijay Abraham I reg_put:
6647d326930SKishon Vijay Abraham I 	regulator_put(vqmmc);
6657d326930SKishon Vijay Abraham I 
6667d326930SKishon Vijay Abraham I 	return ret;
6677d326930SKishon Vijay Abraham I }
6687d326930SKishon Vijay Abraham I 
6697d326930SKishon Vijay Abraham I static const struct sdhci_pltfm_data sdhci_omap_pdata = {
6707d326930SKishon Vijay Abraham I 	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
6717d326930SKishon Vijay Abraham I 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
6727d326930SKishon Vijay Abraham I 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
6737d326930SKishon Vijay Abraham I 		  SDHCI_QUIRK_NO_HISPD_BIT |
6747d326930SKishon Vijay Abraham I 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
6757d326930SKishon Vijay Abraham I 	.quirks2 = SDHCI_QUIRK2_NO_1_8_V |
6767d326930SKishon Vijay Abraham I 		   SDHCI_QUIRK2_ACMD23_BROKEN |
6777d326930SKishon Vijay Abraham I 		   SDHCI_QUIRK2_RSP_136_HAS_CRC,
6787d326930SKishon Vijay Abraham I 	.ops = &sdhci_omap_ops,
6797d326930SKishon Vijay Abraham I };
6807d326930SKishon Vijay Abraham I 
6817d326930SKishon Vijay Abraham I static const struct sdhci_omap_data dra7_data = {
6827d326930SKishon Vijay Abraham I 	.offset = 0x200,
6837d326930SKishon Vijay Abraham I };
6847d326930SKishon Vijay Abraham I 
6857d326930SKishon Vijay Abraham I static const struct of_device_id omap_sdhci_match[] = {
6867d326930SKishon Vijay Abraham I 	{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
6877d326930SKishon Vijay Abraham I 	{},
6887d326930SKishon Vijay Abraham I };
6897d326930SKishon Vijay Abraham I MODULE_DEVICE_TABLE(of, omap_sdhci_match);
6907d326930SKishon Vijay Abraham I 
6917d326930SKishon Vijay Abraham I static int sdhci_omap_probe(struct platform_device *pdev)
6927d326930SKishon Vijay Abraham I {
6937d326930SKishon Vijay Abraham I 	int ret;
6947d326930SKishon Vijay Abraham I 	u32 offset;
6957d326930SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
6967d326930SKishon Vijay Abraham I 	struct sdhci_host *host;
6977d326930SKishon Vijay Abraham I 	struct sdhci_pltfm_host *pltfm_host;
6987d326930SKishon Vijay Abraham I 	struct sdhci_omap_host *omap_host;
6997d326930SKishon Vijay Abraham I 	struct mmc_host *mmc;
7007d326930SKishon Vijay Abraham I 	const struct of_device_id *match;
7017d326930SKishon Vijay Abraham I 	struct sdhci_omap_data *data;
7027d326930SKishon Vijay Abraham I 
7037d326930SKishon Vijay Abraham I 	match = of_match_device(omap_sdhci_match, dev);
7047d326930SKishon Vijay Abraham I 	if (!match)
7057d326930SKishon Vijay Abraham I 		return -EINVAL;
7067d326930SKishon Vijay Abraham I 
7077d326930SKishon Vijay Abraham I 	data = (struct sdhci_omap_data *)match->data;
7087d326930SKishon Vijay Abraham I 	if (!data) {
7097d326930SKishon Vijay Abraham I 		dev_err(dev, "no sdhci omap data\n");
7107d326930SKishon Vijay Abraham I 		return -EINVAL;
7117d326930SKishon Vijay Abraham I 	}
7127d326930SKishon Vijay Abraham I 	offset = data->offset;
7137d326930SKishon Vijay Abraham I 
7147d326930SKishon Vijay Abraham I 	host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata,
7157d326930SKishon Vijay Abraham I 				sizeof(*omap_host));
7167d326930SKishon Vijay Abraham I 	if (IS_ERR(host)) {
7177d326930SKishon Vijay Abraham I 		dev_err(dev, "Failed sdhci_pltfm_init\n");
7187d326930SKishon Vijay Abraham I 		return PTR_ERR(host);
7197d326930SKishon Vijay Abraham I 	}
7207d326930SKishon Vijay Abraham I 
7217d326930SKishon Vijay Abraham I 	pltfm_host = sdhci_priv(host);
7227d326930SKishon Vijay Abraham I 	omap_host = sdhci_pltfm_priv(pltfm_host);
7237d326930SKishon Vijay Abraham I 	omap_host->host = host;
7247d326930SKishon Vijay Abraham I 	omap_host->base = host->ioaddr;
7257d326930SKishon Vijay Abraham I 	omap_host->dev = dev;
726300df508SKishon Vijay Abraham I 	omap_host->power_mode = MMC_POWER_UNDEFINED;
7277d326930SKishon Vijay Abraham I 	host->ioaddr += offset;
7287d326930SKishon Vijay Abraham I 
7297d326930SKishon Vijay Abraham I 	mmc = host->mmc;
7307d326930SKishon Vijay Abraham I 	ret = mmc_of_parse(mmc);
7317d326930SKishon Vijay Abraham I 	if (ret)
7327d326930SKishon Vijay Abraham I 		goto err_pltfm_free;
7337d326930SKishon Vijay Abraham I 
7347d326930SKishon Vijay Abraham I 	pltfm_host->clk = devm_clk_get(dev, "fck");
7357d326930SKishon Vijay Abraham I 	if (IS_ERR(pltfm_host->clk)) {
7367d326930SKishon Vijay Abraham I 		ret = PTR_ERR(pltfm_host->clk);
7377d326930SKishon Vijay Abraham I 		goto err_pltfm_free;
7387d326930SKishon Vijay Abraham I 	}
7397d326930SKishon Vijay Abraham I 
7407d326930SKishon Vijay Abraham I 	ret = clk_set_rate(pltfm_host->clk, mmc->f_max);
7417d326930SKishon Vijay Abraham I 	if (ret) {
7427d326930SKishon Vijay Abraham I 		dev_err(dev, "failed to set clock to %d\n", mmc->f_max);
7437d326930SKishon Vijay Abraham I 		goto err_pltfm_free;
7447d326930SKishon Vijay Abraham I 	}
7457d326930SKishon Vijay Abraham I 
7467d326930SKishon Vijay Abraham I 	omap_host->pbias = devm_regulator_get_optional(dev, "pbias");
7477d326930SKishon Vijay Abraham I 	if (IS_ERR(omap_host->pbias)) {
7487d326930SKishon Vijay Abraham I 		ret = PTR_ERR(omap_host->pbias);
7497d326930SKishon Vijay Abraham I 		if (ret != -ENODEV)
7507d326930SKishon Vijay Abraham I 			goto err_pltfm_free;
7517d326930SKishon Vijay Abraham I 		dev_dbg(dev, "unable to get pbias regulator %d\n", ret);
7527d326930SKishon Vijay Abraham I 	}
7537d326930SKishon Vijay Abraham I 	omap_host->pbias_enabled = false;
7547d326930SKishon Vijay Abraham I 
7557d326930SKishon Vijay Abraham I 	/*
7567d326930SKishon Vijay Abraham I 	 * omap_device_pm_domain has callbacks to enable the main
7577d326930SKishon Vijay Abraham I 	 * functional clock, interface clock and also configure the
7587d326930SKishon Vijay Abraham I 	 * SYSCONFIG register of omap devices. The callback will be invoked
7597d326930SKishon Vijay Abraham I 	 * as part of pm_runtime_get_sync.
7607d326930SKishon Vijay Abraham I 	 */
7617d326930SKishon Vijay Abraham I 	pm_runtime_enable(dev);
7627d326930SKishon Vijay Abraham I 	ret = pm_runtime_get_sync(dev);
7637d326930SKishon Vijay Abraham I 	if (ret < 0) {
7647d326930SKishon Vijay Abraham I 		dev_err(dev, "pm_runtime_get_sync failed\n");
7657d326930SKishon Vijay Abraham I 		pm_runtime_put_noidle(dev);
7667d326930SKishon Vijay Abraham I 		goto err_rpm_disable;
7677d326930SKishon Vijay Abraham I 	}
7687d326930SKishon Vijay Abraham I 
7697d326930SKishon Vijay Abraham I 	ret = sdhci_omap_set_capabilities(omap_host);
7707d326930SKishon Vijay Abraham I 	if (ret) {
7717d326930SKishon Vijay Abraham I 		dev_err(dev, "failed to set system capabilities\n");
7727d326930SKishon Vijay Abraham I 		goto err_put_sync;
7737d326930SKishon Vijay Abraham I 	}
7747d326930SKishon Vijay Abraham I 
7757d326930SKishon Vijay Abraham I 	host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
7767d326930SKishon Vijay Abraham I 	host->mmc_host_ops.start_signal_voltage_switch =
7777d326930SKishon Vijay Abraham I 					sdhci_omap_start_signal_voltage_switch;
7787d326930SKishon Vijay Abraham I 	host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
77920ea26a1SKishon Vijay Abraham I 	host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
7809fc2cd76SKishon Vijay Abraham I 	host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
7817d326930SKishon Vijay Abraham I 
7827d326930SKishon Vijay Abraham I 	sdhci_read_caps(host);
7837d326930SKishon Vijay Abraham I 	host->caps |= SDHCI_CAN_DO_ADMA2;
7847d326930SKishon Vijay Abraham I 
7857d326930SKishon Vijay Abraham I 	ret = sdhci_add_host(host);
7867d326930SKishon Vijay Abraham I 	if (ret)
7877d326930SKishon Vijay Abraham I 		goto err_put_sync;
7887d326930SKishon Vijay Abraham I 
7897d326930SKishon Vijay Abraham I 	return 0;
7907d326930SKishon Vijay Abraham I 
7917d326930SKishon Vijay Abraham I err_put_sync:
7927d326930SKishon Vijay Abraham I 	pm_runtime_put_sync(dev);
7937d326930SKishon Vijay Abraham I 
7947d326930SKishon Vijay Abraham I err_rpm_disable:
7957d326930SKishon Vijay Abraham I 	pm_runtime_disable(dev);
7967d326930SKishon Vijay Abraham I 
7977d326930SKishon Vijay Abraham I err_pltfm_free:
7987d326930SKishon Vijay Abraham I 	sdhci_pltfm_free(pdev);
7997d326930SKishon Vijay Abraham I 	return ret;
8007d326930SKishon Vijay Abraham I }
8017d326930SKishon Vijay Abraham I 
8027d326930SKishon Vijay Abraham I static int sdhci_omap_remove(struct platform_device *pdev)
8037d326930SKishon Vijay Abraham I {
8047d326930SKishon Vijay Abraham I 	struct device *dev = &pdev->dev;
8057d326930SKishon Vijay Abraham I 	struct sdhci_host *host = platform_get_drvdata(pdev);
8067d326930SKishon Vijay Abraham I 
8077d326930SKishon Vijay Abraham I 	sdhci_remove_host(host, true);
8087d326930SKishon Vijay Abraham I 	pm_runtime_put_sync(dev);
8097d326930SKishon Vijay Abraham I 	pm_runtime_disable(dev);
8107d326930SKishon Vijay Abraham I 	sdhci_pltfm_free(pdev);
8117d326930SKishon Vijay Abraham I 
8127d326930SKishon Vijay Abraham I 	return 0;
8137d326930SKishon Vijay Abraham I }
8147d326930SKishon Vijay Abraham I 
8157d326930SKishon Vijay Abraham I static struct platform_driver sdhci_omap_driver = {
8167d326930SKishon Vijay Abraham I 	.probe = sdhci_omap_probe,
8177d326930SKishon Vijay Abraham I 	.remove = sdhci_omap_remove,
8187d326930SKishon Vijay Abraham I 	.driver = {
8197d326930SKishon Vijay Abraham I 		   .name = "sdhci-omap",
8207d326930SKishon Vijay Abraham I 		   .of_match_table = omap_sdhci_match,
8217d326930SKishon Vijay Abraham I 		  },
8227d326930SKishon Vijay Abraham I };
8237d326930SKishon Vijay Abraham I 
8247d326930SKishon Vijay Abraham I module_platform_driver(sdhci_omap_driver);
8257d326930SKishon Vijay Abraham I 
8267d326930SKishon Vijay Abraham I MODULE_DESCRIPTION("SDHCI driver for OMAP SoCs");
8277d326930SKishon Vijay Abraham I MODULE_AUTHOR("Texas Instruments Inc.");
8287d326930SKishon Vijay Abraham I MODULE_LICENSE("GPL v2");
8297d326930SKishon Vijay Abraham I MODULE_ALIAS("platform:sdhci_omap");
830