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 #include "dw_mmc-exynos.h" 25 26 /* Variations in Exynos specific dw-mshc controller */ 27 enum dw_mci_exynos_type { 28 DW_MCI_TYPE_EXYNOS4210, 29 DW_MCI_TYPE_EXYNOS4412, 30 DW_MCI_TYPE_EXYNOS5250, 31 DW_MCI_TYPE_EXYNOS5420, 32 DW_MCI_TYPE_EXYNOS5420_SMU, 33 DW_MCI_TYPE_EXYNOS7, 34 DW_MCI_TYPE_EXYNOS7_SMU, 35 }; 36 37 /* Exynos implementation specific driver private data */ 38 struct dw_mci_exynos_priv_data { 39 enum dw_mci_exynos_type ctrl_type; 40 u8 ciu_div; 41 u32 sdr_timing; 42 u32 ddr_timing; 43 u32 cur_speed; 44 }; 45 46 static struct dw_mci_exynos_compatible { 47 char *compatible; 48 enum dw_mci_exynos_type ctrl_type; 49 } exynos_compat[] = { 50 { 51 .compatible = "samsung,exynos4210-dw-mshc", 52 .ctrl_type = DW_MCI_TYPE_EXYNOS4210, 53 }, { 54 .compatible = "samsung,exynos4412-dw-mshc", 55 .ctrl_type = DW_MCI_TYPE_EXYNOS4412, 56 }, { 57 .compatible = "samsung,exynos5250-dw-mshc", 58 .ctrl_type = DW_MCI_TYPE_EXYNOS5250, 59 }, { 60 .compatible = "samsung,exynos5420-dw-mshc", 61 .ctrl_type = DW_MCI_TYPE_EXYNOS5420, 62 }, { 63 .compatible = "samsung,exynos5420-dw-mshc-smu", 64 .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, 65 }, { 66 .compatible = "samsung,exynos7-dw-mshc", 67 .ctrl_type = DW_MCI_TYPE_EXYNOS7, 68 }, { 69 .compatible = "samsung,exynos7-dw-mshc-smu", 70 .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU, 71 }, 72 }; 73 74 static int dw_mci_exynos_priv_init(struct dw_mci *host) 75 { 76 struct dw_mci_exynos_priv_data *priv = host->priv; 77 78 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || 79 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { 80 mci_writel(host, MPSBEGIN0, 0); 81 mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX); 82 mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT | 83 SDMMC_MPSCTRL_NON_SECURE_READ_BIT | 84 SDMMC_MPSCTRL_VALID | 85 SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT); 86 } 87 88 return 0; 89 } 90 91 static int dw_mci_exynos_setup_clock(struct dw_mci *host) 92 { 93 struct dw_mci_exynos_priv_data *priv = host->priv; 94 95 host->bus_hz /= (priv->ciu_div + 1); 96 97 return 0; 98 } 99 100 #ifdef CONFIG_PM_SLEEP 101 static int dw_mci_exynos_suspend(struct device *dev) 102 { 103 struct dw_mci *host = dev_get_drvdata(dev); 104 105 return dw_mci_suspend(host); 106 } 107 108 static int dw_mci_exynos_resume(struct device *dev) 109 { 110 struct dw_mci *host = dev_get_drvdata(dev); 111 112 dw_mci_exynos_priv_init(host); 113 return dw_mci_resume(host); 114 } 115 116 /** 117 * dw_mci_exynos_resume_noirq - Exynos-specific resume code 118 * 119 * On exynos5420 there is a silicon errata that will sometimes leave the 120 * WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate 121 * that it fired and we can clear it by writing a 1 back. Clear it to prevent 122 * interrupts from going off constantly. 123 * 124 * We run this code on all exynos variants because it doesn't hurt. 125 */ 126 127 static int dw_mci_exynos_resume_noirq(struct device *dev) 128 { 129 struct dw_mci *host = dev_get_drvdata(dev); 130 struct dw_mci_exynos_priv_data *priv = host->priv; 131 u32 clksel; 132 133 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 134 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 135 clksel = mci_readl(host, CLKSEL64); 136 else 137 clksel = mci_readl(host, CLKSEL); 138 139 if (clksel & SDMMC_CLKSEL_WAKEUP_INT) { 140 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 141 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 142 mci_writel(host, CLKSEL64, clksel); 143 else 144 mci_writel(host, CLKSEL, clksel); 145 } 146 147 return 0; 148 } 149 #else 150 #define dw_mci_exynos_suspend NULL 151 #define dw_mci_exynos_resume NULL 152 #define dw_mci_exynos_resume_noirq NULL 153 #endif /* CONFIG_PM_SLEEP */ 154 155 static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) 156 { 157 struct dw_mci_exynos_priv_data *priv = host->priv; 158 /* 159 * Exynos4412 and Exynos5250 extends the use of CMD register with the 160 * use of bit 29 (which is reserved on standard MSHC controllers) for 161 * optionally bypassing the HOLD register for command and data. The 162 * HOLD register should be bypassed in case there is no phase shift 163 * applied on CMD/DATA that is sent to the card. 164 */ 165 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 166 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { 167 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64))) 168 *cmdr |= SDMMC_CMD_USE_HOLD_REG; 169 } else { 170 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) 171 *cmdr |= SDMMC_CMD_USE_HOLD_REG; 172 } 173 } 174 175 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) 176 { 177 struct dw_mci_exynos_priv_data *priv = host->priv; 178 unsigned int wanted = ios->clock; 179 unsigned long actual; 180 u8 div = priv->ciu_div + 1; 181 182 if (ios->timing == MMC_TIMING_MMC_DDR52) { 183 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 184 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 185 mci_writel(host, CLKSEL64, priv->ddr_timing); 186 else 187 mci_writel(host, CLKSEL, priv->ddr_timing); 188 /* Should be double rate for DDR mode */ 189 if (ios->bus_width == MMC_BUS_WIDTH_8) 190 wanted <<= 1; 191 } else { 192 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 193 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 194 mci_writel(host, CLKSEL64, priv->sdr_timing); 195 else 196 mci_writel(host, CLKSEL, priv->sdr_timing); 197 } 198 199 /* 200 * Don't care if wanted clock is zero or 201 * ciu clock is unavailable 202 */ 203 if (!wanted || IS_ERR(host->ciu_clk)) 204 return; 205 206 /* Guaranteed minimum frequency for cclkin */ 207 if (wanted < EXYNOS_CCLKIN_MIN) 208 wanted = EXYNOS_CCLKIN_MIN; 209 210 if (wanted != priv->cur_speed) { 211 int ret = clk_set_rate(host->ciu_clk, wanted * div); 212 if (ret) 213 dev_warn(host->dev, 214 "failed to set clk-rate %u error: %d\n", 215 wanted * div, ret); 216 actual = clk_get_rate(host->ciu_clk); 217 host->bus_hz = actual / div; 218 priv->cur_speed = wanted; 219 host->current_speed = 0; 220 } 221 } 222 223 static int dw_mci_exynos_parse_dt(struct dw_mci *host) 224 { 225 struct dw_mci_exynos_priv_data *priv; 226 struct device_node *np = host->dev->of_node; 227 u32 timing[2]; 228 u32 div = 0; 229 int idx; 230 int ret; 231 232 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); 233 if (!priv) 234 return -ENOMEM; 235 236 for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { 237 if (of_device_is_compatible(np, exynos_compat[idx].compatible)) 238 priv->ctrl_type = exynos_compat[idx].ctrl_type; 239 } 240 241 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) 242 priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1; 243 else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) 244 priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1; 245 else { 246 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); 247 priv->ciu_div = div; 248 } 249 250 ret = of_property_read_u32_array(np, 251 "samsung,dw-mshc-sdr-timing", timing, 2); 252 if (ret) 253 return ret; 254 255 priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); 256 257 ret = of_property_read_u32_array(np, 258 "samsung,dw-mshc-ddr-timing", timing, 2); 259 if (ret) 260 return ret; 261 262 priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); 263 host->priv = priv; 264 return 0; 265 } 266 267 static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) 268 { 269 struct dw_mci_exynos_priv_data *priv = host->priv; 270 271 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 272 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 273 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64)); 274 else 275 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); 276 } 277 278 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) 279 { 280 u32 clksel; 281 struct dw_mci_exynos_priv_data *priv = host->priv; 282 283 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 284 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 285 clksel = mci_readl(host, CLKSEL64); 286 else 287 clksel = mci_readl(host, CLKSEL); 288 clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); 289 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 290 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 291 mci_writel(host, CLKSEL64, clksel); 292 else 293 mci_writel(host, CLKSEL, clksel); 294 } 295 296 static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) 297 { 298 struct dw_mci_exynos_priv_data *priv = host->priv; 299 u32 clksel; 300 u8 sample; 301 302 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 303 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 304 clksel = mci_readl(host, CLKSEL64); 305 else 306 clksel = mci_readl(host, CLKSEL); 307 sample = (clksel + 1) & 0x7; 308 clksel = (clksel & ~0x7) | sample; 309 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || 310 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) 311 mci_writel(host, CLKSEL64, clksel); 312 else 313 mci_writel(host, CLKSEL, clksel); 314 return sample; 315 } 316 317 static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) 318 { 319 const u8 iter = 8; 320 u8 __c; 321 s8 i, loc = -1; 322 323 for (i = 0; i < iter; i++) { 324 __c = ror8(candiates, i); 325 if ((__c & 0xc7) == 0xc7) { 326 loc = i; 327 goto out; 328 } 329 } 330 331 for (i = 0; i < iter; i++) { 332 __c = ror8(candiates, i); 333 if ((__c & 0x83) == 0x83) { 334 loc = i; 335 goto out; 336 } 337 } 338 339 out: 340 return loc; 341 } 342 343 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) 344 { 345 struct dw_mci *host = slot->host; 346 struct mmc_host *mmc = slot->mmc; 347 u8 start_smpl, smpl, candiates = 0; 348 s8 found = -1; 349 int ret = 0; 350 351 start_smpl = dw_mci_exynos_get_clksmpl(host); 352 353 do { 354 mci_writel(host, TMOUT, ~0); 355 smpl = dw_mci_exynos_move_next_clksmpl(host); 356 357 if (!mmc_send_tuning(mmc)) 358 candiates |= (1 << smpl); 359 360 } while (start_smpl != smpl); 361 362 found = dw_mci_exynos_get_best_clksmpl(candiates); 363 if (found >= 0) 364 dw_mci_exynos_set_clksmpl(host, found); 365 else 366 ret = -EIO; 367 368 return ret; 369 } 370 371 /* Common capabilities of Exynos4/Exynos5 SoC */ 372 static unsigned long exynos_dwmmc_caps[4] = { 373 MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23, 374 MMC_CAP_CMD23, 375 MMC_CAP_CMD23, 376 MMC_CAP_CMD23, 377 }; 378 379 static const struct dw_mci_drv_data exynos_drv_data = { 380 .caps = exynos_dwmmc_caps, 381 .init = dw_mci_exynos_priv_init, 382 .setup_clock = dw_mci_exynos_setup_clock, 383 .prepare_command = dw_mci_exynos_prepare_command, 384 .set_ios = dw_mci_exynos_set_ios, 385 .parse_dt = dw_mci_exynos_parse_dt, 386 .execute_tuning = dw_mci_exynos_execute_tuning, 387 }; 388 389 static const struct of_device_id dw_mci_exynos_match[] = { 390 { .compatible = "samsung,exynos4412-dw-mshc", 391 .data = &exynos_drv_data, }, 392 { .compatible = "samsung,exynos5250-dw-mshc", 393 .data = &exynos_drv_data, }, 394 { .compatible = "samsung,exynos5420-dw-mshc", 395 .data = &exynos_drv_data, }, 396 { .compatible = "samsung,exynos5420-dw-mshc-smu", 397 .data = &exynos_drv_data, }, 398 { .compatible = "samsung,exynos7-dw-mshc", 399 .data = &exynos_drv_data, }, 400 { .compatible = "samsung,exynos7-dw-mshc-smu", 401 .data = &exynos_drv_data, }, 402 {}, 403 }; 404 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); 405 406 static int dw_mci_exynos_probe(struct platform_device *pdev) 407 { 408 const struct dw_mci_drv_data *drv_data; 409 const struct of_device_id *match; 410 411 match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node); 412 drv_data = match->data; 413 return dw_mci_pltfm_register(pdev, drv_data); 414 } 415 416 static const struct dev_pm_ops dw_mci_exynos_pmops = { 417 SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume) 418 .resume_noirq = dw_mci_exynos_resume_noirq, 419 .thaw_noirq = dw_mci_exynos_resume_noirq, 420 .restore_noirq = dw_mci_exynos_resume_noirq, 421 }; 422 423 static struct platform_driver dw_mci_exynos_pltfm_driver = { 424 .probe = dw_mci_exynos_probe, 425 .remove = dw_mci_pltfm_remove, 426 .driver = { 427 .name = "dwmmc_exynos", 428 .of_match_table = dw_mci_exynos_match, 429 .pm = &dw_mci_exynos_pmops, 430 }, 431 }; 432 433 module_platform_driver(dw_mci_exynos_pltfm_driver); 434 435 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension"); 436 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com"); 437 MODULE_LICENSE("GPL v2"); 438 MODULE_ALIAS("platform:dwmmc-exynos"); 439