blob: b72ecd438d64775348be21289d8237935a414507 [file] [log] [blame]
Santosh Sajjan374d6592012-01-19 23:16:46 +05301/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/delay.h>
13#include <linux/device.h>
14#include <linux/err.h>
15#include <linux/gpio.h>
16#include <linux/platform_device.h>
17#include <linux/regulator/consumer.h>
18#include <asm/mach-types.h>
19#include <mach/rpc_pmapp.h>
20#include "board-msm7627a.h"
21#include "devices-msm7x2xa.h"
22#include "timer.h"
23
24#define GPIO_WLAN_3V3_EN 119
25static const char *id = "WLAN";
26
27enum {
28 WLAN_VREG_S3 = 0,
29 WLAN_VREG_L17,
30 WLAN_VREG_L19
31};
32
33struct wlan_vreg_info {
34 const char *vreg_id;
35 unsigned int level_min;
36 unsigned int level_max;
37 unsigned int pmapp_id;
38 unsigned int is_vreg_pin_controlled;
39 struct regulator *reg;
40};
41
42static struct wlan_vreg_info vreg_info[] = {
43 {"msme1", 1800000, 1800000, 2, 0, NULL},
44 {"bt", 3300000, 3300000, 21, 1, NULL},
45 {"wlan4", 1800000, 1800000, 23, 1, NULL}
46};
47
48int gpio_wlan_sys_rest_en = 134;
49static void gpio_wlan_config(void)
50{
Chintan Pandyaf4ad4002012-02-28 19:49:03 +053051 if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
Aparna Mallavarapu093e7fd2012-04-05 13:08:50 +053052 || machine_is_msm8625_evb()
53 || machine_is_msm7627a_qrd3()
54 || machine_is_msm8625_qrd7())
Santosh Sajjan374d6592012-01-19 23:16:46 +053055 gpio_wlan_sys_rest_en = 124;
56}
57
58static unsigned int qrf6285_init_regs(void)
59{
60 struct regulator_bulk_data regs[ARRAY_SIZE(vreg_info)];
61 int i = 0, rc = 0;
62
63 for (i = 0; i < ARRAY_SIZE(regs); i++) {
64 regs[i].supply = vreg_info[i].vreg_id;
65 regs[i].min_uV = vreg_info[i].level_min;
66 regs[i].max_uV = vreg_info[i].level_max;
67 }
68
69 rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs);
70 if (rc) {
71 pr_err("%s: could not get regulators: %d\n", __func__, rc);
72 goto out;
73 }
74
75 for (i = 0; i < ARRAY_SIZE(regs); i++)
76 vreg_info[i].reg = regs[i].consumer;
77
78out:
79 return rc;
80}
81
82static unsigned int setup_wlan_gpio(bool on)
83{
84 int rc = 0;
85
86 if (on) {
87 rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1);
88 msleep(100);
89 } else {
90 gpio_set_value_cansleep(gpio_wlan_sys_rest_en, 0);
91 rc = gpio_direction_input(gpio_wlan_sys_rest_en);
92 msleep(100);
93 }
94
95 if (rc)
96 pr_err("%s: WLAN sys_reset_en GPIO: Error", __func__);
97
98 return rc;
99}
100
101static unsigned int setup_wlan_clock(bool on)
102{
103 int rc = 0;
104
105 if (on) {
106 /* Vote for A0 clock */
107 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
108 PMAPP_CLOCK_VOTE_ON);
109 } else {
110 /* Vote against A0 clock */
111 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
112 PMAPP_CLOCK_VOTE_OFF);
113 }
114
115 if (rc)
116 pr_err("%s: Configuring A0 clock for WLAN: Error", __func__);
117
118 return rc;
119}
120
121static unsigned int wlan_switch_regulators(int on)
122{
123 int rc = 0, index = 0;
124
125 if (machine_is_msm7627a_qrd1())
126 index = 2;
127
128 for ( ; index < ARRAY_SIZE(vreg_info); index++) {
129 if (on) {
130 rc = regulator_set_voltage(vreg_info[index].reg,
131 vreg_info[index].level_min,
132 vreg_info[index].level_max);
133 if (rc) {
134 pr_err("%s:%s set voltage failed %d\n",
135 __func__, vreg_info[index].vreg_id, rc);
136 goto reg_disable;
137 }
138
139 rc = regulator_enable(vreg_info[index].reg);
140 if (rc) {
141 pr_err("%s:%s vreg enable failed %d\n",
142 __func__, vreg_info[index].vreg_id, rc);
143 goto reg_disable;
144 }
145
146 if (vreg_info[index].is_vreg_pin_controlled) {
147 rc = pmapp_vreg_lpm_pincntrl_vote(id,
148 vreg_info[index].pmapp_id,
149 PMAPP_CLOCK_ID_A0, 1);
150 if (rc) {
151 pr_err("%s:%s pincntrl failed %d\n",
152 __func__,
153 vreg_info[index].vreg_id, rc);
154 goto pin_cnt_fail;
155 }
156 }
157 } else {
158 if (vreg_info[index].is_vreg_pin_controlled) {
159 rc = pmapp_vreg_lpm_pincntrl_vote(id,
160 vreg_info[index].pmapp_id,
161 PMAPP_CLOCK_ID_A0, 0);
162 if (rc) {
163 pr_err("%s:%s pincntrl failed %d\n",
164 __func__,
165 vreg_info[index].vreg_id, rc);
166 goto pin_cnt_fail;
167 }
168 }
169
170 rc = regulator_disable(vreg_info[index].reg);
171 if (rc) {
172 pr_err("%s:%s vreg disable failed %d\n",
173 __func__,
174 vreg_info[index].vreg_id, rc);
175 goto reg_disable;
176 }
177 }
178 }
179 return 0;
180pin_cnt_fail:
181 if (on)
182 regulator_disable(vreg_info[index].reg);
183reg_disable:
184 if (!machine_is_msm7627a_qrd1()) {
185 while (index) {
186 if (on) {
187 index--;
188 regulator_disable(vreg_info[index].reg);
189 regulator_put(vreg_info[index].reg);
190 }
191 }
192 }
193 return rc;
194}
195
196static unsigned int msm_AR600X_setup_power(bool on)
197{
198 int rc = 0;
199 static bool init_done;
200
201 if (unlikely(!init_done)) {
202 gpio_wlan_config();
203 rc = qrf6285_init_regs();
204 if (rc) {
205 pr_err("%s: qrf6285 init failed = %d\n", __func__, rc);
206 return rc;
207 } else {
208 init_done = true;
209 }
210 }
211
212 rc = wlan_switch_regulators(on);
213 if (rc) {
214 pr_err("%s: wlan_switch_regulators error = %d\n", __func__, rc);
215 goto out;
216 }
217
218 /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
219 if (machine_is_msm7627a_qrd1()) {
220 rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0,
221 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
222 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
223 if (rc) {
224 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
225 __func__, rc);
226 goto reg_disable;
227 }
228 gpio_set_value(GPIO_WLAN_3V3_EN, 1);
229 }
230
231 /*
232 * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a,
233 * EVB1.0 and QRD8625,so the below step is required for those devices.
234 */
Chintan Pandyaf4ad4002012-02-28 19:49:03 +0530235 if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
Aparna Mallavarapu093e7fd2012-04-05 13:08:50 +0530236 || machine_is_msm8625_evb()
237 || machine_is_msm7627a_qrd3()
238 || machine_is_msm8625_qrd7()) {
Santosh Sajjan374d6592012-01-19 23:16:46 +0530239 rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
240 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
241 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
242 if (rc) {
243 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
244 __func__, rc);
245 goto qrd_gpio_fail;
246 }
247 gpio_set_value(gpio_wlan_sys_rest_en, 1);
248 } else {
249 rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
250 if (rc) {
251 pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
252 __func__,
253 gpio_wlan_sys_rest_en, rc);
254 goto qrd_gpio_fail;
255 }
256 rc = setup_wlan_gpio(on);
257 if (rc) {
258 pr_err("%s: wlan_set_gpio = %d\n", __func__, rc);
259 goto gpio_fail;
260 }
261 }
262
263 /* Enable the A0 clock */
264 rc = setup_wlan_clock(on);
265 if (rc) {
266 pr_err("%s: setup_wlan_clock = %d\n", __func__, rc);
267 goto set_gpio_fail;
268 }
269
270 /* Configure A0 clock to be slave to WLAN_CLK_PWR_REQ */
271 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
272 PMAPP_CLOCK_VOTE_PIN_CTRL);
273 if (rc) {
274 pr_err("%s: Configuring A0 to Pin controllable failed %d\n",
275 __func__, rc);
276 goto set_clock_fail;
277 }
278
279 pr_info("WLAN power-up success\n");
280 return 0;
281set_clock_fail:
282 setup_wlan_clock(0);
283set_gpio_fail:
284 setup_wlan_gpio(0);
285gpio_fail:
286 gpio_free(gpio_wlan_sys_rest_en);
287qrd_gpio_fail:
288 gpio_free(GPIO_WLAN_3V3_EN);
289reg_disable:
290 wlan_switch_regulators(0);
291out:
292 pr_info("WLAN power-up failed\n");
293 return rc;
294}
295
296static unsigned int msm_AR600X_shutdown_power(bool on)
297{
298 int rc = 0;
299
300 /* Disable the A0 clock */
301 rc = setup_wlan_clock(on);
302 if (rc) {
303 pr_err("%s: setup_wlan_clock = %d\n", __func__, rc);
304 goto set_clock_fail;
305 }
306
307 /*
308 * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a,
309 * EVB1.0 and QRD8625,so the below step is required for those devices.
310 */
Chintan Pandyaf4ad4002012-02-28 19:49:03 +0530311 if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
Aparna Mallavarapu093e7fd2012-04-05 13:08:50 +0530312 || machine_is_msm8625_evb()
313 || machine_is_msm7627a_qrd3()
314 || machine_is_msm8625_qrd7()) {
Santosh Sajjan374d6592012-01-19 23:16:46 +0530315 rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
316 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
317 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
318 if (rc) {
319 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
320 __func__, rc);
321 goto gpio_fail;
322 }
323 gpio_set_value(gpio_wlan_sys_rest_en, 0);
324 } else {
325 rc = setup_wlan_gpio(on);
326 if (rc) {
327 pr_err("%s: wlan_set_gpio = %d\n", __func__, rc);
328 goto set_gpio_fail;
329 }
330 gpio_free(gpio_wlan_sys_rest_en);
331 }
332
333 /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
334 if (machine_is_msm7627a_qrd1()) {
335 rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0,
336 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
337 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
338 if (rc) {
339 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
340 __func__, rc);
341 goto qrd_gpio_fail;
342 }
343 gpio_set_value(GPIO_WLAN_3V3_EN, 0);
344 }
345
346 rc = wlan_switch_regulators(on);
347 if (rc) {
348 pr_err("%s: wlan_switch_regulators error = %d\n",
349 __func__, rc);
350 goto reg_disable;
351 }
352
353 pr_info("WLAN power-down success\n");
354 return 0;
355set_clock_fail:
356 setup_wlan_clock(0);
357set_gpio_fail:
358 setup_wlan_gpio(0);
359gpio_fail:
360 gpio_free(gpio_wlan_sys_rest_en);
361qrd_gpio_fail:
362 gpio_free(GPIO_WLAN_3V3_EN);
363reg_disable:
364 wlan_switch_regulators(0);
365 pr_info("WLAN power-down failed\n");
366 return rc;
367}
368
369int ar600x_wlan_power(bool on)
370{
371 if (on)
372 msm_AR600X_setup_power(on);
373 else
374 msm_AR600X_shutdown_power(on);
375
376 return 0;
377}