Skip to content

Commit 0395ffc

Browse files
Frederic Danisholtmann
authored andcommitted
Bluetooth: hci_bcm: Add PM for BCM devices
Retrieve "shutdown" and "device_wakeup" GPIOs from ACPI. Set device off during platform device enumeration. Set device on only when attached. As driver can be unbound we need to check if the bcm_device still exists before calling GPIO's functions, this is protected using device_list_lock. Signed-off-by: Frederic Danis <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 8599822 commit 0395ffc

File tree

1 file changed

+214
-2
lines changed

1 file changed

+214
-2
lines changed

drivers/bluetooth/hci_bcm.c

Lines changed: 214 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,43 @@
2525
#include <linux/errno.h>
2626
#include <linux/skbuff.h>
2727
#include <linux/firmware.h>
28+
#include <linux/module.h>
29+
#include <linux/acpi.h>
30+
#include <linux/platform_device.h>
31+
#include <linux/clk.h>
32+
#include <linux/gpio/consumer.h>
33+
#include <linux/tty.h>
2834

2935
#include <net/bluetooth/bluetooth.h>
3036
#include <net/bluetooth/hci_core.h>
3137

3238
#include "btbcm.h"
3339
#include "hci_uart.h"
3440

41+
struct bcm_device {
42+
struct list_head list;
43+
44+
struct platform_device *pdev;
45+
46+
const char *name;
47+
struct gpio_desc *device_wakeup;
48+
struct gpio_desc *shutdown;
49+
50+
struct clk *clk;
51+
bool clk_enabled;
52+
};
53+
3554
struct bcm_data {
36-
struct sk_buff *rx_skb;
37-
struct sk_buff_head txq;
55+
struct sk_buff *rx_skb;
56+
struct sk_buff_head txq;
57+
58+
struct bcm_device *dev;
3859
};
3960

61+
/* List of BCM BT UART devices */
62+
static DEFINE_SPINLOCK(bcm_device_list_lock);
63+
static LIST_HEAD(bcm_device_list);
64+
4065
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
4166
{
4267
struct hci_dev *hdev = hu->hdev;
@@ -86,9 +111,41 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
86111
return 0;
87112
}
88113

114+
/* bcm_device_exists should be protected by bcm_device_list_lock */
115+
static bool bcm_device_exists(struct bcm_device *device)
116+
{
117+
struct list_head *p;
118+
119+
list_for_each(p, &bcm_device_list) {
120+
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
121+
122+
if (device == dev)
123+
return true;
124+
}
125+
126+
return false;
127+
}
128+
129+
static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
130+
{
131+
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
132+
clk_enable(dev->clk);
133+
134+
gpiod_set_value_cansleep(dev->shutdown, powered);
135+
gpiod_set_value_cansleep(dev->device_wakeup, powered);
136+
137+
if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled)
138+
clk_disable(dev->clk);
139+
140+
dev->clk_enabled = powered;
141+
142+
return 0;
143+
}
144+
89145
static int bcm_open(struct hci_uart *hu)
90146
{
91147
struct bcm_data *bcm;
148+
struct list_head *p;
92149

93150
BT_DBG("hu %p", hu);
94151

@@ -99,6 +156,26 @@ static int bcm_open(struct hci_uart *hu)
99156
skb_queue_head_init(&bcm->txq);
100157

101158
hu->priv = bcm;
159+
160+
spin_lock(&bcm_device_list_lock);
161+
list_for_each(p, &bcm_device_list) {
162+
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
163+
164+
/* Retrieve saved bcm_device based on parent of the
165+
* platform device (saved during device probe) and
166+
* parent of tty device used by hci_uart
167+
*/
168+
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
169+
bcm->dev = dev;
170+
break;
171+
}
172+
}
173+
174+
if (bcm->dev)
175+
bcm_gpio_set_power(bcm->dev, true);
176+
177+
spin_unlock(&bcm_device_list_lock);
178+
102179
return 0;
103180
}
104181

@@ -108,6 +185,12 @@ static int bcm_close(struct hci_uart *hu)
108185

109186
BT_DBG("hu %p", hu);
110187

188+
/* Protect bcm->dev against removal of the device or driver */
189+
spin_lock(&bcm_device_list_lock);
190+
if (bcm_device_exists(bcm->dev))
191+
bcm_gpio_set_power(bcm->dev, false);
192+
spin_unlock(&bcm_device_list_lock);
193+
111194
skb_queue_purge(&bcm->txq);
112195
kfree_skb(bcm->rx_skb);
113196
kfree(bcm);
@@ -232,6 +315,113 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
232315
return skb_dequeue(&bcm->txq);
233316
}
234317

318+
static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false };
319+
static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
320+
321+
static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
322+
{ "device-wakeup-gpios", &device_wakeup_gpios, 1 },
323+
{ "shutdown-gpios", &shutdown_gpios, 1 },
324+
{ },
325+
};
326+
327+
static int bcm_acpi_probe(struct bcm_device *dev)
328+
{
329+
struct platform_device *pdev = dev->pdev;
330+
const struct acpi_device_id *id;
331+
struct gpio_desc *gpio;
332+
int ret;
333+
334+
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
335+
if (!id)
336+
return -ENODEV;
337+
338+
/* Retrieve GPIO data */
339+
dev->name = dev_name(&pdev->dev);
340+
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
341+
acpi_bcm_default_gpios);
342+
if (ret)
343+
return ret;
344+
345+
dev->clk = devm_clk_get(&pdev->dev, NULL);
346+
347+
gpio = devm_gpiod_get(&pdev->dev, "device-wakeup");
348+
if (!IS_ERR(gpio)) {
349+
ret = gpiod_direction_output(gpio, 0);
350+
if (ret)
351+
return ret;
352+
dev->device_wakeup = gpio;
353+
}
354+
355+
gpio = devm_gpiod_get(&pdev->dev, "shutdown");
356+
if (!IS_ERR(gpio)) {
357+
ret = gpiod_direction_output(gpio, 0);
358+
if (ret)
359+
return ret;
360+
dev->shutdown = gpio;
361+
}
362+
363+
/* Make sure at-least one of the GPIO is defined and that
364+
* a name is specified for this instance
365+
*/
366+
if ((!dev->device_wakeup && !dev->shutdown) || !dev->name) {
367+
dev_err(&pdev->dev, "invalid platform data\n");
368+
return -EINVAL;
369+
}
370+
371+
return 0;
372+
}
373+
374+
static int bcm_probe(struct platform_device *pdev)
375+
{
376+
struct bcm_device *dev;
377+
struct acpi_device_id *pdata = pdev->dev.platform_data;
378+
int ret;
379+
380+
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
381+
if (!dev)
382+
return -ENOMEM;
383+
384+
dev->pdev = pdev;
385+
386+
if (ACPI_HANDLE(&pdev->dev)) {
387+
ret = bcm_acpi_probe(dev);
388+
if (ret)
389+
return ret;
390+
} else if (pdata) {
391+
dev->name = pdata->id;
392+
} else {
393+
return -ENODEV;
394+
}
395+
396+
platform_set_drvdata(pdev, dev);
397+
398+
dev_info(&pdev->dev, "%s device registered.\n", dev->name);
399+
400+
/* Place this instance on the device list */
401+
spin_lock(&bcm_device_list_lock);
402+
list_add_tail(&dev->list, &bcm_device_list);
403+
spin_unlock(&bcm_device_list_lock);
404+
405+
bcm_gpio_set_power(dev, false);
406+
407+
return 0;
408+
}
409+
410+
static int bcm_remove(struct platform_device *pdev)
411+
{
412+
struct bcm_device *dev = platform_get_drvdata(pdev);
413+
414+
spin_lock(&bcm_device_list_lock);
415+
list_del(&dev->list);
416+
spin_unlock(&bcm_device_list_lock);
417+
418+
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
419+
420+
dev_info(&pdev->dev, "%s device unregistered.\n", dev->name);
421+
422+
return 0;
423+
}
424+
235425
static const struct hci_uart_proto bcm_proto = {
236426
.id = HCI_UART_BCM,
237427
.name = "BCM",
@@ -247,12 +437,34 @@ static const struct hci_uart_proto bcm_proto = {
247437
.dequeue = bcm_dequeue,
248438
};
249439

440+
#ifdef CONFIG_ACPI
441+
static const struct acpi_device_id bcm_acpi_match[] = {
442+
{ "BCM2E39", 0 },
443+
{ "BCM2E67", 0 },
444+
{ },
445+
};
446+
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
447+
#endif
448+
449+
static struct platform_driver bcm_driver = {
450+
.probe = bcm_probe,
451+
.remove = bcm_remove,
452+
.driver = {
453+
.name = "hci_bcm",
454+
.acpi_match_table = ACPI_PTR(bcm_acpi_match),
455+
},
456+
};
457+
250458
int __init bcm_init(void)
251459
{
460+
platform_driver_register(&bcm_driver);
461+
252462
return hci_uart_register_proto(&bcm_proto);
253463
}
254464

255465
int __exit bcm_deinit(void)
256466
{
467+
platform_driver_unregister(&bcm_driver);
468+
257469
return hci_uart_unregister_proto(&bcm_proto);
258470
}

0 commit comments

Comments
 (0)