Commit | Line | Data |
---|---|---|
90c62bf0 TL |
1 | /* |
2 | * linux/arch/arm/mach-omap2/mmc-twl4030.c | |
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 | */ | |
12 | #include <linux/err.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/gpio.h> | |
19 | #include <linux/i2c/twl4030.h> | |
20 | ||
21 | #include <mach/hardware.h> | |
22 | #include <mach/control.h> | |
23 | #include <mach/mmc.h> | |
24 | #include <mach/board.h> | |
25 | ||
26 | #include "mmc-twl4030.h" | |
27 | ||
28 | #if defined(CONFIG_TWL4030_CORE) && \ | |
29 | (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)) | |
30 | ||
31 | #define LDO_CLR 0x00 | |
32 | #define VSEL_S2_CLR 0x40 | |
33 | ||
34 | #define VMMC1_DEV_GRP 0x27 | |
35 | #define VMMC1_CLR 0x00 | |
36 | #define VMMC1_315V 0x03 | |
37 | #define VMMC1_300V 0x02 | |
38 | #define VMMC1_285V 0x01 | |
39 | #define VMMC1_185V 0x00 | |
40 | #define VMMC1_DEDICATED 0x2A | |
41 | ||
42 | #define VMMC2_DEV_GRP 0x2B | |
43 | #define VMMC2_CLR 0x40 | |
44 | #define VMMC2_315V 0x0c | |
45 | #define VMMC2_300V 0x0b | |
46 | #define VMMC2_285V 0x0a | |
47 | #define VMMC2_260V 0x08 | |
48 | #define VMMC2_185V 0x06 | |
49 | #define VMMC2_DEDICATED 0x2E | |
50 | ||
51 | #define VMMC_DEV_GRP_P1 0x20 | |
52 | ||
53 | static u16 control_pbias_offset; | |
54 | static u16 control_devconf1_offset; | |
55 | ||
56 | #define HSMMC_NAME_LEN 9 | |
57 | ||
58 | static struct twl_mmc_controller { | |
59 | struct omap_mmc_platform_data *mmc; | |
60 | u8 twl_vmmc_dev_grp; | |
61 | u8 twl_mmc_dedicated; | |
62 | char name[HSMMC_NAME_LEN]; | |
63 | } hsmmc[] = { | |
64 | { | |
65 | .twl_vmmc_dev_grp = VMMC1_DEV_GRP, | |
66 | .twl_mmc_dedicated = VMMC1_DEDICATED, | |
67 | }, | |
68 | { | |
69 | .twl_vmmc_dev_grp = VMMC2_DEV_GRP, | |
70 | .twl_mmc_dedicated = VMMC2_DEDICATED, | |
71 | }, | |
72 | }; | |
73 | ||
74 | static int twl_mmc_card_detect(int irq) | |
75 | { | |
76 | unsigned i; | |
77 | ||
78 | for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { | |
79 | struct omap_mmc_platform_data *mmc; | |
80 | ||
81 | mmc = hsmmc[i].mmc; | |
82 | if (!mmc) | |
83 | continue; | |
84 | if (irq != mmc->slots[0].card_detect_irq) | |
85 | continue; | |
86 | ||
87 | /* NOTE: assumes card detect signal is active-low */ | |
88 | return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); | |
89 | } | |
90 | return -ENOSYS; | |
91 | } | |
92 | ||
93 | static int twl_mmc_get_ro(struct device *dev, int slot) | |
94 | { | |
95 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
96 | ||
97 | /* NOTE: assumes write protect signal is active-high */ | |
98 | return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); | |
99 | } | |
100 | ||
101 | /* | |
102 | * MMC Slot Initialization. | |
103 | */ | |
104 | static int twl_mmc_late_init(struct device *dev) | |
105 | { | |
106 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
107 | int ret = 0; | |
108 | int i; | |
109 | ||
110 | ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd"); | |
111 | if (ret) | |
112 | goto done; | |
113 | ret = gpio_direction_input(mmc->slots[0].switch_pin); | |
114 | if (ret) | |
115 | goto err; | |
116 | ||
117 | for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { | |
118 | if (hsmmc[i].name == mmc->slots[0].name) { | |
119 | hsmmc[i].mmc = mmc; | |
120 | break; | |
121 | } | |
122 | } | |
123 | ||
124 | return 0; | |
125 | ||
126 | err: | |
127 | gpio_free(mmc->slots[0].switch_pin); | |
128 | done: | |
129 | mmc->slots[0].card_detect_irq = 0; | |
130 | mmc->slots[0].card_detect = NULL; | |
131 | ||
132 | dev_err(dev, "err %d configuring card detect\n", ret); | |
133 | return ret; | |
134 | } | |
135 | ||
136 | static void twl_mmc_cleanup(struct device *dev) | |
137 | { | |
138 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
139 | ||
140 | gpio_free(mmc->slots[0].switch_pin); | |
141 | } | |
142 | ||
143 | #ifdef CONFIG_PM | |
144 | ||
145 | static int twl_mmc_suspend(struct device *dev, int slot) | |
146 | { | |
147 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
148 | ||
149 | disable_irq(mmc->slots[0].card_detect_irq); | |
150 | return 0; | |
151 | } | |
152 | ||
153 | static int twl_mmc_resume(struct device *dev, int slot) | |
154 | { | |
155 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
156 | ||
157 | enable_irq(mmc->slots[0].card_detect_irq); | |
158 | return 0; | |
159 | } | |
160 | ||
161 | #else | |
162 | #define twl_mmc_suspend NULL | |
163 | #define twl_mmc_resume NULL | |
164 | #endif | |
165 | ||
166 | /* | |
167 | * Sets the MMC voltage in twl4030 | |
168 | */ | |
169 | static int twl_mmc_set_voltage(struct twl_mmc_controller *c, int vdd) | |
170 | { | |
171 | int ret; | |
172 | u8 vmmc, dev_grp_val; | |
173 | ||
174 | switch (1 << vdd) { | |
175 | case MMC_VDD_35_36: | |
176 | case MMC_VDD_34_35: | |
177 | case MMC_VDD_33_34: | |
178 | case MMC_VDD_32_33: | |
179 | case MMC_VDD_31_32: | |
180 | case MMC_VDD_30_31: | |
181 | if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) | |
182 | vmmc = VMMC1_315V; | |
183 | else | |
184 | vmmc = VMMC2_315V; | |
185 | break; | |
186 | case MMC_VDD_29_30: | |
187 | if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) | |
188 | vmmc = VMMC1_315V; | |
189 | else | |
190 | vmmc = VMMC2_300V; | |
191 | break; | |
192 | case MMC_VDD_27_28: | |
193 | case MMC_VDD_26_27: | |
194 | if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) | |
195 | vmmc = VMMC1_285V; | |
196 | else | |
197 | vmmc = VMMC2_285V; | |
198 | break; | |
199 | case MMC_VDD_25_26: | |
200 | case MMC_VDD_24_25: | |
201 | case MMC_VDD_23_24: | |
202 | case MMC_VDD_22_23: | |
203 | case MMC_VDD_21_22: | |
204 | case MMC_VDD_20_21: | |
205 | if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) | |
206 | vmmc = VMMC1_285V; | |
207 | else | |
208 | vmmc = VMMC2_260V; | |
209 | break; | |
210 | case MMC_VDD_165_195: | |
211 | if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) | |
212 | vmmc = VMMC1_185V; | |
213 | else | |
214 | vmmc = VMMC2_185V; | |
215 | break; | |
216 | default: | |
217 | vmmc = 0; | |
218 | break; | |
219 | } | |
220 | ||
221 | if (vmmc) | |
222 | dev_grp_val = VMMC_DEV_GRP_P1; /* Power up */ | |
223 | else | |
224 | dev_grp_val = LDO_CLR; /* Power down */ | |
225 | ||
226 | ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, | |
227 | dev_grp_val, c->twl_vmmc_dev_grp); | |
228 | if (ret) | |
229 | return ret; | |
230 | ||
231 | ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, | |
232 | vmmc, c->twl_mmc_dedicated); | |
233 | ||
234 | return ret; | |
235 | } | |
236 | ||
237 | static int twl_mmc1_set_power(struct device *dev, int slot, int power_on, | |
238 | int vdd) | |
239 | { | |
240 | u32 reg; | |
241 | int ret = 0; | |
242 | struct twl_mmc_controller *c = &hsmmc[0]; | |
243 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
244 | ||
245 | if (power_on) { | |
246 | if (cpu_is_omap2430()) { | |
247 | reg = omap_ctrl_readl(OMAP243X_CONTROL_DEVCONF1); | |
248 | if ((1 << vdd) >= MMC_VDD_30_31) | |
249 | reg |= OMAP243X_MMC1_ACTIVE_OVERWRITE; | |
250 | else | |
251 | reg &= ~OMAP243X_MMC1_ACTIVE_OVERWRITE; | |
252 | omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1); | |
253 | } | |
254 | ||
255 | if (mmc->slots[0].internal_clock) { | |
256 | reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); | |
257 | reg |= OMAP2_MMCSDIO1ADPCLKISEL; | |
258 | omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0); | |
259 | } | |
260 | ||
261 | reg = omap_ctrl_readl(control_pbias_offset); | |
262 | reg |= OMAP2_PBIASSPEEDCTRL0; | |
263 | reg &= ~OMAP2_PBIASLITEPWRDNZ0; | |
264 | omap_ctrl_writel(reg, control_pbias_offset); | |
265 | ||
266 | ret = twl_mmc_set_voltage(c, vdd); | |
267 | ||
268 | /* 100ms delay required for PBIAS configuration */ | |
269 | msleep(100); | |
270 | reg = omap_ctrl_readl(control_pbias_offset); | |
271 | reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0); | |
272 | if ((1 << vdd) <= MMC_VDD_165_195) | |
273 | reg &= ~OMAP2_PBIASLITEVMODE0; | |
274 | else | |
275 | reg |= OMAP2_PBIASLITEVMODE0; | |
276 | omap_ctrl_writel(reg, control_pbias_offset); | |
277 | } else { | |
278 | reg = omap_ctrl_readl(control_pbias_offset); | |
279 | reg &= ~OMAP2_PBIASLITEPWRDNZ0; | |
280 | omap_ctrl_writel(reg, control_pbias_offset); | |
281 | ||
282 | ret = twl_mmc_set_voltage(c, 0); | |
283 | ||
284 | /* 100ms delay required for PBIAS configuration */ | |
285 | msleep(100); | |
286 | reg = omap_ctrl_readl(control_pbias_offset); | |
287 | reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 | | |
288 | OMAP2_PBIASLITEVMODE0); | |
289 | omap_ctrl_writel(reg, control_pbias_offset); | |
290 | } | |
291 | ||
292 | return ret; | |
293 | } | |
294 | ||
295 | static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vdd) | |
296 | { | |
297 | int ret; | |
298 | struct twl_mmc_controller *c = &hsmmc[1]; | |
299 | struct omap_mmc_platform_data *mmc = dev->platform_data; | |
300 | ||
301 | if (power_on) { | |
302 | if (mmc->slots[0].internal_clock) { | |
303 | u32 reg; | |
304 | ||
305 | reg = omap_ctrl_readl(control_devconf1_offset); | |
306 | reg |= OMAP2_MMCSDIO2ADPCLKISEL; | |
307 | omap_ctrl_writel(reg, control_devconf1_offset); | |
308 | } | |
309 | ret = twl_mmc_set_voltage(c, vdd); | |
310 | } else { | |
311 | ret = twl_mmc_set_voltage(c, 0); | |
312 | } | |
313 | ||
314 | return ret; | |
315 | } | |
316 | ||
317 | static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata; | |
318 | ||
319 | void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers) | |
320 | { | |
321 | struct twl4030_hsmmc_info *c; | |
322 | int nr_hsmmc = ARRAY_SIZE(hsmmc_data); | |
323 | ||
324 | if (cpu_is_omap2430()) { | |
325 | control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; | |
326 | control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; | |
327 | nr_hsmmc = 2; | |
328 | } else { | |
329 | control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; | |
330 | control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; | |
331 | } | |
332 | ||
333 | for (c = controllers; c->mmc; c++) { | |
334 | struct twl_mmc_controller *twl = hsmmc + c->mmc - 1; | |
335 | struct omap_mmc_platform_data *mmc = hsmmc_data[c->mmc - 1]; | |
336 | ||
337 | if (!c->mmc || c->mmc > nr_hsmmc) { | |
338 | pr_debug("MMC%d: no such controller\n", c->mmc); | |
339 | continue; | |
340 | } | |
341 | if (mmc) { | |
342 | pr_debug("MMC%d: already configured\n", c->mmc); | |
343 | continue; | |
344 | } | |
345 | ||
346 | mmc = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL); | |
347 | if (!mmc) { | |
348 | pr_err("Cannot allocate memory for mmc device!\n"); | |
349 | return; | |
350 | } | |
351 | ||
352 | sprintf(twl->name, "mmc%islot%i", c->mmc, 1); | |
353 | mmc->slots[0].name = twl->name; | |
354 | mmc->nr_slots = 1; | |
355 | mmc->slots[0].ocr_mask = MMC_VDD_165_195 | | |
356 | MMC_VDD_26_27 | MMC_VDD_27_28 | | |
357 | MMC_VDD_29_30 | | |
358 | MMC_VDD_30_31 | MMC_VDD_31_32; | |
359 | mmc->slots[0].wires = c->wires; | |
360 | mmc->slots[0].internal_clock = !c->ext_clock; | |
361 | mmc->dma_mask = 0xffffffff; | |
362 | ||
363 | /* note: twl4030 card detect GPIOs normally switch VMMCx ... */ | |
364 | if (gpio_is_valid(c->gpio_cd)) { | |
365 | mmc->init = twl_mmc_late_init; | |
366 | mmc->cleanup = twl_mmc_cleanup; | |
367 | mmc->suspend = twl_mmc_suspend; | |
368 | mmc->resume = twl_mmc_resume; | |
369 | ||
370 | mmc->slots[0].switch_pin = c->gpio_cd; | |
371 | mmc->slots[0].card_detect_irq = gpio_to_irq(c->gpio_cd); | |
372 | mmc->slots[0].card_detect = twl_mmc_card_detect; | |
373 | } else | |
374 | mmc->slots[0].switch_pin = -EINVAL; | |
375 | ||
376 | /* write protect normally uses an OMAP gpio */ | |
377 | if (gpio_is_valid(c->gpio_wp)) { | |
378 | gpio_request(c->gpio_wp, "mmc_wp"); | |
379 | gpio_direction_input(c->gpio_wp); | |
380 | ||
381 | mmc->slots[0].gpio_wp = c->gpio_wp; | |
382 | mmc->slots[0].get_ro = twl_mmc_get_ro; | |
383 | } else | |
384 | mmc->slots[0].gpio_wp = -EINVAL; | |
385 | ||
386 | /* NOTE: we assume OMAP's MMC1 and MMC2 use | |
387 | * the TWL4030's VMMC1 and VMMC2, respectively; | |
388 | * and that OMAP's MMC3 isn't used. | |
389 | */ | |
390 | ||
391 | switch (c->mmc) { | |
392 | case 1: | |
393 | mmc->slots[0].set_power = twl_mmc1_set_power; | |
394 | break; | |
395 | case 2: | |
396 | mmc->slots[0].set_power = twl_mmc2_set_power; | |
397 | break; | |
398 | default: | |
399 | pr_err("MMC%d configuration not supported!\n", c->mmc); | |
400 | continue; | |
401 | } | |
402 | hsmmc_data[c->mmc - 1] = mmc; | |
403 | } | |
404 | ||
405 | omap2_init_mmc(hsmmc_data, OMAP34XX_NR_MMC); | |
406 | } | |
407 | ||
408 | #endif |