]>
Commit | Line | Data |
---|---|---|
90c62bf0 | 1 | /* |
d02a900b | 2 | * linux/arch/arm/mach-omap2/hsmmc.c |
90c62bf0 TL |
3 | * |
4 | * Copyright (C) 2007-2008 Texas Instruments | |
5 | * Copyright (C) 2008 Nokia Corporation | |
6 | * Author: Texas Instruments | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
db0fefc5 AH |
12 | #include <linux/kernel.h> |
13 | #include <linux/slab.h> | |
14 | #include <linux/string.h> | |
90c62bf0 | 15 | #include <linux/delay.h> |
5e4698fc | 16 | #include <linux/gpio.h> |
826c71a0 | 17 | #include <linux/mmc/host.h> |
4b25408f | 18 | #include <linux/platform_data/gpio-omap.h> |
55143438 | 19 | #include <linux/platform_data/hsmmc-omap.h> |
4b25408f | 20 | |
e4c060db | 21 | #include "soc.h" |
25c7d49e | 22 | #include "omap_device.h" |
1d5aef49 | 23 | #include "omap-pm.h" |
90c62bf0 | 24 | |
d02a900b | 25 | #include "hsmmc.h" |
4814ced5 | 26 | #include "control.h" |
90c62bf0 | 27 | |
502ad2a6 | 28 | #if IS_ENABLED(CONFIG_MMC_OMAP_HS) |
90c62bf0 TL |
29 | |
30 | static u16 control_pbias_offset; | |
31 | static u16 control_devconf1_offset; | |
32 | ||
33 | #define HSMMC_NAME_LEN 9 | |
34 | ||
80412ca8 AF |
35 | static void omap_hsmmc1_before_set_reg(struct device *dev, |
36 | int power_on, int vdd) | |
90c62bf0 | 37 | { |
555d503f | 38 | u32 reg, prog_io; |
55143438 | 39 | struct omap_hsmmc_platform_data *mmc = dev->platform_data; |
90c62bf0 | 40 | |
326119c9 | 41 | if (mmc->remux) |
80412ca8 | 42 | mmc->remux(dev, power_on); |
ce6f0016 | 43 | |
0329c377 DB |
44 | /* |
45 | * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the | |
b583f26d | 46 | * card with Vcc regulator (from twl4030 or whatever). OMAP has both |
0329c377 DB |
47 | * 1.8V and 3.0V modes, controlled by the PBIAS register. |
48 | * | |
49 | * In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which | |
50 | * is most naturally TWL VSIM; those pins also use PBIAS. | |
b583f26d DB |
51 | * |
52 | * FIXME handle VMMC1A as needed ... | |
0329c377 | 53 | */ |
90c62bf0 TL |
54 | if (power_on) { |
55 | if (cpu_is_omap2430()) { | |
56 | reg = omap_ctrl_readl(OMAP243X_CONTROL_DEVCONF1); | |
57 | if ((1 << vdd) >= MMC_VDD_30_31) | |
58 | reg |= OMAP243X_MMC1_ACTIVE_OVERWRITE; | |
59 | else | |
60 | reg &= ~OMAP243X_MMC1_ACTIVE_OVERWRITE; | |
61 | omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1); | |
62 | } | |
63 | ||
326119c9 | 64 | if (mmc->internal_clock) { |
90c62bf0 TL |
65 | reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); |
66 | reg |= OMAP2_MMCSDIO1ADPCLKISEL; | |
67 | omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0); | |
68 | } | |
69 | ||
70 | reg = omap_ctrl_readl(control_pbias_offset); | |
555d503f | 71 | if (cpu_is_omap3630()) { |
6a53bc75 | 72 | /* Set MMC I/O to 52MHz */ |
555d503f M |
73 | prog_io = omap_ctrl_readl(OMAP343X_CONTROL_PROG_IO1); |
74 | prog_io |= OMAP3630_PRG_SDMMC1_SPEEDCTRL; | |
75 | omap_ctrl_writel(prog_io, OMAP343X_CONTROL_PROG_IO1); | |
76 | } else { | |
77 | reg |= OMAP2_PBIASSPEEDCTRL0; | |
78 | } | |
90c62bf0 TL |
79 | reg &= ~OMAP2_PBIASLITEPWRDNZ0; |
80 | omap_ctrl_writel(reg, control_pbias_offset); | |
db0fefc5 AH |
81 | } else { |
82 | reg = omap_ctrl_readl(control_pbias_offset); | |
83 | reg &= ~OMAP2_PBIASLITEPWRDNZ0; | |
84 | omap_ctrl_writel(reg, control_pbias_offset); | |
85 | } | |
86 | } | |
87 | ||
80412ca8 | 88 | static void omap_hsmmc1_after_set_reg(struct device *dev, int power_on, int vdd) |
db0fefc5 AH |
89 | { |
90 | u32 reg; | |
90c62bf0 | 91 | |
db0fefc5 AH |
92 | /* 100ms delay required for PBIAS configuration */ |
93 | msleep(100); | |
90c62bf0 | 94 | |
db0fefc5 | 95 | if (power_on) { |
90c62bf0 TL |
96 | reg = omap_ctrl_readl(control_pbias_offset); |
97 | reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0); | |
98 | if ((1 << vdd) <= MMC_VDD_165_195) | |
99 | reg &= ~OMAP2_PBIASLITEVMODE0; | |
100 | else | |
101 | reg |= OMAP2_PBIASLITEVMODE0; | |
102 | omap_ctrl_writel(reg, control_pbias_offset); | |
103 | } else { | |
90c62bf0 TL |
104 | reg = omap_ctrl_readl(control_pbias_offset); |
105 | reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 | | |
106 | OMAP2_PBIASLITEVMODE0); | |
107 | omap_ctrl_writel(reg, control_pbias_offset); | |
108 | } | |
90c62bf0 TL |
109 | } |
110 | ||
55143438 | 111 | static void hsmmc2_select_input_clk_src(struct omap_hsmmc_platform_data *mmc) |
e62245ba IG |
112 | { |
113 | u32 reg; | |
114 | ||
d82e5190 | 115 | reg = omap_ctrl_readl(control_devconf1_offset); |
326119c9 | 116 | if (mmc->internal_clock) |
e62245ba | 117 | reg |= OMAP2_MMCSDIO2ADPCLKISEL; |
d82e5190 GI |
118 | else |
119 | reg &= ~OMAP2_MMCSDIO2ADPCLKISEL; | |
120 | omap_ctrl_writel(reg, control_devconf1_offset); | |
e62245ba IG |
121 | } |
122 | ||
80412ca8 | 123 | static void hsmmc2_before_set_reg(struct device *dev, int power_on, int vdd) |
90c62bf0 | 124 | { |
55143438 | 125 | struct omap_hsmmc_platform_data *mmc = dev->platform_data; |
b583f26d | 126 | |
326119c9 | 127 | if (mmc->remux) |
80412ca8 | 128 | mmc->remux(dev, power_on); |
ce6f0016 | 129 | |
e62245ba IG |
130 | if (power_on) |
131 | hsmmc2_select_input_clk_src(mmc); | |
132 | } | |
90c62bf0 | 133 | |
80412ca8 | 134 | static int am35x_hsmmc2_set_power(struct device *dev, int power_on, int vdd) |
e62245ba | 135 | { |
55143438 | 136 | struct omap_hsmmc_platform_data *mmc = dev->platform_data; |
e62245ba IG |
137 | |
138 | if (power_on) | |
139 | hsmmc2_select_input_clk_src(mmc); | |
140 | ||
141 | return 0; | |
9b7c18e0 AH |
142 | } |
143 | ||
80412ca8 | 144 | static int nop_mmc_set_power(struct device *dev, int power_on, int vdd) |
03e7e170 | 145 | { |
146 | return 0; | |
147 | } | |
148 | ||
d1589f09 | 149 | static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c, |
55143438 | 150 | struct omap_hsmmc_platform_data *mmc) |
4621d5f8 KK |
151 | { |
152 | char *hc_name; | |
153 | ||
154 | hc_name = kzalloc(sizeof(char) * (HSMMC_NAME_LEN + 1), GFP_KERNEL); | |
155 | if (!hc_name) { | |
4621d5f8 KK |
156 | kfree(hc_name); |
157 | return -ENOMEM; | |
158 | } | |
159 | ||
160 | if (c->name) | |
161 | strncpy(hc_name, c->name, HSMMC_NAME_LEN); | |
162 | else | |
163 | snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i", | |
164 | c->mmc, 1); | |
326119c9 | 165 | mmc->name = hc_name; |
326119c9 AF |
166 | mmc->caps = c->caps; |
167 | mmc->internal_clock = !c->ext_clock; | |
b30e321b | 168 | mmc->reg_offset = 0; |
4621d5f8 | 169 | |
b7a5646f AF |
170 | if (c->cover_only) { |
171 | /* detect if mobile phone cover removed */ | |
172 | mmc->gpio_cd = -EINVAL; | |
173 | mmc->gpio_cod = c->gpio_cd; | |
174 | } else { | |
175 | /* card detect pin on the mmc socket itself */ | |
176 | mmc->gpio_cd = c->gpio_cd; | |
177 | mmc->gpio_cod = -EINVAL; | |
178 | } | |
326119c9 | 179 | mmc->gpio_wp = c->gpio_wp; |
4621d5f8 | 180 | |
326119c9 AF |
181 | mmc->remux = c->remux; |
182 | mmc->init_card = c->init_card; | |
4621d5f8 | 183 | |
4621d5f8 | 184 | if (c->nonremovable) |
326119c9 | 185 | mmc->nonremovable = 1; |
4621d5f8 | 186 | |
4621d5f8 KK |
187 | /* |
188 | * NOTE: MMC slots should have a Vcc regulator set up. | |
189 | * This may be from a TWL4030-family chip, another | |
190 | * controllable regulator, or a fixed supply. | |
191 | * | |
192 | * temporary HACK: ocr_mask instead of fixed supply | |
193 | */ | |
68a88b98 | 194 | if (soc_is_am35xx()) |
326119c9 | 195 | mmc->ocr_mask = MMC_VDD_165_195 | |
e89715a7 A |
196 | MMC_VDD_26_27 | |
197 | MMC_VDD_27_28 | | |
198 | MMC_VDD_29_30 | | |
199 | MMC_VDD_30_31 | | |
200 | MMC_VDD_31_32; | |
201 | else | |
326119c9 | 202 | mmc->ocr_mask = c->ocr_mask; |
4621d5f8 | 203 | |
68a88b98 | 204 | if (!soc_is_am35xx()) |
326119c9 | 205 | mmc->features |= HSMMC_HAS_PBIAS; |
4621d5f8 | 206 | |
4621d5f8 KK |
207 | switch (c->mmc) { |
208 | case 1: | |
326119c9 | 209 | if (mmc->features & HSMMC_HAS_PBIAS) { |
4621d5f8 | 210 | /* on-chip level shifting via PBIAS0/PBIAS1 */ |
326119c9 | 211 | mmc->before_set_reg = |
b30e321b | 212 | omap_hsmmc1_before_set_reg; |
326119c9 | 213 | mmc->after_set_reg = |
b30e321b | 214 | omap_hsmmc1_after_set_reg; |
4621d5f8 KK |
215 | } |
216 | ||
68a88b98 | 217 | if (soc_is_am35xx()) |
326119c9 | 218 | mmc->set_power = nop_mmc_set_power; |
e62245ba | 219 | |
4621d5f8 KK |
220 | /* OMAP3630 HSMMC1 supports only 4-bit */ |
221 | if (cpu_is_omap3630() && | |
222 | (c->caps & MMC_CAP_8_BIT_DATA)) { | |
223 | c->caps &= ~MMC_CAP_8_BIT_DATA; | |
224 | c->caps |= MMC_CAP_4_BIT_DATA; | |
326119c9 | 225 | mmc->caps = c->caps; |
4621d5f8 KK |
226 | } |
227 | break; | |
228 | case 2: | |
68a88b98 | 229 | if (soc_is_am35xx()) |
326119c9 | 230 | mmc->set_power = am35x_hsmmc2_set_power; |
e62245ba | 231 | |
4621d5f8 KK |
232 | if (c->ext_clock) |
233 | c->transceiver = 1; | |
234 | if (c->transceiver && (c->caps & MMC_CAP_8_BIT_DATA)) { | |
235 | c->caps &= ~MMC_CAP_8_BIT_DATA; | |
236 | c->caps |= MMC_CAP_4_BIT_DATA; | |
237 | } | |
326119c9 | 238 | if (mmc->features & HSMMC_HAS_PBIAS) { |
4621d5f8 | 239 | /* off-chip level shifting, or none */ |
326119c9 AF |
240 | mmc->before_set_reg = hsmmc2_before_set_reg; |
241 | mmc->after_set_reg = NULL; | |
4621d5f8 KK |
242 | } |
243 | break; | |
ffa1e4ed | 244 | case 3: |
4621d5f8 KK |
245 | case 4: |
246 | case 5: | |
326119c9 AF |
247 | mmc->before_set_reg = NULL; |
248 | mmc->after_set_reg = NULL; | |
4621d5f8 KK |
249 | break; |
250 | default: | |
251 | pr_err("MMC%d configuration not supported!\n", c->mmc); | |
252 | kfree(hc_name); | |
253 | return -ENODEV; | |
254 | } | |
255 | return 0; | |
256 | } | |
257 | ||
97899e55 | 258 | static int omap_hsmmc_done; |
3b972bf0 TL |
259 | |
260 | void omap_hsmmc_late_init(struct omap2_hsmmc_info *c) | |
261 | { | |
262 | struct platform_device *pdev; | |
55143438 | 263 | struct omap_hsmmc_platform_data *mmc_pdata; |
3b972bf0 TL |
264 | int res; |
265 | ||
266 | if (omap_hsmmc_done != 1) | |
267 | return; | |
268 | ||
269 | omap_hsmmc_done++; | |
270 | ||
271 | for (; c->mmc; c++) { | |
272 | if (!c->deferred) | |
273 | continue; | |
274 | ||
275 | pdev = c->pdev; | |
276 | if (!pdev) | |
277 | continue; | |
278 | ||
279 | mmc_pdata = pdev->dev.platform_data; | |
280 | if (!mmc_pdata) | |
281 | continue; | |
282 | ||
b7a5646f AF |
283 | if (c->cover_only) { |
284 | /* detect if mobile phone cover removed */ | |
285 | mmc_pdata->gpio_cd = -EINVAL; | |
286 | mmc_pdata->gpio_cod = c->gpio_cd; | |
287 | } else { | |
288 | /* card detect pin on the mmc socket itself */ | |
289 | mmc_pdata->gpio_cd = c->gpio_cd; | |
290 | mmc_pdata->gpio_cod = -EINVAL; | |
291 | } | |
326119c9 | 292 | mmc_pdata->gpio_wp = c->gpio_wp; |
3b972bf0 TL |
293 | |
294 | res = omap_device_register(pdev); | |
295 | if (res) | |
296 | pr_err("Could not late init MMC %s\n", | |
297 | c->name); | |
298 | } | |
299 | } | |
300 | ||
4621d5f8 KK |
301 | #define MAX_OMAP_MMC_HWMOD_NAME_LEN 16 |
302 | ||
6028505c | 303 | static void __init omap_hsmmc_init_one(struct omap2_hsmmc_info *hsmmcinfo, |
3b972bf0 | 304 | int ctrl_nr) |
4621d5f8 KK |
305 | { |
306 | struct omap_hwmod *oh; | |
3b972bf0 TL |
307 | struct omap_hwmod *ohs[1]; |
308 | struct omap_device *od; | |
3528c58e | 309 | struct platform_device *pdev; |
4621d5f8 | 310 | char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN]; |
55143438 AF |
311 | struct omap_hsmmc_platform_data *mmc_data; |
312 | struct omap_hsmmc_dev_attr *mmc_dev_attr; | |
4621d5f8 | 313 | char *name; |
3b972bf0 | 314 | int res; |
4621d5f8 | 315 | |
55143438 | 316 | mmc_data = kzalloc(sizeof(*mmc_data), GFP_KERNEL); |
1a61a2a5 | 317 | if (!mmc_data) |
3b972bf0 | 318 | return; |
4621d5f8 | 319 | |
3b972bf0 TL |
320 | res = omap_hsmmc_pdata_init(hsmmcinfo, mmc_data); |
321 | if (res < 0) | |
322 | goto free_mmc; | |
323 | ||
0005ae73 | 324 | name = "omap_hsmmc"; |
3b972bf0 | 325 | res = snprintf(oh_name, MAX_OMAP_MMC_HWMOD_NAME_LEN, |
4621d5f8 | 326 | "mmc%d", ctrl_nr); |
3b972bf0 | 327 | WARN(res >= MAX_OMAP_MMC_HWMOD_NAME_LEN, |
4621d5f8 | 328 | "String buffer overflow in MMC%d device setup\n", ctrl_nr); |
3b972bf0 | 329 | |
4621d5f8 KK |
330 | oh = omap_hwmod_lookup(oh_name); |
331 | if (!oh) { | |
332 | pr_err("Could not look up %s\n", oh_name); | |
3b972bf0 | 333 | goto free_name; |
4621d5f8 | 334 | } |
3b972bf0 | 335 | ohs[0] = oh; |
4621d5f8 KK |
336 | if (oh->dev_attr != NULL) { |
337 | mmc_dev_attr = oh->dev_attr; | |
338 | mmc_data->controller_flags = mmc_dev_attr->flags; | |
26c547fd GI |
339 | /* |
340 | * erratum 2.1.1.128 doesn't apply if board has | |
341 | * a transceiver is attached | |
342 | */ | |
343 | if (hsmmcinfo->transceiver) | |
344 | mmc_data->controller_flags &= | |
345 | ~OMAP_HSMMC_BROKEN_MULTIBLOCK_READ; | |
4621d5f8 KK |
346 | } |
347 | ||
3b972bf0 TL |
348 | pdev = platform_device_alloc(name, ctrl_nr - 1); |
349 | if (!pdev) { | |
350 | pr_err("Could not allocate pdev for %s\n", name); | |
351 | goto free_name; | |
4621d5f8 | 352 | } |
3b972bf0 TL |
353 | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); |
354 | ||
c1d1cd59 | 355 | od = omap_device_alloc(pdev, ohs, 1); |
64de3a00 | 356 | if (IS_ERR(od)) { |
3b972bf0 TL |
357 | pr_err("Could not allocate od for %s\n", name); |
358 | goto put_pdev; | |
359 | } | |
360 | ||
361 | res = platform_device_add_data(pdev, mmc_data, | |
55143438 | 362 | sizeof(struct omap_hsmmc_platform_data)); |
3b972bf0 TL |
363 | if (res) { |
364 | pr_err("Could not add pdata for %s\n", name); | |
365 | goto put_pdev; | |
366 | } | |
367 | ||
368 | hsmmcinfo->pdev = pdev; | |
369 | ||
370 | if (hsmmcinfo->deferred) | |
371 | goto free_mmc; | |
372 | ||
373 | res = omap_device_register(pdev); | |
374 | if (res) { | |
375 | pr_err("Could not register od for %s\n", name); | |
376 | goto free_od; | |
377 | } | |
378 | ||
379 | goto free_mmc; | |
380 | ||
381 | free_od: | |
382 | omap_device_delete(od); | |
383 | ||
384 | put_pdev: | |
385 | platform_device_put(pdev); | |
386 | ||
387 | free_name: | |
326119c9 | 388 | kfree(mmc_data->name); |
4621d5f8 | 389 | |
3b972bf0 | 390 | free_mmc: |
4621d5f8 KK |
391 | kfree(mmc_data); |
392 | } | |
90c62bf0 | 393 | |
d1589f09 | 394 | void __init omap_hsmmc_init(struct omap2_hsmmc_info *controllers) |
90c62bf0 | 395 | { |
97899e55 TL |
396 | if (omap_hsmmc_done) |
397 | return; | |
398 | ||
399 | omap_hsmmc_done = 1; | |
400 | ||
b30e321b TL |
401 | if (cpu_is_omap2430()) { |
402 | control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; | |
403 | control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; | |
90c62bf0 | 404 | } else { |
b30e321b TL |
405 | control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; |
406 | control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; | |
90c62bf0 TL |
407 | } |
408 | ||
4621d5f8 | 409 | for (; controllers->mmc; controllers++) |
3b972bf0 | 410 | omap_hsmmc_init_one(controllers, controllers->mmc); |
01971f65 | 411 | |
90c62bf0 TL |
412 | } |
413 | ||
414 | #endif |