2 * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
4 * Copyright (C) 2012, Samsung Electronics Co., Ltd.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/clk.h>
15 #include <linux/mmc/host.h>
16 #include <linux/mmc/dw_mmc.h>
17 #include <linux/mmc/mmc.h>
19 #include <linux/of_gpio.h>
20 #include <linux/slab.h>
23 #include "dw_mmc-pltfm.h"
25 #define NUM_PINS(x) (x + 2)
27 #define SDMMC_CLKSEL 0x09C
28 #define SDMMC_CLKSEL64 0x0A8
29 #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
30 #define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
31 #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
32 #define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
33 #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
34 SDMMC_CLKSEL_CCLK_DRIVE(y) | \
35 SDMMC_CLKSEL_CCLK_DIVIDER(z))
36 #define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
38 #define EXYNOS4210_FIXED_CIU_CLK_DIV 2
39 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4
41 /* Block number in eMMC */
42 #define DWMCI_BLOCK_NUM 0xFFFFFFFF
44 #define SDMMC_EMMCP_BASE 0x1000
45 #define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
46 #define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
47 #define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
48 #define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
50 /* SMU control bits */
51 #define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
52 #define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
53 #define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
54 #define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
55 #define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
56 #define DWMCI_MPSCTRL_ECB_MODE BIT(2)
57 #define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
58 #define DWMCI_MPSCTRL_VALID BIT(0)
60 #define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
62 /* Variations in Exynos specific dw-mshc controller */
63 enum dw_mci_exynos_type
{
64 DW_MCI_TYPE_EXYNOS4210
,
65 DW_MCI_TYPE_EXYNOS4412
,
66 DW_MCI_TYPE_EXYNOS5250
,
67 DW_MCI_TYPE_EXYNOS5420
,
68 DW_MCI_TYPE_EXYNOS5420_SMU
,
70 DW_MCI_TYPE_EXYNOS7_SMU
,
73 /* Exynos implementation specific driver private data */
74 struct dw_mci_exynos_priv_data
{
75 enum dw_mci_exynos_type ctrl_type
;
82 static struct dw_mci_exynos_compatible
{
84 enum dw_mci_exynos_type ctrl_type
;
87 .compatible
= "samsung,exynos4210-dw-mshc",
88 .ctrl_type
= DW_MCI_TYPE_EXYNOS4210
,
90 .compatible
= "samsung,exynos4412-dw-mshc",
91 .ctrl_type
= DW_MCI_TYPE_EXYNOS4412
,
93 .compatible
= "samsung,exynos5250-dw-mshc",
94 .ctrl_type
= DW_MCI_TYPE_EXYNOS5250
,
96 .compatible
= "samsung,exynos5420-dw-mshc",
97 .ctrl_type
= DW_MCI_TYPE_EXYNOS5420
,
99 .compatible
= "samsung,exynos5420-dw-mshc-smu",
100 .ctrl_type
= DW_MCI_TYPE_EXYNOS5420_SMU
,
102 .compatible
= "samsung,exynos7-dw-mshc",
103 .ctrl_type
= DW_MCI_TYPE_EXYNOS7
,
105 .compatible
= "samsung,exynos7-dw-mshc-smu",
106 .ctrl_type
= DW_MCI_TYPE_EXYNOS7_SMU
,
110 static int dw_mci_exynos_priv_init(struct dw_mci
*host
)
112 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
114 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS5420_SMU
||
115 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
) {
116 mci_writel(host
, MPSBEGIN0
, 0);
117 mci_writel(host
, MPSEND0
, DWMCI_BLOCK_NUM
);
118 mci_writel(host
, MPSCTRL0
, DWMCI_MPSCTRL_SECURE_WRITE_BIT
|
119 DWMCI_MPSCTRL_NON_SECURE_READ_BIT
|
120 DWMCI_MPSCTRL_VALID
|
121 DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT
);
127 static int dw_mci_exynos_setup_clock(struct dw_mci
*host
)
129 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
130 unsigned long rate
= clk_get_rate(host
->ciu_clk
);
132 host
->bus_hz
= rate
/ (priv
->ciu_div
+ 1);
136 #ifdef CONFIG_PM_SLEEP
137 static int dw_mci_exynos_suspend(struct device
*dev
)
139 struct dw_mci
*host
= dev_get_drvdata(dev
);
141 return dw_mci_suspend(host
);
144 static int dw_mci_exynos_resume(struct device
*dev
)
146 struct dw_mci
*host
= dev_get_drvdata(dev
);
148 dw_mci_exynos_priv_init(host
);
149 return dw_mci_resume(host
);
153 * dw_mci_exynos_resume_noirq - Exynos-specific resume code
155 * On exynos5420 there is a silicon errata that will sometimes leave the
156 * WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate
157 * that it fired and we can clear it by writing a 1 back. Clear it to prevent
158 * interrupts from going off constantly.
160 * We run this code on all exynos variants because it doesn't hurt.
163 static int dw_mci_exynos_resume_noirq(struct device
*dev
)
165 struct dw_mci
*host
= dev_get_drvdata(dev
);
166 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
169 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
170 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
171 clksel
= mci_readl(host
, CLKSEL64
);
173 clksel
= mci_readl(host
, CLKSEL
);
175 if (clksel
& SDMMC_CLKSEL_WAKEUP_INT
) {
176 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
177 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
178 mci_writel(host
, CLKSEL64
, clksel
);
180 mci_writel(host
, CLKSEL
, clksel
);
186 #define dw_mci_exynos_suspend NULL
187 #define dw_mci_exynos_resume NULL
188 #define dw_mci_exynos_resume_noirq NULL
189 #endif /* CONFIG_PM_SLEEP */
191 static void dw_mci_exynos_prepare_command(struct dw_mci
*host
, u32
*cmdr
)
193 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
195 * Exynos4412 and Exynos5250 extends the use of CMD register with the
196 * use of bit 29 (which is reserved on standard MSHC controllers) for
197 * optionally bypassing the HOLD register for command and data. The
198 * HOLD register should be bypassed in case there is no phase shift
199 * applied on CMD/DATA that is sent to the card.
201 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
202 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
) {
203 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host
, CLKSEL64
)))
204 *cmdr
|= SDMMC_CMD_USE_HOLD_REG
;
206 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host
, CLKSEL
)))
207 *cmdr
|= SDMMC_CMD_USE_HOLD_REG
;
211 static void dw_mci_exynos_set_ios(struct dw_mci
*host
, struct mmc_ios
*ios
)
213 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
214 unsigned int wanted
= ios
->clock
;
215 unsigned long actual
;
216 u8 div
= priv
->ciu_div
+ 1;
218 if (ios
->timing
== MMC_TIMING_MMC_DDR52
) {
219 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
220 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
221 mci_writel(host
, CLKSEL64
, priv
->ddr_timing
);
223 mci_writel(host
, CLKSEL
, priv
->ddr_timing
);
224 /* Should be double rate for DDR mode */
225 if (ios
->bus_width
== MMC_BUS_WIDTH_8
)
228 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
229 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
230 mci_writel(host
, CLKSEL64
, priv
->sdr_timing
);
232 mci_writel(host
, CLKSEL
, priv
->sdr_timing
);
235 /* Don't care if wanted clock is zero */
239 /* Guaranteed minimum frequency for cclkin */
240 if (wanted
< EXYNOS_CCLKIN_MIN
)
241 wanted
= EXYNOS_CCLKIN_MIN
;
243 if (wanted
!= priv
->cur_speed
) {
244 int ret
= clk_set_rate(host
->ciu_clk
, wanted
* div
);
247 "failed to set clk-rate %u error: %d\n",
249 actual
= clk_get_rate(host
->ciu_clk
);
250 host
->bus_hz
= actual
/ div
;
251 priv
->cur_speed
= wanted
;
252 host
->current_speed
= 0;
256 static int dw_mci_exynos_parse_dt(struct dw_mci
*host
)
258 struct dw_mci_exynos_priv_data
*priv
;
259 struct device_node
*np
= host
->dev
->of_node
;
265 priv
= devm_kzalloc(host
->dev
, sizeof(*priv
), GFP_KERNEL
);
267 dev_err(host
->dev
, "mem alloc failed for private data\n");
271 for (idx
= 0; idx
< ARRAY_SIZE(exynos_compat
); idx
++) {
272 if (of_device_is_compatible(np
, exynos_compat
[idx
].compatible
))
273 priv
->ctrl_type
= exynos_compat
[idx
].ctrl_type
;
276 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS4412
)
277 priv
->ciu_div
= EXYNOS4412_FIXED_CIU_CLK_DIV
- 1;
278 else if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS4210
)
279 priv
->ciu_div
= EXYNOS4210_FIXED_CIU_CLK_DIV
- 1;
281 of_property_read_u32(np
, "samsung,dw-mshc-ciu-div", &div
);
285 ret
= of_property_read_u32_array(np
,
286 "samsung,dw-mshc-sdr-timing", timing
, 2);
290 priv
->sdr_timing
= SDMMC_CLKSEL_TIMING(timing
[0], timing
[1], div
);
292 ret
= of_property_read_u32_array(np
,
293 "samsung,dw-mshc-ddr-timing", timing
, 2);
297 priv
->ddr_timing
= SDMMC_CLKSEL_TIMING(timing
[0], timing
[1], div
);
302 static inline u8
dw_mci_exynos_get_clksmpl(struct dw_mci
*host
)
304 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
306 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
307 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
308 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host
, CLKSEL64
));
310 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host
, CLKSEL
));
313 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci
*host
, u8 sample
)
316 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
318 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
319 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
320 clksel
= mci_readl(host
, CLKSEL64
);
322 clksel
= mci_readl(host
, CLKSEL
);
323 clksel
= (clksel
& ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample
);
324 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
325 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
326 mci_writel(host
, CLKSEL64
, clksel
);
328 mci_writel(host
, CLKSEL
, clksel
);
331 static inline u8
dw_mci_exynos_move_next_clksmpl(struct dw_mci
*host
)
333 struct dw_mci_exynos_priv_data
*priv
= host
->priv
;
337 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
338 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
339 clksel
= mci_readl(host
, CLKSEL64
);
341 clksel
= mci_readl(host
, CLKSEL
);
342 sample
= (clksel
+ 1) & 0x7;
343 clksel
= (clksel
& ~0x7) | sample
;
344 if (priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7
||
345 priv
->ctrl_type
== DW_MCI_TYPE_EXYNOS7_SMU
)
346 mci_writel(host
, CLKSEL64
, clksel
);
348 mci_writel(host
, CLKSEL
, clksel
);
352 static s8
dw_mci_exynos_get_best_clksmpl(u8 candiates
)
358 for (i
= 0; i
< iter
; i
++) {
359 __c
= ror8(candiates
, i
);
360 if ((__c
& 0xc7) == 0xc7) {
366 for (i
= 0; i
< iter
; i
++) {
367 __c
= ror8(candiates
, i
);
368 if ((__c
& 0x83) == 0x83) {
378 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot
*slot
, u32 opcode
,
379 struct dw_mci_tuning_data
*tuning_data
)
381 struct dw_mci
*host
= slot
->host
;
382 struct mmc_host
*mmc
= slot
->mmc
;
383 const u8
*blk_pattern
= tuning_data
->blk_pattern
;
385 unsigned int blksz
= tuning_data
->blksz
;
386 u8 start_smpl
, smpl
, candiates
= 0;
390 blk_test
= kmalloc(blksz
, GFP_KERNEL
);
394 start_smpl
= dw_mci_exynos_get_clksmpl(host
);
397 struct mmc_request mrq
= {NULL
};
398 struct mmc_command cmd
= {0};
399 struct mmc_command stop
= {0};
400 struct mmc_data data
= {0};
401 struct scatterlist sg
;
405 cmd
.flags
= MMC_RSP_R1
| MMC_CMD_ADTC
;
407 stop
.opcode
= MMC_STOP_TRANSMISSION
;
409 stop
.flags
= MMC_RSP_R1B
| MMC_CMD_AC
;
413 data
.flags
= MMC_DATA_READ
;
417 sg_init_one(&sg
, blk_test
, blksz
);
423 mci_writel(host
, TMOUT
, ~0);
424 smpl
= dw_mci_exynos_move_next_clksmpl(host
);
426 mmc_wait_for_req(mmc
, &mrq
);
428 if (!cmd
.error
&& !data
.error
) {
429 if (!memcmp(blk_pattern
, blk_test
, blksz
))
430 candiates
|= (1 << smpl
);
433 "Tuning error: cmd.error:%d, data.error:%d\n",
434 cmd
.error
, data
.error
);
436 } while (start_smpl
!= smpl
);
438 found
= dw_mci_exynos_get_best_clksmpl(candiates
);
440 dw_mci_exynos_set_clksmpl(host
, found
);
448 /* Common capabilities of Exynos4/Exynos5 SoC */
449 static unsigned long exynos_dwmmc_caps
[4] = {
450 MMC_CAP_1_8V_DDR
| MMC_CAP_8_BIT_DATA
| MMC_CAP_CMD23
,
456 static const struct dw_mci_drv_data exynos_drv_data
= {
457 .caps
= exynos_dwmmc_caps
,
458 .init
= dw_mci_exynos_priv_init
,
459 .setup_clock
= dw_mci_exynos_setup_clock
,
460 .prepare_command
= dw_mci_exynos_prepare_command
,
461 .set_ios
= dw_mci_exynos_set_ios
,
462 .parse_dt
= dw_mci_exynos_parse_dt
,
463 .execute_tuning
= dw_mci_exynos_execute_tuning
,
466 static const struct of_device_id dw_mci_exynos_match
[] = {
467 { .compatible
= "samsung,exynos4412-dw-mshc",
468 .data
= &exynos_drv_data
, },
469 { .compatible
= "samsung,exynos5250-dw-mshc",
470 .data
= &exynos_drv_data
, },
471 { .compatible
= "samsung,exynos5420-dw-mshc",
472 .data
= &exynos_drv_data
, },
473 { .compatible
= "samsung,exynos5420-dw-mshc-smu",
474 .data
= &exynos_drv_data
, },
475 { .compatible
= "samsung,exynos7-dw-mshc",
476 .data
= &exynos_drv_data
, },
477 { .compatible
= "samsung,exynos7-dw-mshc-smu",
478 .data
= &exynos_drv_data
, },
481 MODULE_DEVICE_TABLE(of
, dw_mci_exynos_match
);
483 static int dw_mci_exynos_probe(struct platform_device
*pdev
)
485 const struct dw_mci_drv_data
*drv_data
;
486 const struct of_device_id
*match
;
488 match
= of_match_node(dw_mci_exynos_match
, pdev
->dev
.of_node
);
489 drv_data
= match
->data
;
490 return dw_mci_pltfm_register(pdev
, drv_data
);
493 static const struct dev_pm_ops dw_mci_exynos_pmops
= {
494 SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend
, dw_mci_exynos_resume
)
495 .resume_noirq
= dw_mci_exynos_resume_noirq
,
496 .thaw_noirq
= dw_mci_exynos_resume_noirq
,
497 .restore_noirq
= dw_mci_exynos_resume_noirq
,
500 static struct platform_driver dw_mci_exynos_pltfm_driver
= {
501 .probe
= dw_mci_exynos_probe
,
502 .remove
= __exit_p(dw_mci_pltfm_remove
),
504 .name
= "dwmmc_exynos",
505 .of_match_table
= dw_mci_exynos_match
,
506 .pm
= &dw_mci_exynos_pmops
,
510 module_platform_driver(dw_mci_exynos_pltfm_driver
);
512 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
513 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
514 MODULE_LICENSE("GPL v2");
515 MODULE_ALIAS("platform:dwmmc-exynos");