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