Arm: Tegra: Roth: Support Roth platform
[linux-3.10.git] / arch / arm / mach-tegra / board-roth-sdhci.c
1 /*
2  * arch/arm/mach-tegra/board-roth-sdhci.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  * Copyright (C) 2012 NVIDIA Corporation.
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <linux/resource.h>
19 #include <linux/platform_device.h>
20 #include <linux/wlan_plat.h>
21 #include <linux/delay.h>
22 #include <linux/gpio.h>
23 #include <linux/clk.h>
24 #include <linux/err.h>
25 #include <linux/regulator/consumer.h>
26 #include <linux/mmc/host.h>
27 #include <linux/wl12xx.h>
28
29 #include <asm/mach-types.h>
30 #include <mach/irqs.h>
31 #include <mach/iomap.h>
32 #include <mach/sdhci.h>
33 #include<mach/gpio-tegra.h>
34 #include <mach/io_dpd.h>
35
36 #include "gpio-names.h"
37 #include "board.h"
38 #include "board-roth.h"
39
40
41 #define ROTH_WLAN_PWR   TEGRA_GPIO_PCC5
42 #define ROTH_WLAN_RST   TEGRA_GPIO_PX7
43 #define ROTH_WLAN_WOW   TEGRA_GPIO_PU5
44 static void (*wifi_status_cb)(int card_present, void *dev_id);
45 static void *wifi_status_cb_devid;
46 static int roth_wifi_status_register(void (*callback)(int , void *), void *);
47
48 static int roth_wifi_reset(int on);
49 static int roth_wifi_power(int on);
50 static int roth_wifi_set_carddetect(int val);
51
52 static struct wifi_platform_data roth_wifi_control = {
53         .set_power      = roth_wifi_power,
54         .set_reset      = roth_wifi_reset,
55         .set_carddetect = roth_wifi_set_carddetect,
56 };
57
58 static struct resource wifi_resource[] = {
59         [0] = {
60                 .name   = "bcm4329_wlan_irq",
61                 .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL
62                                 | IORESOURCE_IRQ_SHAREABLE,
63         },
64 };
65
66 static struct platform_device roth_wifi_device = {
67         .name           = "bcm4329_wlan",
68         .id             = 1,
69         .num_resources  = 1,
70         .resource       = wifi_resource,
71         .dev            = {
72                 .platform_data = &roth_wifi_control,
73         },
74 };
75
76 static struct resource sdhci_resource0[] = {
77         [0] = {
78                 .start  = INT_SDMMC1,
79                 .end    = INT_SDMMC1,
80                 .flags  = IORESOURCE_IRQ,
81         },
82         [1] = {
83                 .start  = TEGRA_SDMMC1_BASE,
84                 .end    = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
85                 .flags  = IORESOURCE_MEM,
86         },
87 };
88
89 static struct resource sdhci_resource2[] = {
90         [0] = {
91                 .start  = INT_SDMMC3,
92                 .end    = INT_SDMMC3,
93                 .flags  = IORESOURCE_IRQ,
94         },
95         [1] = {
96                 .start  = TEGRA_SDMMC3_BASE,
97                 .end    = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
98                 .flags  = IORESOURCE_MEM,
99         },
100 };
101
102 static struct resource sdhci_resource3[] = {
103         [0] = {
104                 .start  = INT_SDMMC4,
105                 .end    = INT_SDMMC4,
106                 .flags  = IORESOURCE_IRQ,
107         },
108         [1] = {
109                 .start  = TEGRA_SDMMC4_BASE,
110                 .end    = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
111                 .flags  = IORESOURCE_MEM,
112         },
113 };
114
115 #ifdef CONFIG_MMC_EMBEDDED_SDIO
116 static struct embedded_sdio_data embedded_sdio_data0 = {
117         .cccr   = {
118                 .sdio_vsn       = 2,
119                 .multi_block    = 1,
120                 .low_speed      = 0,
121                 .wide_bus       = 0,
122                 .high_power     = 1,
123                 .high_speed     = 1,
124         },
125         .cis  = {
126                 .vendor  = 0x02d0,
127                 .device  = 0x4329,
128         },
129 };
130 #endif
131
132 static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
133         .mmc_data = {
134                 .register_status_notify = roth_wifi_status_register,
135 #ifdef CONFIG_MMC_EMBEDDED_SDIO
136                 .embedded_sdio = &embedded_sdio_data0,
137 #endif
138                 .built_in = 0,
139                 .ocr_mask = MMC_OCR_1V8_MASK,
140         },
141 #ifndef CONFIG_MMC_EMBEDDED_SDIO
142         .pm_flags = MMC_PM_KEEP_POWER,
143 #endif
144         .cd_gpio = -1,
145         .wp_gpio = -1,
146         .power_gpio = -1,
147         .tap_delay = 0x2,
148         .trim_delay = 0x2,
149         .ddr_clk_limit = 41000000,
150 };
151
152 static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
153         .cd_gpio = -1,
154         .wp_gpio = -1,
155         .power_gpio = -1,
156         .tap_delay = 0x3,
157         .trim_delay = 0x3,
158         .ddr_clk_limit = 41000000,
159 };
160
161 static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
162         .cd_gpio = -1,
163         .wp_gpio = -1,
164         .power_gpio = -1,
165         .is_8bit = 1,
166         .tap_delay = 0x5,
167         .trim_delay = 0x3,
168         .ddr_clk_limit = 41000000,
169         .mmc_data = {
170                 .built_in = 1,
171         }
172 };
173
174 static struct platform_device tegra_sdhci_device0 = {
175         .name           = "sdhci-tegra",
176         .id             = 0,
177         .resource       = sdhci_resource0,
178         .num_resources  = ARRAY_SIZE(sdhci_resource0),
179         .dev = {
180                 .platform_data = &tegra_sdhci_platform_data0,
181         },
182 };
183
184 static struct platform_device tegra_sdhci_device2 = {
185         .name           = "sdhci-tegra",
186         .id             = 2,
187         .resource       = sdhci_resource2,
188         .num_resources  = ARRAY_SIZE(sdhci_resource2),
189         .dev = {
190                 .platform_data = &tegra_sdhci_platform_data2,
191         },
192 };
193
194 static struct platform_device tegra_sdhci_device3 = {
195         .name           = "sdhci-tegra",
196         .id             = 3,
197         .resource       = sdhci_resource3,
198         .num_resources  = ARRAY_SIZE(sdhci_resource3),
199         .dev = {
200                 .platform_data = &tegra_sdhci_platform_data3,
201         },
202 };
203
204 static int roth_wifi_status_register(
205                 void (*callback)(int card_present, void *dev_id),
206                 void *dev_id)
207 {
208         if (wifi_status_cb)
209                 return -EAGAIN;
210         wifi_status_cb = callback;
211         wifi_status_cb_devid = dev_id;
212         return 0;
213 }
214
215 static int roth_wifi_set_carddetect(int val)
216 {
217         pr_debug("%s: %d\n", __func__, val);
218         if (wifi_status_cb)
219                 wifi_status_cb(val, wifi_status_cb_devid);
220         else
221                 pr_warning("%s: Nobody to notify\n", __func__);
222         return 0;
223 }
224
225 static struct regulator *roth_vdd_com_3v3;
226 static struct regulator *roth_vddio_com_1v8;
227 #define ROTH_VDD_WIFI_3V3 "vdd_wifi_3v3"
228 #define ROTH_VDD_WIFI_1V8 "vddio_wifi_1v8"
229
230
231 static int roth_wifi_regulator_enable(void)
232 {
233         int ret = 0;
234
235         /* Enable COM's vdd_com_3v3 regulator*/
236         if (IS_ERR_OR_NULL(roth_vdd_com_3v3)) {
237                 roth_vdd_com_3v3 = regulator_get(NULL, ROTH_VDD_WIFI_3V3);
238                 if (IS_ERR_OR_NULL(roth_vdd_com_3v3)) {
239                         pr_err("Couldn't get regulator "
240                                 ROTH_VDD_WIFI_3V3 "\n");
241                         return PTR_ERR(roth_vdd_com_3v3);
242                 }
243
244                 ret = regulator_enable(roth_vdd_com_3v3);
245                 if (ret < 0) {
246                         pr_err("Couldn't enable regulator "
247                                 ROTH_VDD_WIFI_3V3 "\n");
248                         regulator_put(roth_vdd_com_3v3);
249                         roth_vdd_com_3v3 = NULL;
250                         return ret;
251                 }
252         }
253
254         /* Enable COM's vddio_com_1v8 regulator*/
255         if (IS_ERR_OR_NULL(roth_vddio_com_1v8)) {
256                 roth_vddio_com_1v8 = regulator_get(NULL,
257                         ROTH_VDD_WIFI_1V8);
258                 if (IS_ERR_OR_NULL(roth_vddio_com_1v8)) {
259                         pr_err("Couldn't get regulator "
260                                 ROTH_VDD_WIFI_1V8 "\n");
261                         regulator_disable(roth_vdd_com_3v3);
262
263                         regulator_put(roth_vdd_com_3v3);
264                         roth_vdd_com_3v3 = NULL;
265                         return PTR_ERR(roth_vddio_com_1v8);
266                 }
267
268                 ret = regulator_enable(roth_vddio_com_1v8);
269                 if (ret < 0) {
270                         pr_err("Couldn't enable regulator "
271                                 ROTH_VDD_WIFI_1V8 "\n");
272                         regulator_put(roth_vddio_com_1v8);
273                         roth_vddio_com_1v8 = NULL;
274
275                         regulator_disable(roth_vdd_com_3v3);
276                         regulator_put(roth_vdd_com_3v3);
277                         roth_vdd_com_3v3 = NULL;
278                         return ret;
279                 }
280         }
281
282         return ret;
283 }
284
285 static void roth_wifi_regulator_disable(void)
286 {
287         /* Disable COM's vdd_com_3v3 regulator*/
288         if (!IS_ERR_OR_NULL(roth_vdd_com_3v3)) {
289                 regulator_disable(roth_vdd_com_3v3);
290                 regulator_put(roth_vdd_com_3v3);
291                 roth_vdd_com_3v3 = NULL;
292         }
293
294         /* Disable COM's vddio_com_1v8 regulator*/
295         if (!IS_ERR_OR_NULL(roth_vddio_com_1v8)) {
296                 regulator_disable(roth_vddio_com_1v8);
297                 regulator_put(roth_vddio_com_1v8);
298                 roth_vddio_com_1v8 = NULL;
299         }
300 }
301
302 static int roth_wifi_power(int on)
303 {
304         struct tegra_io_dpd *sd_dpd;
305         int ret = 0;
306
307         pr_debug("%s: %d\n", __func__, on);
308         /* Enable COM's regulators on wi-fi poer on*/
309         if (on == 1) {
310                 ret = roth_wifi_regulator_enable();
311                 if (ret < 0) {
312                         pr_err("Failed to enable COM regulators\n");
313                         return ret;
314                 }
315         }
316
317         /*
318          * FIXME : we need to revisit IO DPD code
319          * on how should multiple pins under DPD get controlled
320          *
321          * roth GPIO WLAN enable is part of SDMMC3 pin group
322          */
323         sd_dpd = tegra_io_dpd_get(&tegra_sdhci_device2.dev);
324         if (sd_dpd) {
325                 mutex_lock(&sd_dpd->delay_lock);
326                 tegra_io_dpd_disable(sd_dpd);
327                 mutex_unlock(&sd_dpd->delay_lock);
328         }
329         gpio_set_value(ROTH_WLAN_PWR, on);
330         mdelay(100);
331         gpio_set_value(ROTH_WLAN_RST, on);
332         mdelay(200);
333         if (sd_dpd) {
334                 mutex_lock(&sd_dpd->delay_lock);
335                 tegra_io_dpd_enable(sd_dpd);
336                 mutex_unlock(&sd_dpd->delay_lock);
337         }
338
339         /* Disable COM's regulators on wi-fi poer off*/
340         if (on != 1) {
341                 pr_debug("Disabling COM regulators\n");
342                 roth_wifi_regulator_disable();
343         }
344
345         return ret;
346 }
347
348 static int roth_wifi_reset(int on)
349 {
350         pr_debug("%s: do nothing\n", __func__);
351         return 0;
352 }
353
354 static int __init roth_wifi_init(void)
355 {
356         int rc;
357
358         rc = gpio_request(ROTH_WLAN_PWR, "wlan_power");
359         if (rc)
360                 pr_err("WLAN_PWR gpio request failed:%d\n", rc);
361         rc = gpio_request(ROTH_WLAN_RST, "wlan_rst");
362         if (rc)
363                 pr_err("WLAN_RST gpio request failed:%d\n", rc);
364         rc = gpio_request(ROTH_WLAN_WOW, "bcmsdh_sdmmc");
365         if (rc)
366                 pr_err("WLAN_WOW gpio request failed:%d\n", rc);
367
368         rc = gpio_direction_output(ROTH_WLAN_PWR, 0);
369         if (rc)
370                 pr_err("WLAN_PWR gpio direction configuration failed:%d\n", rc);
371         gpio_direction_output(ROTH_WLAN_RST, 0);
372         if (rc)
373                 pr_err("WLAN_RST gpio direction configuration failed:%d\n", rc);
374         rc = gpio_direction_input(ROTH_WLAN_WOW);
375         if (rc)
376                 pr_err("WLAN_WOW gpio direction configuration failed:%d\n", rc);
377
378         wifi_resource[0].start = wifi_resource[0].end =
379                 gpio_to_irq(ROTH_WLAN_WOW);
380
381         platform_device_register(&roth_wifi_device);
382         return 0;
383 }
384
385 #ifdef CONFIG_TEGRA_PREPOWER_WIFI
386 static int __init roth_wifi_prepower(void)
387 {
388         if (!machine_is_roth())
389                 return 0;
390
391         roth_wifi_power(1);
392
393         return 0;
394 }
395
396 subsys_initcall_sync(roth_wifi_prepower);
397 #endif
398
399 int __init roth_sdhci_init(void)
400 {
401         platform_device_register(&tegra_sdhci_device3);
402         platform_device_register(&tegra_sdhci_device2);
403         platform_device_register(&tegra_sdhci_device0);
404         roth_wifi_init();
405         return 0;
406 }