blob: 13c9080d2389eb3615087b1f63ede3bf6a50a326 [file] [log] [blame]
Pradeep Panigrahiedb41802013-02-22 10:20:20 +05301/* Copyright (c) 2009-2010, 2013 The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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/*
13 * Bluetooth Power Switch Module
14 * controls power to external Bluetooth device
15 * with interface to power management device
16 */
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/platform_device.h>
22#include <linux/rfkill.h>
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053023#include <linux/gpio.h>
24#include <linux/of_gpio.h>
25#include <linux/delay.h>
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070026#include <linux/bluetooth-power.h>
27#include <linux/slab.h>
28#include <linux/regulator/consumer.h>
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053029
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070030#define BT_PWR_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
31#define BT_PWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n" , __func__ , ## arg)
32#define BT_PWR_ERR(fmt, arg...) pr_err("%s: " fmt "\n" , __func__ , ## arg)
33
34
35static struct of_device_id bt_power_match_table[] = {
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053036 { .compatible = "qca,ar3002" },
37 {}
38};
39
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070040static struct bluetooth_power_platform_data *bt_power_pdata;
41static struct platform_device *btpdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042static bool previous;
43
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070044static int bt_vreg_init(struct bt_power_vreg_data *vreg)
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053045{
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070046 int rc = 0;
47 struct device *dev = &btpdev->dev;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053048
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070049 BT_PWR_DBG("vreg_get for : %s", vreg->name);
50
51 /* Get the regulator handle */
52 vreg->reg = regulator_get(dev, vreg->name);
53 if (IS_ERR(vreg->reg)) {
54 rc = PTR_ERR(vreg->reg);
55 pr_err("%s: regulator_get(%s) failed. rc=%d\n",
56 __func__, vreg->name, rc);
57 goto out;
58 }
59
60 if ((regulator_count_voltages(vreg->reg) > 0)
61 && (vreg->low_vol_level) && (vreg->high_vol_level))
62 vreg->set_voltage_sup = 1;
63
64out:
65 return rc;
66}
67
68static int bt_vreg_enable(struct bt_power_vreg_data *vreg)
69{
70 int rc = 0;
71
72 BT_PWR_DBG("vreg_en for : %s", vreg->name);
73
74 if (!vreg->is_enabled) {
75 if (vreg->set_voltage_sup) {
76 rc = regulator_set_voltage(vreg->reg,
77 vreg->low_vol_level,
78 vreg->high_vol_level);
79 if (rc < 0) {
80 BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
81 vreg->name, rc);
82 goto out;
83 }
84 }
85
86 rc = regulator_enable(vreg->reg);
87 if (rc < 0) {
88 BT_PWR_ERR("regulator_enable(%s) failed. rc=%d\n",
89 vreg->name, rc);
90 goto out;
91 }
92 vreg->is_enabled = true;
93 }
94out:
95 return rc;
96}
97
98static int bt_vreg_disable(struct bt_power_vreg_data *vreg)
99{
100 int rc = 0;
101
102 if (!vreg)
103 return rc;
104
105 BT_PWR_DBG("vreg_disable for : %s", vreg->name);
106
107 if (vreg->is_enabled) {
108 rc = regulator_disable(vreg->reg);
109 if (rc < 0) {
110 BT_PWR_ERR("regulator_disable(%s) failed. rc=%d\n",
111 vreg->name, rc);
112 goto out;
113 }
114 vreg->is_enabled = false;
115
116 if (vreg->set_voltage_sup) {
117 /* Set the min voltage to 0 */
118 rc = regulator_set_voltage(vreg->reg,
119 0,
120 vreg->high_vol_level);
121 if (rc < 0) {
122 BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
123 vreg->name, rc);
124 goto out;
125
126 }
127 }
128 }
129out:
130 return rc;
131}
132
133static int bt_configure_vreg(struct bt_power_vreg_data *vreg)
134{
135 int rc = 0;
136
137 BT_PWR_DBG("config %s", vreg->name);
138
139 /* Get the regulator handle for vreg */
140 if (!(vreg->reg)) {
141 rc = bt_vreg_init(vreg);
142 if (rc < 0)
143 return rc;
144 }
145 rc = bt_vreg_enable(vreg);
146
147 return rc;
148}
149
150static int bt_configure_gpios(int on)
151{
152 int rc = 0;
153 int bt_reset_gpio = bt_power_pdata->bt_gpio_sys_rst;
154
155 BT_PWR_DBG("%s bt_gpio= %d on: %d", __func__, bt_reset_gpio, on);
156
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530157 if (on) {
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700158 rc = gpio_request(bt_reset_gpio, "bt_sys_rst_n");
159 if (rc) {
160 BT_PWR_ERR("unable to request gpio %d (%d)\n",
161 bt_reset_gpio, rc);
162 return rc;
163 }
164
165 rc = gpio_direction_output(bt_reset_gpio, 0);
166 if (rc) {
167 BT_PWR_ERR("Unable to set direction\n");
168 return rc;
169 }
170
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530171 rc = gpio_direction_output(bt_reset_gpio, 1);
172 if (rc) {
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700173 BT_PWR_ERR("Unable to set direction\n");
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530174 return rc;
175 }
176 msleep(100);
177 } else {
178 gpio_set_value(bt_reset_gpio, 0);
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700179
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530180 rc = gpio_direction_input(bt_reset_gpio);
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700181 if (rc)
182 BT_PWR_ERR("Unable to set direction\n");
183
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530184 msleep(100);
185 }
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700186 return rc;
187}
188
189static int bluetooth_power(int on)
190{
191 int rc = 0;
192
193 BT_PWR_DBG("on: %d", on);
194
195 if (on) {
196 if (bt_power_pdata->bt_vdd_io) {
197 rc = bt_configure_vreg(bt_power_pdata->bt_vdd_io);
198 if (rc < 0) {
199 BT_PWR_ERR("bt_power vddio config failed");
200 goto out;
201 }
202 }
203 if (bt_power_pdata->bt_vdd_ldo) {
204 rc = bt_configure_vreg(bt_power_pdata->bt_vdd_ldo);
205 if (rc < 0) {
206 BT_PWR_ERR("bt_power vddldo config failed");
207 goto vdd_ldo_fail;
208 }
209 }
210 if (bt_power_pdata->bt_vdd_pa) {
211 rc = bt_configure_vreg(bt_power_pdata->bt_vdd_pa);
212 if (rc < 0) {
213 BT_PWR_ERR("bt_power vddpa config failed");
214 goto vdd_pa_fail;
215 }
216 }
217 if (bt_power_pdata->bt_chip_pwd) {
218 rc = bt_configure_vreg(bt_power_pdata->bt_chip_pwd);
219 if (rc < 0) {
220 BT_PWR_ERR("bt_power vddldo config failed");
221 goto chip_pwd_fail;
222 }
223 }
224 if (bt_power_pdata->bt_gpio_sys_rst) {
225 rc = bt_configure_gpios(on);
226 if (rc < 0) {
227 BT_PWR_ERR("bt_power gpio config failed");
228 goto gpio_fail;
229 }
230 }
231 } else {
232 bt_configure_gpios(on);
233gpio_fail:
234 if (bt_power_pdata->bt_gpio_sys_rst)
235 gpio_free(bt_power_pdata->bt_gpio_sys_rst);
236 bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
237chip_pwd_fail:
238 bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
239vdd_pa_fail:
240 bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
241vdd_ldo_fail:
242 bt_vreg_disable(bt_power_pdata->bt_vdd_io);
243 }
244
245out:
246 return rc;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530247}
248
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249static int bluetooth_toggle_radio(void *data, bool blocked)
250{
251 int ret = 0;
252 int (*power_control)(int enable);
253
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700254 power_control =
255 ((struct bluetooth_power_platform_data *)data)->bt_power_setup;
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 if (previous != blocked)
258 ret = (*power_control)(!blocked);
259 if (!ret)
260 previous = blocked;
261 return ret;
262}
263
264static const struct rfkill_ops bluetooth_power_rfkill_ops = {
265 .set_block = bluetooth_toggle_radio,
266};
267
268static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
269{
270 struct rfkill *rfkill;
271 int ret;
272
273 rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
274 &bluetooth_power_rfkill_ops,
275 pdev->dev.platform_data);
276
277 if (!rfkill) {
278 dev_err(&pdev->dev, "rfkill allocate failed\n");
279 return -ENOMEM;
280 }
281
282 /* force Bluetooth off during init to allow for user control */
283 rfkill_init_sw_state(rfkill, 1);
284 previous = 1;
285
286 ret = rfkill_register(rfkill);
287 if (ret) {
288 dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
289 rfkill_destroy(rfkill);
290 return ret;
291 }
292
293 platform_set_drvdata(pdev, rfkill);
294
295 return 0;
296}
297
298static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
299{
300 struct rfkill *rfkill;
301
302 dev_dbg(&pdev->dev, "%s\n", __func__);
303
304 rfkill = platform_get_drvdata(pdev);
305 if (rfkill)
306 rfkill_unregister(rfkill);
307 rfkill_destroy(rfkill);
308 platform_set_drvdata(pdev, NULL);
309}
310
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700311#define MAX_PROP_SIZE 32
312static int bt_dt_parse_vreg_info(struct device *dev,
313 struct bt_power_vreg_data **vreg_data, const char *vreg_name)
314{
315 int len, ret = 0;
316 const __be32 *prop;
317 char prop_name[MAX_PROP_SIZE];
318 struct bt_power_vreg_data *vreg;
319 struct device_node *np = dev->of_node;
320
321 BT_PWR_DBG("vreg dev tree parse for %s", vreg_name);
322
323 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
324 if (of_parse_phandle(np, prop_name, 0)) {
325 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
326 if (!vreg) {
327 dev_err(dev, "No memory for vreg: %s\n", vreg_name);
328 ret = -ENOMEM;
329 goto err;
330 }
331
332 vreg->name = vreg_name;
333
334 snprintf(prop_name, MAX_PROP_SIZE,
335 "qcom,%s-voltage-level", vreg_name);
336 prop = of_get_property(np, prop_name, &len);
337 if (!prop || (len != (2 * sizeof(__be32)))) {
338 dev_warn(dev, "%s %s property\n",
339 prop ? "invalid format" : "no", prop_name);
340 } else {
341 vreg->low_vol_level = be32_to_cpup(&prop[0]);
342 vreg->high_vol_level = be32_to_cpup(&prop[1]);
343 }
344
345 *vreg_data = vreg;
346 BT_PWR_DBG("%s: vol=[%d %d]uV\n",
347 vreg->name, vreg->low_vol_level,
348 vreg->high_vol_level);
349 } else
350 BT_PWR_INFO("%s: is not provided in device tree", vreg_name);
351
352err:
353 return ret;
354}
355
356static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
357{
358 int rc;
359
360 BT_PWR_DBG("");
361
362 if (!bt_power_pdata)
363 return -ENOMEM;
364
365 if (pdev->dev.of_node) {
366 bt_power_pdata->bt_gpio_sys_rst =
367 of_get_named_gpio(pdev->dev.of_node,
368 "qca,bt-reset-gpio", 0);
369 if (bt_power_pdata->bt_gpio_sys_rst < 0) {
370 BT_PWR_ERR("bt-reset-gpio not provided in device tree");
371 return bt_power_pdata->bt_gpio_sys_rst;
372 }
373
374 rc = bt_dt_parse_vreg_info(&pdev->dev,
375 &bt_power_pdata->bt_vdd_io,
376 "qca,bt-vdd-io");
377 if (rc < 0)
378 return rc;
379
380 rc = bt_dt_parse_vreg_info(&pdev->dev,
381 &bt_power_pdata->bt_vdd_pa,
382 "qca,bt-vdd-pa");
383 if (rc < 0)
384 return rc;
385
386 rc = bt_dt_parse_vreg_info(&pdev->dev,
387 &bt_power_pdata->bt_vdd_ldo,
388 "qca,bt-vdd-ldo");
389 if (rc < 0)
390 return rc;
391
392 rc = bt_dt_parse_vreg_info(&pdev->dev,
393 &bt_power_pdata->bt_chip_pwd,
394 "qca,bt-chip-pwd");
395 if (rc < 0)
396 return rc;
397
398 }
399
400 bt_power_pdata->bt_power_setup = bluetooth_power;
401
402 return 0;
403}
404
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405static int __devinit bt_power_probe(struct platform_device *pdev)
406{
407 int ret = 0;
408
409 dev_dbg(&pdev->dev, "%s\n", __func__);
410
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700411 bt_power_pdata =
412 kzalloc(sizeof(struct bluetooth_power_platform_data),
413 GFP_KERNEL);
414
415 if (!bt_power_pdata) {
416 BT_PWR_ERR("Failed to allocate memory");
417 return -ENOMEM;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530418 }
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700419
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530420 if (pdev->dev.of_node) {
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700421 ret = bt_power_populate_dt_pinfo(pdev);
422 if (ret < 0) {
423 BT_PWR_ERR("Failed to populate device tree info");
424 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530425 }
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700426 pdev->dev.platform_data = bt_power_pdata;
427 } else if (pdev->dev.platform_data) {
428 /* Optional data set to default if not provided */
429 if (!((struct bluetooth_power_platform_data *)
430 (pdev->dev.platform_data))->bt_power_setup)
431 ((struct bluetooth_power_platform_data *)
432 (pdev->dev.platform_data))->bt_power_setup =
433 bluetooth_power;
434
435 memcpy(bt_power_pdata, pdev->dev.platform_data,
436 sizeof(struct bluetooth_power_platform_data));
437 } else {
438 BT_PWR_ERR("Failed to get platform data");
439 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530440 }
441
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700442 if (bluetooth_power_rfkill_probe(pdev) < 0)
443 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530444
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700445 btpdev = pdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700447 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700449free_pdata:
450 kfree(bt_power_pdata);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451 return ret;
452}
453
454static int __devexit bt_power_remove(struct platform_device *pdev)
455{
456 dev_dbg(&pdev->dev, "%s\n", __func__);
457
458 bluetooth_power_rfkill_remove(pdev);
459
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700460 if (bt_power_pdata->bt_chip_pwd->reg)
461 regulator_put(bt_power_pdata->bt_chip_pwd->reg);
462
463 kfree(bt_power_pdata);
464
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 return 0;
466}
467
468static struct platform_driver bt_power_driver = {
469 .probe = bt_power_probe,
470 .remove = __devexit_p(bt_power_remove),
471 .driver = {
472 .name = "bt_power",
473 .owner = THIS_MODULE,
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700474 .of_match_table = bt_power_match_table,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475 },
476};
477
478static int __init bluetooth_power_init(void)
479{
480 int ret;
481
482 ret = platform_driver_register(&bt_power_driver);
483 return ret;
484}
485
486static void __exit bluetooth_power_exit(void)
487{
488 platform_driver_unregister(&bt_power_driver);
489}
490
491MODULE_LICENSE("GPL v2");
492MODULE_DESCRIPTION("MSM Bluetooth power control driver");
493MODULE_VERSION("1.40");
494
495module_init(bluetooth_power_init);
496module_exit(bluetooth_power_exit);