blob: 9ccee05c7c20d631ca10daaa7a55210a1dffd827 [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)
Pradeep Panigrahi3f00c062014-01-28 15:53:25 +053033#define BT_VDD_PA_CURRENT 60000
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -070034
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 }
Pradeep Panigrahi3f00c062014-01-28 15:53:25 +0530216 regulator_set_optimum_mode(
217 bt_power_pdata->bt_vdd_pa->reg,
218 BT_VDD_PA_CURRENT);
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700219 }
220 if (bt_power_pdata->bt_chip_pwd) {
221 rc = bt_configure_vreg(bt_power_pdata->bt_chip_pwd);
222 if (rc < 0) {
223 BT_PWR_ERR("bt_power vddldo config failed");
224 goto chip_pwd_fail;
225 }
226 }
227 if (bt_power_pdata->bt_gpio_sys_rst) {
228 rc = bt_configure_gpios(on);
229 if (rc < 0) {
230 BT_PWR_ERR("bt_power gpio config failed");
231 goto gpio_fail;
232 }
233 }
234 } else {
235 bt_configure_gpios(on);
236gpio_fail:
237 if (bt_power_pdata->bt_gpio_sys_rst)
238 gpio_free(bt_power_pdata->bt_gpio_sys_rst);
239 bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
240chip_pwd_fail:
241 bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
242vdd_pa_fail:
243 bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
244vdd_ldo_fail:
245 bt_vreg_disable(bt_power_pdata->bt_vdd_io);
246 }
247
248out:
249 return rc;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530250}
251
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252static int bluetooth_toggle_radio(void *data, bool blocked)
253{
254 int ret = 0;
255 int (*power_control)(int enable);
256
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700257 power_control =
258 ((struct bluetooth_power_platform_data *)data)->bt_power_setup;
259
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 if (previous != blocked)
261 ret = (*power_control)(!blocked);
262 if (!ret)
263 previous = blocked;
264 return ret;
265}
266
267static const struct rfkill_ops bluetooth_power_rfkill_ops = {
268 .set_block = bluetooth_toggle_radio,
269};
270
271static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
272{
273 struct rfkill *rfkill;
274 int ret;
275
276 rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
277 &bluetooth_power_rfkill_ops,
278 pdev->dev.platform_data);
279
280 if (!rfkill) {
281 dev_err(&pdev->dev, "rfkill allocate failed\n");
282 return -ENOMEM;
283 }
284
285 /* force Bluetooth off during init to allow for user control */
286 rfkill_init_sw_state(rfkill, 1);
287 previous = 1;
288
289 ret = rfkill_register(rfkill);
290 if (ret) {
291 dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
292 rfkill_destroy(rfkill);
293 return ret;
294 }
295
296 platform_set_drvdata(pdev, rfkill);
297
298 return 0;
299}
300
301static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
302{
303 struct rfkill *rfkill;
304
305 dev_dbg(&pdev->dev, "%s\n", __func__);
306
307 rfkill = platform_get_drvdata(pdev);
308 if (rfkill)
309 rfkill_unregister(rfkill);
310 rfkill_destroy(rfkill);
311 platform_set_drvdata(pdev, NULL);
312}
313
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700314#define MAX_PROP_SIZE 32
315static int bt_dt_parse_vreg_info(struct device *dev,
316 struct bt_power_vreg_data **vreg_data, const char *vreg_name)
317{
318 int len, ret = 0;
319 const __be32 *prop;
320 char prop_name[MAX_PROP_SIZE];
321 struct bt_power_vreg_data *vreg;
322 struct device_node *np = dev->of_node;
323
324 BT_PWR_DBG("vreg dev tree parse for %s", vreg_name);
325
326 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
327 if (of_parse_phandle(np, prop_name, 0)) {
328 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
329 if (!vreg) {
330 dev_err(dev, "No memory for vreg: %s\n", vreg_name);
331 ret = -ENOMEM;
332 goto err;
333 }
334
335 vreg->name = vreg_name;
336
337 snprintf(prop_name, MAX_PROP_SIZE,
338 "qcom,%s-voltage-level", vreg_name);
339 prop = of_get_property(np, prop_name, &len);
340 if (!prop || (len != (2 * sizeof(__be32)))) {
341 dev_warn(dev, "%s %s property\n",
342 prop ? "invalid format" : "no", prop_name);
343 } else {
344 vreg->low_vol_level = be32_to_cpup(&prop[0]);
345 vreg->high_vol_level = be32_to_cpup(&prop[1]);
346 }
347
348 *vreg_data = vreg;
349 BT_PWR_DBG("%s: vol=[%d %d]uV\n",
350 vreg->name, vreg->low_vol_level,
351 vreg->high_vol_level);
352 } else
353 BT_PWR_INFO("%s: is not provided in device tree", vreg_name);
354
355err:
356 return ret;
357}
358
359static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
360{
361 int rc;
362
363 BT_PWR_DBG("");
364
365 if (!bt_power_pdata)
366 return -ENOMEM;
367
368 if (pdev->dev.of_node) {
369 bt_power_pdata->bt_gpio_sys_rst =
370 of_get_named_gpio(pdev->dev.of_node,
371 "qca,bt-reset-gpio", 0);
372 if (bt_power_pdata->bt_gpio_sys_rst < 0) {
373 BT_PWR_ERR("bt-reset-gpio not provided in device tree");
374 return bt_power_pdata->bt_gpio_sys_rst;
375 }
376
377 rc = bt_dt_parse_vreg_info(&pdev->dev,
378 &bt_power_pdata->bt_vdd_io,
379 "qca,bt-vdd-io");
380 if (rc < 0)
381 return rc;
382
383 rc = bt_dt_parse_vreg_info(&pdev->dev,
384 &bt_power_pdata->bt_vdd_pa,
385 "qca,bt-vdd-pa");
386 if (rc < 0)
387 return rc;
388
389 rc = bt_dt_parse_vreg_info(&pdev->dev,
390 &bt_power_pdata->bt_vdd_ldo,
391 "qca,bt-vdd-ldo");
392 if (rc < 0)
393 return rc;
394
395 rc = bt_dt_parse_vreg_info(&pdev->dev,
396 &bt_power_pdata->bt_chip_pwd,
397 "qca,bt-chip-pwd");
398 if (rc < 0)
399 return rc;
400
401 }
402
403 bt_power_pdata->bt_power_setup = bluetooth_power;
404
405 return 0;
406}
407
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408static int __devinit bt_power_probe(struct platform_device *pdev)
409{
410 int ret = 0;
411
412 dev_dbg(&pdev->dev, "%s\n", __func__);
413
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700414 bt_power_pdata =
415 kzalloc(sizeof(struct bluetooth_power_platform_data),
416 GFP_KERNEL);
417
418 if (!bt_power_pdata) {
419 BT_PWR_ERR("Failed to allocate memory");
420 return -ENOMEM;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530421 }
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700422
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530423 if (pdev->dev.of_node) {
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700424 ret = bt_power_populate_dt_pinfo(pdev);
425 if (ret < 0) {
426 BT_PWR_ERR("Failed to populate device tree info");
427 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530428 }
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700429 pdev->dev.platform_data = bt_power_pdata;
430 } else if (pdev->dev.platform_data) {
431 /* Optional data set to default if not provided */
432 if (!((struct bluetooth_power_platform_data *)
433 (pdev->dev.platform_data))->bt_power_setup)
434 ((struct bluetooth_power_platform_data *)
435 (pdev->dev.platform_data))->bt_power_setup =
436 bluetooth_power;
437
438 memcpy(bt_power_pdata, pdev->dev.platform_data,
439 sizeof(struct bluetooth_power_platform_data));
440 } else {
441 BT_PWR_ERR("Failed to get platform data");
442 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530443 }
444
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700445 if (bluetooth_power_rfkill_probe(pdev) < 0)
446 goto free_pdata;
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530447
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700448 btpdev = pdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700450 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700452free_pdata:
453 kfree(bt_power_pdata);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454 return ret;
455}
456
457static int __devexit bt_power_remove(struct platform_device *pdev)
458{
459 dev_dbg(&pdev->dev, "%s\n", __func__);
460
461 bluetooth_power_rfkill_remove(pdev);
462
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700463 if (bt_power_pdata->bt_chip_pwd->reg)
464 regulator_put(bt_power_pdata->bt_chip_pwd->reg);
465
466 kfree(bt_power_pdata);
467
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468 return 0;
469}
470
471static struct platform_driver bt_power_driver = {
472 .probe = bt_power_probe,
473 .remove = __devexit_p(bt_power_remove),
474 .driver = {
475 .name = "bt_power",
476 .owner = THIS_MODULE,
Ram Mohan Korukonda964484e2013-04-11 00:52:26 -0700477 .of_match_table = bt_power_match_table,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478 },
479};
480
481static int __init bluetooth_power_init(void)
482{
483 int ret;
484
485 ret = platform_driver_register(&bt_power_driver);
486 return ret;
487}
488
489static void __exit bluetooth_power_exit(void)
490{
491 platform_driver_unregister(&bt_power_driver);
492}
493
494MODULE_LICENSE("GPL v2");
495MODULE_DESCRIPTION("MSM Bluetooth power control driver");
496MODULE_VERSION("1.40");
497
498module_init(bluetooth_power_init);
499module_exit(bluetooth_power_exit);