blob: d7c69dba31e43c66316cfc6430b634e79034a9ad [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>
26
27static struct of_device_id ar3002_match_table[] = {
28 { .compatible = "qca,ar3002" },
29 {}
30};
31
32static int bt_reset_gpio;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34static bool previous;
35
Pradeep Panigrahiedb41802013-02-22 10:20:20 +053036static int bluetooth_power(int on)
37{
38 int rc;
39
40 pr_debug("%s bt_gpio= %d\n", __func__, bt_reset_gpio);
41 if (on) {
42 rc = gpio_direction_output(bt_reset_gpio, 1);
43 if (rc) {
44 pr_err("%s: Unable to set direction\n", __func__);
45 return rc;
46 }
47 msleep(100);
48 } else {
49 gpio_set_value(bt_reset_gpio, 0);
50 rc = gpio_direction_input(bt_reset_gpio);
51 if (rc) {
52 pr_err("%s: Unable to set direction\n", __func__);
53 return rc;
54 }
55 msleep(100);
56 }
57 return 0;
58}
59
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060static int bluetooth_toggle_radio(void *data, bool blocked)
61{
62 int ret = 0;
63 int (*power_control)(int enable);
64
65 power_control = data;
66 if (previous != blocked)
67 ret = (*power_control)(!blocked);
68 if (!ret)
69 previous = blocked;
70 return ret;
71}
72
73static const struct rfkill_ops bluetooth_power_rfkill_ops = {
74 .set_block = bluetooth_toggle_radio,
75};
76
77static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
78{
79 struct rfkill *rfkill;
80 int ret;
81
82 rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
83 &bluetooth_power_rfkill_ops,
84 pdev->dev.platform_data);
85
86 if (!rfkill) {
87 dev_err(&pdev->dev, "rfkill allocate failed\n");
88 return -ENOMEM;
89 }
90
91 /* force Bluetooth off during init to allow for user control */
92 rfkill_init_sw_state(rfkill, 1);
93 previous = 1;
94
95 ret = rfkill_register(rfkill);
96 if (ret) {
97 dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
98 rfkill_destroy(rfkill);
99 return ret;
100 }
101
102 platform_set_drvdata(pdev, rfkill);
103
104 return 0;
105}
106
107static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
108{
109 struct rfkill *rfkill;
110
111 dev_dbg(&pdev->dev, "%s\n", __func__);
112
113 rfkill = platform_get_drvdata(pdev);
114 if (rfkill)
115 rfkill_unregister(rfkill);
116 rfkill_destroy(rfkill);
117 platform_set_drvdata(pdev, NULL);
118}
119
120static int __devinit bt_power_probe(struct platform_device *pdev)
121{
122 int ret = 0;
123
124 dev_dbg(&pdev->dev, "%s\n", __func__);
125
126 if (!pdev->dev.platform_data) {
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530127 /* Update the platform data if the
128 device node exists as part of device tree.*/
129 if (pdev->dev.of_node) {
130 pdev->dev.platform_data = bluetooth_power;
131 } else {
132 dev_err(&pdev->dev, "device node not set\n");
133 return -ENOSYS;
134 }
135 }
136 if (pdev->dev.of_node) {
137 bt_reset_gpio = of_get_named_gpio(pdev->dev.of_node,
138 "qca,bt-reset-gpio", 0);
139 if (bt_reset_gpio < 0) {
140 pr_err("bt-reset-gpio not available");
141 return bt_reset_gpio;
142 }
143 }
144
145 ret = gpio_request(bt_reset_gpio, "bt sys_rst_n");
146 if (ret) {
147 pr_err("%s: unable to request gpio %d (%d)\n",
148 __func__, bt_reset_gpio, ret);
149 return ret;
150 }
151
152 /* When booting up, de-assert BT reset pin */
153 ret = gpio_direction_output(bt_reset_gpio, 0);
154 if (ret) {
155 pr_err("%s: Unable to set direction\n", __func__);
156 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 }
158
159 ret = bluetooth_power_rfkill_probe(pdev);
160
161 return ret;
162}
163
164static int __devexit bt_power_remove(struct platform_device *pdev)
165{
166 dev_dbg(&pdev->dev, "%s\n", __func__);
167
168 bluetooth_power_rfkill_remove(pdev);
169
170 return 0;
171}
172
173static struct platform_driver bt_power_driver = {
174 .probe = bt_power_probe,
175 .remove = __devexit_p(bt_power_remove),
176 .driver = {
177 .name = "bt_power",
178 .owner = THIS_MODULE,
Pradeep Panigrahiedb41802013-02-22 10:20:20 +0530179 .of_match_table = ar3002_match_table,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 },
181};
182
183static int __init bluetooth_power_init(void)
184{
185 int ret;
186
187 ret = platform_driver_register(&bt_power_driver);
188 return ret;
189}
190
191static void __exit bluetooth_power_exit(void)
192{
193 platform_driver_unregister(&bt_power_driver);
194}
195
196MODULE_LICENSE("GPL v2");
197MODULE_DESCRIPTION("MSM Bluetooth power control driver");
198MODULE_VERSION("1.40");
199
200module_init(bluetooth_power_init);
201module_exit(bluetooth_power_exit);