1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2016, Linaro Ltd.
4 * Copyright (c) 2015, Sony Mobile Communications Inc.
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/rpmsg.h>
12 #include <linux/soc/qcom/wcnss_ctrl.h>
13 #include <linux/platform_device.h>
15 #include <net/bluetooth/bluetooth.h>
16 #include <net/bluetooth/hci_core.h>
23 struct rpmsg_endpoint
*acl_channel
;
24 struct rpmsg_endpoint
*cmd_channel
;
27 static int btqcomsmd_recv(struct hci_dev
*hdev
, unsigned int type
,
28 const void *data
, size_t count
)
32 /* Use GFP_ATOMIC as we're in IRQ context */
33 skb
= bt_skb_alloc(count
, GFP_ATOMIC
);
39 hci_skb_pkt_type(skb
) = type
;
40 skb_put_data(skb
, data
, count
);
42 return hci_recv_frame(hdev
, skb
);
45 static int btqcomsmd_acl_callback(struct rpmsg_device
*rpdev
, void *data
,
46 int count
, void *priv
, u32 addr
)
48 struct btqcomsmd
*btq
= priv
;
50 btq
->hdev
->stat
.byte_rx
+= count
;
51 return btqcomsmd_recv(btq
->hdev
, HCI_ACLDATA_PKT
, data
, count
);
54 static int btqcomsmd_cmd_callback(struct rpmsg_device
*rpdev
, void *data
,
55 int count
, void *priv
, u32 addr
)
57 struct btqcomsmd
*btq
= priv
;
59 btq
->hdev
->stat
.byte_rx
+= count
;
60 return btqcomsmd_recv(btq
->hdev
, HCI_EVENT_PKT
, data
, count
);
63 static int btqcomsmd_send(struct hci_dev
*hdev
, struct sk_buff
*skb
)
65 struct btqcomsmd
*btq
= hci_get_drvdata(hdev
);
68 switch (hci_skb_pkt_type(skb
)) {
70 ret
= rpmsg_send(btq
->acl_channel
, skb
->data
, skb
->len
);
76 hdev
->stat
.byte_tx
+= skb
->len
;
79 ret
= rpmsg_send(btq
->cmd_channel
, skb
->data
, skb
->len
);
85 hdev
->stat
.byte_tx
+= skb
->len
;
98 static int btqcomsmd_open(struct hci_dev
*hdev
)
103 static int btqcomsmd_close(struct hci_dev
*hdev
)
108 static int btqcomsmd_setup(struct hci_dev
*hdev
)
112 skb
= __hci_cmd_sync(hdev
, HCI_OP_RESET
, 0, NULL
, HCI_INIT_TIMEOUT
);
117 /* Devices do not have persistent storage for BD address. Retrieve
118 * it from the firmware node property.
120 set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY
, &hdev
->quirks
);
125 static int btqcomsmd_set_bdaddr(struct hci_dev
*hdev
, const bdaddr_t
*bdaddr
)
129 ret
= qca_set_bdaddr_rome(hdev
, bdaddr
);
133 /* The firmware stops responding for a while after setting the bdaddr,
134 * causing timeouts for subsequent commands. Sleep a bit to avoid this.
136 usleep_range(1000, 10000);
140 static int btqcomsmd_probe(struct platform_device
*pdev
)
142 struct btqcomsmd
*btq
;
143 struct hci_dev
*hdev
;
147 btq
= devm_kzalloc(&pdev
->dev
, sizeof(*btq
), GFP_KERNEL
);
151 wcnss
= dev_get_drvdata(pdev
->dev
.parent
);
153 btq
->acl_channel
= qcom_wcnss_open_channel(wcnss
, "APPS_RIVA_BT_ACL",
154 btqcomsmd_acl_callback
, btq
);
155 if (IS_ERR(btq
->acl_channel
))
156 return PTR_ERR(btq
->acl_channel
);
158 btq
->cmd_channel
= qcom_wcnss_open_channel(wcnss
, "APPS_RIVA_BT_CMD",
159 btqcomsmd_cmd_callback
, btq
);
160 if (IS_ERR(btq
->cmd_channel
)) {
161 ret
= PTR_ERR(btq
->cmd_channel
);
162 goto destroy_acl_channel
;
165 hdev
= hci_alloc_dev();
168 goto destroy_cmd_channel
;
171 hci_set_drvdata(hdev
, btq
);
173 SET_HCIDEV_DEV(hdev
, &pdev
->dev
);
176 hdev
->open
= btqcomsmd_open
;
177 hdev
->close
= btqcomsmd_close
;
178 hdev
->send
= btqcomsmd_send
;
179 hdev
->setup
= btqcomsmd_setup
;
180 hdev
->set_bdaddr
= btqcomsmd_set_bdaddr
;
182 ret
= hci_register_dev(hdev
);
186 platform_set_drvdata(pdev
, btq
);
193 rpmsg_destroy_ept(btq
->cmd_channel
);
195 rpmsg_destroy_ept(btq
->acl_channel
);
200 static void btqcomsmd_remove(struct platform_device
*pdev
)
202 struct btqcomsmd
*btq
= platform_get_drvdata(pdev
);
204 hci_unregister_dev(btq
->hdev
);
205 hci_free_dev(btq
->hdev
);
207 rpmsg_destroy_ept(btq
->cmd_channel
);
208 rpmsg_destroy_ept(btq
->acl_channel
);
211 static const struct of_device_id btqcomsmd_of_match
[] = {
212 { .compatible
= "qcom,wcnss-bt", },
215 MODULE_DEVICE_TABLE(of
, btqcomsmd_of_match
);
217 static struct platform_driver btqcomsmd_driver
= {
218 .probe
= btqcomsmd_probe
,
219 .remove_new
= btqcomsmd_remove
,
222 .of_match_table
= btqcomsmd_of_match
,
226 module_platform_driver(btqcomsmd_driver
);
228 MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
229 MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
230 MODULE_LICENSE("GPL v2");