162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016, Linaro Ltd.
462306a36Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/rpmsg.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/soc/qcom/wcnss_ctrl.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
1662306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "btqca.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct btqcomsmd {
2162306a36Sopenharmony_ci	struct hci_dev *hdev;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	struct rpmsg_endpoint *acl_channel;
2462306a36Sopenharmony_ci	struct rpmsg_endpoint *cmd_channel;
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
2862306a36Sopenharmony_ci			   const void *data, size_t count)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct sk_buff *skb;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Use GFP_ATOMIC as we're in IRQ context */
3362306a36Sopenharmony_ci	skb = bt_skb_alloc(count, GFP_ATOMIC);
3462306a36Sopenharmony_ci	if (!skb) {
3562306a36Sopenharmony_ci		hdev->stat.err_rx++;
3662306a36Sopenharmony_ci		return -ENOMEM;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	hci_skb_pkt_type(skb) = type;
4062306a36Sopenharmony_ci	skb_put_data(skb, data, count);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return hci_recv_frame(hdev, skb);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data,
4662306a36Sopenharmony_ci				  int count, void *priv, u32 addr)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct btqcomsmd *btq = priv;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	btq->hdev->stat.byte_rx += count;
5162306a36Sopenharmony_ci	return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
5562306a36Sopenharmony_ci				  int count, void *priv, u32 addr)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct btqcomsmd *btq = priv;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	btq->hdev->stat.byte_rx += count;
6062306a36Sopenharmony_ci	return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct btqcomsmd *btq = hci_get_drvdata(hdev);
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	switch (hci_skb_pkt_type(skb)) {
6962306a36Sopenharmony_ci	case HCI_ACLDATA_PKT:
7062306a36Sopenharmony_ci		ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
7162306a36Sopenharmony_ci		if (ret) {
7262306a36Sopenharmony_ci			hdev->stat.err_tx++;
7362306a36Sopenharmony_ci			break;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci		hdev->stat.acl_tx++;
7662306a36Sopenharmony_ci		hdev->stat.byte_tx += skb->len;
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	case HCI_COMMAND_PKT:
7962306a36Sopenharmony_ci		ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
8062306a36Sopenharmony_ci		if (ret) {
8162306a36Sopenharmony_ci			hdev->stat.err_tx++;
8262306a36Sopenharmony_ci			break;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci		hdev->stat.cmd_tx++;
8562306a36Sopenharmony_ci		hdev->stat.byte_tx += skb->len;
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci	default:
8862306a36Sopenharmony_ci		ret = -EILSEQ;
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!ret)
9362306a36Sopenharmony_ci		kfree_skb(skb);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return ret;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int btqcomsmd_open(struct hci_dev *hdev)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int btqcomsmd_close(struct hci_dev *hdev)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int btqcomsmd_setup(struct hci_dev *hdev)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct sk_buff *skb;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
11362306a36Sopenharmony_ci	if (IS_ERR(skb))
11462306a36Sopenharmony_ci		return PTR_ERR(skb);
11562306a36Sopenharmony_ci	kfree_skb(skb);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Devices do not have persistent storage for BD address. Retrieve
11862306a36Sopenharmony_ci	 * it from the firmware node property.
11962306a36Sopenharmony_ci	 */
12062306a36Sopenharmony_ci	set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int btqcomsmd_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int ret;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = qca_set_bdaddr_rome(hdev, bdaddr);
13062306a36Sopenharmony_ci	if (ret)
13162306a36Sopenharmony_ci		return ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* The firmware stops responding for a while after setting the bdaddr,
13462306a36Sopenharmony_ci	 * causing timeouts for subsequent commands. Sleep a bit to avoid this.
13562306a36Sopenharmony_ci	 */
13662306a36Sopenharmony_ci	usleep_range(1000, 10000);
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int btqcomsmd_probe(struct platform_device *pdev)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct btqcomsmd *btq;
14362306a36Sopenharmony_ci	struct hci_dev *hdev;
14462306a36Sopenharmony_ci	void *wcnss;
14562306a36Sopenharmony_ci	int ret;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
14862306a36Sopenharmony_ci	if (!btq)
14962306a36Sopenharmony_ci		return -ENOMEM;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	wcnss = dev_get_drvdata(pdev->dev.parent);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
15462306a36Sopenharmony_ci						   btqcomsmd_acl_callback, btq);
15562306a36Sopenharmony_ci	if (IS_ERR(btq->acl_channel))
15662306a36Sopenharmony_ci		return PTR_ERR(btq->acl_channel);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
15962306a36Sopenharmony_ci						   btqcomsmd_cmd_callback, btq);
16062306a36Sopenharmony_ci	if (IS_ERR(btq->cmd_channel)) {
16162306a36Sopenharmony_ci		ret = PTR_ERR(btq->cmd_channel);
16262306a36Sopenharmony_ci		goto destroy_acl_channel;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	hdev = hci_alloc_dev();
16662306a36Sopenharmony_ci	if (!hdev) {
16762306a36Sopenharmony_ci		ret = -ENOMEM;
16862306a36Sopenharmony_ci		goto destroy_cmd_channel;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	hci_set_drvdata(hdev, btq);
17262306a36Sopenharmony_ci	btq->hdev = hdev;
17362306a36Sopenharmony_ci	SET_HCIDEV_DEV(hdev, &pdev->dev);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	hdev->bus = HCI_SMD;
17662306a36Sopenharmony_ci	hdev->open = btqcomsmd_open;
17762306a36Sopenharmony_ci	hdev->close = btqcomsmd_close;
17862306a36Sopenharmony_ci	hdev->send = btqcomsmd_send;
17962306a36Sopenharmony_ci	hdev->setup = btqcomsmd_setup;
18062306a36Sopenharmony_ci	hdev->set_bdaddr = btqcomsmd_set_bdaddr;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = hci_register_dev(hdev);
18362306a36Sopenharmony_ci	if (ret < 0)
18462306a36Sopenharmony_ci		goto hci_free_dev;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	platform_set_drvdata(pdev, btq);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cihci_free_dev:
19162306a36Sopenharmony_ci	hci_free_dev(hdev);
19262306a36Sopenharmony_cidestroy_cmd_channel:
19362306a36Sopenharmony_ci	rpmsg_destroy_ept(btq->cmd_channel);
19462306a36Sopenharmony_cidestroy_acl_channel:
19562306a36Sopenharmony_ci	rpmsg_destroy_ept(btq->acl_channel);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return ret;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int btqcomsmd_remove(struct platform_device *pdev)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct btqcomsmd *btq = platform_get_drvdata(pdev);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	hci_unregister_dev(btq->hdev);
20562306a36Sopenharmony_ci	hci_free_dev(btq->hdev);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	rpmsg_destroy_ept(btq->cmd_channel);
20862306a36Sopenharmony_ci	rpmsg_destroy_ept(btq->acl_channel);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct of_device_id btqcomsmd_of_match[] = {
21462306a36Sopenharmony_ci	{ .compatible = "qcom,wcnss-bt", },
21562306a36Sopenharmony_ci	{ },
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, btqcomsmd_of_match);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic struct platform_driver btqcomsmd_driver = {
22062306a36Sopenharmony_ci	.probe = btqcomsmd_probe,
22162306a36Sopenharmony_ci	.remove = btqcomsmd_remove,
22262306a36Sopenharmony_ci	.driver  = {
22362306a36Sopenharmony_ci		.name  = "btqcomsmd",
22462306a36Sopenharmony_ci		.of_match_table = btqcomsmd_of_match,
22562306a36Sopenharmony_ci	},
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cimodule_platform_driver(btqcomsmd_driver);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
23162306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm SMD HCI driver");
23262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
233