aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/mmc-twl4030.c
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2008-12-10 20:37:17 -0500
committerTony Lindgren <tony@atomide.com>2008-12-10 20:37:17 -0500
commit90c62bf08f5823faa097271f3346a9142769b9ac (patch)
treeaa3bf442380815268b03092fd4b9c47924f9c3ee /arch/arm/mach-omap2/mmc-twl4030.c
parentd88746652b4d133284d1fdd05b5e999e8f44c998 (diff)
omap mmc: Add low-level initialization for hsmmc controller
Add low-level initialization for hsmmc controller. Merged into this patch patch are various improvments and board support by Grazvydas Ignotas and David Brownell. Also change wire4 to be wires, as some newer controllers support 8 data lines. Cc: Pierre Ossman <drzeus-mmc@drzeus.cx> Signed-off-by: Grazvydas Ignotas <notasas@gmail.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch/arm/mach-omap2/mmc-twl4030.c')
-rw-r--r--arch/arm/mach-omap2/mmc-twl4030.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
new file mode 100644
index 000000000000..437f52073f6e
--- /dev/null
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -0,0 +1,408 @@
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
53static u16 control_pbias_offset;
54static u16 control_devconf1_offset;
55
56#define HSMMC_NAME_LEN 9
57
58static 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
74static 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
93static 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 */
104static 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
126err:
127 gpio_free(mmc->slots[0].switch_pin);
128done:
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
136static 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
145static 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
153static 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 */
169static 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
237static 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
295static 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
317static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
318
319void __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