diff options
Diffstat (limited to 'drivers/bluetooth/hci_bcm.c')
-rw-r--r-- | drivers/bluetooth/hci_bcm.c | 133 |
1 files changed, 115 insertions, 18 deletions
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 6a662d0161b4..e2540113d0da 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -27,6 +27,8 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/acpi.h> +#include <linux/of.h> +#include <linux/property.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/gpio/consumer.h> @@ -34,6 +36,7 @@ #include <linux/interrupt.h> #include <linux/dmi.h> #include <linux/pm_runtime.h> +#include <linux/serdev.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -41,11 +44,15 @@ #include "btbcm.h" #include "hci_uart.h" +#define BCM_NULL_PKT 0x00 +#define BCM_NULL_SIZE 0 + #define BCM_LM_DIAG_PKT 0x07 #define BCM_LM_DIAG_SIZE 63 #define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */ +/* platform device driver resources */ struct bcm_device { struct list_head list; @@ -59,6 +66,7 @@ struct bcm_device { bool clk_enabled; u32 init_speed; + u32 oper_speed; int irq; u8 irq_polarity; @@ -68,6 +76,12 @@ struct bcm_device { #endif }; +/* serdev driver resources */ +struct bcm_serdev { + struct hci_uart hu; +}; + +/* generic bcm uart resources */ struct bcm_data { struct sk_buff *rx_skb; struct sk_buff_head txq; @@ -79,6 +93,14 @@ struct bcm_data { static DEFINE_MUTEX(bcm_device_lock); static LIST_HEAD(bcm_device_list); +static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) +{ + if (hu->serdev) + serdev_device_set_baudrate(hu->serdev, speed); + else + hci_uart_set_baudrate(hu, speed); +} + static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) { struct hci_dev *hdev = hu->hdev; @@ -176,7 +198,7 @@ static irqreturn_t bcm_host_wake(int irq, void *data) static int bcm_request_irq(struct bcm_data *bcm) { struct bcm_device *bdev = bcm->dev; - int err = 0; + int err; /* If this is not a platform device, do not enable PM functionalities */ mutex_lock(&bcm_device_lock); @@ -185,21 +207,23 @@ static int bcm_request_irq(struct bcm_data *bcm) goto unlock; } - if (bdev->irq > 0) { - err = devm_request_irq(&bdev->pdev->dev, bdev->irq, - bcm_host_wake, IRQF_TRIGGER_RISING, - "host_wake", bdev); - if (err) - goto unlock; + if (bdev->irq <= 0) { + err = -EOPNOTSUPP; + goto unlock; + } + + err = devm_request_irq(&bdev->pdev->dev, bdev->irq, bcm_host_wake, + IRQF_TRIGGER_RISING, "host_wake", bdev); + if (err) + goto unlock; - device_init_wakeup(&bdev->pdev->dev, true); + device_init_wakeup(&bdev->pdev->dev, true); - pm_runtime_set_autosuspend_delay(&bdev->pdev->dev, - BCM_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(&bdev->pdev->dev); - pm_runtime_set_active(&bdev->pdev->dev); - pm_runtime_enable(&bdev->pdev->dev); - } + pm_runtime_set_autosuspend_delay(&bdev->pdev->dev, + BCM_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&bdev->pdev->dev); + pm_runtime_set_active(&bdev->pdev->dev); + pm_runtime_enable(&bdev->pdev->dev); unlock: mutex_unlock(&bcm_device_lock); @@ -287,6 +311,14 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; + /* If this is a serdev defined device, then only use + * serdev open primitive and skip the rest. + */ + if (hu->serdev) { + serdev_device_open(hu->serdev); + goto out; + } + if (!hu->tty->dev) goto out; @@ -301,6 +333,7 @@ static int bcm_open(struct hci_uart *hu) if (hu->tty->dev->parent == dev->pdev->dev.parent) { bcm->dev = dev; hu->init_speed = dev->init_speed; + hu->oper_speed = dev->oper_speed; #ifdef CONFIG_PM dev->hu = hu; #endif @@ -321,6 +354,12 @@ static int bcm_close(struct hci_uart *hu) bt_dev_dbg(hu->hdev, "hu %p", hu); + /* If this is a serdev defined device, only use serdev + * close primitive and then continue as usual. + */ + if (hu->serdev) + serdev_device_close(hu->serdev); + /* Protect bcm->dev against removal of the device or driver */ mutex_lock(&bcm_device_lock); if (bcm_device_exists(bdev)) { @@ -396,7 +435,7 @@ static int bcm_setup(struct hci_uart *hu) speed = 0; if (speed) - hci_uart_set_baudrate(hu, speed); + host_set_baudrate(hu, speed); /* Operational speed if any */ if (hu->oper_speed) @@ -409,7 +448,7 @@ static int bcm_setup(struct hci_uart *hu) if (speed) { err = bcm_set_baudrate(hu, speed); if (!err) - hci_uart_set_baudrate(hu, speed); + host_set_baudrate(hu, speed); } finalize: @@ -432,11 +471,19 @@ finalize: .lsize = 0, \ .maxlen = BCM_LM_DIAG_SIZE +#define BCM_RECV_NULL \ + .type = BCM_NULL_PKT, \ + .hlen = BCM_NULL_SIZE, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = BCM_NULL_SIZE + static const struct h4_recv_pkt bcm_recv_pkts[] = { { H4_RECV_ACL, .recv = hci_recv_frame }, { H4_RECV_SCO, .recv = hci_recv_frame }, { H4_RECV_EVENT, .recv = hci_recv_frame }, { BCM_RECV_LM_DIAG, .recv = hci_recv_diag }, + { BCM_RECV_NULL, .recv = hci_recv_diag }, }; static int bcm_recv(struct hci_uart *hu, const void *data, int count) @@ -697,8 +744,10 @@ static int bcm_resource(struct acpi_resource *ares, void *data) case ACPI_RESOURCE_TYPE_SERIAL_BUS: sb = &ares->data.uart_serial_bus; - if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART) + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART) { dev->init_speed = sb->default_baud_rate; + dev->oper_speed = 4000000; + } break; default: @@ -851,7 +900,6 @@ static const struct hci_uart_proto bcm_proto = { .name = "Broadcom", .manufacturer = 15, .init_speed = 115200, - .oper_speed = 4000000, .open = bcm_open, .close = bcm_close, .flush = bcm_flush, @@ -901,9 +949,57 @@ static struct platform_driver bcm_driver = { }, }; +static int bcm_serdev_probe(struct serdev_device *serdev) +{ + struct bcm_serdev *bcmdev; + u32 speed; + int err; + + bcmdev = devm_kzalloc(&serdev->dev, sizeof(*bcmdev), GFP_KERNEL); + if (!bcmdev) + return -ENOMEM; + + bcmdev->hu.serdev = serdev; + serdev_device_set_drvdata(serdev, bcmdev); + + err = device_property_read_u32(&serdev->dev, "max-speed", &speed); + if (!err) + bcmdev->hu.oper_speed = speed; + + return hci_uart_register_device(&bcmdev->hu, &bcm_proto); +} + +static void bcm_serdev_remove(struct serdev_device *serdev) +{ + struct bcm_serdev *bcmdev = serdev_device_get_drvdata(serdev); + + hci_uart_unregister_device(&bcmdev->hu); +} + +#ifdef CONFIG_OF +static const struct of_device_id bcm_bluetooth_of_match[] = { + { .compatible = "brcm,bcm43438-bt" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match); +#endif + +static struct serdev_device_driver bcm_serdev_driver = { + .probe = bcm_serdev_probe, + .remove = bcm_serdev_remove, + .driver = { + .name = "hci_uart_bcm", + .of_match_table = of_match_ptr(bcm_bluetooth_of_match), + }, +}; + int __init bcm_init(void) { + /* For now, we need to keep both platform device + * driver (ACPI generated) and serdev driver (DT). + */ platform_driver_register(&bcm_driver); + serdev_device_driver_register(&bcm_serdev_driver); return hci_uart_register_proto(&bcm_proto); } @@ -911,6 +1007,7 @@ int __init bcm_init(void) int __exit bcm_deinit(void) { platform_driver_unregister(&bcm_driver); + serdev_device_driver_unregister(&bcm_serdev_driver); return hci_uart_unregister_proto(&bcm_proto); } |