blob: 78ed97fc49d39a2f06a3ce54785726bfe7bc4635 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* linux/arch/arm/mach-msm/board-mahimahi-mmc.c
2 *
3 * Copyright (C) 2009 Google, Inc.
4 * Copyright (C) 2009 HTC Corporation
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/delay.h>
18#include <linux/debugfs.h>
19#include <linux/err.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/mmc/host.h>
23#include <linux/mmc/sdio_ids.h>
24#include <linux/platform_device.h>
25
26#include <asm/gpio.h>
27#include <asm/io.h>
28#include <asm/mach-types.h>
29#include <asm/mach/mmc.h>
30
31#include <mach/vreg.h>
32
33#include "board-mahimahi.h"
34#include "devices.h"
35#include "proc_comm.h"
36
37#undef MAHIMAHI_DEBUG_MMC
38
39static bool opt_disable_sdcard;
40static int __init mahimahi_disablesdcard_setup(char *str)
41{
42 opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0);
43 return 1;
44}
45
46__setup("board_mahimahi.disable_sdcard=", mahimahi_disablesdcard_setup);
47
48static void config_gpio_table(uint32_t *table, int len)
49{
50 int n;
51 unsigned id;
52 for(n = 0; n < len; n++) {
53 id = table[n];
54 msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
55 }
56}
57
58static uint32_t sdcard_on_gpio_table[] = {
59 PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
60 PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
61 PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
62 PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
63 PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
64 PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
65};
66
67static uint32_t sdcard_off_gpio_table[] = {
68 PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
69 PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
70 PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
71 PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
72 PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
73 PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
74};
75
76static struct vreg *sdslot_vreg;
77static uint32_t sdslot_vdd = 0xffffffff;
78static uint32_t sdslot_vreg_enabled;
79
80static struct {
81 int mask;
82 int level;
83} mmc_vdd_table[] = {
84 { MMC_VDD_165_195, 1800 },
85 { MMC_VDD_20_21, 2050 },
86 { MMC_VDD_21_22, 2150 },
87 { MMC_VDD_22_23, 2250 },
88 { MMC_VDD_23_24, 2350 },
89 { MMC_VDD_24_25, 2450 },
90 { MMC_VDD_25_26, 2550 },
91 { MMC_VDD_26_27, 2650 },
92 { MMC_VDD_27_28, 2750 },
93 { MMC_VDD_28_29, 2850 },
94 { MMC_VDD_29_30, 2950 },
95};
96
97static uint32_t mahimahi_sdslot_switchvdd(struct device *dev, unsigned int vdd)
98{
99 int i;
100 int ret;
101
102 if (vdd == sdslot_vdd)
103 return 0;
104
105 sdslot_vdd = vdd;
106
107 if (vdd == 0) {
108 config_gpio_table(sdcard_off_gpio_table,
109 ARRAY_SIZE(sdcard_off_gpio_table));
110 vreg_disable(sdslot_vreg);
111 sdslot_vreg_enabled = 0;
112 return 0;
113 }
114
115 if (!sdslot_vreg_enabled) {
116 ret = vreg_enable(sdslot_vreg);
117 if (ret)
118 pr_err("%s: Error enabling vreg (%d)\n", __func__, ret);
119 config_gpio_table(sdcard_on_gpio_table,
120 ARRAY_SIZE(sdcard_on_gpio_table));
121 sdslot_vreg_enabled = 1;
122 }
123
124 for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
125 if (mmc_vdd_table[i].mask != (1 << vdd))
126 continue;
127 ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level);
128 if (ret)
129 pr_err("%s: Error setting level (%d)\n", __func__, ret);
130 return 0;
131 }
132
133 pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd);
134 return 0;
135}
136
137static uint32_t mahimahi_cdma_sdslot_switchvdd(struct device *dev, unsigned int vdd)
138{
139 if (!vdd == !sdslot_vdd)
140 return 0;
141
142 /* In CDMA version, the vdd of sdslot is not configurable, and it is
143 * fixed in 2.85V by hardware design.
144 */
145
146 sdslot_vdd = vdd ? MMC_VDD_28_29 : 0;
147
148 if (vdd) {
149 gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 1);
150 config_gpio_table(sdcard_on_gpio_table,
151 ARRAY_SIZE(sdcard_on_gpio_table));
152 } else {
153 config_gpio_table(sdcard_off_gpio_table,
154 ARRAY_SIZE(sdcard_off_gpio_table));
155 gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 0);
156 }
157
158 sdslot_vreg_enabled = !!vdd;
159
160 return 0;
161}
162
163static unsigned int mahimahi_sdslot_status_rev0(struct device *dev)
164{
165 return !gpio_get_value(MAHIMAHI_GPIO_SDMC_CD_REV0_N);
166}
167
168#define MAHIMAHI_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | \
169 MMC_VDD_21_22 | MMC_VDD_22_23 | \
170 MMC_VDD_23_24 | MMC_VDD_24_25 | \
171 MMC_VDD_25_26 | MMC_VDD_26_27 | \
172 MMC_VDD_27_28 | MMC_VDD_28_29 | \
173 MMC_VDD_29_30)
174
175int mahimahi_microp_sdslot_status_register(void (*cb)(int, void *), void *);
176unsigned int mahimahi_microp_sdslot_status(struct device *);
177
178static struct mmc_platform_data mahimahi_sdslot_data = {
179 .ocr_mask = MAHIMAHI_MMC_VDD,
180 .status = mahimahi_microp_sdslot_status,
181 .register_status_notify = mahimahi_microp_sdslot_status_register,
182 .translate_vdd = mahimahi_sdslot_switchvdd,
183};
184
185static uint32_t wifi_on_gpio_table[] = {
186 PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */
187 PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */
188 PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */
189 PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */
190 PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */
191 PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */
192 PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
193};
194
195static uint32_t wifi_off_gpio_table[] = {
196 PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */
197 PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */
198 PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */
199 PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */
200 PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */
201 PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */
202 PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */
203};
204
205/* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
206 * we use predefined value (sdio_vsn=2) here to initial sdio driver well
207 */
208static struct embedded_sdio_data mahimahi_wifi_emb_data = {
209 .cccr = {
210 .sdio_vsn = 2,
211 .multi_block = 1,
212 .low_speed = 0,
213 .wide_bus = 0,
214 .high_power = 1,
215 .high_speed = 1,
216 },
217};
218
219static int mahimahi_wifi_cd = 0; /* WIFI virtual 'card detect' status */
220static void (*wifi_status_cb)(int card_present, void *dev_id);
221static void *wifi_status_cb_devid;
222
223static int mahimahi_wifi_status_register(
224 void (*callback)(int card_present, void *dev_id),
225 void *dev_id)
226{
227 if (wifi_status_cb)
228 return -EAGAIN;
229 wifi_status_cb = callback;
230 wifi_status_cb_devid = dev_id;
231 return 0;
232}
233
234static unsigned int mahimahi_wifi_status(struct device *dev)
235{
236 return mahimahi_wifi_cd;
237}
238
239static struct mmc_platform_data mahimahi_wifi_data = {
240 .ocr_mask = MMC_VDD_28_29,
241 .built_in = 1,
242 .status = mahimahi_wifi_status,
243 .register_status_notify = mahimahi_wifi_status_register,
244 .embedded_sdio = &mahimahi_wifi_emb_data,
245};
246
247int mahimahi_wifi_set_carddetect(int val)
248{
249 pr_info("%s: %d\n", __func__, val);
250 mahimahi_wifi_cd = val;
251 if (wifi_status_cb) {
252 wifi_status_cb(val, wifi_status_cb_devid);
253 } else
254 pr_warning("%s: Nobody to notify\n", __func__);
255 return 0;
256}
257
258static int mahimahi_wifi_power_state;
259
260int mahimahi_wifi_power(int on)
261{
262 printk("%s: %d\n", __func__, on);
263
264 if (on) {
265 config_gpio_table(wifi_on_gpio_table,
266 ARRAY_SIZE(wifi_on_gpio_table));
267 mdelay(50);
268 } else {
269 config_gpio_table(wifi_off_gpio_table,
270 ARRAY_SIZE(wifi_off_gpio_table));
271 }
272
273 mdelay(100);
274 gpio_set_value(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */
275 mdelay(200);
276
277 mahimahi_wifi_power_state = on;
278 return 0;
279}
280
281static int mahimahi_wifi_reset_state;
282
283int mahimahi_wifi_reset(int on)
284{
285 printk("%s: do nothing\n", __func__);
286 mahimahi_wifi_reset_state = on;
287 return 0;
288}
289
290int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
291 unsigned int stat_irq, unsigned long stat_irq_flags);
292
293int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart)
294{
295 uint32_t id;
296
297 printk("%s()+\n", __func__);
298
299 /* initial WIFI_SHUTDOWN# */
300 id = PCOM_GPIO_CFG(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA),
301 msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
302
303 msm_add_sdcc(1, &mahimahi_wifi_data, 0, 0);
304
305 if (debug_uart) {
306 pr_info("%s: sdcard disabled due to debug uart\n", __func__);
307 goto done;
308 }
309 if (opt_disable_sdcard) {
310 pr_info("%s: sdcard disabled on cmdline\n", __func__);
311 goto done;
312 }
313
314 sdslot_vreg_enabled = 0;
315
316 if (is_cdma_version(sys_rev)) {
317 /* In the CDMA version, sdslot is supplied by a gpio. */
318 int rc = gpio_request(MAHIMAHI_CDMA_SD_2V85_EN, "sdslot_en");
319 if (rc < 0) {
320 pr_err("%s: gpio_request(%d) failed: %d\n", __func__,
321 MAHIMAHI_CDMA_SD_2V85_EN, rc);
322 return rc;
323 }
324 mahimahi_sdslot_data.translate_vdd = mahimahi_cdma_sdslot_switchvdd;
325 } else {
326 /* in UMTS version, sdslot is supplied by pmic */
327 sdslot_vreg = vreg_get(0, "gp6");
328 if (IS_ERR(sdslot_vreg))
329 return PTR_ERR(sdslot_vreg);
330 }
331
332 if (system_rev > 0)
333 msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0);
334 else {
335 mahimahi_sdslot_data.status = mahimahi_sdslot_status_rev0;
336 mahimahi_sdslot_data.register_status_notify = NULL;
337 set_irq_wake(MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), 1);
338 msm_add_sdcc(2, &mahimahi_sdslot_data,
339 MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N),
340 IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE);
341 }
342
343done:
344 printk("%s()-\n", __func__);
345 return 0;
346}
347
348#if defined(MAHIMAHI_DEBUG_MMC) && defined(CONFIG_DEBUG_FS)
349
350static int mahimahimmc_dbg_wifi_reset_set(void *data, u64 val)
351{
352 mahimahi_wifi_reset((int) val);
353 return 0;
354}
355
356static int mahimahimmc_dbg_wifi_reset_get(void *data, u64 *val)
357{
358 *val = mahimahi_wifi_reset_state;
359 return 0;
360}
361
362static int mahimahimmc_dbg_wifi_cd_set(void *data, u64 val)
363{
364 mahimahi_wifi_set_carddetect((int) val);
365 return 0;
366}
367
368static int mahimahimmc_dbg_wifi_cd_get(void *data, u64 *val)
369{
370 *val = mahimahi_wifi_cd;
371 return 0;
372}
373
374static int mahimahimmc_dbg_wifi_pwr_set(void *data, u64 val)
375{
376 mahimahi_wifi_power((int) val);
377 return 0;
378}
379
380static int mahimahimmc_dbg_wifi_pwr_get(void *data, u64 *val)
381{
382 *val = mahimahi_wifi_power_state;
383 return 0;
384}
385
386static int mahimahimmc_dbg_sd_pwr_set(void *data, u64 val)
387{
388 mahimahi_sdslot_switchvdd(NULL, (unsigned int) val);
389 return 0;
390}
391
392static int mahimahimmc_dbg_sd_pwr_get(void *data, u64 *val)
393{
394 *val = sdslot_vdd;
395 return 0;
396}
397
398static int mahimahimmc_dbg_sd_cd_set(void *data, u64 val)
399{
400 return -ENOSYS;
401}
402
403static int mahimahimmc_dbg_sd_cd_get(void *data, u64 *val)
404{
405 *val = mahimahi_sdslot_data.status(NULL);
406 return 0;
407}
408
409DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_reset_fops,
410 mahimahimmc_dbg_wifi_reset_get,
411 mahimahimmc_dbg_wifi_reset_set, "%llu\n");
412
413DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_cd_fops,
414 mahimahimmc_dbg_wifi_cd_get,
415 mahimahimmc_dbg_wifi_cd_set, "%llu\n");
416
417DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_pwr_fops,
418 mahimahimmc_dbg_wifi_pwr_get,
419 mahimahimmc_dbg_wifi_pwr_set, "%llu\n");
420
421DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_pwr_fops,
422 mahimahimmc_dbg_sd_pwr_get,
423 mahimahimmc_dbg_sd_pwr_set, "%llu\n");
424
425DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_cd_fops,
426 mahimahimmc_dbg_sd_cd_get,
427 mahimahimmc_dbg_sd_cd_set, "%llu\n");
428
429static int __init mahimahimmc_dbg_init(void)
430{
431 struct dentry *dent;
432
433 if (!machine_is_mahimahi())
434 return 0;
435
436 dent = debugfs_create_dir("mahimahi_mmc_dbg", 0);
437 if (IS_ERR(dent))
438 return PTR_ERR(dent);
439
440 debugfs_create_file("wifi_reset", 0644, dent, NULL,
441 &mahimahimmc_dbg_wifi_reset_fops);
442 debugfs_create_file("wifi_cd", 0644, dent, NULL,
443 &mahimahimmc_dbg_wifi_cd_fops);
444 debugfs_create_file("wifi_pwr", 0644, dent, NULL,
445 &mahimahimmc_dbg_wifi_pwr_fops);
446 debugfs_create_file("sd_pwr", 0644, dent, NULL,
447 &mahimahimmc_dbg_sd_pwr_fops);
448 debugfs_create_file("sd_cd", 0644, dent, NULL,
449 &mahimahimmc_dbg_sd_cd_fops);
450 return 0;
451}
452
453device_initcall(mahimahimmc_dbg_init);
454#endif