summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/hci_h5.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-08-15 15:04:25 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-08-15 15:04:25 -0700
commit9a76aba02a37718242d7cdc294f0a3901928aa57 (patch)
tree2040d038f85d2120f21af83b0793efd5af1864e3 /drivers/bluetooth/hci_h5.c
parent0a957467c5fd46142bc9c52758ffc552d4c5e2f7 (diff)
parent26a1ccc6c117be8e33e0410fce8c5298b0015b99 (diff)
downloadlwn-9a76aba02a37718242d7cdc294f0a3901928aa57.tar.gz
lwn-9a76aba02a37718242d7cdc294f0a3901928aa57.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: - Gustavo A. R. Silva keeps working on the implicit switch fallthru changes. - Support 802.11ax High-Efficiency wireless in cfg80211 et al, From Luca Coelho. - Re-enable ASPM in r8169, from Kai-Heng Feng. - Add virtual XFRM interfaces, which avoids all of the limitations of existing IPSEC tunnels. From Steffen Klassert. - Convert GRO over to use a hash table, so that when we have many flows active we don't traverse a long list during accumluation. - Many new self tests for routing, TC, tunnels, etc. Too many contributors to mention them all, but I'm really happy to keep seeing this stuff. - Hardware timestamping support for dpaa_eth/fsl-fman from Yangbo Lu. - Lots of cleanups and fixes in L2TP code from Guillaume Nault. - Add IPSEC offload support to netdevsim, from Shannon Nelson. - Add support for slotting with non-uniform distribution to netem packet scheduler, from Yousuk Seung. - Add UDP GSO support to mlx5e, from Boris Pismenny. - Support offloading of Team LAG in NFP, from John Hurley. - Allow to configure TX queue selection based upon RX queue, from Amritha Nambiar. - Support ethtool ring size configuration in aquantia, from Anton Mikaev. - Support DSCP and flowlabel per-transport in SCTP, from Xin Long. - Support list based batching and stack traversal of SKBs, this is very exciting work. From Edward Cree. - Busyloop optimizations in vhost_net, from Toshiaki Makita. - Introduce the ETF qdisc, which allows time based transmissions. IGB can offload this in hardware. From Vinicius Costa Gomes. - Add parameter support to devlink, from Moshe Shemesh. - Several multiplication and division optimizations for BPF JIT in nfp driver, from Jiong Wang. - Lots of prepatory work to make more of the packet scheduler layer lockless, when possible, from Vlad Buslov. - Add ACK filter and NAT awareness to sch_cake packet scheduler, from Toke Høiland-Jørgensen. - Support regions and region snapshots in devlink, from Alex Vesker. - Allow to attach XDP programs to both HW and SW at the same time on a given device, with initial support in nfp. From Jakub Kicinski. - Add TLS RX offload and support in mlx5, from Ilya Lesokhin. - Use PHYLIB in r8169 driver, from Heiner Kallweit. - All sorts of changes to support Spectrum 2 in mlxsw driver, from Ido Schimmel. - PTP support in mv88e6xxx DSA driver, from Andrew Lunn. - Make TCP_USER_TIMEOUT socket option more accurate, from Jon Maxwell. - Support for templates in packet scheduler classifier, from Jiri Pirko. - IPV6 support in RDS, from Ka-Cheong Poon. - Native tproxy support in nf_tables, from Máté Eckl. - Maintain IP fragment queue in an rbtree, but optimize properly for in-order frags. From Peter Oskolkov. - Improvde handling of ACKs on hole repairs, from Yuchung Cheng" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1996 commits) bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT" hv/netvsc: Fix NULL dereference at single queue mode fallback net: filter: mark expected switch fall-through xen-netfront: fix warn message as irq device name has '/' cxgb4: Add new T5 PCI device ids 0x50af and 0x50b0 net: dsa: mv88e6xxx: missing unlock on error path rds: fix building with IPV6=m inet/connection_sock: prefer _THIS_IP_ to current_text_addr net: dsa: mv88e6xxx: bitwise vs logical bug net: sock_diag: Fix spectre v1 gadget in __sock_diag_cmd() ieee802154: hwsim: using right kind of iteration net: hns3: Add vlan filter setting by ethtool command -K net: hns3: Set tx ring' tc info when netdev is up net: hns3: Remove tx ring BD len register in hns3_enet net: hns3: Fix desc num set to default when setting channel net: hns3: Fix for phy link issue when using marvell phy driver net: hns3: Fix for information of phydev lost problem when down/up net: hns3: Fix for command format parsing error in hclge_is_all_function_id_zero net: hns3: Add support for serdes loopback selftest bnxt_en: take coredump_record structure off stack ...
Diffstat (limited to 'drivers/bluetooth/hci_h5.c')
-rw-r--r--drivers/bluetooth/hci_h5.c206
1 files changed, 201 insertions, 5 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index 6a8d0d06aba7..8eede1197cd2 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -21,13 +21,18 @@
*
*/
-#include <linux/kernel.h>
+#include <linux/acpi.h>
#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "btrtl.h"
#include "hci_uart.h"
#define HCI_3WIRE_ACK_PKT 0
@@ -65,6 +70,9 @@ enum {
};
struct h5 {
+ /* Must be the first member, hci_serdev.c expects this. */
+ struct hci_uart serdev_hu;
+
struct sk_buff_head unack; /* Unack'ed packets queue */
struct sk_buff_head rel; /* Reliable packets queue */
struct sk_buff_head unrel; /* Unreliable packets queue */
@@ -95,6 +103,19 @@ struct h5 {
H5_SLEEPING,
H5_WAKING_UP,
} sleep;
+
+ const struct h5_vnd *vnd;
+ const char *id;
+
+ struct gpio_desc *enable_gpio;
+ struct gpio_desc *device_wake_gpio;
+};
+
+struct h5_vnd {
+ int (*setup)(struct h5 *h5);
+ void (*open)(struct h5 *h5);
+ void (*close)(struct h5 *h5);
+ const struct acpi_gpio_mapping *acpi_gpio_map;
};
static void h5_reset_rx(struct h5 *h5);
@@ -193,9 +214,13 @@ static int h5_open(struct hci_uart *hu)
BT_DBG("hu %p", hu);
- h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
- if (!h5)
- return -ENOMEM;
+ if (hu->serdev) {
+ h5 = serdev_device_get_drvdata(hu->serdev);
+ } else {
+ h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
+ if (!h5)
+ return -ENOMEM;
+ }
hu->priv = h5;
h5->hu = hu;
@@ -210,6 +235,9 @@ static int h5_open(struct hci_uart *hu)
h5->tx_win = H5_TX_WIN_MAX;
+ if (h5->vnd && h5->vnd->open)
+ h5->vnd->open(h5);
+
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
/* Send initial sync request */
@@ -229,7 +257,21 @@ static int h5_close(struct hci_uart *hu)
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
- kfree(h5);
+ if (h5->vnd && h5->vnd->close)
+ h5->vnd->close(h5);
+
+ if (!hu->serdev)
+ kfree(h5);
+
+ return 0;
+}
+
+static int h5_setup(struct hci_uart *hu)
+{
+ struct h5 *h5 = hu->priv;
+
+ if (h5->vnd && h5->vnd->setup)
+ return h5->vnd->setup(h5);
return 0;
}
@@ -744,18 +786,172 @@ static const struct hci_uart_proto h5p = {
.name = "Three-wire (H5)",
.open = h5_open,
.close = h5_close,
+ .setup = h5_setup,
.recv = h5_recv,
.enqueue = h5_enqueue,
.dequeue = h5_dequeue,
.flush = h5_flush,
};
+static int h5_serdev_probe(struct serdev_device *serdev)
+{
+ const struct acpi_device_id *match;
+ struct device *dev = &serdev->dev;
+ struct h5 *h5;
+
+ h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
+ if (!h5)
+ return -ENOMEM;
+
+ set_bit(HCI_UART_RESET_ON_INIT, &h5->serdev_hu.flags);
+
+ h5->hu = &h5->serdev_hu;
+ h5->serdev_hu.serdev = serdev;
+ serdev_device_set_drvdata(serdev, h5);
+
+ if (has_acpi_companion(dev)) {
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return -ENODEV;
+
+ h5->vnd = (const struct h5_vnd *)match->driver_data;
+ h5->id = (char *)match->id;
+
+ if (h5->vnd->acpi_gpio_map)
+ devm_acpi_dev_add_driver_gpios(dev,
+ h5->vnd->acpi_gpio_map);
+ }
+
+ h5->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(h5->enable_gpio))
+ return PTR_ERR(h5->enable_gpio);
+
+ h5->device_wake_gpio = devm_gpiod_get_optional(dev, "device-wake",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(h5->device_wake_gpio))
+ return PTR_ERR(h5->device_wake_gpio);
+
+ return hci_uart_register_device(&h5->serdev_hu, &h5p);
+}
+
+static void h5_serdev_remove(struct serdev_device *serdev)
+{
+ struct h5 *h5 = serdev_device_get_drvdata(serdev);
+
+ hci_uart_unregister_device(&h5->serdev_hu);
+}
+
+#ifdef CONFIG_BT_HCIUART_RTL
+static int h5_btrtl_setup(struct h5 *h5)
+{
+ struct btrtl_device_info *btrtl_dev;
+ struct sk_buff *skb;
+ __le32 baudrate_data;
+ u32 device_baudrate;
+ unsigned int controller_baudrate;
+ bool flow_control;
+ int err;
+
+ btrtl_dev = btrtl_initialize(h5->hu->hdev, h5->id);
+ if (IS_ERR(btrtl_dev))
+ return PTR_ERR(btrtl_dev);
+
+ err = btrtl_get_uart_settings(h5->hu->hdev, btrtl_dev,
+ &controller_baudrate, &device_baudrate,
+ &flow_control);
+ if (err)
+ goto out_free;
+
+ baudrate_data = cpu_to_le32(device_baudrate);
+ skb = __hci_cmd_sync(h5->hu->hdev, 0xfc17, sizeof(baudrate_data),
+ &baudrate_data, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ rtl_dev_err(h5->hu->hdev, "set baud rate command failed\n");
+ err = PTR_ERR(skb);
+ goto out_free;
+ } else {
+ kfree_skb(skb);
+ }
+ /* Give the device some time to set up the new baudrate. */
+ usleep_range(10000, 20000);
+
+ serdev_device_set_baudrate(h5->hu->serdev, controller_baudrate);
+ serdev_device_set_flow_control(h5->hu->serdev, flow_control);
+
+ err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
+ /* Give the device some time before the hci-core sends it a reset */
+ usleep_range(10000, 20000);
+
+out_free:
+ btrtl_free(btrtl_dev);
+
+ return err;
+}
+
+static void h5_btrtl_open(struct h5 *h5)
+{
+ /* Devices always start with these fixed parameters */
+ serdev_device_set_flow_control(h5->hu->serdev, false);
+ serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
+ serdev_device_set_baudrate(h5->hu->serdev, 115200);
+
+ /* The controller needs up to 500ms to wakeup */
+ gpiod_set_value_cansleep(h5->enable_gpio, 1);
+ gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
+ msleep(500);
+}
+
+static void h5_btrtl_close(struct h5 *h5)
+{
+ gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
+ gpiod_set_value_cansleep(h5->enable_gpio, 0);
+}
+
+static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false };
+static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false };
+static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false };
+static const struct acpi_gpio_mapping acpi_btrtl_gpios[] = {
+ { "device-wake-gpios", &btrtl_device_wake_gpios, 1 },
+ { "enable-gpios", &btrtl_enable_gpios, 1 },
+ { "host-wake-gpios", &btrtl_host_wake_gpios, 1 },
+ {},
+};
+
+static struct h5_vnd rtl_vnd = {
+ .setup = h5_btrtl_setup,
+ .open = h5_btrtl_open,
+ .close = h5_btrtl_close,
+ .acpi_gpio_map = acpi_btrtl_gpios,
+};
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id h5_acpi_match[] = {
+#ifdef CONFIG_BT_HCIUART_RTL
+ { "OBDA8723", (kernel_ulong_t)&rtl_vnd },
+#endif
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
+#endif
+
+static struct serdev_device_driver h5_serdev_driver = {
+ .probe = h5_serdev_probe,
+ .remove = h5_serdev_remove,
+ .driver = {
+ .name = "hci_uart_h5",
+ .acpi_match_table = ACPI_PTR(h5_acpi_match),
+ },
+};
+
int __init h5_init(void)
{
+ serdev_device_driver_register(&h5_serdev_driver);
return hci_uart_register_proto(&h5p);
}
int __exit h5_deinit(void)
{
+ serdev_device_driver_unregister(&h5_serdev_driver);
return hci_uart_unregister_proto(&h5p);
}