1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller
4  *
5  * Copyright (C) 2018 Synaptics Incorporated
6  *
7  * Author: Jisheng Zhang <jszhang@kernel.org>
8  */
9 
10 #include <linux/acpi.h>
11 #include <linux/clk.h>
12 #include <linux/dma-mapping.h>
13 #include <linux/iopoll.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/of_device.h>
18 #include <linux/reset.h>
19 #include <linux/sizes.h>
20 
21 #include "sdhci-pltfm.h"
22 
23 #define SDHCI_DWCMSHC_ARG2_STUFF	GENMASK(31, 16)
24 
25 /* DWCMSHC specific Mode Select value */
26 #define DWCMSHC_CTRL_HS400		0x7
27 
28 /* DWC IP vendor area 1 pointer */
29 #define DWCMSHC_P_VENDOR_AREA1		0xe8
30 #define DWCMSHC_AREA1_MASK		GENMASK(11, 0)
31 /* Offset inside the  vendor area 1 */
32 #define DWCMSHC_HOST_CTRL3		0x8
33 #define DWCMSHC_EMMC_CONTROL		0x2c
34 #define DWCMSHC_ENHANCED_STROBE		BIT(8)
35 #define DWCMSHC_EMMC_ATCTRL		0x40
36 
37 /* Rockchip specific Registers */
38 #define DWCMSHC_EMMC_DLL_CTRL		0x800
39 #define DWCMSHC_EMMC_DLL_RXCLK		0x804
40 #define DWCMSHC_EMMC_DLL_TXCLK		0x808
41 #define DWCMSHC_EMMC_DLL_STRBIN		0x80c
42 #define DLL_STRBIN_TAPNUM_FROM_SW	BIT(24)
43 #define DWCMSHC_EMMC_DLL_STATUS0	0x840
44 #define DWCMSHC_EMMC_DLL_START		BIT(0)
45 #define DWCMSHC_EMMC_DLL_LOCKED		BIT(8)
46 #define DWCMSHC_EMMC_DLL_TIMEOUT	BIT(9)
47 #define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL	29
48 #define DWCMSHC_EMMC_DLL_START_POINT	16
49 #define DWCMSHC_EMMC_DLL_INC		8
50 #define DWCMSHC_EMMC_DLL_DLYENA		BIT(27)
51 #define DLL_TXCLK_TAPNUM_DEFAULT	0x8
52 #define DLL_STRBIN_TAPNUM_DEFAULT	0x8
53 #define DLL_TXCLK_TAPNUM_FROM_SW	BIT(24)
54 #define DLL_RXCLK_NO_INVERTER		1
55 #define DLL_RXCLK_INVERTER		0
56 #define DLL_LOCK_WO_TMOUT(x) \
57 	((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
58 	(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
59 #define RK3568_MAX_CLKS 3
60 
61 #define BOUNDARY_OK(addr, len) \
62 	((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
63 
64 struct rk3568_priv {
65 	/* Rockchip specified optional clocks */
66 	struct clk_bulk_data rockchip_clks[RK3568_MAX_CLKS];
67 	struct reset_control *reset;
68 	u8 txclk_tapnum;
69 };
70 
71 struct dwcmshc_priv {
72 	struct clk	*bus_clk;
73 	int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */
74 	void *priv; /* pointer to SoC private stuff */
75 };
76 
77 /*
78  * If DMA addr spans 128MB boundary, we split the DMA transfer into two
79  * so that each DMA transfer doesn't exceed the boundary.
80  */
81 static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
82 				    dma_addr_t addr, int len, unsigned int cmd)
83 {
84 	int tmplen, offset;
85 
86 	if (likely(!len || BOUNDARY_OK(addr, len))) {
87 		sdhci_adma_write_desc(host, desc, addr, len, cmd);
88 		return;
89 	}
90 
91 	offset = addr & (SZ_128M - 1);
92 	tmplen = SZ_128M - offset;
93 	sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
94 
95 	addr += tmplen;
96 	len -= tmplen;
97 	sdhci_adma_write_desc(host, desc, addr, len, cmd);
98 }
99 
100 static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
101 {
102 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
103 
104 	if (pltfm_host->clk)
105 		return sdhci_pltfm_clk_get_max_clock(host);
106 	else
107 		return pltfm_host->clock;
108 }
109 
110 static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc,
111 				     struct mmc_request *mrq)
112 {
113 	struct sdhci_host *host = mmc_priv(mmc);
114 
115 	/*
116 	 * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit
117 	 * block count register which doesn't support stuff bits of
118 	 * CMD23 argument on dwcmsch host controller.
119 	 */
120 	if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF))
121 		host->flags &= ~SDHCI_AUTO_CMD23;
122 	else
123 		host->flags |= SDHCI_AUTO_CMD23;
124 }
125 
126 static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
127 {
128 	dwcmshc_check_auto_cmd23(mmc, mrq);
129 
130 	sdhci_request(mmc, mrq);
131 }
132 
133 static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
134 				      unsigned int timing)
135 {
136 	u16 ctrl_2;
137 
138 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
139 	/* Select Bus Speed Mode for host */
140 	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
141 	if ((timing == MMC_TIMING_MMC_HS200) ||
142 	    (timing == MMC_TIMING_UHS_SDR104))
143 		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
144 	else if (timing == MMC_TIMING_UHS_SDR12)
145 		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
146 	else if ((timing == MMC_TIMING_UHS_SDR25) ||
147 		 (timing == MMC_TIMING_MMC_HS))
148 		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
149 	else if (timing == MMC_TIMING_UHS_SDR50)
150 		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
151 	else if ((timing == MMC_TIMING_UHS_DDR50) ||
152 		 (timing == MMC_TIMING_MMC_DDR52))
153 		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
154 	else if (timing == MMC_TIMING_MMC_HS400)
155 		ctrl_2 |= DWCMSHC_CTRL_HS400;
156 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
157 }
158 
159 static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc,
160 					  struct mmc_ios *ios)
161 {
162 	u32 vendor;
163 	struct sdhci_host *host = mmc_priv(mmc);
164 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
165 	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
166 	int reg = priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL;
167 
168 	vendor = sdhci_readl(host, reg);
169 	if (ios->enhanced_strobe)
170 		vendor |= DWCMSHC_ENHANCED_STROBE;
171 	else
172 		vendor &= ~DWCMSHC_ENHANCED_STROBE;
173 
174 	sdhci_writel(host, vendor, reg);
175 }
176 
177 static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
178 {
179 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
180 	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
181 	struct rk3568_priv *priv = dwc_priv->priv;
182 	u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
183 	u32 extra, reg;
184 	int err;
185 
186 	host->mmc->actual_clock = 0;
187 
188 	/*
189 	 * DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled
190 	 * by default, but it shouldn't be enabled. We should anyway
191 	 * disable it before issuing any cmds.
192 	 */
193 	extra = DWCMSHC_EMMC_DLL_DLYENA |
194 		DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
195 	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
196 
197 	if (clock == 0)
198 		return;
199 
200 	/* Rockchip platform only support 375KHz for identify mode */
201 	if (clock <= 400000)
202 		clock = 375000;
203 
204 	err = clk_set_rate(pltfm_host->clk, clock);
205 	if (err)
206 		dev_err(mmc_dev(host->mmc), "fail to set clock %d", clock);
207 
208 	sdhci_set_clock(host, clock);
209 
210 	/* Disable cmd conflict check */
211 	reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
212 	extra = sdhci_readl(host, reg);
213 	extra &= ~BIT(0);
214 	sdhci_writel(host, extra, reg);
215 
216 	if (clock <= 400000) {
217 		/* Disable DLL to reset sample clock */
218 		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
219 		return;
220 	}
221 
222 	/* Reset DLL */
223 	sdhci_writel(host, BIT(1), DWCMSHC_EMMC_DLL_CTRL);
224 	udelay(1);
225 	sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);
226 
227 	/* Init DLL settings */
228 	extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
229 		0x2 << DWCMSHC_EMMC_DLL_INC |
230 		DWCMSHC_EMMC_DLL_START;
231 	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL);
232 	err = readl_poll_timeout(host->ioaddr + DWCMSHC_EMMC_DLL_STATUS0,
233 				 extra, DLL_LOCK_WO_TMOUT(extra), 1,
234 				 500 * USEC_PER_MSEC);
235 	if (err) {
236 		dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n");
237 		return;
238 	}
239 
240 	extra = 0x1 << 16 | /* tune clock stop en */
241 		0x2 << 17 | /* pre-change delay */
242 		0x3 << 19;  /* post-change delay */
243 	sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
244 
245 	if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
246 	    host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
247 		txclk_tapnum = priv->txclk_tapnum;
248 
249 	extra = DWCMSHC_EMMC_DLL_DLYENA |
250 		DLL_TXCLK_TAPNUM_FROM_SW |
251 		txclk_tapnum;
252 	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
253 
254 	extra = DWCMSHC_EMMC_DLL_DLYENA |
255 		DLL_STRBIN_TAPNUM_DEFAULT |
256 		DLL_STRBIN_TAPNUM_FROM_SW;
257 	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
258 }
259 
260 static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
261 {
262 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
263 	struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
264 	struct rk35xx_priv *priv = dwc_priv->priv;
265 
266 	if (mask & SDHCI_RESET_ALL && priv->reset) {
267 		reset_control_assert(priv->reset);
268 		udelay(1);
269 		reset_control_deassert(priv->reset);
270 	}
271 
272 	sdhci_reset(host, mask);
273 }
274 
275 static const struct sdhci_ops sdhci_dwcmshc_ops = {
276 	.set_clock		= sdhci_set_clock,
277 	.set_bus_width		= sdhci_set_bus_width,
278 	.set_uhs_signaling	= dwcmshc_set_uhs_signaling,
279 	.get_max_clock		= dwcmshc_get_max_clock,
280 	.reset			= sdhci_reset,
281 	.adma_write_desc	= dwcmshc_adma_write_desc,
282 };
283 
284 static const struct sdhci_ops sdhci_dwcmshc_rk3568_ops = {
285 	.set_clock		= dwcmshc_rk3568_set_clock,
286 	.set_bus_width		= sdhci_set_bus_width,
287 	.set_uhs_signaling	= dwcmshc_set_uhs_signaling,
288 	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
289 	.reset			= rk35xx_sdhci_reset,
290 	.adma_write_desc	= dwcmshc_adma_write_desc,
291 };
292 
293 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
294 	.ops = &sdhci_dwcmshc_ops,
295 	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
296 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
297 };
298 
299 static const struct sdhci_pltfm_data sdhci_dwcmshc_rk3568_pdata = {
300 	.ops = &sdhci_dwcmshc_rk3568_ops,
301 	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
302 		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
303 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
304 		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
305 };
306 
307 static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
308 {
309 	int err;
310 	struct rk3568_priv *priv = dwc_priv->priv;
311 
312 	priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
313 	if (IS_ERR(priv->reset)) {
314 		err = PTR_ERR(priv->reset);
315 		dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
316 		return err;
317 	}
318 
319 	priv->rockchip_clks[0].id = "axi";
320 	priv->rockchip_clks[1].id = "block";
321 	priv->rockchip_clks[2].id = "timer";
322 	err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK3568_MAX_CLKS,
323 					 priv->rockchip_clks);
324 	if (err) {
325 		dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
326 		return err;
327 	}
328 
329 	err = clk_bulk_prepare_enable(RK3568_MAX_CLKS, priv->rockchip_clks);
330 	if (err) {
331 		dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
332 		return err;
333 	}
334 
335 	if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
336 				&priv->txclk_tapnum))
337 		priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
338 
339 	/* Disable cmd conflict check */
340 	sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3);
341 	/* Reset previous settings */
342 	sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
343 	sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
344 
345 	return 0;
346 }
347 
348 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
349 	{
350 		.compatible = "rockchip,rk3568-dwcmshc",
351 		.data = &sdhci_dwcmshc_rk3568_pdata,
352 	},
353 	{
354 		.compatible = "snps,dwcmshc-sdhci",
355 		.data = &sdhci_dwcmshc_pdata,
356 	},
357 	{},
358 };
359 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
360 
361 #ifdef CONFIG_ACPI
362 static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = {
363 	{ .id = "MLNXBF30" },
364 	{}
365 };
366 #endif
367 
368 static int dwcmshc_probe(struct platform_device *pdev)
369 {
370 	struct device *dev = &pdev->dev;
371 	struct sdhci_pltfm_host *pltfm_host;
372 	struct sdhci_host *host;
373 	struct dwcmshc_priv *priv;
374 	struct rk3568_priv *rk_priv = NULL;
375 	const struct sdhci_pltfm_data *pltfm_data;
376 	int err;
377 	u32 extra;
378 
379 	pltfm_data = of_device_get_match_data(&pdev->dev);
380 	if (!pltfm_data) {
381 		dev_err(&pdev->dev, "Error: No device match data found\n");
382 		return -ENODEV;
383 	}
384 
385 	host = sdhci_pltfm_init(pdev, pltfm_data,
386 				sizeof(struct dwcmshc_priv));
387 	if (IS_ERR(host))
388 		return PTR_ERR(host);
389 
390 	/*
391 	 * extra adma table cnt for cross 128M boundary handling.
392 	 */
393 	extra = DIV_ROUND_UP_ULL(dma_get_required_mask(dev), SZ_128M);
394 	if (extra > SDHCI_MAX_SEGS)
395 		extra = SDHCI_MAX_SEGS;
396 	host->adma_table_cnt += extra;
397 
398 	pltfm_host = sdhci_priv(host);
399 	priv = sdhci_pltfm_priv(pltfm_host);
400 
401 	if (dev->of_node) {
402 		pltfm_host->clk = devm_clk_get(dev, "core");
403 		if (IS_ERR(pltfm_host->clk)) {
404 			err = PTR_ERR(pltfm_host->clk);
405 			dev_err(dev, "failed to get core clk: %d\n", err);
406 			goto free_pltfm;
407 		}
408 		err = clk_prepare_enable(pltfm_host->clk);
409 		if (err)
410 			goto free_pltfm;
411 
412 		priv->bus_clk = devm_clk_get(dev, "bus");
413 		if (!IS_ERR(priv->bus_clk))
414 			clk_prepare_enable(priv->bus_clk);
415 	}
416 
417 	err = mmc_of_parse(host->mmc);
418 	if (err)
419 		goto err_clk;
420 
421 	sdhci_get_of_property(pdev);
422 
423 	priv->vendor_specific_area1 =
424 		sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK;
425 
426 	host->mmc_host_ops.request = dwcmshc_request;
427 	host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
428 
429 	if (pltfm_data == &sdhci_dwcmshc_rk3568_pdata) {
430 		rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk3568_priv), GFP_KERNEL);
431 		if (!rk_priv) {
432 			err = -ENOMEM;
433 			goto err_clk;
434 		}
435 
436 		priv->priv = rk_priv;
437 
438 		err = dwcmshc_rk3568_init(host, priv);
439 		if (err)
440 			goto err_clk;
441 	}
442 
443 	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
444 
445 	err = sdhci_add_host(host);
446 	if (err)
447 		goto err_clk;
448 
449 	return 0;
450 
451 err_clk:
452 	clk_disable_unprepare(pltfm_host->clk);
453 	clk_disable_unprepare(priv->bus_clk);
454 	if (rk_priv)
455 		clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
456 					   rk_priv->rockchip_clks);
457 free_pltfm:
458 	sdhci_pltfm_free(pdev);
459 	return err;
460 }
461 
462 static int dwcmshc_remove(struct platform_device *pdev)
463 {
464 	struct sdhci_host *host = platform_get_drvdata(pdev);
465 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
466 	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
467 	struct rk3568_priv *rk_priv = priv->priv;
468 
469 	sdhci_remove_host(host, 0);
470 
471 	clk_disable_unprepare(pltfm_host->clk);
472 	clk_disable_unprepare(priv->bus_clk);
473 	if (rk_priv)
474 		clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
475 					   rk_priv->rockchip_clks);
476 	sdhci_pltfm_free(pdev);
477 
478 	return 0;
479 }
480 
481 #ifdef CONFIG_PM_SLEEP
482 static int dwcmshc_suspend(struct device *dev)
483 {
484 	struct sdhci_host *host = dev_get_drvdata(dev);
485 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
486 	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
487 	struct rk3568_priv *rk_priv = priv->priv;
488 	int ret;
489 
490 	ret = sdhci_suspend_host(host);
491 	if (ret)
492 		return ret;
493 
494 	clk_disable_unprepare(pltfm_host->clk);
495 	if (!IS_ERR(priv->bus_clk))
496 		clk_disable_unprepare(priv->bus_clk);
497 
498 	if (rk_priv)
499 		clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
500 					   rk_priv->rockchip_clks);
501 
502 	return ret;
503 }
504 
505 static int dwcmshc_resume(struct device *dev)
506 {
507 	struct sdhci_host *host = dev_get_drvdata(dev);
508 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
509 	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
510 	struct rk3568_priv *rk_priv = priv->priv;
511 	int ret;
512 
513 	ret = clk_prepare_enable(pltfm_host->clk);
514 	if (ret)
515 		return ret;
516 
517 	if (!IS_ERR(priv->bus_clk)) {
518 		ret = clk_prepare_enable(priv->bus_clk);
519 		if (ret)
520 			return ret;
521 	}
522 
523 	if (rk_priv) {
524 		ret = clk_bulk_prepare_enable(RK3568_MAX_CLKS,
525 					      rk_priv->rockchip_clks);
526 		if (ret)
527 			return ret;
528 	}
529 
530 	return sdhci_resume_host(host);
531 }
532 #endif
533 
534 static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
535 
536 static struct platform_driver sdhci_dwcmshc_driver = {
537 	.driver	= {
538 		.name	= "sdhci-dwcmshc",
539 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
540 		.of_match_table = sdhci_dwcmshc_dt_ids,
541 		.acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids),
542 		.pm = &dwcmshc_pmops,
543 	},
544 	.probe	= dwcmshc_probe,
545 	.remove	= dwcmshc_remove,
546 };
547 module_platform_driver(sdhci_dwcmshc_driver);
548 
549 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC");
550 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
551 MODULE_LICENSE("GPL v2");
552