Skip to content

Commit

Permalink
Bluetooth: hci_bcm: Add serdev support
Browse files Browse the repository at this point in the history
Add basic support for Broadcom serial slave devices.
Probe the serial device, retrieve its maximum speed and
register a new hci uart device.

Tested/compatible with bcm43438 (RPi3).

Signed-off-by: Loic Poulain <loic.poulain@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
loicpoulain authored and holtmann committed Aug 17, 2017
1 parent e76dc1d commit 33cd149
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/bluetooth/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ config BT_HCIUART_INTEL
config BT_HCIUART_BCM
bool "Broadcom protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
select BT_HCIUART_H4
select BT_BCM
help
Expand Down
85 changes: 83 additions & 2 deletions drivers/bluetooth/hci_bcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@
#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>
#include <linux/tty.h>
#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>
Expand All @@ -46,6 +49,7 @@

#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */

/* platform device driver resources */
struct bcm_device {
struct list_head list;

Expand All @@ -69,6 +73,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;
Expand All @@ -80,6 +90,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;
Expand Down Expand Up @@ -290,6 +308,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;

Expand Down Expand Up @@ -325,6 +351,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)) {
Expand Down Expand Up @@ -400,7 +432,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)
Expand All @@ -413,7 +445,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:
Expand Down Expand Up @@ -906,16 +938,65 @@ 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);
}

int __exit bcm_deinit(void)
{
platform_driver_unregister(&bcm_driver);
serdev_device_driver_unregister(&bcm_serdev_driver);

return hci_uart_unregister_proto(&bcm_proto);
}

0 comments on commit 33cd149

Please sign in to comment.