Commit | Line | Data |
---|---|---|
f707079d | 1 | // SPDX-License-Identifier: GPL-2.0 |
a87d5638 | 2 | /* |
9d08428a | 3 | * Renesas SDHI |
a87d5638 | 4 | * |
f49bdcde WS |
5 | * Copyright (C) 2015-19 Renesas Electronics Corporation |
6 | * Copyright (C) 2016-19 Sang Engineering, Wolfram Sang | |
87317c4d | 7 | * Copyright (C) 2016-17 Horms Solutions, Simon Horman |
a87d5638 MD |
8 | * Copyright (C) 2009 Magnus Damm |
9 | * | |
a87d5638 MD |
10 | * Based on "Compaq ASIC3 support": |
11 | * | |
12 | * Copyright 2001 Compaq Computer Corporation. | |
13 | * Copyright 2004-2005 Phil Blundell | |
14 | * Copyright 2007-2008 OpenedHand Ltd. | |
15 | * | |
16 | * Authors: Phil Blundell <pb@handhelds.org>, | |
17 | * Samuel Ortiz <sameo@openedhand.com> | |
18 | * | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/clk.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
967a6a07 | 24 | #include <linux/module.h> |
5a00a971 | 25 | #include <linux/of_device.h> |
a87d5638 | 26 | #include <linux/platform_device.h> |
3c49e810 | 27 | #include <linux/mmc/host.h> |
ef5332c1 | 28 | #include <linux/mmc/slot-gpio.h> |
a87d5638 | 29 | #include <linux/mfd/tmio.h> |
056676da | 30 | #include <linux/sh_dma.h> |
973ed3af | 31 | #include <linux/delay.h> |
057a4592 WS |
32 | #include <linux/pinctrl/consumer.h> |
33 | #include <linux/pinctrl/pinctrl-state.h> | |
34 | #include <linux/regulator/consumer.h> | |
164691aa | 35 | #include <linux/sys_soc.h> |
a87d5638 | 36 | |
c2a96987 | 37 | #include "renesas_sdhi.h" |
42051e8a GL |
38 | #include "tmio_mmc.h" |
39 | ||
4472f0fc | 40 | #define HOST_MODE 0xe4 |
e3c418f1 | 41 | |
a2a16c77 | 42 | #define SDHI_VER_GEN2_SDR50 0x490c |
c7825151 | 43 | #define SDHI_VER_RZ_A1 0x820b |
a2a16c77 WS |
44 | /* very old datasheets said 0x490c for SDR104, too. They are wrong! */ |
45 | #define SDHI_VER_GEN2_SDR104 0xcb0d | |
46 | #define SDHI_VER_GEN3_SD 0xcc10 | |
47 | #define SDHI_VER_GEN3_SDMMC 0xcd10 | |
48 | ||
164691aa | 49 | struct renesas_sdhi_quirks { |
0f4e2054 | 50 | bool hs400_disabled; |
164691aa NS |
51 | bool hs400_4taps; |
52 | }; | |
53 | ||
b5b6a5f4 | 54 | static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) |
f45394d5 KM |
55 | { |
56 | u32 val; | |
57 | ||
58 | /* | |
59 | * see also | |
b5b6a5f4 | 60 | * renesas_sdhi_of_data :: dma_buswidth |
f45394d5 KM |
61 | */ |
62 | switch (sd_ctrl_read16(host, CTL_VERSION)) { | |
a2a16c77 | 63 | case SDHI_VER_GEN2_SDR50: |
f45394d5 KM |
64 | val = (width == 32) ? 0x0001 : 0x0000; |
65 | break; | |
a2a16c77 | 66 | case SDHI_VER_GEN2_SDR104: |
f45394d5 KM |
67 | val = (width == 32) ? 0x0000 : 0x0001; |
68 | break; | |
a2a16c77 WS |
69 | case SDHI_VER_GEN3_SD: |
70 | case SDHI_VER_GEN3_SDMMC: | |
a72e8b17 WS |
71 | if (width == 64) |
72 | val = 0x0000; | |
73 | else if (width == 32) | |
74 | val = 0x0101; | |
75 | else | |
76 | val = 0x0001; | |
77 | break; | |
f45394d5 KM |
78 | default: |
79 | /* nothing to do */ | |
80 | return; | |
81 | } | |
82 | ||
4472f0fc | 83 | sd_ctrl_write16(host, HOST_MODE, val); |
f45394d5 KM |
84 | } |
85 | ||
b5b6a5f4 | 86 | static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host) |
56c49287 | 87 | { |
0ea28210 | 88 | struct mmc_host *mmc = host->mmc; |
b5b6a5f4 | 89 | struct renesas_sdhi *priv = host_to_priv(host); |
00fb3d2a | 90 | int ret = clk_prepare_enable(priv->clk); |
2fe35968 | 91 | |
56c49287 GL |
92 | if (ret < 0) |
93 | return ret; | |
94 | ||
34a16547 CB |
95 | ret = clk_prepare_enable(priv->clk_cd); |
96 | if (ret < 0) { | |
97 | clk_disable_unprepare(priv->clk); | |
98 | return ret; | |
99 | } | |
100 | ||
2fb55956 BH |
101 | /* |
102 | * The clock driver may not know what maximum frequency | |
103 | * actually works, so it should be set with the max-frequency | |
104 | * property which will already have been read to f_max. If it | |
105 | * was missing, assume the current frequency is the maximum. | |
106 | */ | |
107 | if (!mmc->f_max) | |
108 | mmc->f_max = clk_get_rate(priv->clk); | |
109 | ||
110 | /* | |
111 | * Minimum frequency is the minimum input clock frequency | |
112 | * divided by our maximum divider. | |
113 | */ | |
114 | mmc->f_min = max(clk_round_rate(priv->clk, 1) / 512, 1L); | |
f45394d5 KM |
115 | |
116 | /* enable 16bit data access on SDBUF as default */ | |
b5b6a5f4 | 117 | renesas_sdhi_sdbuf_width(host, 16); |
f45394d5 | 118 | |
56c49287 GL |
119 | return 0; |
120 | } | |
121 | ||
b5b6a5f4 | 122 | static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host, |
2fe35968 | 123 | unsigned int new_clock) |
2fb55956 | 124 | { |
b5b6a5f4 | 125 | struct renesas_sdhi *priv = host_to_priv(host); |
3072ba8c | 126 | unsigned int freq, diff, best_freq = 0, diff_min = ~0; |
75eaf49f | 127 | int i; |
2fb55956 | 128 | |
d63c2bf4 | 129 | /* tested only on R-Car Gen2+ currently; may work for others */ |
8fc00998 WS |
130 | if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) |
131 | return clk_get_rate(priv->clk); | |
132 | ||
2fb55956 BH |
133 | /* |
134 | * We want the bus clock to be as close as possible to, but no | |
135 | * greater than, new_clock. As we can divide by 1 << i for | |
136 | * any i in [0, 9] we want the input clock to be as close as | |
137 | * possible, but no greater than, new_clock << i. | |
138 | */ | |
139 | for (i = min(9, ilog2(UINT_MAX / new_clock)); i >= 0; i--) { | |
140 | freq = clk_round_rate(priv->clk, new_clock << i); | |
141 | if (freq > (new_clock << i)) { | |
142 | /* Too fast; look for a slightly slower option */ | |
143 | freq = clk_round_rate(priv->clk, | |
144 | (new_clock << i) / 4 * 3); | |
145 | if (freq > (new_clock << i)) | |
146 | continue; | |
147 | } | |
148 | ||
149 | diff = new_clock - (freq >> i); | |
150 | if (diff <= diff_min) { | |
151 | best_freq = freq; | |
152 | diff_min = diff; | |
153 | } | |
154 | } | |
155 | ||
75eaf49f | 156 | clk_set_rate(priv->clk, best_freq); |
2fb55956 | 157 | |
75eaf49f | 158 | return clk_get_rate(priv->clk); |
2fb55956 BH |
159 | } |
160 | ||
68f83127 MY |
161 | static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, |
162 | unsigned int new_clock) | |
0196c8db | 163 | { |
68f83127 | 164 | u32 clk = 0, clock; |
0196c8db | 165 | |
0196c8db MY |
166 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & |
167 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
168 | ||
75eaf49f TS |
169 | if (new_clock == 0) { |
170 | host->mmc->actual_clock = 0; | |
68f83127 | 171 | goto out; |
75eaf49f | 172 | } |
0196c8db | 173 | |
75eaf49f TS |
174 | host->mmc->actual_clock = renesas_sdhi_clk_update(host, new_clock); |
175 | clock = host->mmc->actual_clock / 512; | |
0196c8db MY |
176 | |
177 | for (clk = 0x80000080; new_clock >= (clock << 1); clk >>= 1) | |
178 | clock <<= 1; | |
179 | ||
180 | /* 1/1 clock is option */ | |
181 | if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1)) { | |
182 | if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400)) | |
183 | clk |= 0xff; | |
184 | else | |
185 | clk &= ~0xff; | |
186 | } | |
187 | ||
0196c8db MY |
188 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK); |
189 | if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) | |
190 | usleep_range(10000, 11000); | |
191 | ||
68f83127 MY |
192 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | |
193 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
194 | ||
195 | out: | |
196 | /* HW engineers overrode docs: no sleep needed on R-Car2+ */ | |
197 | if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) | |
198 | usleep_range(10000, 11000); | |
0196c8db MY |
199 | } |
200 | ||
b5b6a5f4 | 201 | static void renesas_sdhi_clk_disable(struct tmio_mmc_host *host) |
56c49287 | 202 | { |
b5b6a5f4 | 203 | struct renesas_sdhi *priv = host_to_priv(host); |
0ea28210 | 204 | |
00fb3d2a | 205 | clk_disable_unprepare(priv->clk); |
34a16547 | 206 | clk_disable_unprepare(priv->clk_cd); |
56c49287 GL |
207 | } |
208 | ||
b5b6a5f4 | 209 | static int renesas_sdhi_card_busy(struct mmc_host *mmc) |
6a4679f3 WS |
210 | { |
211 | struct tmio_mmc_host *host = mmc_priv(mmc); | |
212 | ||
2fe35968 SH |
213 | return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & |
214 | TMIO_STAT_DAT0); | |
6a4679f3 WS |
215 | } |
216 | ||
b5b6a5f4 | 217 | static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, |
2fe35968 | 218 | struct mmc_ios *ios) |
057a4592 WS |
219 | { |
220 | struct tmio_mmc_host *host = mmc_priv(mmc); | |
b5b6a5f4 | 221 | struct renesas_sdhi *priv = host_to_priv(host); |
057a4592 WS |
222 | struct pinctrl_state *pin_state; |
223 | int ret; | |
224 | ||
225 | switch (ios->signal_voltage) { | |
226 | case MMC_SIGNAL_VOLTAGE_330: | |
227 | pin_state = priv->pins_default; | |
228 | break; | |
229 | case MMC_SIGNAL_VOLTAGE_180: | |
230 | pin_state = priv->pins_uhs; | |
231 | break; | |
232 | default: | |
233 | return -EINVAL; | |
234 | } | |
235 | ||
236 | /* | |
237 | * If anything is missing, assume signal voltage is fixed at | |
238 | * 3.3V and succeed/fail accordingly. | |
239 | */ | |
240 | if (IS_ERR(priv->pinctrl) || IS_ERR(pin_state)) | |
241 | return ios->signal_voltage == | |
242 | MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL; | |
243 | ||
244 | ret = mmc_regulator_set_vqmmc(host->mmc, ios); | |
245 | if (ret) | |
246 | return ret; | |
247 | ||
2272c841 | 248 | return pinctrl_select_state(priv->pinctrl, pin_state); |
057a4592 WS |
249 | } |
250 | ||
06f438dd SH |
251 | /* SCC registers */ |
252 | #define SH_MOBILE_SDHI_SCC_DTCNTL 0x000 | |
253 | #define SH_MOBILE_SDHI_SCC_TAPSET 0x002 | |
254 | #define SH_MOBILE_SDHI_SCC_DT2FF 0x004 | |
255 | #define SH_MOBILE_SDHI_SCC_CKSEL 0x006 | |
256 | #define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008 | |
257 | #define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A | |
26eb2607 | 258 | #define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E |
06f438dd SH |
259 | |
260 | /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */ | |
261 | #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0) | |
262 | #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 | |
263 | #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff | |
264 | ||
265 | /* Definitions for values the SH_MOBILE_SDHI_SCC_CKSEL register */ | |
266 | #define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL BIT(0) | |
267 | /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSCNTL register */ | |
268 | #define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0) | |
269 | /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */ | |
270 | #define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) | |
26eb2607 MH |
271 | /* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */ |
272 | #define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) | |
273 | #define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) | |
06f438dd SH |
274 | |
275 | static inline u32 sd_scc_read32(struct tmio_mmc_host *host, | |
b5b6a5f4 | 276 | struct renesas_sdhi *priv, int addr) |
06f438dd SH |
277 | { |
278 | return readl(priv->scc_ctl + (addr << host->bus_shift)); | |
279 | } | |
280 | ||
281 | static inline void sd_scc_write32(struct tmio_mmc_host *host, | |
b5b6a5f4 | 282 | struct renesas_sdhi *priv, |
06f438dd SH |
283 | int addr, u32 val) |
284 | { | |
285 | writel(val, priv->scc_ctl + (addr << host->bus_shift)); | |
286 | } | |
287 | ||
b5b6a5f4 | 288 | static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) |
06f438dd | 289 | { |
b5b6a5f4 | 290 | struct renesas_sdhi *priv; |
06f438dd | 291 | |
06f438dd SH |
292 | priv = host_to_priv(host); |
293 | ||
06f438dd SH |
294 | /* Initialize SCC */ |
295 | sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x0); | |
296 | ||
06f438dd SH |
297 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & |
298 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
299 | ||
26eb2607 MH |
300 | /* set sampling clock selection range */ |
301 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, | |
302 | SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | | |
303 | 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); | |
304 | ||
06f438dd SH |
305 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, |
306 | SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | | |
307 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); | |
308 | ||
06f438dd SH |
309 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, |
310 | ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & | |
311 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); | |
312 | ||
852d258f | 313 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos); |
06f438dd | 314 | |
26eb2607 MH |
315 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | |
316 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
317 | ||
06f438dd SH |
318 | /* Read TAPNUM */ |
319 | return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >> | |
320 | SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & | |
321 | SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK; | |
322 | } | |
323 | ||
b5b6a5f4 | 324 | static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, |
2fe35968 | 325 | unsigned long tap) |
06f438dd | 326 | { |
b5b6a5f4 | 327 | struct renesas_sdhi *priv = host_to_priv(host); |
06f438dd SH |
328 | |
329 | /* Set sampling clock position */ | |
330 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap); | |
331 | } | |
332 | ||
26eb2607 MH |
333 | static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) |
334 | { | |
335 | struct renesas_sdhi *priv = host_to_priv(host); | |
336 | ||
337 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & | |
338 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
339 | ||
340 | /* Set HS400 mode */ | |
341 | sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 | | |
342 | sd_ctrl_read16(host, CTL_SDIF_MODE)); | |
f0c8234c TS |
343 | |
344 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, | |
345 | priv->scc_tappos_hs400); | |
346 | ||
26eb2607 MH |
347 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, |
348 | (SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | | |
349 | SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) | | |
350 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); | |
351 | ||
352 | /* Set the sampling clock selection range of HS400 mode */ | |
353 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, | |
354 | SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | | |
355 | 0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); | |
356 | ||
357 | ||
358 | if (host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400) | |
359 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, | |
360 | host->tap_set / 2); | |
361 | ||
362 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, | |
363 | SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | | |
364 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); | |
365 | ||
366 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | | |
367 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
368 | } | |
369 | ||
370 | static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host, | |
371 | struct renesas_sdhi *priv) | |
372 | { | |
373 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & | |
374 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
375 | ||
376 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, | |
377 | ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL & | |
378 | sd_scc_read32(host, priv, | |
379 | SH_MOBILE_SDHI_SCC_CKSEL)); | |
380 | } | |
381 | ||
382 | static void renesas_sdhi_disable_scc(struct tmio_mmc_host *host) | |
383 | { | |
384 | struct renesas_sdhi *priv = host_to_priv(host); | |
385 | ||
386 | renesas_sdhi_reset_scc(host, priv); | |
387 | ||
388 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, | |
389 | ~SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN & | |
390 | sd_scc_read32(host, priv, | |
391 | SH_MOBILE_SDHI_SCC_DTCNTL)); | |
392 | ||
393 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | | |
394 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
395 | } | |
396 | ||
397 | static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host, | |
398 | struct renesas_sdhi *priv) | |
399 | { | |
400 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & | |
401 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
402 | ||
403 | /* Reset HS400 mode */ | |
404 | sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 & | |
405 | sd_ctrl_read16(host, CTL_SDIF_MODE)); | |
f0c8234c TS |
406 | |
407 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos); | |
408 | ||
26eb2607 MH |
409 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, |
410 | ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | | |
411 | SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) & | |
412 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); | |
413 | ||
414 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | | |
415 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
416 | } | |
417 | ||
418 | static void renesas_sdhi_prepare_hs400_tuning(struct tmio_mmc_host *host) | |
419 | { | |
420 | renesas_sdhi_reset_hs400_mode(host, host_to_priv(host)); | |
421 | } | |
422 | ||
06f438dd SH |
423 | #define SH_MOBILE_SDHI_MAX_TAP 3 |
424 | ||
b5b6a5f4 | 425 | static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) |
06f438dd | 426 | { |
b5b6a5f4 | 427 | struct renesas_sdhi *priv = host_to_priv(host); |
06f438dd | 428 | unsigned long tap_cnt; /* counter of tuning success */ |
06f438dd SH |
429 | unsigned long tap_start;/* start position of tuning success */ |
430 | unsigned long tap_end; /* end position of tuning success */ | |
431 | unsigned long ntap; /* temporary counter of tuning success */ | |
432 | unsigned long i; | |
433 | ||
434 | /* Clear SCC_RVSREQ */ | |
435 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); | |
436 | ||
5c99826b NS |
437 | /* |
438 | * When tuning CMD19 is issued twice for each tap, merge the | |
439 | * result requiring the tap to be good in both runs before | |
440 | * considering it for tuning selection. | |
441 | */ | |
442 | for (i = 0; i < host->tap_num * 2; i++) { | |
443 | int offset = host->tap_num * (i < host->tap_num ? 1 : -1); | |
444 | ||
445 | if (!test_bit(i, host->taps)) | |
446 | clear_bit(i + offset, host->taps); | |
447 | } | |
448 | ||
06f438dd SH |
449 | /* |
450 | * Find the longest consecutive run of successful probes. If that | |
451 | * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the | |
452 | * center index as the tap. | |
453 | */ | |
454 | tap_cnt = 0; | |
455 | ntap = 0; | |
456 | tap_start = 0; | |
457 | tap_end = 0; | |
458 | for (i = 0; i < host->tap_num * 2; i++) { | |
2fe35968 | 459 | if (test_bit(i, host->taps)) { |
06f438dd | 460 | ntap++; |
2fe35968 | 461 | } else { |
06f438dd SH |
462 | if (ntap > tap_cnt) { |
463 | tap_start = i - ntap; | |
464 | tap_end = i - 1; | |
465 | tap_cnt = ntap; | |
466 | } | |
467 | ntap = 0; | |
468 | } | |
469 | } | |
470 | ||
471 | if (ntap > tap_cnt) { | |
472 | tap_start = i - ntap; | |
473 | tap_end = i - 1; | |
474 | tap_cnt = ntap; | |
475 | } | |
476 | ||
477 | if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) | |
26eb2607 | 478 | host->tap_set = (tap_start + tap_end) / 2 % host->tap_num; |
06f438dd SH |
479 | else |
480 | return -EIO; | |
481 | ||
482 | /* Set SCC */ | |
26eb2607 | 483 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, host->tap_set); |
06f438dd SH |
484 | |
485 | /* Enable auto re-tuning */ | |
486 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, | |
487 | SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN | | |
488 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
b5b6a5f4 | 493 | static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) |
06f438dd | 494 | { |
b5b6a5f4 | 495 | struct renesas_sdhi *priv = host_to_priv(host); |
75f349a1 MH |
496 | bool use_4tap = host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400; |
497 | ||
498 | /* | |
499 | * Skip checking SCC errors when running on 4 taps in HS400 mode as | |
500 | * any retuning would still result in the same 4 taps being used. | |
501 | */ | |
502 | if (!(host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) && | |
503 | !(host->mmc->ios.timing == MMC_TIMING_MMC_HS200) && | |
504 | !(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && !use_4tap)) | |
505 | return false; | |
506 | ||
507 | if (mmc_doing_retune(host->mmc)) | |
508 | return false; | |
06f438dd SH |
509 | |
510 | /* Check SCC error */ | |
511 | if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & | |
512 | SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN && | |
513 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ) & | |
514 | SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) { | |
515 | /* Clear SCC error */ | |
516 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); | |
517 | return true; | |
518 | } | |
519 | ||
520 | return false; | |
521 | } | |
522 | ||
b5b6a5f4 | 523 | static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) |
06f438dd | 524 | { |
b5b6a5f4 | 525 | struct renesas_sdhi *priv; |
06f438dd | 526 | |
06f438dd SH |
527 | priv = host_to_priv(host); |
528 | ||
26eb2607 MH |
529 | renesas_sdhi_reset_scc(host, priv); |
530 | renesas_sdhi_reset_hs400_mode(host, priv); | |
06f438dd SH |
531 | |
532 | sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | | |
533 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); | |
534 | ||
535 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, | |
536 | ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & | |
537 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); | |
538 | ||
539 | sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, | |
540 | ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & | |
541 | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); | |
202367cb NS |
542 | |
543 | if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) | |
544 | sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, | |
545 | TMIO_MASK_INIT_RCAR2); | |
06f438dd SH |
546 | } |
547 | ||
4dc48a95 | 548 | static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) |
973ed3af SH |
549 | { |
550 | int timeout = 1000; | |
4dc48a95 WS |
551 | /* CBSY is set when busy, SCLKDIVEN is cleared when busy */ |
552 | u32 wait_state = (bit == TMIO_STAT_CMD_BUSY ? TMIO_STAT_CMD_BUSY : 0); | |
973ed3af | 553 | |
4dc48a95 WS |
554 | while (--timeout && (sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) |
555 | & bit) == wait_state) | |
973ed3af SH |
556 | udelay(1); |
557 | ||
558 | if (!timeout) { | |
94b110af | 559 | dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n"); |
973ed3af SH |
560 | return -EBUSY; |
561 | } | |
562 | ||
563 | return 0; | |
564 | } | |
565 | ||
b5b6a5f4 | 566 | static int renesas_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) |
973ed3af | 567 | { |
4dc48a95 WS |
568 | u32 bit = TMIO_STAT_SCLKDIVEN; |
569 | ||
2fe35968 | 570 | switch (addr) { |
973ed3af SH |
571 | case CTL_SD_CMD: |
572 | case CTL_STOP_INTERNAL_ACTION: | |
573 | case CTL_XFER_BLK_COUNT: | |
973ed3af SH |
574 | case CTL_SD_XFER_LEN: |
575 | case CTL_SD_MEM_CARD_OPT: | |
576 | case CTL_TRANSACTION_CTL: | |
577 | case CTL_DMA_ENABLE: | |
4472f0fc | 578 | case HOST_MODE: |
5124b592 | 579 | if (host->pdata->flags & TMIO_MMC_HAVE_CBSY) |
4dc48a95 WS |
580 | bit = TMIO_STAT_CMD_BUSY; |
581 | /* fallthrough */ | |
582 | case CTL_SD_CARD_CLK_CTL: | |
583 | return renesas_sdhi_wait_idle(host, bit); | |
973ed3af SH |
584 | } |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
b5b6a5f4 | 589 | static int renesas_sdhi_multi_io_quirk(struct mmc_card *card, |
2fe35968 | 590 | unsigned int direction, int blk_size) |
8b4efe2f KM |
591 | { |
592 | /* | |
593 | * In Renesas controllers, when performing a | |
594 | * multiple block read of one or two blocks, | |
595 | * depending on the timing with which the | |
596 | * response register is read, the response | |
597 | * value may not be read properly. | |
598 | * Use single block read for this HW bug | |
599 | */ | |
600 | if ((direction == MMC_DATA_READ) && | |
601 | blk_size == 2) | |
602 | return 1; | |
603 | ||
604 | return blk_size; | |
605 | } | |
606 | ||
b5b6a5f4 | 607 | static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) |
0c47f6ae | 608 | { |
41279f01 WS |
609 | /* Iff regs are 8 byte apart, sdbuf is 64 bit. Otherwise always 32. */ |
610 | int width = (host->bus_shift == 2) ? 64 : 32; | |
f45394d5 | 611 | |
41279f01 WS |
612 | sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? DMA_ENABLE_DMASDRW : 0); |
613 | renesas_sdhi_sdbuf_width(host, enable ? width : 16); | |
0c47f6ae KM |
614 | } |
615 | ||
6a686986 | 616 | static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400 = { |
0f4e2054 NS |
617 | .hs400_disabled = true, |
618 | .hs400_4taps = true, | |
619 | }; | |
620 | ||
6a686986 | 621 | static const struct renesas_sdhi_quirks sdhi_quirks_4tap = { |
164691aa NS |
622 | .hs400_4taps = true, |
623 | }; | |
624 | ||
97bf85b6 WS |
625 | static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = { |
626 | .hs400_disabled = true, | |
627 | }; | |
628 | ||
164691aa | 629 | static const struct soc_device_attribute sdhi_quirks_match[] = { |
6a686986 WS |
630 | { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 }, |
631 | { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap }, | |
632 | { .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 }, | |
633 | { .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 }, | |
97bf85b6 | 634 | { .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 }, |
164691aa NS |
635 | { /* Sentinel. */ }, |
636 | }; | |
637 | ||
9d08428a SH |
638 | int renesas_sdhi_probe(struct platform_device *pdev, |
639 | const struct tmio_mmc_dma_ops *dma_ops) | |
a87d5638 | 640 | { |
f33c9d65 | 641 | struct tmio_mmc_data *mmd = pdev->dev.platform_data; |
164691aa | 642 | const struct renesas_sdhi_quirks *quirks = NULL; |
2fe35968 | 643 | const struct renesas_sdhi_of_data *of_data; |
164691aa | 644 | const struct soc_device_attribute *attr; |
2fe35968 SH |
645 | struct tmio_mmc_data *mmc_data; |
646 | struct tmio_mmc_dma *dma_priv; | |
42051e8a | 647 | struct tmio_mmc_host *host; |
2fe35968 | 648 | struct renesas_sdhi *priv; |
3b159a6e | 649 | struct resource *res; |
06f438dd | 650 | int irq, ret, i; |
c9a9497c | 651 | u16 ver; |
2fe35968 SH |
652 | |
653 | of_data = of_device_get_match_data(&pdev->dev); | |
a87d5638 | 654 | |
164691aa NS |
655 | attr = soc_device_match(sdhi_quirks_match); |
656 | if (attr) | |
657 | quirks = attr->data; | |
658 | ||
3b159a6e KM |
659 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
660 | if (!res) | |
661 | return -EINVAL; | |
662 | ||
2fe35968 SH |
663 | priv = devm_kzalloc(&pdev->dev, sizeof(struct renesas_sdhi), |
664 | GFP_KERNEL); | |
2bf8ab6b | 665 | if (!priv) |
a87d5638 | 666 | return -ENOMEM; |
a87d5638 | 667 | |
056676da | 668 | mmc_data = &priv->mmc_data; |
87ae7bbe | 669 | dma_priv = &priv->dma_priv; |
056676da | 670 | |
ac51b961 | 671 | priv->clk = devm_clk_get(&pdev->dev, NULL); |
a87d5638 | 672 | if (IS_ERR(priv->clk)) { |
a87d5638 | 673 | ret = PTR_ERR(priv->clk); |
56ae1adc | 674 | dev_err(&pdev->dev, "cannot get clock: %d\n", ret); |
4ce62817 | 675 | return ret; |
a87d5638 MD |
676 | } |
677 | ||
34a16547 CB |
678 | /* |
679 | * Some controllers provide a 2nd clock just to run the internal card | |
680 | * detection logic. Unfortunately, the existing driver architecture does | |
681 | * not support a separation of clocks for runtime PM usage. When | |
682 | * native hotplug is used, the tmio driver assumes that the core | |
683 | * must continue to run for card detect to stay active, so we cannot | |
684 | * disable it. | |
685 | * Additionally, it is prohibited to supply a clock to the core but not | |
686 | * to the card detect circuit. That leaves us with if separate clocks | |
687 | * are presented, we must treat them both as virtually 1 clock. | |
688 | */ | |
689 | priv->clk_cd = devm_clk_get(&pdev->dev, "cd"); | |
690 | if (IS_ERR(priv->clk_cd)) | |
691 | priv->clk_cd = NULL; | |
692 | ||
057a4592 WS |
693 | priv->pinctrl = devm_pinctrl_get(&pdev->dev); |
694 | if (!IS_ERR(priv->pinctrl)) { | |
695 | priv->pins_default = pinctrl_lookup_state(priv->pinctrl, | |
696 | PINCTRL_STATE_DEFAULT); | |
697 | priv->pins_uhs = pinctrl_lookup_state(priv->pinctrl, | |
698 | "state_uhs"); | |
699 | } | |
700 | ||
b21fc294 | 701 | host = tmio_mmc_host_alloc(pdev, mmc_data); |
8d09a133 MY |
702 | if (IS_ERR(host)) |
703 | return PTR_ERR(host); | |
94b110af | 704 | |
dc9f1a8d | 705 | if (of_data) { |
a72e8b17 | 706 | mmc_data->flags |= of_data->tmio_flags; |
f19417f3 | 707 | mmc_data->ocr_mask = of_data->tmio_ocr_mask; |
a72e8b17 WS |
708 | mmc_data->capabilities |= of_data->capabilities; |
709 | mmc_data->capabilities2 |= of_data->capabilities2; | |
710 | mmc_data->dma_rx_offset = of_data->dma_rx_offset; | |
603aa14d YS |
711 | mmc_data->max_blk_count = of_data->max_blk_count; |
712 | mmc_data->max_segs = of_data->max_segs; | |
a72e8b17 WS |
713 | dma_priv->dma_buswidth = of_data->dma_buswidth; |
714 | host->bus_shift = of_data->bus_shift; | |
715 | } | |
716 | ||
b5b6a5f4 SH |
717 | host->write16_hook = renesas_sdhi_write16_hook; |
718 | host->clk_enable = renesas_sdhi_clk_enable; | |
b5b6a5f4 | 719 | host->clk_disable = renesas_sdhi_clk_disable; |
0196c8db | 720 | host->set_clock = renesas_sdhi_set_clock; |
b5b6a5f4 | 721 | host->multi_io_quirk = renesas_sdhi_multi_io_quirk; |
bc45719c | 722 | host->dma_ops = dma_ops; |
ff026099 | 723 | |
0f4e2054 NS |
724 | if (quirks && quirks->hs400_disabled) |
725 | host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); | |
726 | ||
164691aa NS |
727 | if (quirks && quirks->hs400_4taps) |
728 | mmc_data->flags |= TMIO_MMC_HAVE_4TAP_HS400; | |
729 | ||
ef5332c1 WS |
730 | /* For some SoC, we disable internal WP. GPIO may override this */ |
731 | if (mmc_can_gpio_ro(host->mmc)) | |
732 | mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT; | |
733 | ||
ff026099 WS |
734 | /* SDR speeds are only available on Gen2+ */ |
735 | if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) { | |
736 | /* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */ | |
2aaa3c51 MY |
737 | host->ops.card_busy = renesas_sdhi_card_busy; |
738 | host->ops.start_signal_voltage_switch = | |
b5b6a5f4 | 739 | renesas_sdhi_start_signal_voltage_switch; |
1970701f | 740 | host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27; |
d30ae056 TS |
741 | |
742 | /* SDR and HS200/400 registers requires HW reset */ | |
743 | if (of_data && of_data->scc_offset) { | |
744 | priv->scc_ctl = host->ctl + of_data->scc_offset; | |
745 | host->mmc->caps |= MMC_CAP_HW_RESET; | |
746 | host->hw_reset = renesas_sdhi_hw_reset; | |
747 | } | |
ff026099 | 748 | } |
a72e8b17 WS |
749 | |
750 | /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */ | |
751 | if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */ | |
95a7dc36 | 752 | host->bus_shift = 1; |
7ecc09ba | 753 | |
84f11d5b | 754 | if (mmd) |
f33c9d65 | 755 | *mmc_data = *mmd; |
84f11d5b | 756 | |
87ae7bbe | 757 | dma_priv->filter = shdma_chan_filter; |
b5b6a5f4 | 758 | dma_priv->enable = renesas_sdhi_enable_dma; |
87ae7bbe | 759 | |
e471df0b | 760 | mmc_data->alignment_shift = 1; /* 2-byte alignment */ |
f33c9d65 | 761 | mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED; |
e471df0b | 762 | |
f1334fb3 YG |
763 | /* |
764 | * All SDHI blocks support 2-byte and larger block sizes in 4-bit | |
765 | * bus width mode. | |
766 | */ | |
767 | mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; | |
768 | ||
23b66071 AH |
769 | /* |
770 | * All SDHI blocks support SDIO IRQ signalling. | |
771 | */ | |
772 | mmc_data->flags |= TMIO_MMC_SDIO_IRQ; | |
773 | ||
2fe35968 | 774 | /* All SDHI have CMD12 control bit */ |
b8d11962 SU |
775 | mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL; |
776 | ||
20dd0373 WS |
777 | /* All SDHI have SDIO status bits which must be 1 */ |
778 | mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS; | |
6b98757e | 779 | |
b21fc294 MY |
780 | ret = renesas_sdhi_clk_enable(host); |
781 | if (ret) | |
94b110af | 782 | goto efree; |
a87d5638 | 783 | |
c9a9497c WS |
784 | ver = sd_ctrl_read16(host, CTL_VERSION); |
785 | /* GEN2_SDR104 is first known SDHI to use 32bit block count */ | |
786 | if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX) | |
787 | mmc_data->max_blk_count = U16_MAX; | |
788 | ||
5124b592 | 789 | /* One Gen2 SDHI incarnation does NOT have a CBSY bit */ |
c9a9497c | 790 | if (ver == SDHI_VER_GEN2_SDR50) |
5124b592 WS |
791 | mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY; |
792 | ||
91ecbe50 WS |
793 | ret = tmio_mmc_host_probe(host); |
794 | if (ret < 0) | |
795 | goto edisclk; | |
796 | ||
e831ead3 | 797 | /* Enable tuning iff we have an SCC and a supported mode */ |
b1c95170 WS |
798 | if (of_data && of_data->scc_offset && |
799 | (host->mmc->caps & MMC_CAP_UHS_SDR104 || | |
26eb2607 MH |
800 | host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | |
801 | MMC_CAP2_HS400_1_8V))) { | |
b5b6a5f4 | 802 | const struct renesas_sdhi_scc *taps = of_data->taps; |
6ade9a2c WS |
803 | bool hit = false; |
804 | ||
6ade9a2c WS |
805 | for (i = 0; i < of_data->taps_num; i++) { |
806 | if (taps[i].clk_rate == 0 || | |
807 | taps[i].clk_rate == host->mmc->f_max) { | |
852d258f | 808 | priv->scc_tappos = taps->tap; |
f0c8234c | 809 | priv->scc_tappos_hs400 = taps->tap_hs400; |
6ade9a2c WS |
810 | hit = true; |
811 | break; | |
06f438dd | 812 | } |
6ade9a2c | 813 | } |
06f438dd | 814 | |
6ade9a2c WS |
815 | if (!hit) |
816 | dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n"); | |
06f438dd | 817 | |
b5b6a5f4 SH |
818 | host->init_tuning = renesas_sdhi_init_tuning; |
819 | host->prepare_tuning = renesas_sdhi_prepare_tuning; | |
820 | host->select_tuning = renesas_sdhi_select_tuning; | |
821 | host->check_scc_error = renesas_sdhi_check_scc_error; | |
26eb2607 MH |
822 | host->prepare_hs400_tuning = |
823 | renesas_sdhi_prepare_hs400_tuning; | |
824 | host->hs400_downgrade = renesas_sdhi_disable_scc; | |
825 | host->hs400_complete = renesas_sdhi_hs400_complete; | |
06f438dd SH |
826 | } |
827 | ||
828 | i = 0; | |
adcbc949 WS |
829 | while (1) { |
830 | irq = platform_get_irq(pdev, i); | |
831 | if (irq < 0) | |
832 | break; | |
833 | i++; | |
834 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0, | |
2fe35968 | 835 | dev_name(&pdev->dev), host); |
d5098cb6 | 836 | if (ret) |
ac51b961 | 837 | goto eirq; |
d5098cb6 SH |
838 | } |
839 | ||
adcbc949 WS |
840 | /* There must be at least one IRQ source */ |
841 | if (!i) { | |
d5098cb6 | 842 | ret = irq; |
ac51b961 | 843 | goto eirq; |
d5098cb6 SH |
844 | } |
845 | ||
2fb55956 | 846 | dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n", |
1f7d6819 | 847 | mmc_hostname(host->mmc), (unsigned long) |
58126c87 | 848 | (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), |
369213bd | 849 | host->mmc->f_max / 1000000); |
a87d5638 | 850 | |
42051e8a | 851 | return ret; |
a87d5638 | 852 | |
ac51b961 | 853 | eirq: |
8e7bfdb3 | 854 | tmio_mmc_host_remove(host); |
b21fc294 MY |
855 | edisclk: |
856 | renesas_sdhi_clk_disable(host); | |
94b110af KM |
857 | efree: |
858 | tmio_mmc_host_free(host); | |
4ce62817 | 859 | |
a87d5638 MD |
860 | return ret; |
861 | } | |
9d08428a | 862 | EXPORT_SYMBOL_GPL(renesas_sdhi_probe); |
a87d5638 | 863 | |
9d08428a | 864 | int renesas_sdhi_remove(struct platform_device *pdev) |
a87d5638 | 865 | { |
a3b05373 | 866 | struct tmio_mmc_host *host = platform_get_drvdata(pdev); |
d6a1f863 | 867 | |
742a0c7c | 868 | tmio_mmc_host_remove(host); |
b21fc294 | 869 | renesas_sdhi_clk_disable(host); |
742a0c7c | 870 | |
a87d5638 MD |
871 | return 0; |
872 | } | |
9d08428a | 873 | EXPORT_SYMBOL_GPL(renesas_sdhi_remove); |
967a6a07 MH |
874 | |
875 | MODULE_LICENSE("GPL v2"); |