1 /* 2 * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver 3 * 4 * Copyright (C) 2012, Samsung Electronics Co., Ltd. 5 * 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. 10 */ 11 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> 18 #include <linux/of.h> 19 #include <linux/of_gpio.h> 20 #include <linux/slab.h> 21 22 #include "dw_mmc.h" 23 #include "dw_mmc-pltfm.h" 24 25 #define NUM_PINS(x) (x + 2) 26 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) 37 38 #define EXYNOS4210_FIXED_CIU_CLK_DIV 2 39 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4 40 41 /* Block number in eMMC */ 42 #define DWMCI_BLOCK_NUM 0xFFFFFFFF 43 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) 49 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) 59 60 #define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */ 61 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, 69 DW_MCI_TYPE_EXYNOS7, 70 DW_MCI_TYPE_EXYNOS7_SMU, 71 }; 72 73 /* Exynos implementation specific driver private data */ 74 struct dw_mci_exynos_priv_data { 75 enum dw_mci_exynos_type ctrl_type; 76 u8 ciu_div; 77 u32 sdr_timing; 78 u32 ddr_timing; 79 u32 cur_speed; 80 }; 81 82 static struct dw_mci_exynos_compatible { 83 char *compatible; 84 enum dw_mci_exynos_type ctrl_type; 85 } exynos_compat[] = { 86 { 87 .compatible = "samsung,exynos4210-dw-mshc", 88 .ctrl_type = DW_MCI_TYPE_EXYNOS4210, 89 }, { 90 .compatible = "samsung,exynos4412-dw-mshc", 91 .ctrl_type = DW_MCI_TYPE_EXYNOS4412, 92 }, { 93 .compatible = "samsung,exynos5250-dw-mshc", 94 .ctrl_type = DW_MCI_TYPE_EXYNOS5250, 95 }, { 96 .compatible = "samsung,exynos5420-dw-mshc", 97 .ctrl_type = DW_MCI_TYPE_EXYNOS5420, 98 }, { 99 .compatible = "samsung,exynos5420-dw-mshc-smu", 100 .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, 101 }, { 102 .compatible = "samsung,exynos7-dw-mshc", 103 .ctrl_type = DW_MCI_TYPE_EXYNOS7, 104 }, { 105 .compatible = "samsung,exynos7-dw-mshc-smu", 106 .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU, 107 }, 108 }; 109 110 static int dw_mci_exynos_priv_init(struct dw_mci *host) 111 { 112 struct dw_mci_exynos_priv_data *priv = host->priv; 113 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); 122 } 123 124 return 0; 125 } 126 127 static int dw_mci_exynos_setup_clock(struct dw_mci *host) 128 { 129 struct dw_mci_exynos_priv_data *priv = host->priv; 130 unsigned long rate = clk_get_rate(host->ciu_clk); 131 132 host->bus_hz = rate / (priv->ciu_div + 1); 133 return 0; 134 } 135 136 #ifdef CONFIG_PM_SLEEP 137 static int dw_mci_exynos_suspend(struct device *dev) 138 { 139 struct dw_mci *host = dev_get_drvdata(dev); 140 141 return dw_mci_suspend(host); 142 } 143 144 static int dw_mci_exynos_resume(struct device *dev) 145 { 146 struct dw_mci *host = dev_get_drvdata(dev); 147 148 dw_mci_exynos_priv_init(host); 149 return dw_mci_resume(host); 150 } 151 152 /** 153 * dw_mci_exynos_resume_noirq - Exynos-specific resume code 154 * 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. 159 * 160 * We run this code on all exynos variants because it doesn't hurt. 161 */ 162 163 static int dw_mci_exynos_resume_noirq(struct device *dev) 164 { 165 struct dw_mci *host = dev_get_drvdata(dev); 166 struct dw_mci_exynos_priv_data *priv = host->priv; 167 u32 clksel; 168 169 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 170 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 171 clksel = mci_readl(host, CLKSEL64); 172 else 173 clksel = mci_readl(host, CLKSEL); 174 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); 179 else 180 mci_writel(host, CLKSEL, clksel); 181 } 182 183 return 0; 184 } 185 #else 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 */ 190 191 static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) 192 { 193 struct dw_mci_exynos_priv_data *priv = host->priv; 194 /* 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. 200 */ 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; 205 } else { 206 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) 207 *cmdr |= SDMMC_CMD_USE_HOLD_REG; 208 } 209 } 210 211 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) 212 { 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; 217 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); 222 else 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) 226 wanted <<= 1; 227 } else { 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); 231 else 232 mci_writel(host, CLKSEL, priv->sdr_timing); 233 } 234 235 /* Don't care if wanted clock is zero */ 236 if (!wanted) 237 return; 238 239 /* Guaranteed minimum frequency for cclkin */ 240 if (wanted < EXYNOS_CCLKIN_MIN) 241 wanted = EXYNOS_CCLKIN_MIN; 242 243 if (wanted != priv->cur_speed) { 244 int ret = clk_set_rate(host->ciu_clk, wanted * div); 245 if (ret) 246 dev_warn(host->dev, 247 "failed to set clk-rate %u error: %d\n", 248 wanted * div, ret); 249 actual = clk_get_rate(host->ciu_clk); 250 host->bus_hz = actual / div; 251 priv->cur_speed = wanted; 252 host->current_speed = 0; 253 } 254 } 255 256 static int dw_mci_exynos_parse_dt(struct dw_mci *host) 257 { 258 struct dw_mci_exynos_priv_data *priv; 259 struct device_node *np = host->dev->of_node; 260 u32 timing[2]; 261 u32 div = 0; 262 int idx; 263 int ret; 264 265 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); 266 if (!priv) { 267 dev_err(host->dev, "mem alloc failed for private data\n"); 268 return -ENOMEM; 269 } 270 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; 274 } 275 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; 280 else { 281 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); 282 priv->ciu_div = div; 283 } 284 285 ret = of_property_read_u32_array(np, 286 "samsung,dw-mshc-sdr-timing", timing, 2); 287 if (ret) 288 return ret; 289 290 priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); 291 292 ret = of_property_read_u32_array(np, 293 "samsung,dw-mshc-ddr-timing", timing, 2); 294 if (ret) 295 return ret; 296 297 priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); 298 host->priv = priv; 299 return 0; 300 } 301 302 static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) 303 { 304 struct dw_mci_exynos_priv_data *priv = host->priv; 305 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)); 309 else 310 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); 311 } 312 313 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) 314 { 315 u32 clksel; 316 struct dw_mci_exynos_priv_data *priv = host->priv; 317 318 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 319 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 320 clksel = mci_readl(host, CLKSEL64); 321 else 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); 327 else 328 mci_writel(host, CLKSEL, clksel); 329 } 330 331 static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) 332 { 333 struct dw_mci_exynos_priv_data *priv = host->priv; 334 u32 clksel; 335 u8 sample; 336 337 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 338 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 339 clksel = mci_readl(host, CLKSEL64); 340 else 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); 347 else 348 mci_writel(host, CLKSEL, clksel); 349 return sample; 350 } 351 352 static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) 353 { 354 const u8 iter = 8; 355 u8 __c; 356 s8 i, loc = -1; 357 358 for (i = 0; i < iter; i++) { 359 __c = ror8(candiates, i); 360 if ((__c & 0xc7) == 0xc7) { 361 loc = i; 362 goto out; 363 } 364 } 365 366 for (i = 0; i < iter; i++) { 367 __c = ror8(candiates, i); 368 if ((__c & 0x83) == 0x83) { 369 loc = i; 370 goto out; 371 } 372 } 373 374 out: 375 return loc; 376 } 377 378 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, 379 struct dw_mci_tuning_data *tuning_data) 380 { 381 struct dw_mci *host = slot->host; 382 struct mmc_host *mmc = slot->mmc; 383 const u8 *blk_pattern = tuning_data->blk_pattern; 384 u8 *blk_test; 385 unsigned int blksz = tuning_data->blksz; 386 u8 start_smpl, smpl, candiates = 0; 387 s8 found = -1; 388 int ret = 0; 389 390 blk_test = kmalloc(blksz, GFP_KERNEL); 391 if (!blk_test) 392 return -ENOMEM; 393 394 start_smpl = dw_mci_exynos_get_clksmpl(host); 395 396 do { 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; 402 403 cmd.opcode = opcode; 404 cmd.arg = 0; 405 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; 406 407 stop.opcode = MMC_STOP_TRANSMISSION; 408 stop.arg = 0; 409 stop.flags = MMC_RSP_R1B | MMC_CMD_AC; 410 411 data.blksz = blksz; 412 data.blocks = 1; 413 data.flags = MMC_DATA_READ; 414 data.sg = &sg; 415 data.sg_len = 1; 416 417 sg_init_one(&sg, blk_test, blksz); 418 mrq.cmd = &cmd; 419 mrq.stop = &stop; 420 mrq.data = &data; 421 host->mrq = &mrq; 422 423 mci_writel(host, TMOUT, ~0); 424 smpl = dw_mci_exynos_move_next_clksmpl(host); 425 426 mmc_wait_for_req(mmc, &mrq); 427 428 if (!cmd.error && !data.error) { 429 if (!memcmp(blk_pattern, blk_test, blksz)) 430 candiates |= (1 << smpl); 431 } else { 432 dev_dbg(host->dev, 433 "Tuning error: cmd.error:%d, data.error:%d\n", 434 cmd.error, data.error); 435 } 436 } while (start_smpl != smpl); 437 438 found = dw_mci_exynos_get_best_clksmpl(candiates); 439 if (found >= 0) 440 dw_mci_exynos_set_clksmpl(host, found); 441 else 442 ret = -EIO; 443 444 kfree(blk_test); 445 return ret; 446 } 447 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, 451 MMC_CAP_CMD23, 452 MMC_CAP_CMD23, 453 MMC_CAP_CMD23, 454 }; 455 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, 464 }; 465 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, }, 479 {}, 480 }; 481 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); 482 483 static int dw_mci_exynos_probe(struct platform_device *pdev) 484 { 485 const struct dw_mci_drv_data *drv_data; 486 const struct of_device_id *match; 487 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); 491 } 492 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, 498 }; 499 500 static struct platform_driver dw_mci_exynos_pltfm_driver = { 501 .probe = dw_mci_exynos_probe, 502 .remove = __exit_p(dw_mci_pltfm_remove), 503 .driver = { 504 .name = "dwmmc_exynos", 505 .of_match_table = dw_mci_exynos_match, 506 .pm = &dw_mci_exynos_pmops, 507 }, 508 }; 509 510 module_platform_driver(dw_mci_exynos_pltfm_driver); 511 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"); 516