aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth/ti_bluesleep.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/ti_bluesleep.c')
-rw-r--r--drivers/bluetooth/ti_bluesleep.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/drivers/bluetooth/ti_bluesleep.c b/drivers/bluetooth/ti_bluesleep.c
new file mode 100644
index 00000000000..d86fd261e67
--- /dev/null
+++ b/drivers/bluetooth/ti_bluesleep.c
@@ -0,0 +1,371 @@
1/*
2 * TI Bluesleep driver
3 * Kernel module responsible for Wake up of Host
4 * Copyright (C) 2009-2010 Texas Instruments
5
6
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * for more details.
19 *
20 * Copyright (C) 2006-2007 - Motorola
21 * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
22 *
23 * Date Author Comment
24 * ----------- -------------- --------------------------------
25 * 2006-Apr-28 Motorola The kernel module for running the Bluetooth(R)
26 * Sleep-Mode Protocol from the Host side
27 * 2006-Sep-08 Motorola Added workqueue for handling sleep work.
28 * 2007-Jan-24 Motorola Added mbm_handle_ioi() call to ISR.
29 * 2009-Aug-10 Motorola Changed "add_timer" to "mod_timer" to solve
30 * race when flurry of queued work comes in.
31*/
32
33#include <linux/module.h> /* kernel module definitions */
34#include <linux/errno.h>
35#include <linux/init.h>
36#include <linux/interrupt.h>
37#include <linux/kernel.h>
38#include <linux/notifier.h>
39#include <linux/proc_fs.h>
40#include <linux/spinlock.h>
41#include <linux/timer.h>
42#include <linux/uaccess.h>
43#include <linux/version.h>
44#include <linux/workqueue.h>
45#include <linux/platform_device.h>
46
47#include <linux/irq.h>
48#include <linux/ioport.h>
49#include <linux/param.h>
50#include <linux/bitops.h>
51#include <linux/termios.h>
52#include <linux/wakelock.h>
53#include <mach/gpio.h>
54#include <linux/serial_core.h>
55#include <linux/tegra_uart.h>
56
57#include <net/bluetooth/bluetooth.h>
58#include <net/bluetooth/hci_core.h> /* event notifications */
59#include "hci_uart.h"
60
61/*
62 * Defines
63 */
64
65#define VERSION "1.1"
66
67#define POLARITY_LOW 0
68#define POLARITY_HIGH 1
69
70struct bluesleep_info {
71 unsigned host_wake_irq;
72 struct uart_port *uport;
73 struct wake_lock wake_lock;
74 int irq_polarity;
75 int has_ext_wake;
76};
77
78
79/* state variable names and bit positions */
80#define FLAG_RESET 0x00
81#define BT_ACTIVE 0x02
82#define BT_SUSPEND 0x04
83
84
85/* work function */
86static void hostwake_sleep_work(struct work_struct *work);
87
88/* work queue */
89DECLARE_DELAYED_WORK(ti_sleep_workqueue, hostwake_sleep_work);
90
91/* Macros for handling sleep work */
92#define hostwake_workqueue() schedule_delayed_work(&ti_sleep_workqueue, 0)
93
94static struct bluesleep_info *bsi;
95
96/* module usage */
97static atomic_t open_count = ATOMIC_INIT(1);
98
99/*
100 * Global variables
101 */
102/** Global state flags */
103static unsigned long flags;
104
105/** Tasklet to respond to change in hostwake line */
106static struct tasklet_struct hostwake_task;
107
108/** Lock for state transitions */
109static spinlock_t rw_lock;
110
111/*
112 * Local functions
113 */
114static void hsuart_power(int on)
115{
116 pr_debug("%s", __func__);
117
118 if (on) {
119 tegra_uart_request_clock_on(bsi->uport);
120 tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS);
121 } else {
122 tegra_uart_set_mctrl(bsi->uport, 0);
123 tegra_uart_request_clock_off(bsi->uport);
124 }
125}
126
127
128
129/**
130 * @brief@ main sleep work handling function which update the flags
131 * and activate and deactivate UART .
132 */
133
134static void hostwake_sleep_work(struct work_struct *work)
135{
136 pr_debug("%s", __func__);
137 free_irq(bsi->host_wake_irq, "tibluesleep");
138 /*Activating UART */
139 if (test_bit(BT_SUSPEND, &flags)) {
140 BT_DBG("Activate UART");
141 hsuart_power(1);
142
143 }
144 bsi->has_ext_wake = 0;
145 clear_bit(BT_SUSPEND, &flags);
146 set_bit(BT_ACTIVE, &flags);
147
148}
149
150
151/**
152 * A tasklet function that runs in tasklet context
153 * @param data Not used.
154 */
155static void bluesleep_hostwake_task(unsigned long data)
156{
157 pr_debug("%s", __func__);
158 disable_irq(bsi->host_wake_irq);
159 spin_lock(&rw_lock);
160 hostwake_workqueue();
161 spin_unlock(&rw_lock);
162}
163
164
165/**
166 * Schedules a tasklet to run when receiving an interrupt on the
167 * <code>HOST_WAKE</code> GPIO pin.
168 * @param irq Not used.
169 * @param dev_id Not used.
170 */
171static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id)
172{
173
174 pr_debug("%s", __func__);
175 /* schedule a tasklet to handle the change in the host wake line */
176 bsi->has_ext_wake = 1;
177 tasklet_schedule(&hostwake_task);
178 return IRQ_HANDLED;
179}
180
181/**
182 * Starts the Sleep-Mode Protocol on the Host.
183 * @return On success, 0. On error, -1, and <code>errno</code> is set
184 * appropriately.
185 */
186
187 int bluesleep_start(struct uart_port *uport)
188{
189 int retval;
190 bsi->uport = uport;
191 pr_debug("%s", __func__);
192
193 if (test_bit(BT_SUSPEND, &flags)) {
194 BT_DBG("bluesleep_acquire irq");
195 if (bsi->irq_polarity == POLARITY_LOW) {
196 retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
197 IRQF_DISABLED | IRQF_TRIGGER_FALLING,
198 "bluetooth hostwake", "tibluesleep");
199 } else {
200 retval = request_irq(bsi->host_wake_irq, bluesleep_hostwake_isr,
201 IRQF_DISABLED | IRQF_TRIGGER_RISING,
202 "bluetooth hostwake", "tibluesleep");
203 }
204 if (retval < 0) {
205 BT_ERR("Couldn't acquire BT_HOST_WAKE IRQ");
206 goto fail;
207 }
208
209 retval = enable_irq_wake(bsi->host_wake_irq);
210 if (retval < 0) {
211 BT_ERR("Couldn't enable BT_HOST_WAKE as wakeup"
212 "interrupt retval %d\n", retval);
213 free_irq(bsi->host_wake_irq, NULL);
214 goto fail;
215 }
216 }
217
218 return 0;
219fail:
220 atomic_inc(&open_count);
221 return retval;
222}
223
224/**
225 * Stops the Sleep-Mode Protocol on the Host.
226 */
227 void bluesleep_stop(void)
228{
229
230 pr_debug("%s", __func__);
231
232 if (disable_irq_wake(bsi->host_wake_irq))
233 BT_ERR("Couldn't disable hostwake IRQ wakeup mode\n");
234
235 free_irq(bsi->host_wake_irq, NULL);
236}
237
238static int bluesleep_probe(struct platform_device *pdev)
239{
240 int ret;
241 struct resource *res;
242
243 bsi = kzalloc(sizeof(struct bluesleep_info), GFP_KERNEL);
244 if (!bsi)
245 return -ENOMEM;
246
247 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
248 "host_wake");
249 if (!res) {
250 BT_ERR("couldn't find host_wake irq\n");
251 ret = -ENODEV;
252 goto free_bsi;
253 }
254
255 bsi->host_wake_irq = res->start;
256
257 if (bsi->host_wake_irq < 0) {
258 BT_ERR("couldn't find host_wake irq");
259 ret = -ENODEV;
260 goto free_bsi;
261 }
262 if (res->flags & IORESOURCE_IRQ_LOWEDGE)
263 bsi->irq_polarity = POLARITY_LOW;/*low edge (falling edge)*/
264 else
265 bsi->irq_polarity = POLARITY_HIGH;/*anything else*/
266
267 wake_lock_init(&bsi->wake_lock, WAKE_LOCK_SUSPEND, "bluesleep");
268 clear_bit(BT_SUSPEND, &flags);
269 set_bit(BT_ACTIVE, &flags);
270
271 return 0;
272
273free_bsi:
274 kfree(bsi);
275 return ret;
276}
277
278static int bluesleep_remove(struct platform_device *pdev)
279{
280 pr_debug("%s", __func__);
281 kfree(bsi);
282 return 0;
283}
284
285
286static int bluesleep_resume(struct platform_device *pdev)
287{
288
289 pr_debug("%s", __func__);
290 if (test_bit(BT_SUSPEND, &flags)) {
291
292 if ((bsi->uport != NULL) && (bsi->has_ext_wake)) {
293 tegra_uart_request_clock_on(bsi->uport);
294 tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS);
295 }
296 clear_bit(BT_SUSPEND, &flags);
297 set_bit(BT_ACTIVE, &flags);
298 }
299
300 return 0;
301}
302
303static int bluesleep_suspend(struct platform_device *pdev, pm_message_t state)
304{
305 pr_debug("%s", __func__);
306 set_bit(BT_SUSPEND, &flags);
307 return 0;
308}
309
310static struct platform_driver bluesleep_driver = {
311 .probe = bluesleep_probe,
312 .remove = bluesleep_remove,
313 .suspend = bluesleep_suspend,
314 .resume = bluesleep_resume,
315 .driver = {
316 .name = "tibluesleep",
317 .owner = THIS_MODULE,
318 },
319};
320/**
321 * Initializes the module.
322 * @return On success, 0. On error, -1, and <code>errno</code> is set
323 * appropriately.
324 */
325static int __init bluesleep_init(void)
326{
327 int retval;
328
329 BT_INFO("BlueSleep Mode Driver Ver %s", VERSION);
330
331 retval = platform_driver_register(&bluesleep_driver);
332 if (retval)
333 goto fail;
334
335 if (bsi == NULL)
336 return 0;
337
338 flags = FLAG_RESET; /* clear all status bits */
339
340 /* Initialize spinlock. */
341 spin_lock_init(&rw_lock);
342
343 /* initialize host wake tasklet */
344 tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0);
345
346 return 0;
347fail:
348 return retval;
349}
350
351/**
352 * Cleans up the module.
353 */
354
355static void __exit bluesleep_exit(void)
356{
357 if (bsi == NULL)
358 return;
359 /* assert bt wake */
360 free_irq(bsi->host_wake_irq, NULL);
361 platform_driver_unregister(&bluesleep_driver);
362
363}
364
365module_init(bluesleep_init);
366module_exit(bluesleep_exit);
367
368MODULE_DESCRIPTION("TI Bluetooth Sleep Mode Driver ver %s " VERSION);
369#ifdef MODULE_LICENSE
370MODULE_LICENSE("GPL");
371#endif