1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 Socionext Inc. |
4 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> |
5 | */ |
6 | |
7 | #include <linux/bitfield.h> |
8 | #include <linux/bits.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/module.h> |
11 | #include <linux/mmc/host.h> |
12 | #include <linux/mmc/mmc.h> |
13 | #include <linux/of.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/reset.h> |
16 | |
17 | #include "sdhci-pltfm.h" |
18 | |
19 | /* HRS - Host Register Set (specific to Cadence) */ |
20 | #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ |
21 | #define SDHCI_CDNS_HRS04_ACK BIT(26) |
22 | #define SDHCI_CDNS_HRS04_RD BIT(25) |
23 | #define SDHCI_CDNS_HRS04_WR BIT(24) |
24 | #define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16) |
25 | #define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8) |
26 | #define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0) |
27 | |
28 | #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */ |
29 | #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15) |
30 | #define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8) |
31 | #define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0) |
32 | #define SDHCI_CDNS_HRS06_MODE_SD 0x0 |
33 | #define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2 |
34 | #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3 |
35 | #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4 |
36 | #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 |
37 | #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 |
38 | |
39 | /* SRS - Slot Register Set (SDHCI-compatible) */ |
40 | #define SDHCI_CDNS_SRS_BASE 0x200 |
41 | |
42 | /* PHY */ |
43 | #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00 |
44 | #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01 |
45 | #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02 |
46 | #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03 |
47 | #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04 |
48 | #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05 |
49 | #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06 |
50 | #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07 |
51 | #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08 |
52 | #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b |
53 | #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c |
54 | #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d |
55 | |
56 | /* |
57 | * The tuned val register is 6 bit-wide, but not the whole of the range is |
58 | * available. The range 0-42 seems to be available (then 43 wraps around to 0) |
59 | * but I am not quite sure if it is official. Use only 0 to 39 for safety. |
60 | */ |
61 | #define SDHCI_CDNS_MAX_TUNING_LOOP 40 |
62 | |
63 | struct sdhci_cdns_phy_param { |
64 | u8 addr; |
65 | u8 data; |
66 | }; |
67 | |
68 | struct sdhci_cdns_priv { |
69 | void __iomem *hrs_addr; |
70 | void __iomem *ctl_addr; /* write control */ |
71 | spinlock_t wrlock; /* write lock */ |
72 | bool enhanced_strobe; |
73 | void (*priv_writel)(struct sdhci_cdns_priv *priv, u32 val, void __iomem *reg); |
74 | struct reset_control *rst_hw; |
75 | unsigned int nr_phy_params; |
76 | struct sdhci_cdns_phy_param phy_params[]; |
77 | }; |
78 | |
79 | struct sdhci_cdns_phy_cfg { |
80 | const char *property; |
81 | u8 addr; |
82 | }; |
83 | |
84 | struct sdhci_cdns_drv_data { |
85 | int (*init)(struct platform_device *pdev); |
86 | const struct sdhci_pltfm_data pltfm_data; |
87 | }; |
88 | |
89 | static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { |
90 | { "cdns,phy-input-delay-sd-highspeed" , SDHCI_CDNS_PHY_DLY_SD_HS, }, |
91 | { "cdns,phy-input-delay-legacy" , SDHCI_CDNS_PHY_DLY_SD_DEFAULT, }, |
92 | { "cdns,phy-input-delay-sd-uhs-sdr12" , SDHCI_CDNS_PHY_DLY_UHS_SDR12, }, |
93 | { "cdns,phy-input-delay-sd-uhs-sdr25" , SDHCI_CDNS_PHY_DLY_UHS_SDR25, }, |
94 | { "cdns,phy-input-delay-sd-uhs-sdr50" , SDHCI_CDNS_PHY_DLY_UHS_SDR50, }, |
95 | { "cdns,phy-input-delay-sd-uhs-ddr50" , SDHCI_CDNS_PHY_DLY_UHS_DDR50, }, |
96 | { "cdns,phy-input-delay-mmc-highspeed" , SDHCI_CDNS_PHY_DLY_EMMC_SDR, }, |
97 | { "cdns,phy-input-delay-mmc-ddr" , SDHCI_CDNS_PHY_DLY_EMMC_DDR, }, |
98 | { "cdns,phy-dll-delay-sdclk" , SDHCI_CDNS_PHY_DLY_SDCLK, }, |
99 | { "cdns,phy-dll-delay-sdclk-hsmmc" , SDHCI_CDNS_PHY_DLY_HSMMC, }, |
100 | { "cdns,phy-dll-delay-strobe" , SDHCI_CDNS_PHY_DLY_STROBE, }, |
101 | }; |
102 | |
103 | static inline void cdns_writel(struct sdhci_cdns_priv *priv, u32 val, |
104 | void __iomem *reg) |
105 | { |
106 | writel(val, addr: reg); |
107 | } |
108 | |
109 | static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, |
110 | u8 addr, u8 data) |
111 | { |
112 | void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04; |
113 | u32 tmp; |
114 | int ret; |
115 | |
116 | ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK), |
117 | 0, 10); |
118 | if (ret) |
119 | return ret; |
120 | |
121 | tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) | |
122 | FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr); |
123 | priv->priv_writel(priv, tmp, reg); |
124 | |
125 | tmp |= SDHCI_CDNS_HRS04_WR; |
126 | priv->priv_writel(priv, tmp, reg); |
127 | |
128 | ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10); |
129 | if (ret) |
130 | return ret; |
131 | |
132 | tmp &= ~SDHCI_CDNS_HRS04_WR; |
133 | priv->priv_writel(priv, tmp, reg); |
134 | |
135 | ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK), |
136 | 0, 10); |
137 | |
138 | return ret; |
139 | } |
140 | |
141 | static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) |
142 | { |
143 | unsigned int count = 0; |
144 | int i; |
145 | |
146 | for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) |
147 | if (of_property_read_bool(np, propname: sdhci_cdns_phy_cfgs[i].property)) |
148 | count++; |
149 | |
150 | return count; |
151 | } |
152 | |
153 | static void sdhci_cdns_phy_param_parse(struct device_node *np, |
154 | struct sdhci_cdns_priv *priv) |
155 | { |
156 | struct sdhci_cdns_phy_param *p = priv->phy_params; |
157 | u32 val; |
158 | int ret, i; |
159 | |
160 | for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) { |
161 | ret = of_property_read_u32(np, propname: sdhci_cdns_phy_cfgs[i].property, |
162 | out_value: &val); |
163 | if (ret) |
164 | continue; |
165 | |
166 | p->addr = sdhci_cdns_phy_cfgs[i].addr; |
167 | p->data = val; |
168 | p++; |
169 | } |
170 | } |
171 | |
172 | static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv) |
173 | { |
174 | int ret, i; |
175 | |
176 | for (i = 0; i < priv->nr_phy_params; i++) { |
177 | ret = sdhci_cdns_write_phy_reg(priv, addr: priv->phy_params[i].addr, |
178 | data: priv->phy_params[i].data); |
179 | if (ret) |
180 | return ret; |
181 | } |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static void *sdhci_cdns_priv(struct sdhci_host *host) |
187 | { |
188 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
189 | |
190 | return sdhci_pltfm_priv(host: pltfm_host); |
191 | } |
192 | |
193 | static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host) |
194 | { |
195 | /* |
196 | * Cadence's spec says the Timeout Clock Frequency is the same as the |
197 | * Base Clock Frequency. |
198 | */ |
199 | return host->max_clk; |
200 | } |
201 | |
202 | static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode) |
203 | { |
204 | u32 tmp; |
205 | |
206 | /* The speed mode for eMMC is selected by HRS06 register */ |
207 | tmp = readl(addr: priv->hrs_addr + SDHCI_CDNS_HRS06); |
208 | tmp &= ~SDHCI_CDNS_HRS06_MODE; |
209 | tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); |
210 | priv->priv_writel(priv, tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); |
211 | } |
212 | |
213 | static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv) |
214 | { |
215 | u32 tmp; |
216 | |
217 | tmp = readl(addr: priv->hrs_addr + SDHCI_CDNS_HRS06); |
218 | return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp); |
219 | } |
220 | |
221 | static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) |
222 | { |
223 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
224 | void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06; |
225 | u32 tmp; |
226 | int i, ret; |
227 | |
228 | if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) |
229 | return -EINVAL; |
230 | |
231 | tmp = readl(addr: reg); |
232 | tmp &= ~SDHCI_CDNS_HRS06_TUNE; |
233 | tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); |
234 | |
235 | /* |
236 | * Workaround for IP errata: |
237 | * The IP6116 SD/eMMC PHY design has a timing issue on receive data |
238 | * path. Send tune request twice. |
239 | */ |
240 | for (i = 0; i < 2; i++) { |
241 | tmp |= SDHCI_CDNS_HRS06_TUNE_UP; |
242 | priv->priv_writel(priv, tmp, reg); |
243 | |
244 | ret = readl_poll_timeout(reg, tmp, |
245 | !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), |
246 | 0, 1); |
247 | if (ret) |
248 | return ret; |
249 | } |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | /* |
255 | * In SD mode, software must not use the hardware tuning and instead perform |
256 | * an almost identical procedure to eMMC. |
257 | */ |
258 | static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) |
259 | { |
260 | int cur_streak = 0; |
261 | int max_streak = 0; |
262 | int end_of_streak = 0; |
263 | int i; |
264 | |
265 | /* |
266 | * Do not execute tuning for UHS_SDR50 or UHS_DDR50. |
267 | * The delay is set by probe, based on the DT properties. |
268 | */ |
269 | if (host->timing != MMC_TIMING_MMC_HS200 && |
270 | host->timing != MMC_TIMING_UHS_SDR104) |
271 | return 0; |
272 | |
273 | for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) { |
274 | if (sdhci_cdns_set_tune_val(host, val: i) || |
275 | mmc_send_tuning(host: host->mmc, opcode, NULL)) { /* bad */ |
276 | cur_streak = 0; |
277 | } else { /* good */ |
278 | cur_streak++; |
279 | if (cur_streak > max_streak) { |
280 | max_streak = cur_streak; |
281 | end_of_streak = i; |
282 | } |
283 | } |
284 | } |
285 | |
286 | if (!max_streak) { |
287 | dev_err(mmc_dev(host->mmc), "no tuning point found\n" ); |
288 | return -EIO; |
289 | } |
290 | |
291 | return sdhci_cdns_set_tune_val(host, val: end_of_streak - max_streak / 2); |
292 | } |
293 | |
294 | static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, |
295 | unsigned int timing) |
296 | { |
297 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
298 | u32 mode; |
299 | |
300 | switch (timing) { |
301 | case MMC_TIMING_MMC_HS: |
302 | mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; |
303 | break; |
304 | case MMC_TIMING_MMC_DDR52: |
305 | mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; |
306 | break; |
307 | case MMC_TIMING_MMC_HS200: |
308 | mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; |
309 | break; |
310 | case MMC_TIMING_MMC_HS400: |
311 | if (priv->enhanced_strobe) |
312 | mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES; |
313 | else |
314 | mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; |
315 | break; |
316 | default: |
317 | mode = SDHCI_CDNS_HRS06_MODE_SD; |
318 | break; |
319 | } |
320 | |
321 | sdhci_cdns_set_emmc_mode(priv, mode); |
322 | |
323 | /* For SD, fall back to the default handler */ |
324 | if (mode == SDHCI_CDNS_HRS06_MODE_SD) |
325 | sdhci_set_uhs_signaling(host, timing); |
326 | } |
327 | |
328 | /* Elba control register bits [6:3] are byte-lane enables */ |
329 | #define ELBA_BYTE_ENABLE_MASK(x) ((x) << 3) |
330 | |
331 | /* |
332 | * The Pensando Elba SoC explicitly controls byte-lane enabling on writes |
333 | * which includes writes to the HRS registers. The write lock (wrlock) |
334 | * is used to ensure byte-lane enable, using write control (ctl_addr), |
335 | * occurs before the data write. |
336 | */ |
337 | static void elba_priv_writel(struct sdhci_cdns_priv *priv, u32 val, |
338 | void __iomem *reg) |
339 | { |
340 | unsigned long flags; |
341 | |
342 | spin_lock_irqsave(&priv->wrlock, flags); |
343 | writel(GENMASK(7, 3), addr: priv->ctl_addr); |
344 | writel(val, addr: reg); |
345 | spin_unlock_irqrestore(lock: &priv->wrlock, flags); |
346 | } |
347 | |
348 | static void elba_write_l(struct sdhci_host *host, u32 val, int reg) |
349 | { |
350 | elba_priv_writel(priv: sdhci_cdns_priv(host), val, reg: host->ioaddr + reg); |
351 | } |
352 | |
353 | static void elba_write_w(struct sdhci_host *host, u16 val, int reg) |
354 | { |
355 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
356 | u32 shift = reg & GENMASK(1, 0); |
357 | unsigned long flags; |
358 | u32 byte_enables; |
359 | |
360 | byte_enables = GENMASK(1, 0) << shift; |
361 | spin_lock_irqsave(&priv->wrlock, flags); |
362 | writel(ELBA_BYTE_ENABLE_MASK(byte_enables), addr: priv->ctl_addr); |
363 | writew(val, addr: host->ioaddr + reg); |
364 | spin_unlock_irqrestore(lock: &priv->wrlock, flags); |
365 | } |
366 | |
367 | static void elba_write_b(struct sdhci_host *host, u8 val, int reg) |
368 | { |
369 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
370 | u32 shift = reg & GENMASK(1, 0); |
371 | unsigned long flags; |
372 | u32 byte_enables; |
373 | |
374 | byte_enables = BIT(0) << shift; |
375 | spin_lock_irqsave(&priv->wrlock, flags); |
376 | writel(ELBA_BYTE_ENABLE_MASK(byte_enables), addr: priv->ctl_addr); |
377 | writeb(val, addr: host->ioaddr + reg); |
378 | spin_unlock_irqrestore(lock: &priv->wrlock, flags); |
379 | } |
380 | |
381 | static const struct sdhci_ops sdhci_elba_ops = { |
382 | .write_l = elba_write_l, |
383 | .write_w = elba_write_w, |
384 | .write_b = elba_write_b, |
385 | .set_clock = sdhci_set_clock, |
386 | .get_timeout_clock = sdhci_cdns_get_timeout_clock, |
387 | .set_bus_width = sdhci_set_bus_width, |
388 | .reset = sdhci_reset, |
389 | .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, |
390 | }; |
391 | |
392 | static int elba_drv_init(struct platform_device *pdev) |
393 | { |
394 | struct sdhci_host *host = platform_get_drvdata(pdev); |
395 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
396 | void __iomem *ioaddr; |
397 | |
398 | host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA; |
399 | spin_lock_init(&priv->wrlock); |
400 | |
401 | /* Byte-lane control register */ |
402 | ioaddr = devm_platform_ioremap_resource(pdev, index: 1); |
403 | if (IS_ERR(ptr: ioaddr)) |
404 | return PTR_ERR(ptr: ioaddr); |
405 | |
406 | priv->ctl_addr = ioaddr; |
407 | priv->priv_writel = elba_priv_writel; |
408 | writel(ELBA_BYTE_ENABLE_MASK(0xf), addr: priv->ctl_addr); |
409 | |
410 | return 0; |
411 | } |
412 | |
413 | static const struct sdhci_ops sdhci_cdns_ops = { |
414 | .set_clock = sdhci_set_clock, |
415 | .get_timeout_clock = sdhci_cdns_get_timeout_clock, |
416 | .set_bus_width = sdhci_set_bus_width, |
417 | .reset = sdhci_reset, |
418 | .platform_execute_tuning = sdhci_cdns_execute_tuning, |
419 | .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, |
420 | }; |
421 | |
422 | static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = { |
423 | .pltfm_data = { |
424 | .ops = &sdhci_cdns_ops, |
425 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
426 | }, |
427 | }; |
428 | |
429 | static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { |
430 | .init = elba_drv_init, |
431 | .pltfm_data = { |
432 | .ops = &sdhci_elba_ops, |
433 | }, |
434 | }; |
435 | |
436 | static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { |
437 | .pltfm_data = { |
438 | .ops = &sdhci_cdns_ops, |
439 | }, |
440 | }; |
441 | |
442 | static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, |
443 | struct mmc_ios *ios) |
444 | { |
445 | struct sdhci_host *host = mmc_priv(host: mmc); |
446 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
447 | u32 mode; |
448 | |
449 | priv->enhanced_strobe = ios->enhanced_strobe; |
450 | |
451 | mode = sdhci_cdns_get_emmc_mode(priv); |
452 | |
453 | if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe) |
454 | sdhci_cdns_set_emmc_mode(priv, |
455 | SDHCI_CDNS_HRS06_MODE_MMC_HS400ES); |
456 | |
457 | if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe) |
458 | sdhci_cdns_set_emmc_mode(priv, |
459 | SDHCI_CDNS_HRS06_MODE_MMC_HS400); |
460 | } |
461 | |
462 | static void sdhci_cdns_mmc_hw_reset(struct mmc_host *mmc) |
463 | { |
464 | struct sdhci_host *host = mmc_priv(host: mmc); |
465 | struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); |
466 | |
467 | dev_dbg(mmc_dev(host->mmc), "emmc hardware reset\n" ); |
468 | |
469 | reset_control_assert(rstc: priv->rst_hw); |
470 | /* For eMMC, minimum is 1us but give it 3us for good measure */ |
471 | udelay(3); |
472 | |
473 | reset_control_deassert(rstc: priv->rst_hw); |
474 | /* For eMMC, minimum is 200us but give it 300us for good measure */ |
475 | usleep_range(min: 300, max: 1000); |
476 | } |
477 | |
478 | static int sdhci_cdns_probe(struct platform_device *pdev) |
479 | { |
480 | struct sdhci_host *host; |
481 | const struct sdhci_cdns_drv_data *data; |
482 | struct sdhci_pltfm_host *pltfm_host; |
483 | struct sdhci_cdns_priv *priv; |
484 | struct clk *clk; |
485 | unsigned int nr_phy_params; |
486 | int ret; |
487 | struct device *dev = &pdev->dev; |
488 | static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT; |
489 | |
490 | clk = devm_clk_get_enabled(dev, NULL); |
491 | if (IS_ERR(ptr: clk)) |
492 | return PTR_ERR(ptr: clk); |
493 | |
494 | data = of_device_get_match_data(dev); |
495 | if (!data) |
496 | data = &sdhci_cdns_drv_data; |
497 | |
498 | nr_phy_params = sdhci_cdns_phy_param_count(np: dev->of_node); |
499 | host = sdhci_pltfm_init(pdev, pdata: &data->pltfm_data, |
500 | struct_size(priv, phy_params, nr_phy_params)); |
501 | if (IS_ERR(ptr: host)) |
502 | return PTR_ERR(ptr: host); |
503 | |
504 | pltfm_host = sdhci_priv(host); |
505 | pltfm_host->clk = clk; |
506 | |
507 | priv = sdhci_pltfm_priv(host: pltfm_host); |
508 | priv->nr_phy_params = nr_phy_params; |
509 | priv->hrs_addr = host->ioaddr; |
510 | priv->enhanced_strobe = false; |
511 | priv->priv_writel = cdns_writel; |
512 | host->ioaddr += SDHCI_CDNS_SRS_BASE; |
513 | host->mmc_host_ops.hs400_enhanced_strobe = |
514 | sdhci_cdns_hs400_enhanced_strobe; |
515 | if (data->init) { |
516 | ret = data->init(pdev); |
517 | if (ret) |
518 | goto free; |
519 | } |
520 | sdhci_enable_v4_mode(host); |
521 | __sdhci_read_caps(host, ver: &version, NULL, NULL); |
522 | |
523 | sdhci_get_of_property(pdev); |
524 | |
525 | ret = mmc_of_parse(host: host->mmc); |
526 | if (ret) |
527 | goto free; |
528 | |
529 | sdhci_cdns_phy_param_parse(np: dev->of_node, priv); |
530 | |
531 | ret = sdhci_cdns_phy_init(priv); |
532 | if (ret) |
533 | goto free; |
534 | |
535 | if (host->mmc->caps & MMC_CAP_HW_RESET) { |
536 | priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL); |
537 | if (IS_ERR(ptr: priv->rst_hw)) { |
538 | ret = dev_err_probe(mmc_dev(host->mmc), err: PTR_ERR(ptr: priv->rst_hw), |
539 | fmt: "reset controller error\n" ); |
540 | goto free; |
541 | } |
542 | if (priv->rst_hw) |
543 | host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset; |
544 | } |
545 | |
546 | ret = sdhci_add_host(host); |
547 | if (ret) |
548 | goto free; |
549 | |
550 | return 0; |
551 | free: |
552 | sdhci_pltfm_free(pdev); |
553 | return ret; |
554 | } |
555 | |
556 | #ifdef CONFIG_PM_SLEEP |
557 | static int sdhci_cdns_resume(struct device *dev) |
558 | { |
559 | struct sdhci_host *host = dev_get_drvdata(dev); |
560 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
561 | struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(host: pltfm_host); |
562 | int ret; |
563 | |
564 | ret = clk_prepare_enable(clk: pltfm_host->clk); |
565 | if (ret) |
566 | return ret; |
567 | |
568 | ret = sdhci_cdns_phy_init(priv); |
569 | if (ret) |
570 | goto disable_clk; |
571 | |
572 | ret = sdhci_resume_host(host); |
573 | if (ret) |
574 | goto disable_clk; |
575 | |
576 | return 0; |
577 | |
578 | disable_clk: |
579 | clk_disable_unprepare(clk: pltfm_host->clk); |
580 | |
581 | return ret; |
582 | } |
583 | #endif |
584 | |
585 | static const struct dev_pm_ops sdhci_cdns_pm_ops = { |
586 | SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume) |
587 | }; |
588 | |
589 | static const struct of_device_id sdhci_cdns_match[] = { |
590 | { |
591 | .compatible = "socionext,uniphier-sd4hc" , |
592 | .data = &sdhci_cdns_uniphier_drv_data, |
593 | }, |
594 | { |
595 | .compatible = "amd,pensando-elba-sd4hc" , |
596 | .data = &sdhci_elba_drv_data, |
597 | }, |
598 | { .compatible = "cdns,sd4hc" }, |
599 | { /* sentinel */ } |
600 | }; |
601 | MODULE_DEVICE_TABLE(of, sdhci_cdns_match); |
602 | |
603 | static struct platform_driver sdhci_cdns_driver = { |
604 | .driver = { |
605 | .name = "sdhci-cdns" , |
606 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
607 | .pm = &sdhci_cdns_pm_ops, |
608 | .of_match_table = sdhci_cdns_match, |
609 | }, |
610 | .probe = sdhci_cdns_probe, |
611 | .remove_new = sdhci_pltfm_remove, |
612 | }; |
613 | module_platform_driver(sdhci_cdns_driver); |
614 | |
615 | MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>" ); |
616 | MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver" ); |
617 | MODULE_LICENSE("GPL" ); |
618 | |