blob: 53d3c56a7077f622da3f9b263c8d295c5ec12aa9 [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()
52 || machine_is_msm8625_evb())
Santosh Sajjan374d6592012-01-19 23:16:46 +053053 gpio_wlan_sys_rest_en = 124;
54}
55
56static unsigned int qrf6285_init_regs(void)
57{
58 struct regulator_bulk_data regs[ARRAY_SIZE(vreg_info)];
59 int i = 0, rc = 0;
60
61 for (i = 0; i < ARRAY_SIZE(regs); i++) {
62 regs[i].supply = vreg_info[i].vreg_id;
63 regs[i].min_uV = vreg_info[i].level_min;
64 regs[i].max_uV = vreg_info[i].level_max;
65 }
66
67 rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs);
68 if (rc) {
69 pr_err("%s: could not get regulators: %d\n", __func__, rc);
70 goto out;
71 }
72
73 for (i = 0; i < ARRAY_SIZE(regs); i++)
74 vreg_info[i].reg = regs[i].consumer;
75
76out:
77 return rc;
78}
79
80static unsigned int setup_wlan_gpio(bool on)
81{
82 int rc = 0;
83
84 if (on) {
85 rc = gpio_direction_output(gpio_wlan_sys_rest_en, 1);
86 msleep(100);
87 } else {
88 gpio_set_value_cansleep(gpio_wlan_sys_rest_en, 0);
89 rc = gpio_direction_input(gpio_wlan_sys_rest_en);
90 msleep(100);
91 }
92
93 if (rc)
94 pr_err("%s: WLAN sys_reset_en GPIO: Error", __func__);
95
96 return rc;
97}
98
99static unsigned int setup_wlan_clock(bool on)
100{
101 int rc = 0;
102
103 if (on) {
104 /* Vote for A0 clock */
105 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
106 PMAPP_CLOCK_VOTE_ON);
107 } else {
108 /* Vote against A0 clock */
109 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
110 PMAPP_CLOCK_VOTE_OFF);
111 }
112
113 if (rc)
114 pr_err("%s: Configuring A0 clock for WLAN: Error", __func__);
115
116 return rc;
117}
118
119static unsigned int wlan_switch_regulators(int on)
120{
121 int rc = 0, index = 0;
122
123 if (machine_is_msm7627a_qrd1())
124 index = 2;
125
126 for ( ; index < ARRAY_SIZE(vreg_info); index++) {
127 if (on) {
128 rc = regulator_set_voltage(vreg_info[index].reg,
129 vreg_info[index].level_min,
130 vreg_info[index].level_max);
131 if (rc) {
132 pr_err("%s:%s set voltage failed %d\n",
133 __func__, vreg_info[index].vreg_id, rc);
134 goto reg_disable;
135 }
136
137 rc = regulator_enable(vreg_info[index].reg);
138 if (rc) {
139 pr_err("%s:%s vreg enable failed %d\n",
140 __func__, vreg_info[index].vreg_id, rc);
141 goto reg_disable;
142 }
143
144 if (vreg_info[index].is_vreg_pin_controlled) {
145 rc = pmapp_vreg_lpm_pincntrl_vote(id,
146 vreg_info[index].pmapp_id,
147 PMAPP_CLOCK_ID_A0, 1);
148 if (rc) {
149 pr_err("%s:%s pincntrl failed %d\n",
150 __func__,
151 vreg_info[index].vreg_id, rc);
152 goto pin_cnt_fail;
153 }
154 }
155 } else {
156 if (vreg_info[index].is_vreg_pin_controlled) {
157 rc = pmapp_vreg_lpm_pincntrl_vote(id,
158 vreg_info[index].pmapp_id,
159 PMAPP_CLOCK_ID_A0, 0);
160 if (rc) {
161 pr_err("%s:%s pincntrl failed %d\n",
162 __func__,
163 vreg_info[index].vreg_id, rc);
164 goto pin_cnt_fail;
165 }
166 }
167
168 rc = regulator_disable(vreg_info[index].reg);
169 if (rc) {
170 pr_err("%s:%s vreg disable failed %d\n",
171 __func__,
172 vreg_info[index].vreg_id, rc);
173 goto reg_disable;
174 }
175 }
176 }
177 return 0;
178pin_cnt_fail:
179 if (on)
180 regulator_disable(vreg_info[index].reg);
181reg_disable:
182 if (!machine_is_msm7627a_qrd1()) {
183 while (index) {
184 if (on) {
185 index--;
186 regulator_disable(vreg_info[index].reg);
187 regulator_put(vreg_info[index].reg);
188 }
189 }
190 }
191 return rc;
192}
193
194static unsigned int msm_AR600X_setup_power(bool on)
195{
196 int rc = 0;
197 static bool init_done;
198
199 if (unlikely(!init_done)) {
200 gpio_wlan_config();
201 rc = qrf6285_init_regs();
202 if (rc) {
203 pr_err("%s: qrf6285 init failed = %d\n", __func__, rc);
204 return rc;
205 } else {
206 init_done = true;
207 }
208 }
209
210 rc = wlan_switch_regulators(on);
211 if (rc) {
212 pr_err("%s: wlan_switch_regulators error = %d\n", __func__, rc);
213 goto out;
214 }
215
216 /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
217 if (machine_is_msm7627a_qrd1()) {
218 rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0,
219 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
220 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
221 if (rc) {
222 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
223 __func__, rc);
224 goto reg_disable;
225 }
226 gpio_set_value(GPIO_WLAN_3V3_EN, 1);
227 }
228
229 /*
230 * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a,
231 * EVB1.0 and QRD8625,so the below step is required for those devices.
232 */
Chintan Pandyaf4ad4002012-02-28 19:49:03 +0530233 if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
234 || machine_is_msm8625_evb()) {
Santosh Sajjan374d6592012-01-19 23:16:46 +0530235 rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
236 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
237 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
238 if (rc) {
239 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
240 __func__, rc);
241 goto qrd_gpio_fail;
242 }
243 gpio_set_value(gpio_wlan_sys_rest_en, 1);
244 } else {
245 rc = gpio_request(gpio_wlan_sys_rest_en, "WLAN_DEEP_SLEEP_N");
246 if (rc) {
247 pr_err("%s: WLAN sys_rest_en GPIO %d request failed %d\n",
248 __func__,
249 gpio_wlan_sys_rest_en, rc);
250 goto qrd_gpio_fail;
251 }
252 rc = setup_wlan_gpio(on);
253 if (rc) {
254 pr_err("%s: wlan_set_gpio = %d\n", __func__, rc);
255 goto gpio_fail;
256 }
257 }
258
259 /* Enable the A0 clock */
260 rc = setup_wlan_clock(on);
261 if (rc) {
262 pr_err("%s: setup_wlan_clock = %d\n", __func__, rc);
263 goto set_gpio_fail;
264 }
265
266 /* Configure A0 clock to be slave to WLAN_CLK_PWR_REQ */
267 rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
268 PMAPP_CLOCK_VOTE_PIN_CTRL);
269 if (rc) {
270 pr_err("%s: Configuring A0 to Pin controllable failed %d\n",
271 __func__, rc);
272 goto set_clock_fail;
273 }
274
275 pr_info("WLAN power-up success\n");
276 return 0;
277set_clock_fail:
278 setup_wlan_clock(0);
279set_gpio_fail:
280 setup_wlan_gpio(0);
281gpio_fail:
282 gpio_free(gpio_wlan_sys_rest_en);
283qrd_gpio_fail:
284 gpio_free(GPIO_WLAN_3V3_EN);
285reg_disable:
286 wlan_switch_regulators(0);
287out:
288 pr_info("WLAN power-up failed\n");
289 return rc;
290}
291
292static unsigned int msm_AR600X_shutdown_power(bool on)
293{
294 int rc = 0;
295
296 /* Disable the A0 clock */
297 rc = setup_wlan_clock(on);
298 if (rc) {
299 pr_err("%s: setup_wlan_clock = %d\n", __func__, rc);
300 goto set_clock_fail;
301 }
302
303 /*
304 * gpio_wlan_sys_rest_en is not from the GPIO expander for QRD7627a,
305 * EVB1.0 and QRD8625,so the below step is required for those devices.
306 */
Chintan Pandyaf4ad4002012-02-28 19:49:03 +0530307 if (machine_is_msm7627a_qrd1() || machine_is_msm7627a_evb()
308 || machine_is_msm8625_evb()) {
Santosh Sajjan374d6592012-01-19 23:16:46 +0530309 rc = gpio_tlmm_config(GPIO_CFG(gpio_wlan_sys_rest_en, 0,
310 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
311 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
312 if (rc) {
313 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
314 __func__, rc);
315 goto gpio_fail;
316 }
317 gpio_set_value(gpio_wlan_sys_rest_en, 0);
318 } else {
319 rc = setup_wlan_gpio(on);
320 if (rc) {
321 pr_err("%s: wlan_set_gpio = %d\n", __func__, rc);
322 goto set_gpio_fail;
323 }
324 gpio_free(gpio_wlan_sys_rest_en);
325 }
326
327 /* GPIO_WLAN_3V3_EN is only required for the QRD7627a */
328 if (machine_is_msm7627a_qrd1()) {
329 rc = gpio_tlmm_config(GPIO_CFG(GPIO_WLAN_3V3_EN, 0,
330 GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
331 GPIO_CFG_2MA), GPIO_CFG_ENABLE);
332 if (rc) {
333 pr_err("%s gpio_tlmm_config 119 failed,error = %d\n",
334 __func__, rc);
335 goto qrd_gpio_fail;
336 }
337 gpio_set_value(GPIO_WLAN_3V3_EN, 0);
338 }
339
340 rc = wlan_switch_regulators(on);
341 if (rc) {
342 pr_err("%s: wlan_switch_regulators error = %d\n",
343 __func__, rc);
344 goto reg_disable;
345 }
346
347 pr_info("WLAN power-down success\n");
348 return 0;
349set_clock_fail:
350 setup_wlan_clock(0);
351set_gpio_fail:
352 setup_wlan_gpio(0);
353gpio_fail:
354 gpio_free(gpio_wlan_sys_rest_en);
355qrd_gpio_fail:
356 gpio_free(GPIO_WLAN_3V3_EN);
357reg_disable:
358 wlan_switch_regulators(0);
359 pr_info("WLAN power-down failed\n");
360 return rc;
361}
362
363int ar600x_wlan_power(bool on)
364{
365 if (on)
366 msm_AR600X_setup_power(on);
367 else
368 msm_AR600X_shutdown_power(on);
369
370 return 0;
371}