xref: /openbmc/linux/drivers/mmc/host/sdhci-msm.c (revision ff06ce41)
10eb0d9f4SGeorgi Djakov /*
20eb0d9f4SGeorgi Djakov  * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
30eb0d9f4SGeorgi Djakov  *
40eb0d9f4SGeorgi Djakov  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
50eb0d9f4SGeorgi Djakov  *
60eb0d9f4SGeorgi Djakov  * This program is free software; you can redistribute it and/or modify
70eb0d9f4SGeorgi Djakov  * it under the terms of the GNU General Public License version 2 and
80eb0d9f4SGeorgi Djakov  * only version 2 as published by the Free Software Foundation.
90eb0d9f4SGeorgi Djakov  *
100eb0d9f4SGeorgi Djakov  * This program is distributed in the hope that it will be useful,
110eb0d9f4SGeorgi Djakov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
120eb0d9f4SGeorgi Djakov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
130eb0d9f4SGeorgi Djakov  * GNU General Public License for more details.
140eb0d9f4SGeorgi Djakov  *
150eb0d9f4SGeorgi Djakov  */
160eb0d9f4SGeorgi Djakov 
170eb0d9f4SGeorgi Djakov #include <linux/module.h>
180eb0d9f4SGeorgi Djakov #include <linux/of_device.h>
190eb0d9f4SGeorgi Djakov #include <linux/delay.h>
20415b5a75SGeorgi Djakov #include <linux/mmc/mmc.h>
2167e6db11SPramod Gurav #include <linux/pm_runtime.h>
22415b5a75SGeorgi Djakov #include <linux/slab.h>
230eb0d9f4SGeorgi Djakov 
240eb0d9f4SGeorgi Djakov #include "sdhci-pltfm.h"
250eb0d9f4SGeorgi Djakov 
263a3ad3e9SGeorgi Djakov #define CORE_MCI_VERSION		0x50
273a3ad3e9SGeorgi Djakov #define CORE_VERSION_MAJOR_SHIFT	28
283a3ad3e9SGeorgi Djakov #define CORE_VERSION_MAJOR_MASK		(0xf << CORE_VERSION_MAJOR_SHIFT)
293a3ad3e9SGeorgi Djakov #define CORE_VERSION_MINOR_MASK		0xff
303a3ad3e9SGeorgi Djakov 
310eb0d9f4SGeorgi Djakov #define CORE_HC_MODE		0x78
320eb0d9f4SGeorgi Djakov #define HC_MODE_EN		0x1
330eb0d9f4SGeorgi Djakov #define CORE_POWER		0x0
340eb0d9f4SGeorgi Djakov #define CORE_SW_RST		BIT(7)
35ff06ce41SVenkat Gopalakrishnan #define FF_CLK_SW_RST_DIS	BIT(13)
360eb0d9f4SGeorgi Djakov 
37ad81d387SGeorgi Djakov #define CORE_PWRCTL_STATUS	0xdc
38ad81d387SGeorgi Djakov #define CORE_PWRCTL_MASK	0xe0
39ad81d387SGeorgi Djakov #define CORE_PWRCTL_CLEAR	0xe4
40ad81d387SGeorgi Djakov #define CORE_PWRCTL_CTL		0xe8
41ad81d387SGeorgi Djakov #define CORE_PWRCTL_BUS_OFF	BIT(0)
42ad81d387SGeorgi Djakov #define CORE_PWRCTL_BUS_ON	BIT(1)
43ad81d387SGeorgi Djakov #define CORE_PWRCTL_IO_LOW	BIT(2)
44ad81d387SGeorgi Djakov #define CORE_PWRCTL_IO_HIGH	BIT(3)
45ad81d387SGeorgi Djakov #define CORE_PWRCTL_BUS_SUCCESS BIT(0)
46ad81d387SGeorgi Djakov #define CORE_PWRCTL_IO_SUCCESS	BIT(2)
47ad81d387SGeorgi Djakov #define REQ_BUS_OFF		BIT(0)
48ad81d387SGeorgi Djakov #define REQ_BUS_ON		BIT(1)
49ad81d387SGeorgi Djakov #define REQ_IO_LOW		BIT(2)
50ad81d387SGeorgi Djakov #define REQ_IO_HIGH		BIT(3)
51ad81d387SGeorgi Djakov #define INT_MASK		0xf
52415b5a75SGeorgi Djakov #define MAX_PHASES		16
53415b5a75SGeorgi Djakov #define CORE_DLL_LOCK		BIT(7)
54415b5a75SGeorgi Djakov #define CORE_DLL_EN		BIT(16)
55415b5a75SGeorgi Djakov #define CORE_CDR_EN		BIT(17)
56415b5a75SGeorgi Djakov #define CORE_CK_OUT_EN		BIT(18)
57415b5a75SGeorgi Djakov #define CORE_CDR_EXT_EN		BIT(19)
58415b5a75SGeorgi Djakov #define CORE_DLL_PDN		BIT(29)
59415b5a75SGeorgi Djakov #define CORE_DLL_RST		BIT(30)
60415b5a75SGeorgi Djakov #define CORE_DLL_CONFIG		0x100
61415b5a75SGeorgi Djakov #define CORE_DLL_STATUS		0x108
62415b5a75SGeorgi Djakov 
6383736352SVenkat Gopalakrishnan #define CORE_DLL_CONFIG_2	0x1b4
6483736352SVenkat Gopalakrishnan #define CORE_FLL_CYCLE_CNT	BIT(18)
6583736352SVenkat Gopalakrishnan #define CORE_DLL_CLOCK_DISABLE	BIT(21)
6683736352SVenkat Gopalakrishnan 
67415b5a75SGeorgi Djakov #define CORE_VENDOR_SPEC	0x10c
68415b5a75SGeorgi Djakov #define CORE_CLK_PWRSAVE	BIT(1)
69ff06ce41SVenkat Gopalakrishnan #define CORE_HC_MCLK_SEL_DFLT	(2 << 8)
70ff06ce41SVenkat Gopalakrishnan #define CORE_HC_MCLK_SEL_HS400	(3 << 8)
71ff06ce41SVenkat Gopalakrishnan #define CORE_HC_MCLK_SEL_MASK	(3 << 8)
72ff06ce41SVenkat Gopalakrishnan #define CORE_HC_SELECT_IN_EN	BIT(18)
73ff06ce41SVenkat Gopalakrishnan #define CORE_HC_SELECT_IN_HS400	(6 << 19)
74ff06ce41SVenkat Gopalakrishnan #define CORE_HC_SELECT_IN_MASK	(7 << 19)
75415b5a75SGeorgi Djakov 
763a3ad3e9SGeorgi Djakov #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
773a3ad3e9SGeorgi Djakov 
7880031bdeSRitesh Harjani #define SDHCI_MSM_MIN_CLOCK	400000
79ff06ce41SVenkat Gopalakrishnan #define CORE_FREQ_100MHZ	(100 * 1000 * 1000)
8080031bdeSRitesh Harjani 
81415b5a75SGeorgi Djakov #define CDR_SELEXT_SHIFT	20
82415b5a75SGeorgi Djakov #define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT)
83415b5a75SGeorgi Djakov #define CMUX_SHIFT_PHASE_SHIFT	24
84415b5a75SGeorgi Djakov #define CMUX_SHIFT_PHASE_MASK	(7 << CMUX_SHIFT_PHASE_SHIFT)
85415b5a75SGeorgi Djakov 
8667e6db11SPramod Gurav #define MSM_MMC_AUTOSUSPEND_DELAY_MS	50
870eb0d9f4SGeorgi Djakov struct sdhci_msm_host {
880eb0d9f4SGeorgi Djakov 	struct platform_device *pdev;
890eb0d9f4SGeorgi Djakov 	void __iomem *core_mem;	/* MSM SDCC mapped address */
90ad81d387SGeorgi Djakov 	int pwr_irq;		/* power irq */
910eb0d9f4SGeorgi Djakov 	struct clk *clk;	/* main SD/MMC bus clock */
920eb0d9f4SGeorgi Djakov 	struct clk *pclk;	/* SDHC peripheral bus clock */
930eb0d9f4SGeorgi Djakov 	struct clk *bus_clk;	/* SDHC bus voter clock */
9483736352SVenkat Gopalakrishnan 	struct clk *xo_clk;	/* TCXO clk needed for FLL feature of cm_dll*/
95edc609fdSRitesh Harjani 	unsigned long clk_rate;
960eb0d9f4SGeorgi Djakov 	struct mmc_host *mmc;
9783736352SVenkat Gopalakrishnan 	bool use_14lpp_dll_reset;
98ff06ce41SVenkat Gopalakrishnan 	bool tuning_done;
99ff06ce41SVenkat Gopalakrishnan 	bool calibration_done;
1000eb0d9f4SGeorgi Djakov };
1010eb0d9f4SGeorgi Djakov 
1020eb0d9f4SGeorgi Djakov /* Platform specific tuning */
103415b5a75SGeorgi Djakov static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
104415b5a75SGeorgi Djakov {
105415b5a75SGeorgi Djakov 	u32 wait_cnt = 50;
106415b5a75SGeorgi Djakov 	u8 ck_out_en;
107415b5a75SGeorgi Djakov 	struct mmc_host *mmc = host->mmc;
108415b5a75SGeorgi Djakov 
109415b5a75SGeorgi Djakov 	/* Poll for CK_OUT_EN bit.  max. poll time = 50us */
110415b5a75SGeorgi Djakov 	ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
111415b5a75SGeorgi Djakov 			CORE_CK_OUT_EN);
112415b5a75SGeorgi Djakov 
113415b5a75SGeorgi Djakov 	while (ck_out_en != poll) {
114415b5a75SGeorgi Djakov 		if (--wait_cnt == 0) {
115415b5a75SGeorgi Djakov 			dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
116415b5a75SGeorgi Djakov 			       mmc_hostname(mmc), poll);
117415b5a75SGeorgi Djakov 			return -ETIMEDOUT;
118415b5a75SGeorgi Djakov 		}
119415b5a75SGeorgi Djakov 		udelay(1);
120415b5a75SGeorgi Djakov 
121415b5a75SGeorgi Djakov 		ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
122415b5a75SGeorgi Djakov 				CORE_CK_OUT_EN);
123415b5a75SGeorgi Djakov 	}
124415b5a75SGeorgi Djakov 
125415b5a75SGeorgi Djakov 	return 0;
126415b5a75SGeorgi Djakov }
127415b5a75SGeorgi Djakov 
128415b5a75SGeorgi Djakov static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
129415b5a75SGeorgi Djakov {
130415b5a75SGeorgi Djakov 	int rc;
131415b5a75SGeorgi Djakov 	static const u8 grey_coded_phase_table[] = {
132415b5a75SGeorgi Djakov 		0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
133415b5a75SGeorgi Djakov 		0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
134415b5a75SGeorgi Djakov 	};
135415b5a75SGeorgi Djakov 	unsigned long flags;
136415b5a75SGeorgi Djakov 	u32 config;
137415b5a75SGeorgi Djakov 	struct mmc_host *mmc = host->mmc;
138415b5a75SGeorgi Djakov 
139415b5a75SGeorgi Djakov 	spin_lock_irqsave(&host->lock, flags);
140415b5a75SGeorgi Djakov 
141415b5a75SGeorgi Djakov 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
142415b5a75SGeorgi Djakov 	config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
143415b5a75SGeorgi Djakov 	config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
144415b5a75SGeorgi Djakov 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
145415b5a75SGeorgi Djakov 
146415b5a75SGeorgi Djakov 	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
147415b5a75SGeorgi Djakov 	rc = msm_dll_poll_ck_out_en(host, 0);
148415b5a75SGeorgi Djakov 	if (rc)
149415b5a75SGeorgi Djakov 		goto err_out;
150415b5a75SGeorgi Djakov 
151415b5a75SGeorgi Djakov 	/*
152415b5a75SGeorgi Djakov 	 * Write the selected DLL clock output phase (0 ... 15)
153415b5a75SGeorgi Djakov 	 * to CDR_SELEXT bit field of DLL_CONFIG register.
154415b5a75SGeorgi Djakov 	 */
155415b5a75SGeorgi Djakov 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
156415b5a75SGeorgi Djakov 	config &= ~CDR_SELEXT_MASK;
157415b5a75SGeorgi Djakov 	config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
158415b5a75SGeorgi Djakov 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
159415b5a75SGeorgi Djakov 
16029301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
16129301f40SRitesh Harjani 	config |= CORE_CK_OUT_EN;
16229301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
163415b5a75SGeorgi Djakov 
164415b5a75SGeorgi Djakov 	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
165415b5a75SGeorgi Djakov 	rc = msm_dll_poll_ck_out_en(host, 1);
166415b5a75SGeorgi Djakov 	if (rc)
167415b5a75SGeorgi Djakov 		goto err_out;
168415b5a75SGeorgi Djakov 
169415b5a75SGeorgi Djakov 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
170415b5a75SGeorgi Djakov 	config |= CORE_CDR_EN;
171415b5a75SGeorgi Djakov 	config &= ~CORE_CDR_EXT_EN;
172415b5a75SGeorgi Djakov 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
173415b5a75SGeorgi Djakov 	goto out;
174415b5a75SGeorgi Djakov 
175415b5a75SGeorgi Djakov err_out:
176415b5a75SGeorgi Djakov 	dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
177415b5a75SGeorgi Djakov 	       mmc_hostname(mmc), phase);
178415b5a75SGeorgi Djakov out:
179415b5a75SGeorgi Djakov 	spin_unlock_irqrestore(&host->lock, flags);
180415b5a75SGeorgi Djakov 	return rc;
181415b5a75SGeorgi Djakov }
182415b5a75SGeorgi Djakov 
183415b5a75SGeorgi Djakov /*
184415b5a75SGeorgi Djakov  * Find out the greatest range of consecuitive selected
185415b5a75SGeorgi Djakov  * DLL clock output phases that can be used as sampling
186415b5a75SGeorgi Djakov  * setting for SD3.0 UHS-I card read operation (in SDR104
187ff06ce41SVenkat Gopalakrishnan  * timing mode) or for eMMC4.5 card read operation (in
188ff06ce41SVenkat Gopalakrishnan  * HS400/HS200 timing mode).
189415b5a75SGeorgi Djakov  * Select the 3/4 of the range and configure the DLL with the
190415b5a75SGeorgi Djakov  * selected DLL clock output phase.
191415b5a75SGeorgi Djakov  */
192415b5a75SGeorgi Djakov 
193415b5a75SGeorgi Djakov static int msm_find_most_appropriate_phase(struct sdhci_host *host,
194415b5a75SGeorgi Djakov 					   u8 *phase_table, u8 total_phases)
195415b5a75SGeorgi Djakov {
196415b5a75SGeorgi Djakov 	int ret;
197415b5a75SGeorgi Djakov 	u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
198415b5a75SGeorgi Djakov 	u8 phases_per_row[MAX_PHASES] = { 0 };
199415b5a75SGeorgi Djakov 	int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
200415b5a75SGeorgi Djakov 	int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
201415b5a75SGeorgi Djakov 	bool phase_0_found = false, phase_15_found = false;
202415b5a75SGeorgi Djakov 	struct mmc_host *mmc = host->mmc;
203415b5a75SGeorgi Djakov 
204415b5a75SGeorgi Djakov 	if (!total_phases || (total_phases > MAX_PHASES)) {
205415b5a75SGeorgi Djakov 		dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
206415b5a75SGeorgi Djakov 		       mmc_hostname(mmc), total_phases);
207415b5a75SGeorgi Djakov 		return -EINVAL;
208415b5a75SGeorgi Djakov 	}
209415b5a75SGeorgi Djakov 
210415b5a75SGeorgi Djakov 	for (cnt = 0; cnt < total_phases; cnt++) {
211415b5a75SGeorgi Djakov 		ranges[row_index][col_index] = phase_table[cnt];
212415b5a75SGeorgi Djakov 		phases_per_row[row_index] += 1;
213415b5a75SGeorgi Djakov 		col_index++;
214415b5a75SGeorgi Djakov 
215415b5a75SGeorgi Djakov 		if ((cnt + 1) == total_phases) {
216415b5a75SGeorgi Djakov 			continue;
217415b5a75SGeorgi Djakov 		/* check if next phase in phase_table is consecutive or not */
218415b5a75SGeorgi Djakov 		} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
219415b5a75SGeorgi Djakov 			row_index++;
220415b5a75SGeorgi Djakov 			col_index = 0;
221415b5a75SGeorgi Djakov 		}
222415b5a75SGeorgi Djakov 	}
223415b5a75SGeorgi Djakov 
224415b5a75SGeorgi Djakov 	if (row_index >= MAX_PHASES)
225415b5a75SGeorgi Djakov 		return -EINVAL;
226415b5a75SGeorgi Djakov 
227415b5a75SGeorgi Djakov 	/* Check if phase-0 is present in first valid window? */
228415b5a75SGeorgi Djakov 	if (!ranges[0][0]) {
229415b5a75SGeorgi Djakov 		phase_0_found = true;
230415b5a75SGeorgi Djakov 		phase_0_raw_index = 0;
231415b5a75SGeorgi Djakov 		/* Check if cycle exist between 2 valid windows */
232415b5a75SGeorgi Djakov 		for (cnt = 1; cnt <= row_index; cnt++) {
233415b5a75SGeorgi Djakov 			if (phases_per_row[cnt]) {
234415b5a75SGeorgi Djakov 				for (i = 0; i < phases_per_row[cnt]; i++) {
235415b5a75SGeorgi Djakov 					if (ranges[cnt][i] == 15) {
236415b5a75SGeorgi Djakov 						phase_15_found = true;
237415b5a75SGeorgi Djakov 						phase_15_raw_index = cnt;
238415b5a75SGeorgi Djakov 						break;
239415b5a75SGeorgi Djakov 					}
240415b5a75SGeorgi Djakov 				}
241415b5a75SGeorgi Djakov 			}
242415b5a75SGeorgi Djakov 		}
243415b5a75SGeorgi Djakov 	}
244415b5a75SGeorgi Djakov 
245415b5a75SGeorgi Djakov 	/* If 2 valid windows form cycle then merge them as single window */
246415b5a75SGeorgi Djakov 	if (phase_0_found && phase_15_found) {
247415b5a75SGeorgi Djakov 		/* number of phases in raw where phase 0 is present */
248415b5a75SGeorgi Djakov 		u8 phases_0 = phases_per_row[phase_0_raw_index];
249415b5a75SGeorgi Djakov 		/* number of phases in raw where phase 15 is present */
250415b5a75SGeorgi Djakov 		u8 phases_15 = phases_per_row[phase_15_raw_index];
251415b5a75SGeorgi Djakov 
252415b5a75SGeorgi Djakov 		if (phases_0 + phases_15 >= MAX_PHASES)
253415b5a75SGeorgi Djakov 			/*
254415b5a75SGeorgi Djakov 			 * If there are more than 1 phase windows then total
255415b5a75SGeorgi Djakov 			 * number of phases in both the windows should not be
256415b5a75SGeorgi Djakov 			 * more than or equal to MAX_PHASES.
257415b5a75SGeorgi Djakov 			 */
258415b5a75SGeorgi Djakov 			return -EINVAL;
259415b5a75SGeorgi Djakov 
260415b5a75SGeorgi Djakov 		/* Merge 2 cyclic windows */
261415b5a75SGeorgi Djakov 		i = phases_15;
262415b5a75SGeorgi Djakov 		for (cnt = 0; cnt < phases_0; cnt++) {
263415b5a75SGeorgi Djakov 			ranges[phase_15_raw_index][i] =
264415b5a75SGeorgi Djakov 			    ranges[phase_0_raw_index][cnt];
265415b5a75SGeorgi Djakov 			if (++i >= MAX_PHASES)
266415b5a75SGeorgi Djakov 				break;
267415b5a75SGeorgi Djakov 		}
268415b5a75SGeorgi Djakov 
269415b5a75SGeorgi Djakov 		phases_per_row[phase_0_raw_index] = 0;
270415b5a75SGeorgi Djakov 		phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
271415b5a75SGeorgi Djakov 	}
272415b5a75SGeorgi Djakov 
273415b5a75SGeorgi Djakov 	for (cnt = 0; cnt <= row_index; cnt++) {
274415b5a75SGeorgi Djakov 		if (phases_per_row[cnt] > curr_max) {
275415b5a75SGeorgi Djakov 			curr_max = phases_per_row[cnt];
276415b5a75SGeorgi Djakov 			selected_row_index = cnt;
277415b5a75SGeorgi Djakov 		}
278415b5a75SGeorgi Djakov 	}
279415b5a75SGeorgi Djakov 
280415b5a75SGeorgi Djakov 	i = (curr_max * 3) / 4;
281415b5a75SGeorgi Djakov 	if (i)
282415b5a75SGeorgi Djakov 		i--;
283415b5a75SGeorgi Djakov 
284415b5a75SGeorgi Djakov 	ret = ranges[selected_row_index][i];
285415b5a75SGeorgi Djakov 
286415b5a75SGeorgi Djakov 	if (ret >= MAX_PHASES) {
287415b5a75SGeorgi Djakov 		ret = -EINVAL;
288415b5a75SGeorgi Djakov 		dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
289415b5a75SGeorgi Djakov 		       mmc_hostname(mmc), ret);
290415b5a75SGeorgi Djakov 	}
291415b5a75SGeorgi Djakov 
292415b5a75SGeorgi Djakov 	return ret;
293415b5a75SGeorgi Djakov }
294415b5a75SGeorgi Djakov 
295415b5a75SGeorgi Djakov static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
296415b5a75SGeorgi Djakov {
297415b5a75SGeorgi Djakov 	u32 mclk_freq = 0, config;
298415b5a75SGeorgi Djakov 
299415b5a75SGeorgi Djakov 	/* Program the MCLK value to MCLK_FREQ bit field */
300415b5a75SGeorgi Djakov 	if (host->clock <= 112000000)
301415b5a75SGeorgi Djakov 		mclk_freq = 0;
302415b5a75SGeorgi Djakov 	else if (host->clock <= 125000000)
303415b5a75SGeorgi Djakov 		mclk_freq = 1;
304415b5a75SGeorgi Djakov 	else if (host->clock <= 137000000)
305415b5a75SGeorgi Djakov 		mclk_freq = 2;
306415b5a75SGeorgi Djakov 	else if (host->clock <= 150000000)
307415b5a75SGeorgi Djakov 		mclk_freq = 3;
308415b5a75SGeorgi Djakov 	else if (host->clock <= 162000000)
309415b5a75SGeorgi Djakov 		mclk_freq = 4;
310415b5a75SGeorgi Djakov 	else if (host->clock <= 175000000)
311415b5a75SGeorgi Djakov 		mclk_freq = 5;
312415b5a75SGeorgi Djakov 	else if (host->clock <= 187000000)
313415b5a75SGeorgi Djakov 		mclk_freq = 6;
314415b5a75SGeorgi Djakov 	else if (host->clock <= 200000000)
315415b5a75SGeorgi Djakov 		mclk_freq = 7;
316415b5a75SGeorgi Djakov 
317415b5a75SGeorgi Djakov 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
318415b5a75SGeorgi Djakov 	config &= ~CMUX_SHIFT_PHASE_MASK;
319415b5a75SGeorgi Djakov 	config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
320415b5a75SGeorgi Djakov 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
321415b5a75SGeorgi Djakov }
322415b5a75SGeorgi Djakov 
323415b5a75SGeorgi Djakov /* Initialize the DLL (Programmable Delay Line) */
324415b5a75SGeorgi Djakov static int msm_init_cm_dll(struct sdhci_host *host)
325415b5a75SGeorgi Djakov {
326415b5a75SGeorgi Djakov 	struct mmc_host *mmc = host->mmc;
32783736352SVenkat Gopalakrishnan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
32883736352SVenkat Gopalakrishnan 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
329415b5a75SGeorgi Djakov 	int wait_cnt = 50;
330415b5a75SGeorgi Djakov 	unsigned long flags;
33129301f40SRitesh Harjani 	u32 config;
332415b5a75SGeorgi Djakov 
333415b5a75SGeorgi Djakov 	spin_lock_irqsave(&host->lock, flags);
334415b5a75SGeorgi Djakov 
335415b5a75SGeorgi Djakov 	/*
336415b5a75SGeorgi Djakov 	 * Make sure that clock is always enabled when DLL
337415b5a75SGeorgi Djakov 	 * tuning is in progress. Keeping PWRSAVE ON may
338415b5a75SGeorgi Djakov 	 * turn off the clock.
339415b5a75SGeorgi Djakov 	 */
34029301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
34129301f40SRitesh Harjani 	config &= ~CORE_CLK_PWRSAVE;
34229301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
343415b5a75SGeorgi Djakov 
34483736352SVenkat Gopalakrishnan 	if (msm_host->use_14lpp_dll_reset) {
34583736352SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
34683736352SVenkat Gopalakrishnan 		config &= ~CORE_CK_OUT_EN;
34783736352SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
34883736352SVenkat Gopalakrishnan 
34983736352SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
35083736352SVenkat Gopalakrishnan 		config |= CORE_DLL_CLOCK_DISABLE;
35183736352SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
35283736352SVenkat Gopalakrishnan 	}
35383736352SVenkat Gopalakrishnan 
35429301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
35529301f40SRitesh Harjani 	config |= CORE_DLL_RST;
35629301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
357415b5a75SGeorgi Djakov 
35829301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
35929301f40SRitesh Harjani 	config |= CORE_DLL_PDN;
36029301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
361415b5a75SGeorgi Djakov 	msm_cm_dll_set_freq(host);
362415b5a75SGeorgi Djakov 
36383736352SVenkat Gopalakrishnan 	if (msm_host->use_14lpp_dll_reset &&
36483736352SVenkat Gopalakrishnan 	    !IS_ERR_OR_NULL(msm_host->xo_clk)) {
36583736352SVenkat Gopalakrishnan 		u32 mclk_freq = 0;
36683736352SVenkat Gopalakrishnan 
36783736352SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
36883736352SVenkat Gopalakrishnan 		config &= CORE_FLL_CYCLE_CNT;
36983736352SVenkat Gopalakrishnan 		if (config)
37083736352SVenkat Gopalakrishnan 			mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 8),
37183736352SVenkat Gopalakrishnan 					clk_get_rate(msm_host->xo_clk));
37283736352SVenkat Gopalakrishnan 		else
37383736352SVenkat Gopalakrishnan 			mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 4),
37483736352SVenkat Gopalakrishnan 					clk_get_rate(msm_host->xo_clk));
37583736352SVenkat Gopalakrishnan 
37683736352SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
37783736352SVenkat Gopalakrishnan 		config &= ~(0xFF << 10);
37883736352SVenkat Gopalakrishnan 		config |= mclk_freq << 10;
37983736352SVenkat Gopalakrishnan 
38083736352SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
38183736352SVenkat Gopalakrishnan 		/* wait for 5us before enabling DLL clock */
38283736352SVenkat Gopalakrishnan 		udelay(5);
38383736352SVenkat Gopalakrishnan 	}
38483736352SVenkat Gopalakrishnan 
38529301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
38629301f40SRitesh Harjani 	config &= ~CORE_DLL_RST;
38729301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
388415b5a75SGeorgi Djakov 
38929301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
39029301f40SRitesh Harjani 	config &= ~CORE_DLL_PDN;
39129301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
392415b5a75SGeorgi Djakov 
39383736352SVenkat Gopalakrishnan 	if (msm_host->use_14lpp_dll_reset) {
39483736352SVenkat Gopalakrishnan 		msm_cm_dll_set_freq(host);
39583736352SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
39683736352SVenkat Gopalakrishnan 		config &= ~CORE_DLL_CLOCK_DISABLE;
39783736352SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
39883736352SVenkat Gopalakrishnan 	}
39983736352SVenkat Gopalakrishnan 
40029301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
40129301f40SRitesh Harjani 	config |= CORE_DLL_EN;
40229301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
403415b5a75SGeorgi Djakov 
40429301f40SRitesh Harjani 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
40529301f40SRitesh Harjani 	config |= CORE_CK_OUT_EN;
40629301f40SRitesh Harjani 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
407415b5a75SGeorgi Djakov 
408415b5a75SGeorgi Djakov 	/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
409415b5a75SGeorgi Djakov 	while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
410415b5a75SGeorgi Djakov 		 CORE_DLL_LOCK)) {
411415b5a75SGeorgi Djakov 		/* max. wait for 50us sec for LOCK bit to be set */
412415b5a75SGeorgi Djakov 		if (--wait_cnt == 0) {
413415b5a75SGeorgi Djakov 			dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
414415b5a75SGeorgi Djakov 			       mmc_hostname(mmc));
415415b5a75SGeorgi Djakov 			spin_unlock_irqrestore(&host->lock, flags);
416415b5a75SGeorgi Djakov 			return -ETIMEDOUT;
417415b5a75SGeorgi Djakov 		}
418415b5a75SGeorgi Djakov 		udelay(1);
419415b5a75SGeorgi Djakov 	}
420415b5a75SGeorgi Djakov 
421415b5a75SGeorgi Djakov 	spin_unlock_irqrestore(&host->lock, flags);
422415b5a75SGeorgi Djakov 	return 0;
423415b5a75SGeorgi Djakov }
424415b5a75SGeorgi Djakov 
4250eb0d9f4SGeorgi Djakov static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
4260eb0d9f4SGeorgi Djakov {
427415b5a75SGeorgi Djakov 	int tuning_seq_cnt = 3;
42833d73935SUlf Hansson 	u8 phase, tuned_phases[16], tuned_phase_cnt = 0;
429415b5a75SGeorgi Djakov 	int rc;
430415b5a75SGeorgi Djakov 	struct mmc_host *mmc = host->mmc;
431415b5a75SGeorgi Djakov 	struct mmc_ios ios = host->mmc->ios;
432415b5a75SGeorgi Djakov 
4330eb0d9f4SGeorgi Djakov 	/*
434415b5a75SGeorgi Djakov 	 * Tuning is required for SDR104, HS200 and HS400 cards and
435415b5a75SGeorgi Djakov 	 * if clock frequency is greater than 100MHz in these modes.
4360eb0d9f4SGeorgi Djakov 	 */
437ff06ce41SVenkat Gopalakrishnan 	if (host->clock <= CORE_FREQ_100MHZ ||
438ff06ce41SVenkat Gopalakrishnan 	    !(ios.timing == MMC_TIMING_MMC_HS400 ||
439ff06ce41SVenkat Gopalakrishnan 	    ios.timing == MMC_TIMING_MMC_HS200 ||
440ff06ce41SVenkat Gopalakrishnan 	    ios.timing == MMC_TIMING_UHS_SDR104))
4410eb0d9f4SGeorgi Djakov 		return 0;
442415b5a75SGeorgi Djakov 
443415b5a75SGeorgi Djakov retry:
444415b5a75SGeorgi Djakov 	/* First of all reset the tuning block */
445415b5a75SGeorgi Djakov 	rc = msm_init_cm_dll(host);
446415b5a75SGeorgi Djakov 	if (rc)
44733d73935SUlf Hansson 		return rc;
448415b5a75SGeorgi Djakov 
449415b5a75SGeorgi Djakov 	phase = 0;
450415b5a75SGeorgi Djakov 	do {
451415b5a75SGeorgi Djakov 		/* Set the phase in delay line hw block */
452415b5a75SGeorgi Djakov 		rc = msm_config_cm_dll_phase(host, phase);
453415b5a75SGeorgi Djakov 		if (rc)
45433d73935SUlf Hansson 			return rc;
455415b5a75SGeorgi Djakov 
4569979dbe5SChaotian Jing 		rc = mmc_send_tuning(mmc, opcode, NULL);
45733d73935SUlf Hansson 		if (!rc) {
458415b5a75SGeorgi Djakov 			/* Tuning is successful at this tuning point */
459415b5a75SGeorgi Djakov 			tuned_phases[tuned_phase_cnt++] = phase;
460415b5a75SGeorgi Djakov 			dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
461415b5a75SGeorgi Djakov 				 mmc_hostname(mmc), phase);
462415b5a75SGeorgi Djakov 		}
463415b5a75SGeorgi Djakov 	} while (++phase < ARRAY_SIZE(tuned_phases));
464415b5a75SGeorgi Djakov 
465415b5a75SGeorgi Djakov 	if (tuned_phase_cnt) {
466415b5a75SGeorgi Djakov 		rc = msm_find_most_appropriate_phase(host, tuned_phases,
467415b5a75SGeorgi Djakov 						     tuned_phase_cnt);
468415b5a75SGeorgi Djakov 		if (rc < 0)
46933d73935SUlf Hansson 			return rc;
470415b5a75SGeorgi Djakov 		else
471415b5a75SGeorgi Djakov 			phase = rc;
472415b5a75SGeorgi Djakov 
473415b5a75SGeorgi Djakov 		/*
474415b5a75SGeorgi Djakov 		 * Finally set the selected phase in delay
475415b5a75SGeorgi Djakov 		 * line hw block.
476415b5a75SGeorgi Djakov 		 */
477415b5a75SGeorgi Djakov 		rc = msm_config_cm_dll_phase(host, phase);
478415b5a75SGeorgi Djakov 		if (rc)
47933d73935SUlf Hansson 			return rc;
480415b5a75SGeorgi Djakov 		dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
481415b5a75SGeorgi Djakov 			 mmc_hostname(mmc), phase);
482415b5a75SGeorgi Djakov 	} else {
483415b5a75SGeorgi Djakov 		if (--tuning_seq_cnt)
484415b5a75SGeorgi Djakov 			goto retry;
485415b5a75SGeorgi Djakov 		/* Tuning failed */
486415b5a75SGeorgi Djakov 		dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
487415b5a75SGeorgi Djakov 		       mmc_hostname(mmc));
488415b5a75SGeorgi Djakov 		rc = -EIO;
489415b5a75SGeorgi Djakov 	}
490415b5a75SGeorgi Djakov 
491ff06ce41SVenkat Gopalakrishnan 	if (!rc)
492ff06ce41SVenkat Gopalakrishnan 		msm_host->tuning_done = true;
493415b5a75SGeorgi Djakov 	return rc;
4940eb0d9f4SGeorgi Djakov }
4950eb0d9f4SGeorgi Djakov 
496ee320674SRitesh Harjani static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
497ee320674SRitesh Harjani 					unsigned int uhs)
498ee320674SRitesh Harjani {
499ee320674SRitesh Harjani 	struct mmc_host *mmc = host->mmc;
500ff06ce41SVenkat Gopalakrishnan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
501ff06ce41SVenkat Gopalakrishnan 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
502ee320674SRitesh Harjani 	u16 ctrl_2;
503ff06ce41SVenkat Gopalakrishnan 	u32 config;
504ee320674SRitesh Harjani 
505ee320674SRitesh Harjani 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
506ee320674SRitesh Harjani 	/* Select Bus Speed Mode for host */
507ee320674SRitesh Harjani 	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
508ee320674SRitesh Harjani 	switch (uhs) {
509ee320674SRitesh Harjani 	case MMC_TIMING_UHS_SDR12:
510ee320674SRitesh Harjani 		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
511ee320674SRitesh Harjani 		break;
512ee320674SRitesh Harjani 	case MMC_TIMING_UHS_SDR25:
513ee320674SRitesh Harjani 		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
514ee320674SRitesh Harjani 		break;
515ee320674SRitesh Harjani 	case MMC_TIMING_UHS_SDR50:
516ee320674SRitesh Harjani 		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
517ee320674SRitesh Harjani 		break;
518ff06ce41SVenkat Gopalakrishnan 	case MMC_TIMING_MMC_HS400:
519ee320674SRitesh Harjani 	case MMC_TIMING_MMC_HS200:
520ee320674SRitesh Harjani 	case MMC_TIMING_UHS_SDR104:
521ee320674SRitesh Harjani 		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
522ee320674SRitesh Harjani 		break;
523ee320674SRitesh Harjani 	case MMC_TIMING_UHS_DDR50:
524ee320674SRitesh Harjani 	case MMC_TIMING_MMC_DDR52:
525ee320674SRitesh Harjani 		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
526ee320674SRitesh Harjani 		break;
527ee320674SRitesh Harjani 	}
528ee320674SRitesh Harjani 
529ee320674SRitesh Harjani 	/*
530ee320674SRitesh Harjani 	 * When clock frequency is less than 100MHz, the feedback clock must be
531ee320674SRitesh Harjani 	 * provided and DLL must not be used so that tuning can be skipped. To
532ee320674SRitesh Harjani 	 * provide feedback clock, the mode selection can be any value less
533ee320674SRitesh Harjani 	 * than 3'b011 in bits [2:0] of HOST CONTROL2 register.
534ee320674SRitesh Harjani 	 */
535ff06ce41SVenkat Gopalakrishnan 	if (host->clock <= CORE_FREQ_100MHZ) {
536ff06ce41SVenkat Gopalakrishnan 		if (uhs == MMC_TIMING_MMC_HS400 ||
537ee320674SRitesh Harjani 		    uhs == MMC_TIMING_MMC_HS200 ||
538ff06ce41SVenkat Gopalakrishnan 		    uhs == MMC_TIMING_UHS_SDR104)
539ee320674SRitesh Harjani 			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
540ff06ce41SVenkat Gopalakrishnan 		/*
541ff06ce41SVenkat Gopalakrishnan 		 * DLL is not required for clock <= 100MHz
542ff06ce41SVenkat Gopalakrishnan 		 * Thus, make sure DLL it is disabled when not required
543ff06ce41SVenkat Gopalakrishnan 		 */
544ff06ce41SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
545ff06ce41SVenkat Gopalakrishnan 		config |= CORE_DLL_RST;
546ff06ce41SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
547ff06ce41SVenkat Gopalakrishnan 
548ff06ce41SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
549ff06ce41SVenkat Gopalakrishnan 		config |= CORE_DLL_PDN;
550ff06ce41SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
551ff06ce41SVenkat Gopalakrishnan 
552ff06ce41SVenkat Gopalakrishnan 		/*
553ff06ce41SVenkat Gopalakrishnan 		 * The DLL needs to be restored and CDCLP533 recalibrated
554ff06ce41SVenkat Gopalakrishnan 		 * when the clock frequency is set back to 400MHz.
555ff06ce41SVenkat Gopalakrishnan 		 */
556ff06ce41SVenkat Gopalakrishnan 		msm_host->calibration_done = false;
557ff06ce41SVenkat Gopalakrishnan 	}
558ee320674SRitesh Harjani 
559ee320674SRitesh Harjani 	dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
560ee320674SRitesh Harjani 		mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
561ee320674SRitesh Harjani 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
562ee320674SRitesh Harjani }
563ee320674SRitesh Harjani 
564ad81d387SGeorgi Djakov static void sdhci_msm_voltage_switch(struct sdhci_host *host)
565ad81d387SGeorgi Djakov {
566ad81d387SGeorgi Djakov 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
567ad81d387SGeorgi Djakov 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
568ad81d387SGeorgi Djakov 	u32 irq_status, irq_ack = 0;
569ad81d387SGeorgi Djakov 
570ad81d387SGeorgi Djakov 	irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
571ad81d387SGeorgi Djakov 	irq_status &= INT_MASK;
572ad81d387SGeorgi Djakov 
573ad81d387SGeorgi Djakov 	writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR);
574ad81d387SGeorgi Djakov 
575ad81d387SGeorgi Djakov 	if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
576ad81d387SGeorgi Djakov 		irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
577ad81d387SGeorgi Djakov 	if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH))
578ad81d387SGeorgi Djakov 		irq_ack |= CORE_PWRCTL_IO_SUCCESS;
579ad81d387SGeorgi Djakov 
580ad81d387SGeorgi Djakov 	/*
581ad81d387SGeorgi Djakov 	 * The driver has to acknowledge the interrupt, switch voltages and
582ad81d387SGeorgi Djakov 	 * report back if it succeded or not to this register. The voltage
583ad81d387SGeorgi Djakov 	 * switches are handled by the sdhci core, so just report success.
584ad81d387SGeorgi Djakov 	 */
585ad81d387SGeorgi Djakov 	writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
586ad81d387SGeorgi Djakov }
587ad81d387SGeorgi Djakov 
588ad81d387SGeorgi Djakov static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
589ad81d387SGeorgi Djakov {
590ad81d387SGeorgi Djakov 	struct sdhci_host *host = (struct sdhci_host *)data;
591ad81d387SGeorgi Djakov 
592ad81d387SGeorgi Djakov 	sdhci_msm_voltage_switch(host);
593ad81d387SGeorgi Djakov 
594ad81d387SGeorgi Djakov 	return IRQ_HANDLED;
595ad81d387SGeorgi Djakov }
596ad81d387SGeorgi Djakov 
59780031bdeSRitesh Harjani static unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host)
59880031bdeSRitesh Harjani {
59980031bdeSRitesh Harjani 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
60080031bdeSRitesh Harjani 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
60180031bdeSRitesh Harjani 
60280031bdeSRitesh Harjani 	return clk_round_rate(msm_host->clk, ULONG_MAX);
60380031bdeSRitesh Harjani }
60480031bdeSRitesh Harjani 
60580031bdeSRitesh Harjani static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
60680031bdeSRitesh Harjani {
60780031bdeSRitesh Harjani 	return SDHCI_MSM_MIN_CLOCK;
60880031bdeSRitesh Harjani }
60980031bdeSRitesh Harjani 
610edc609fdSRitesh Harjani /**
611edc609fdSRitesh Harjani  * __sdhci_msm_set_clock - sdhci_msm clock control.
612edc609fdSRitesh Harjani  *
613edc609fdSRitesh Harjani  * Description:
614edc609fdSRitesh Harjani  * MSM controller does not use internal divider and
615edc609fdSRitesh Harjani  * instead directly control the GCC clock as per
616edc609fdSRitesh Harjani  * HW recommendation.
617edc609fdSRitesh Harjani  **/
618edc609fdSRitesh Harjani void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
619edc609fdSRitesh Harjani {
620edc609fdSRitesh Harjani 	u16 clk;
621edc609fdSRitesh Harjani 	/*
622edc609fdSRitesh Harjani 	 * Keep actual_clock as zero -
623edc609fdSRitesh Harjani 	 * - since there is no divider used so no need of having actual_clock.
624edc609fdSRitesh Harjani 	 * - MSM controller uses SDCLK for data timeout calculation. If
625edc609fdSRitesh Harjani 	 *   actual_clock is zero, host->clock is taken for calculation.
626edc609fdSRitesh Harjani 	 */
627edc609fdSRitesh Harjani 	host->mmc->actual_clock = 0;
628edc609fdSRitesh Harjani 
629edc609fdSRitesh Harjani 	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
630edc609fdSRitesh Harjani 
631edc609fdSRitesh Harjani 	if (clock == 0)
632edc609fdSRitesh Harjani 		return;
633edc609fdSRitesh Harjani 
634edc609fdSRitesh Harjani 	/*
635edc609fdSRitesh Harjani 	 * MSM controller do not use clock divider.
636edc609fdSRitesh Harjani 	 * Thus read SDHCI_CLOCK_CONTROL and only enable
637edc609fdSRitesh Harjani 	 * clock with no divider value programmed.
638edc609fdSRitesh Harjani 	 */
639edc609fdSRitesh Harjani 	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
640edc609fdSRitesh Harjani 	sdhci_enable_clk(host, clk);
641edc609fdSRitesh Harjani }
642edc609fdSRitesh Harjani 
643edc609fdSRitesh Harjani /* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
644edc609fdSRitesh Harjani static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
645edc609fdSRitesh Harjani {
646edc609fdSRitesh Harjani 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
647edc609fdSRitesh Harjani 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
648b12d44dbSRitesh Harjani 	struct mmc_ios curr_ios = host->mmc->ios;
649ff06ce41SVenkat Gopalakrishnan 	u32 config;
650edc609fdSRitesh Harjani 	int rc;
651edc609fdSRitesh Harjani 
652edc609fdSRitesh Harjani 	if (!clock) {
653edc609fdSRitesh Harjani 		msm_host->clk_rate = clock;
654edc609fdSRitesh Harjani 		goto out;
655edc609fdSRitesh Harjani 	}
656edc609fdSRitesh Harjani 
657edc609fdSRitesh Harjani 	spin_unlock_irq(&host->lock);
658b12d44dbSRitesh Harjani 	/*
659b12d44dbSRitesh Harjani 	 * The SDHC requires internal clock frequency to be double the
660b12d44dbSRitesh Harjani 	 * actual clock that will be set for DDR mode. The controller
661b12d44dbSRitesh Harjani 	 * uses the faster clock(100/400MHz) for some of its parts and
662b12d44dbSRitesh Harjani 	 * send the actual required clock (50/200MHz) to the card.
663b12d44dbSRitesh Harjani 	 */
664b12d44dbSRitesh Harjani 	if (curr_ios.timing == MMC_TIMING_UHS_DDR50 ||
665b12d44dbSRitesh Harjani 	    curr_ios.timing == MMC_TIMING_MMC_DDR52 ||
666b12d44dbSRitesh Harjani 	    curr_ios.timing == MMC_TIMING_MMC_HS400)
667b12d44dbSRitesh Harjani 		clock *= 2;
668ff06ce41SVenkat Gopalakrishnan 	/*
669ff06ce41SVenkat Gopalakrishnan 	 * In general all timing modes are controlled via UHS mode select in
670ff06ce41SVenkat Gopalakrishnan 	 * Host Control2 register. eMMC specific HS200/HS400 doesn't have
671ff06ce41SVenkat Gopalakrishnan 	 * their respective modes defined here, hence we use these values.
672ff06ce41SVenkat Gopalakrishnan 	 *
673ff06ce41SVenkat Gopalakrishnan 	 * HS200 - SDR104 (Since they both are equivalent in functionality)
674ff06ce41SVenkat Gopalakrishnan 	 * HS400 - This involves multiple configurations
675ff06ce41SVenkat Gopalakrishnan 	 *		Initially SDR104 - when tuning is required as HS200
676ff06ce41SVenkat Gopalakrishnan 	 *		Then when switching to DDR @ 400MHz (HS400) we use
677ff06ce41SVenkat Gopalakrishnan 	 *		the vendor specific HC_SELECT_IN to control the mode.
678ff06ce41SVenkat Gopalakrishnan 	 *
679ff06ce41SVenkat Gopalakrishnan 	 * In addition to controlling the modes we also need to select the
680ff06ce41SVenkat Gopalakrishnan 	 * correct input clock for DLL depending on the mode.
681ff06ce41SVenkat Gopalakrishnan 	 *
682ff06ce41SVenkat Gopalakrishnan 	 * HS400 - divided clock (free running MCLK/2)
683ff06ce41SVenkat Gopalakrishnan 	 * All other modes - default (free running MCLK)
684ff06ce41SVenkat Gopalakrishnan 	 */
685ff06ce41SVenkat Gopalakrishnan 	if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
686ff06ce41SVenkat Gopalakrishnan 		/* Select the divided clock (free running MCLK/2) */
687ff06ce41SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
688ff06ce41SVenkat Gopalakrishnan 		config &= ~CORE_HC_MCLK_SEL_MASK;
689ff06ce41SVenkat Gopalakrishnan 		config |= CORE_HC_MCLK_SEL_HS400;
690ff06ce41SVenkat Gopalakrishnan 
691ff06ce41SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
692ff06ce41SVenkat Gopalakrishnan 		/*
693ff06ce41SVenkat Gopalakrishnan 		 * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
694ff06ce41SVenkat Gopalakrishnan 		 * register
695ff06ce41SVenkat Gopalakrishnan 		 */
696ff06ce41SVenkat Gopalakrishnan 		if (msm_host->tuning_done && !msm_host->calibration_done) {
697ff06ce41SVenkat Gopalakrishnan 			/*
698ff06ce41SVenkat Gopalakrishnan 			 * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
699ff06ce41SVenkat Gopalakrishnan 			 * field in VENDOR_SPEC_FUNC
700ff06ce41SVenkat Gopalakrishnan 			 */
701ff06ce41SVenkat Gopalakrishnan 			config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
702ff06ce41SVenkat Gopalakrishnan 			config |= CORE_HC_SELECT_IN_HS400;
703ff06ce41SVenkat Gopalakrishnan 			config |= CORE_HC_SELECT_IN_EN;
704ff06ce41SVenkat Gopalakrishnan 			writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
705ff06ce41SVenkat Gopalakrishnan 		}
706ff06ce41SVenkat Gopalakrishnan 	} else {
707ff06ce41SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
708ff06ce41SVenkat Gopalakrishnan 		config &= ~CORE_HC_MCLK_SEL_MASK;
709ff06ce41SVenkat Gopalakrishnan 		config |= CORE_HC_MCLK_SEL_DFLT;
710ff06ce41SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
711ff06ce41SVenkat Gopalakrishnan 
712ff06ce41SVenkat Gopalakrishnan 		/*
713ff06ce41SVenkat Gopalakrishnan 		 * Disable HC_SELECT_IN to be able to use the UHS mode select
714ff06ce41SVenkat Gopalakrishnan 		 * configuration from Host Control2 register for all other
715ff06ce41SVenkat Gopalakrishnan 		 * modes.
716ff06ce41SVenkat Gopalakrishnan 		 * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
717ff06ce41SVenkat Gopalakrishnan 		 * in VENDOR_SPEC_FUNC
718ff06ce41SVenkat Gopalakrishnan 		 */
719ff06ce41SVenkat Gopalakrishnan 		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
720ff06ce41SVenkat Gopalakrishnan 		config &= ~CORE_HC_SELECT_IN_EN;
721ff06ce41SVenkat Gopalakrishnan 		config &= ~CORE_HC_SELECT_IN_MASK;
722ff06ce41SVenkat Gopalakrishnan 		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
723ff06ce41SVenkat Gopalakrishnan 	}
724ff06ce41SVenkat Gopalakrishnan 
725ff06ce41SVenkat Gopalakrishnan 	/*
726ff06ce41SVenkat Gopalakrishnan 	 * Make sure above writes impacting free running MCLK are completed
727ff06ce41SVenkat Gopalakrishnan 	 * before changing the clk_rate at GCC.
728ff06ce41SVenkat Gopalakrishnan 	 */
729ff06ce41SVenkat Gopalakrishnan 	wmb();
730edc609fdSRitesh Harjani 
731edc609fdSRitesh Harjani 	rc = clk_set_rate(msm_host->clk, clock);
732edc609fdSRitesh Harjani 	if (rc) {
733b12d44dbSRitesh Harjani 		pr_err("%s: Failed to set clock at rate %u at timing %d\n",
734b12d44dbSRitesh Harjani 		       mmc_hostname(host->mmc), clock,
735b12d44dbSRitesh Harjani 		       curr_ios.timing);
736edc609fdSRitesh Harjani 		goto out_lock;
737edc609fdSRitesh Harjani 	}
738edc609fdSRitesh Harjani 	msm_host->clk_rate = clock;
739b12d44dbSRitesh Harjani 	pr_debug("%s: Setting clock at rate %lu at timing %d\n",
740b12d44dbSRitesh Harjani 		 mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
741b12d44dbSRitesh Harjani 		 curr_ios.timing);
742edc609fdSRitesh Harjani 
743edc609fdSRitesh Harjani out_lock:
744edc609fdSRitesh Harjani 	spin_lock_irq(&host->lock);
745edc609fdSRitesh Harjani out:
746edc609fdSRitesh Harjani 	__sdhci_msm_set_clock(host, clock);
747edc609fdSRitesh Harjani }
748edc609fdSRitesh Harjani 
7490eb0d9f4SGeorgi Djakov static const struct of_device_id sdhci_msm_dt_match[] = {
7500eb0d9f4SGeorgi Djakov 	{ .compatible = "qcom,sdhci-msm-v4" },
7510eb0d9f4SGeorgi Djakov 	{},
7520eb0d9f4SGeorgi Djakov };
7530eb0d9f4SGeorgi Djakov 
7540eb0d9f4SGeorgi Djakov MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
7550eb0d9f4SGeorgi Djakov 
756a50396a4SJisheng Zhang static const struct sdhci_ops sdhci_msm_ops = {
7570eb0d9f4SGeorgi Djakov 	.platform_execute_tuning = sdhci_msm_execute_tuning,
758ed1761d7SStephen Boyd 	.reset = sdhci_reset,
759edc609fdSRitesh Harjani 	.set_clock = sdhci_msm_set_clock,
76080031bdeSRitesh Harjani 	.get_min_clock = sdhci_msm_get_min_clock,
76180031bdeSRitesh Harjani 	.get_max_clock = sdhci_msm_get_max_clock,
762ed1761d7SStephen Boyd 	.set_bus_width = sdhci_set_bus_width,
763ee320674SRitesh Harjani 	.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
764ad81d387SGeorgi Djakov 	.voltage_switch = sdhci_msm_voltage_switch,
7650eb0d9f4SGeorgi Djakov };
7660eb0d9f4SGeorgi Djakov 
767a50396a4SJisheng Zhang static const struct sdhci_pltfm_data sdhci_msm_pdata = {
768a50396a4SJisheng Zhang 	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
7699718f84bSGeorgi Djakov 		  SDHCI_QUIRK_NO_CARD_NO_RESET |
770a0e31428SRitesh Harjani 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
771a0e31428SRitesh Harjani 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
772a0e31428SRitesh Harjani 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
773a50396a4SJisheng Zhang 	.ops = &sdhci_msm_ops,
774a50396a4SJisheng Zhang };
775a50396a4SJisheng Zhang 
7760eb0d9f4SGeorgi Djakov static int sdhci_msm_probe(struct platform_device *pdev)
7770eb0d9f4SGeorgi Djakov {
7780eb0d9f4SGeorgi Djakov 	struct sdhci_host *host;
7790eb0d9f4SGeorgi Djakov 	struct sdhci_pltfm_host *pltfm_host;
7800eb0d9f4SGeorgi Djakov 	struct sdhci_msm_host *msm_host;
7810eb0d9f4SGeorgi Djakov 	struct resource *core_memres;
7820eb0d9f4SGeorgi Djakov 	int ret;
7833a3ad3e9SGeorgi Djakov 	u16 host_version, core_minor;
78429301f40SRitesh Harjani 	u32 core_version, config;
7853a3ad3e9SGeorgi Djakov 	u8 core_major;
7860eb0d9f4SGeorgi Djakov 
7876f699531SJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
7880eb0d9f4SGeorgi Djakov 	if (IS_ERR(host))
7890eb0d9f4SGeorgi Djakov 		return PTR_ERR(host);
7900eb0d9f4SGeorgi Djakov 
7910eb0d9f4SGeorgi Djakov 	pltfm_host = sdhci_priv(host);
7926f699531SJisheng Zhang 	msm_host = sdhci_pltfm_priv(pltfm_host);
7930eb0d9f4SGeorgi Djakov 	msm_host->mmc = host->mmc;
7940eb0d9f4SGeorgi Djakov 	msm_host->pdev = pdev;
7950eb0d9f4SGeorgi Djakov 
7960eb0d9f4SGeorgi Djakov 	ret = mmc_of_parse(host->mmc);
7970eb0d9f4SGeorgi Djakov 	if (ret)
7980eb0d9f4SGeorgi Djakov 		goto pltfm_free;
7990eb0d9f4SGeorgi Djakov 
8000eb0d9f4SGeorgi Djakov 	sdhci_get_of_property(pdev);
8010eb0d9f4SGeorgi Djakov 
8020eb0d9f4SGeorgi Djakov 	/* Setup SDCC bus voter clock. */
8030eb0d9f4SGeorgi Djakov 	msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
8040eb0d9f4SGeorgi Djakov 	if (!IS_ERR(msm_host->bus_clk)) {
8050eb0d9f4SGeorgi Djakov 		/* Vote for max. clk rate for max. performance */
8060eb0d9f4SGeorgi Djakov 		ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
8070eb0d9f4SGeorgi Djakov 		if (ret)
8080eb0d9f4SGeorgi Djakov 			goto pltfm_free;
8090eb0d9f4SGeorgi Djakov 		ret = clk_prepare_enable(msm_host->bus_clk);
8100eb0d9f4SGeorgi Djakov 		if (ret)
8110eb0d9f4SGeorgi Djakov 			goto pltfm_free;
8120eb0d9f4SGeorgi Djakov 	}
8130eb0d9f4SGeorgi Djakov 
8140eb0d9f4SGeorgi Djakov 	/* Setup main peripheral bus clock */
8150eb0d9f4SGeorgi Djakov 	msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
8160eb0d9f4SGeorgi Djakov 	if (IS_ERR(msm_host->pclk)) {
8170eb0d9f4SGeorgi Djakov 		ret = PTR_ERR(msm_host->pclk);
8182801b95eSColin Ian King 		dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret);
8190eb0d9f4SGeorgi Djakov 		goto bus_clk_disable;
8200eb0d9f4SGeorgi Djakov 	}
8210eb0d9f4SGeorgi Djakov 
8220eb0d9f4SGeorgi Djakov 	ret = clk_prepare_enable(msm_host->pclk);
8230eb0d9f4SGeorgi Djakov 	if (ret)
8240eb0d9f4SGeorgi Djakov 		goto bus_clk_disable;
8250eb0d9f4SGeorgi Djakov 
8260eb0d9f4SGeorgi Djakov 	/* Setup SDC MMC clock */
8270eb0d9f4SGeorgi Djakov 	msm_host->clk = devm_clk_get(&pdev->dev, "core");
8280eb0d9f4SGeorgi Djakov 	if (IS_ERR(msm_host->clk)) {
8290eb0d9f4SGeorgi Djakov 		ret = PTR_ERR(msm_host->clk);
8300eb0d9f4SGeorgi Djakov 		dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
8310eb0d9f4SGeorgi Djakov 		goto pclk_disable;
8320eb0d9f4SGeorgi Djakov 	}
8330eb0d9f4SGeorgi Djakov 
83483736352SVenkat Gopalakrishnan 	/*
83583736352SVenkat Gopalakrishnan 	 * xo clock is needed for FLL feature of cm_dll.
83683736352SVenkat Gopalakrishnan 	 * In case if xo clock is not mentioned in DT, warn and proceed.
83783736352SVenkat Gopalakrishnan 	 */
83883736352SVenkat Gopalakrishnan 	msm_host->xo_clk = devm_clk_get(&pdev->dev, "xo");
83983736352SVenkat Gopalakrishnan 	if (IS_ERR(msm_host->xo_clk)) {
84083736352SVenkat Gopalakrishnan 		ret = PTR_ERR(msm_host->xo_clk);
84183736352SVenkat Gopalakrishnan 		dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret);
84283736352SVenkat Gopalakrishnan 	}
84383736352SVenkat Gopalakrishnan 
844951b8c87SIvan T. Ivanov 	/* Vote for maximum clock rate for maximum performance */
845951b8c87SIvan T. Ivanov 	ret = clk_set_rate(msm_host->clk, INT_MAX);
846951b8c87SIvan T. Ivanov 	if (ret)
847951b8c87SIvan T. Ivanov 		dev_warn(&pdev->dev, "core clock boost failed\n");
848951b8c87SIvan T. Ivanov 
8490eb0d9f4SGeorgi Djakov 	ret = clk_prepare_enable(msm_host->clk);
8500eb0d9f4SGeorgi Djakov 	if (ret)
8510eb0d9f4SGeorgi Djakov 		goto pclk_disable;
8520eb0d9f4SGeorgi Djakov 
8530eb0d9f4SGeorgi Djakov 	core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
8540eb0d9f4SGeorgi Djakov 	msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
8550eb0d9f4SGeorgi Djakov 
8560eb0d9f4SGeorgi Djakov 	if (IS_ERR(msm_host->core_mem)) {
8570eb0d9f4SGeorgi Djakov 		dev_err(&pdev->dev, "Failed to remap registers\n");
8580eb0d9f4SGeorgi Djakov 		ret = PTR_ERR(msm_host->core_mem);
8590eb0d9f4SGeorgi Djakov 		goto clk_disable;
8600eb0d9f4SGeorgi Djakov 	}
8610eb0d9f4SGeorgi Djakov 
86229301f40SRitesh Harjani 	config = readl_relaxed(msm_host->core_mem + CORE_POWER);
86329301f40SRitesh Harjani 	config |= CORE_SW_RST;
86429301f40SRitesh Harjani 	writel_relaxed(config, msm_host->core_mem + CORE_POWER);
8650eb0d9f4SGeorgi Djakov 
8660eb0d9f4SGeorgi Djakov 	/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
8670eb0d9f4SGeorgi Djakov 	usleep_range(1000, 5000);
8680eb0d9f4SGeorgi Djakov 	if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
8690eb0d9f4SGeorgi Djakov 		dev_err(&pdev->dev, "Stuck in reset\n");
8700eb0d9f4SGeorgi Djakov 		ret = -ETIMEDOUT;
8710eb0d9f4SGeorgi Djakov 		goto clk_disable;
8720eb0d9f4SGeorgi Djakov 	}
8730eb0d9f4SGeorgi Djakov 
8740eb0d9f4SGeorgi Djakov 	/* Set HC_MODE_EN bit in HC_MODE register */
8750eb0d9f4SGeorgi Djakov 	writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
8760eb0d9f4SGeorgi Djakov 
877ff06ce41SVenkat Gopalakrishnan 	config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE);
878ff06ce41SVenkat Gopalakrishnan 	config |= FF_CLK_SW_RST_DIS;
879ff06ce41SVenkat Gopalakrishnan 	writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE);
880ff06ce41SVenkat Gopalakrishnan 
8810eb0d9f4SGeorgi Djakov 	host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
8820eb0d9f4SGeorgi Djakov 	dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
8830eb0d9f4SGeorgi Djakov 		host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
8840eb0d9f4SGeorgi Djakov 			       SDHCI_VENDOR_VER_SHIFT));
8850eb0d9f4SGeorgi Djakov 
8863a3ad3e9SGeorgi Djakov 	core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
8873a3ad3e9SGeorgi Djakov 	core_major = (core_version & CORE_VERSION_MAJOR_MASK) >>
8883a3ad3e9SGeorgi Djakov 		      CORE_VERSION_MAJOR_SHIFT;
8893a3ad3e9SGeorgi Djakov 	core_minor = core_version & CORE_VERSION_MINOR_MASK;
8903a3ad3e9SGeorgi Djakov 	dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n",
8913a3ad3e9SGeorgi Djakov 		core_version, core_major, core_minor);
8923a3ad3e9SGeorgi Djakov 
89383736352SVenkat Gopalakrishnan 	if (core_major == 1 && core_minor >= 0x42)
89483736352SVenkat Gopalakrishnan 		msm_host->use_14lpp_dll_reset = true;
89583736352SVenkat Gopalakrishnan 
8963a3ad3e9SGeorgi Djakov 	/*
8973a3ad3e9SGeorgi Djakov 	 * Support for some capabilities is not advertised by newer
8983a3ad3e9SGeorgi Djakov 	 * controller versions and must be explicitly enabled.
8993a3ad3e9SGeorgi Djakov 	 */
9003a3ad3e9SGeorgi Djakov 	if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
90129301f40SRitesh Harjani 		config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
90229301f40SRitesh Harjani 		config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
90329301f40SRitesh Harjani 		writel_relaxed(config, host->ioaddr +
9043a3ad3e9SGeorgi Djakov 			       CORE_VENDOR_SPEC_CAPABILITIES0);
9053a3ad3e9SGeorgi Djakov 	}
9063a3ad3e9SGeorgi Djakov 
907ad81d387SGeorgi Djakov 	/* Setup IRQ for handling power/voltage tasks with PMIC */
908ad81d387SGeorgi Djakov 	msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
909ad81d387SGeorgi Djakov 	if (msm_host->pwr_irq < 0) {
910ad81d387SGeorgi Djakov 		dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
911ad81d387SGeorgi Djakov 			msm_host->pwr_irq);
912d1f63f0cSWei Yongjun 		ret = msm_host->pwr_irq;
913ad81d387SGeorgi Djakov 		goto clk_disable;
914ad81d387SGeorgi Djakov 	}
915ad81d387SGeorgi Djakov 
916ad81d387SGeorgi Djakov 	ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
917ad81d387SGeorgi Djakov 					sdhci_msm_pwr_irq, IRQF_ONESHOT,
918ad81d387SGeorgi Djakov 					dev_name(&pdev->dev), host);
919ad81d387SGeorgi Djakov 	if (ret) {
920ad81d387SGeorgi Djakov 		dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret);
921ad81d387SGeorgi Djakov 		goto clk_disable;
922ad81d387SGeorgi Djakov 	}
923ad81d387SGeorgi Djakov 
92467e6db11SPramod Gurav 	pm_runtime_get_noresume(&pdev->dev);
92567e6db11SPramod Gurav 	pm_runtime_set_active(&pdev->dev);
92667e6db11SPramod Gurav 	pm_runtime_enable(&pdev->dev);
92767e6db11SPramod Gurav 	pm_runtime_set_autosuspend_delay(&pdev->dev,
92867e6db11SPramod Gurav 					 MSM_MMC_AUTOSUSPEND_DELAY_MS);
92967e6db11SPramod Gurav 	pm_runtime_use_autosuspend(&pdev->dev);
93067e6db11SPramod Gurav 
9310eb0d9f4SGeorgi Djakov 	ret = sdhci_add_host(host);
9320eb0d9f4SGeorgi Djakov 	if (ret)
93367e6db11SPramod Gurav 		goto pm_runtime_disable;
93467e6db11SPramod Gurav 
93567e6db11SPramod Gurav 	pm_runtime_mark_last_busy(&pdev->dev);
93667e6db11SPramod Gurav 	pm_runtime_put_autosuspend(&pdev->dev);
9370eb0d9f4SGeorgi Djakov 
9380eb0d9f4SGeorgi Djakov 	return 0;
9390eb0d9f4SGeorgi Djakov 
94067e6db11SPramod Gurav pm_runtime_disable:
94167e6db11SPramod Gurav 	pm_runtime_disable(&pdev->dev);
94267e6db11SPramod Gurav 	pm_runtime_set_suspended(&pdev->dev);
94367e6db11SPramod Gurav 	pm_runtime_put_noidle(&pdev->dev);
9440eb0d9f4SGeorgi Djakov clk_disable:
9450eb0d9f4SGeorgi Djakov 	clk_disable_unprepare(msm_host->clk);
9460eb0d9f4SGeorgi Djakov pclk_disable:
9470eb0d9f4SGeorgi Djakov 	clk_disable_unprepare(msm_host->pclk);
9480eb0d9f4SGeorgi Djakov bus_clk_disable:
9490eb0d9f4SGeorgi Djakov 	if (!IS_ERR(msm_host->bus_clk))
9500eb0d9f4SGeorgi Djakov 		clk_disable_unprepare(msm_host->bus_clk);
9510eb0d9f4SGeorgi Djakov pltfm_free:
9520eb0d9f4SGeorgi Djakov 	sdhci_pltfm_free(pdev);
9530eb0d9f4SGeorgi Djakov 	return ret;
9540eb0d9f4SGeorgi Djakov }
9550eb0d9f4SGeorgi Djakov 
9560eb0d9f4SGeorgi Djakov static int sdhci_msm_remove(struct platform_device *pdev)
9570eb0d9f4SGeorgi Djakov {
9580eb0d9f4SGeorgi Djakov 	struct sdhci_host *host = platform_get_drvdata(pdev);
9590eb0d9f4SGeorgi Djakov 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
9606f699531SJisheng Zhang 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
9610eb0d9f4SGeorgi Djakov 	int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
9620eb0d9f4SGeorgi Djakov 		    0xffffffff);
9630eb0d9f4SGeorgi Djakov 
9640eb0d9f4SGeorgi Djakov 	sdhci_remove_host(host, dead);
96567e6db11SPramod Gurav 
96667e6db11SPramod Gurav 	pm_runtime_get_sync(&pdev->dev);
96767e6db11SPramod Gurav 	pm_runtime_disable(&pdev->dev);
96867e6db11SPramod Gurav 	pm_runtime_put_noidle(&pdev->dev);
96967e6db11SPramod Gurav 
9700eb0d9f4SGeorgi Djakov 	clk_disable_unprepare(msm_host->clk);
9710eb0d9f4SGeorgi Djakov 	clk_disable_unprepare(msm_host->pclk);
9720eb0d9f4SGeorgi Djakov 	if (!IS_ERR(msm_host->bus_clk))
9730eb0d9f4SGeorgi Djakov 		clk_disable_unprepare(msm_host->bus_clk);
9746f699531SJisheng Zhang 	sdhci_pltfm_free(pdev);
9750eb0d9f4SGeorgi Djakov 	return 0;
9760eb0d9f4SGeorgi Djakov }
9770eb0d9f4SGeorgi Djakov 
97867e6db11SPramod Gurav #ifdef CONFIG_PM
97967e6db11SPramod Gurav static int sdhci_msm_runtime_suspend(struct device *dev)
98067e6db11SPramod Gurav {
98167e6db11SPramod Gurav 	struct sdhci_host *host = dev_get_drvdata(dev);
98267e6db11SPramod Gurav 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
98367e6db11SPramod Gurav 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
98467e6db11SPramod Gurav 
98567e6db11SPramod Gurav 	clk_disable_unprepare(msm_host->clk);
98667e6db11SPramod Gurav 	clk_disable_unprepare(msm_host->pclk);
98767e6db11SPramod Gurav 
98867e6db11SPramod Gurav 	return 0;
98967e6db11SPramod Gurav }
99067e6db11SPramod Gurav 
99167e6db11SPramod Gurav static int sdhci_msm_runtime_resume(struct device *dev)
99267e6db11SPramod Gurav {
99367e6db11SPramod Gurav 	struct sdhci_host *host = dev_get_drvdata(dev);
99467e6db11SPramod Gurav 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
99567e6db11SPramod Gurav 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
99667e6db11SPramod Gurav 	int ret;
99767e6db11SPramod Gurav 
99867e6db11SPramod Gurav 	ret = clk_prepare_enable(msm_host->clk);
99967e6db11SPramod Gurav 	if (ret) {
100067e6db11SPramod Gurav 		dev_err(dev, "clk_enable failed for core_clk: %d\n", ret);
100167e6db11SPramod Gurav 		return ret;
100267e6db11SPramod Gurav 	}
100367e6db11SPramod Gurav 	ret = clk_prepare_enable(msm_host->pclk);
100467e6db11SPramod Gurav 	if (ret) {
100567e6db11SPramod Gurav 		dev_err(dev, "clk_enable failed for iface_clk: %d\n", ret);
100667e6db11SPramod Gurav 		clk_disable_unprepare(msm_host->clk);
100767e6db11SPramod Gurav 		return ret;
100867e6db11SPramod Gurav 	}
100967e6db11SPramod Gurav 
101067e6db11SPramod Gurav 	return 0;
101167e6db11SPramod Gurav }
101267e6db11SPramod Gurav #endif
101367e6db11SPramod Gurav 
101467e6db11SPramod Gurav static const struct dev_pm_ops sdhci_msm_pm_ops = {
101567e6db11SPramod Gurav 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
101667e6db11SPramod Gurav 				pm_runtime_force_resume)
101767e6db11SPramod Gurav 	SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend,
101867e6db11SPramod Gurav 			   sdhci_msm_runtime_resume,
101967e6db11SPramod Gurav 			   NULL)
102067e6db11SPramod Gurav };
102167e6db11SPramod Gurav 
10220eb0d9f4SGeorgi Djakov static struct platform_driver sdhci_msm_driver = {
10230eb0d9f4SGeorgi Djakov 	.probe = sdhci_msm_probe,
10240eb0d9f4SGeorgi Djakov 	.remove = sdhci_msm_remove,
10250eb0d9f4SGeorgi Djakov 	.driver = {
10260eb0d9f4SGeorgi Djakov 		   .name = "sdhci_msm",
10270eb0d9f4SGeorgi Djakov 		   .of_match_table = sdhci_msm_dt_match,
102867e6db11SPramod Gurav 		   .pm = &sdhci_msm_pm_ops,
10290eb0d9f4SGeorgi Djakov 	},
10300eb0d9f4SGeorgi Djakov };
10310eb0d9f4SGeorgi Djakov 
10320eb0d9f4SGeorgi Djakov module_platform_driver(sdhci_msm_driver);
10330eb0d9f4SGeorgi Djakov 
10340eb0d9f4SGeorgi Djakov MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
10350eb0d9f4SGeorgi Djakov MODULE_LICENSE("GPL v2");
1036