blob: a619ba67b9d483e82a60144195cc487a4d262d0f [file] [log] [blame]
Jes Sorensen42b4e9e2009-12-16 12:08:15 -05001/*
2 * Toshiba Bluetooth Enable Driver
3 *
4 * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com>
Azael Avalos5d3fc1d2015-03-26 14:56:07 -06005 * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
Jes Sorensen42b4e9e2009-12-16 12:08:15 -05006 *
7 * Thanks to Matthew Garrett for background info on ACPI innards which
8 * normal people aren't meant to understand :-)
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * Note the Toshiba Bluetooth RFKill switch seems to be a strange
15 * fish. It only provides a BT event when the switch is flipped to
16 * the 'on' position. When flipping it to 'off', the USB device is
17 * simply pulled away underneath us, without any BT event being
18 * delivered.
19 */
20
Joe Perches7e334602011-03-29 15:21:52 -070021#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050023#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/types.h>
Lv Zheng8b484632013-12-03 08:49:16 +080027#include <linux/acpi.h>
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050028
Azael Avalos5d3fc1d2015-03-26 14:56:07 -060029#define BT_KILLSWITCH_MASK 0x01
30#define BT_PLUGGED_MASK 0x40
31#define BT_POWER_MASK 0x80
32
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050033MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
34MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
35MODULE_LICENSE("GPL");
36
Azael Avalos84c06912015-05-03 17:42:06 -060037struct toshiba_bluetooth_dev {
38 struct acpi_device *acpi_dev;
39
40 bool killswitch;
41 bool plugged;
42 bool powered;
43};
44
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050045static int toshiba_bt_rfkill_add(struct acpi_device *device);
Rafael J. Wysocki51fac832013-01-24 00:24:48 +010046static int toshiba_bt_rfkill_remove(struct acpi_device *device);
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050047static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050048
49static const struct acpi_device_id bt_device_ids[] = {
50 { "TOS6205", 0},
51 { "", 0},
52};
53MODULE_DEVICE_TABLE(acpi, bt_device_ids);
54
Rafael J. Wysocki3567a4e22012-08-09 23:00:13 +020055#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockid69239a2012-06-27 23:27:48 +020056static int toshiba_bt_resume(struct device *dev);
Rafael J. Wysocki3567a4e22012-08-09 23:00:13 +020057#endif
Rafael J. Wysockid69239a2012-06-27 23:27:48 +020058static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
59
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050060static struct acpi_driver toshiba_bt_rfkill_driver = {
61 .name = "Toshiba BT",
62 .class = "Toshiba",
63 .ids = bt_device_ids,
64 .ops = {
65 .add = toshiba_bt_rfkill_add,
66 .remove = toshiba_bt_rfkill_remove,
67 .notify = toshiba_bt_rfkill_notify,
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050068 },
69 .owner = THIS_MODULE,
Rafael J. Wysockid69239a2012-06-27 23:27:48 +020070 .drv.pm = &toshiba_bt_pm,
Jes Sorensen42b4e9e2009-12-16 12:08:15 -050071};
72
Azael Avalosbb2ea962015-03-26 14:56:05 -060073static int toshiba_bluetooth_present(acpi_handle handle)
74{
75 acpi_status result;
76 u64 bt_present;
77
Azael Avalos18b86962015-03-26 14:56:06 -060078 /*
79 * Some Toshiba laptops may have a fake TOS6205 device in
80 * their ACPI BIOS, so query the _STA method to see if there
81 * is really anything there.
82 */
Azael Avalosbb2ea962015-03-26 14:56:05 -060083 result = acpi_evaluate_integer(handle, "_STA", NULL, &bt_present);
84 if (ACPI_FAILURE(result)) {
85 pr_err("ACPI call to query Bluetooth presence failed");
86 return -ENXIO;
87 } else if (!bt_present) {
88 pr_info("Bluetooth device not present\n");
89 return -ENODEV;
90 }
91
92 return 0;
93}
94
95static int toshiba_bluetooth_status(acpi_handle handle)
96{
97 acpi_status result;
98 u64 status;
99
100 result = acpi_evaluate_integer(handle, "BTST", NULL, &status);
101 if (ACPI_FAILURE(result)) {
102 pr_err("Could not get Bluetooth device status\n");
103 return -ENXIO;
104 }
105
106 pr_info("Bluetooth status %llu\n", status);
107
108 return status;
109}
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500110
111static int toshiba_bluetooth_enable(acpi_handle handle)
112{
Azael Avalos5d3fc1d2015-03-26 14:56:07 -0600113 acpi_status result;
114 bool killswitch;
115 bool powered;
116 bool plugged;
117 int status;
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500118
119 /*
120 * Query ACPI to verify RFKill switch is set to 'on'.
121 * If not, we return silently, no need to report it as
122 * an error.
123 */
Azael Avalos5d3fc1d2015-03-26 14:56:07 -0600124 status = toshiba_bluetooth_status(handle);
125 if (status < 0)
126 return status;
127
128 killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;
129 powered = (status & BT_POWER_MASK) ? true : false;
130 plugged = (status & BT_PLUGGED_MASK) ? true : false;
131
132 if (!killswitch)
133 return 0;
134 /*
135 * This check ensures to only enable the device if it is powered
136 * off or detached, as some recent devices somehow pass the killswitch
137 * test, causing a loop enabling/disabling the device, see bug 93911.
138 */
139 if (powered || plugged)
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500140 return 0;
141
Azael Avalos5d3fc1d2015-03-26 14:56:07 -0600142 result = acpi_evaluate_object(handle, "AUSB", NULL, NULL);
143 if (ACPI_FAILURE(result)) {
144 pr_err("Could not attach USB Bluetooth device\n");
145 return -ENXIO;
146 }
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500147
Azael Avalos5d3fc1d2015-03-26 14:56:07 -0600148 result = acpi_evaluate_object(handle, "BTPO", NULL, NULL);
149 if (ACPI_FAILURE(result)) {
150 pr_err("Could not power ON Bluetooth device\n");
151 return -ENXIO;
152 }
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500153
Azael Avalos5d3fc1d2015-03-26 14:56:07 -0600154 return 0;
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500155}
156
Azael Avalosbb2ea962015-03-26 14:56:05 -0600157static int toshiba_bluetooth_disable(acpi_handle handle)
158{
159 acpi_status result;
160
161 result = acpi_evaluate_object(handle, "BTPF", NULL, NULL);
162 if (ACPI_FAILURE(result)) {
163 pr_err("Could not power OFF Bluetooth device\n");
164 return -ENXIO;
165 }
166
167 result = acpi_evaluate_object(handle, "DUSB", NULL, NULL);
168 if (ACPI_FAILURE(result)) {
169 pr_err("Could not detach USB Bluetooth device\n");
170 return -ENXIO;
171 }
172
173 return 0;
174}
175
Azael Avalos84c06912015-05-03 17:42:06 -0600176/* Helper function */
177static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
178{
179 int status;
180
181 status = toshiba_bluetooth_status(bt_dev->acpi_dev->handle);
182 if (status < 0) {
183 pr_err("Could not sync bluetooth device status\n");
184 return status;
185 }
186
187 bt_dev->killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;
188 bt_dev->plugged = (status & BT_PLUGGED_MASK) ? true : false;
189 bt_dev->powered = (status & BT_POWER_MASK) ? true : false;
190
191 return 0;
192}
193
194/* ACPI driver functions */
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500195static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
196{
197 toshiba_bluetooth_enable(device->handle);
198}
199
Rafael J. Wysocki3567a4e22012-08-09 23:00:13 +0200200#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockid69239a2012-06-27 23:27:48 +0200201static int toshiba_bt_resume(struct device *dev)
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500202{
Rafael J. Wysockid69239a2012-06-27 23:27:48 +0200203 return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500204}
Rafael J. Wysocki3567a4e22012-08-09 23:00:13 +0200205#endif
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500206
207static int toshiba_bt_rfkill_add(struct acpi_device *device)
208{
Azael Avalos84c06912015-05-03 17:42:06 -0600209 struct toshiba_bluetooth_dev *bt_dev;
Azael Avalos18b86962015-03-26 14:56:06 -0600210 int result;
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500211
Azael Avalos18b86962015-03-26 14:56:06 -0600212 result = toshiba_bluetooth_present(device->handle);
213 if (result)
214 return result;
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500215
Azael Avalos18b86962015-03-26 14:56:06 -0600216 pr_info("Toshiba ACPI Bluetooth device driver\n");
217
Azael Avalos84c06912015-05-03 17:42:06 -0600218 bt_dev = kzalloc(sizeof(*bt_dev), GFP_KERNEL);
219 if (!bt_dev)
220 return -ENOMEM;
221 bt_dev->acpi_dev = device;
222 device->driver_data = bt_dev;
223 dev_set_drvdata(&device->dev, bt_dev);
224
225 result = toshiba_bluetooth_sync_status(bt_dev);
226 if (result) {
227 kfree(bt_dev);
228 return result;
229 }
230
Azael Avalos18b86962015-03-26 14:56:06 -0600231 /* Enable the BT device */
232 result = toshiba_bluetooth_enable(device->handle);
233 if (result)
Azael Avalos84c06912015-05-03 17:42:06 -0600234 kfree(bt_dev);
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500235
236 return result;
237}
238
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100239static int toshiba_bt_rfkill_remove(struct acpi_device *device)
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500240{
Azael Avalos84c06912015-05-03 17:42:06 -0600241 struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
242
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500243 /* clean up */
Azael Avalos84c06912015-05-03 17:42:06 -0600244 kfree(bt_dev);
245
Azael Avalos18b86962015-03-26 14:56:06 -0600246 return toshiba_bluetooth_disable(device->handle);
Jes Sorensen42b4e9e2009-12-16 12:08:15 -0500247}
248
Mika Westerberg01d17752012-09-07 10:31:48 +0300249module_acpi_driver(toshiba_bt_rfkill_driver);