diff options
author | Sean Wang <sean.wang@mediatek.com> | 2019-02-14 18:19:37 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2019-02-18 08:08:55 -0500 |
commit | e0b67035a90b58d01f911fed77b6e3da153da66e (patch) | |
tree | 2a8b0a394cbd2674ebc9709c16594c80bc80d541 /drivers/bluetooth | |
parent | 88e5f366a1903bfb717ad37a72f4657dae4d81da (diff) |
Bluetooth: mediatek: update the common setup between MT7622 and other devices
Update the setup sequence on MT7622 to apply the same flow with MT7663U
and MT7668U USB [1] as much as possible. These additional commands are
required to parse the corresponding event to determine what current state
the Bluetooth device is on and thus it's necessary to extend
mtk_hci_wmt_sync to support the reading status in the same patch.
[1] http://lists.infradead.org/pipermail/linux-mediatek/2019-January/017074.html
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/btmtkuart.c | 204 |
1 files changed, 190 insertions, 14 deletions
diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index 4451b1db139a..e73b1013ba73 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/atomic.h> | 12 | #include <linux/atomic.h> |
13 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
14 | #include <linux/firmware.h> | 14 | #include <linux/firmware.h> |
15 | #include <linux/iopoll.h> | ||
15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
17 | #include <linux/of.h> | 18 | #include <linux/of.h> |
@@ -37,7 +38,17 @@ | |||
37 | enum { | 38 | enum { |
38 | MTK_WMT_PATCH_DWNLD = 0x1, | 39 | MTK_WMT_PATCH_DWNLD = 0x1, |
39 | MTK_WMT_FUNC_CTRL = 0x6, | 40 | MTK_WMT_FUNC_CTRL = 0x6, |
40 | MTK_WMT_RST = 0x7 | 41 | MTK_WMT_RST = 0x7, |
42 | MTK_WMT_SEMAPHORE = 0x17, | ||
43 | }; | ||
44 | |||
45 | enum { | ||
46 | BTMTK_WMT_INVALID, | ||
47 | BTMTK_WMT_PATCH_UNDONE, | ||
48 | BTMTK_WMT_PATCH_DONE, | ||
49 | BTMTK_WMT_ON_UNDONE, | ||
50 | BTMTK_WMT_ON_DONE, | ||
51 | BTMTK_WMT_ON_PROGRESS, | ||
41 | }; | 52 | }; |
42 | 53 | ||
43 | struct mtk_stp_hdr { | 54 | struct mtk_stp_hdr { |
@@ -58,6 +69,24 @@ struct mtk_hci_wmt_cmd { | |||
58 | u8 data[256]; | 69 | u8 data[256]; |
59 | } __packed; | 70 | } __packed; |
60 | 71 | ||
72 | struct btmtk_hci_wmt_evt { | ||
73 | struct hci_event_hdr hhdr; | ||
74 | struct mtk_wmt_hdr whdr; | ||
75 | } __packed; | ||
76 | |||
77 | struct btmtk_hci_wmt_evt_funcc { | ||
78 | struct btmtk_hci_wmt_evt hwhdr; | ||
79 | __be16 status; | ||
80 | } __packed; | ||
81 | |||
82 | struct btmtk_tci_sleep { | ||
83 | u8 mode; | ||
84 | __le16 duration; | ||
85 | __le16 host_duration; | ||
86 | u8 host_wakeup_pin; | ||
87 | u8 time_compensation; | ||
88 | } __packed; | ||
89 | |||
61 | struct btmtk_hci_wmt_params { | 90 | struct btmtk_hci_wmt_params { |
62 | u8 op; | 91 | u8 op; |
63 | u8 flag; | 92 | u8 flag; |
@@ -76,6 +105,7 @@ struct btmtkuart_dev { | |||
76 | struct sk_buff_head txq; | 105 | struct sk_buff_head txq; |
77 | 106 | ||
78 | struct sk_buff *rx_skb; | 107 | struct sk_buff *rx_skb; |
108 | struct sk_buff *evt_skb; | ||
79 | 109 | ||
80 | u8 stp_pad[6]; | 110 | u8 stp_pad[6]; |
81 | u8 stp_cursor; | 111 | u8 stp_cursor; |
@@ -86,9 +116,11 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, | |||
86 | struct btmtk_hci_wmt_params *wmt_params) | 116 | struct btmtk_hci_wmt_params *wmt_params) |
87 | { | 117 | { |
88 | struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); | 118 | struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); |
119 | struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; | ||
120 | u32 hlen, status = BTMTK_WMT_INVALID; | ||
121 | struct btmtk_hci_wmt_evt *wmt_evt; | ||
89 | struct mtk_hci_wmt_cmd wc; | 122 | struct mtk_hci_wmt_cmd wc; |
90 | struct mtk_wmt_hdr *hdr; | 123 | struct mtk_wmt_hdr *hdr; |
91 | u32 hlen; | ||
92 | int err; | 124 | int err; |
93 | 125 | ||
94 | hlen = sizeof(*hdr) + wmt_params->dlen; | 126 | hlen = sizeof(*hdr) + wmt_params->dlen; |
@@ -133,7 +165,41 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, | |||
133 | return -ETIMEDOUT; | 165 | return -ETIMEDOUT; |
134 | } | 166 | } |
135 | 167 | ||
136 | return 0; | 168 | /* Parse and handle the return WMT event */ |
169 | wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data; | ||
170 | if (wmt_evt->whdr.op != hdr->op) { | ||
171 | bt_dev_err(hdev, "Wrong op received %d expected %d", | ||
172 | wmt_evt->whdr.op, hdr->op); | ||
173 | err = -EIO; | ||
174 | goto err_free_skb; | ||
175 | } | ||
176 | |||
177 | switch (wmt_evt->whdr.op) { | ||
178 | case MTK_WMT_SEMAPHORE: | ||
179 | if (wmt_evt->whdr.flag == 2) | ||
180 | status = BTMTK_WMT_PATCH_UNDONE; | ||
181 | else | ||
182 | status = BTMTK_WMT_PATCH_DONE; | ||
183 | break; | ||
184 | case MTK_WMT_FUNC_CTRL: | ||
185 | wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; | ||
186 | if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) | ||
187 | status = BTMTK_WMT_ON_DONE; | ||
188 | else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420) | ||
189 | status = BTMTK_WMT_ON_PROGRESS; | ||
190 | else | ||
191 | status = BTMTK_WMT_ON_UNDONE; | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | if (wmt_params->status) | ||
196 | *wmt_params->status = status; | ||
197 | |||
198 | err_free_skb: | ||
199 | kfree_skb(bdev->evt_skb); | ||
200 | bdev->evt_skb = NULL; | ||
201 | |||
202 | return err; | ||
137 | } | 203 | } |
138 | 204 | ||
139 | static int mtk_setup_fw(struct hci_dev *hdev) | 205 | static int mtk_setup_fw(struct hci_dev *hdev) |
@@ -184,13 +250,29 @@ static int mtk_setup_fw(struct hci_dev *hdev) | |||
184 | if (err < 0) { | 250 | if (err < 0) { |
185 | bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", | 251 | bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", |
186 | err); | 252 | err); |
187 | break; | 253 | goto free_fw; |
188 | } | 254 | } |
189 | 255 | ||
190 | fw_size -= dlen; | 256 | fw_size -= dlen; |
191 | fw_ptr += dlen; | 257 | fw_ptr += dlen; |
192 | } | 258 | } |
193 | 259 | ||
260 | wmt_params.op = MTK_WMT_RST; | ||
261 | wmt_params.flag = 4; | ||
262 | wmt_params.dlen = 0; | ||
263 | wmt_params.data = NULL; | ||
264 | wmt_params.status = NULL; | ||
265 | |||
266 | /* Activate funciton the firmware providing to */ | ||
267 | err = mtk_hci_wmt_sync(hdev, &wmt_params); | ||
268 | if (err < 0) { | ||
269 | bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); | ||
270 | goto free_fw; | ||
271 | } | ||
272 | |||
273 | /* Wait a few moments for firmware activation done */ | ||
274 | usleep_range(10000, 12000); | ||
275 | |||
194 | free_fw: | 276 | free_fw: |
195 | release_firmware(fw); | 277 | release_firmware(fw); |
196 | return err; | 278 | return err; |
@@ -209,7 +291,20 @@ static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb) | |||
209 | if (hdr->evt == 0xe4) | 291 | if (hdr->evt == 0xe4) |
210 | hdr->evt = HCI_EV_VENDOR; | 292 | hdr->evt = HCI_EV_VENDOR; |
211 | 293 | ||
294 | /* When someone waits for the WMT event, the skb is being cloned | ||
295 | * and being processed the events from there then. | ||
296 | */ | ||
297 | if (test_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state)) { | ||
298 | bdev->evt_skb = skb_clone(skb, GFP_KERNEL); | ||
299 | if (!bdev->evt_skb) { | ||
300 | err = -ENOMEM; | ||
301 | goto err_out; | ||
302 | } | ||
303 | } | ||
304 | |||
212 | err = hci_recv_frame(hdev, skb); | 305 | err = hci_recv_frame(hdev, skb); |
306 | if (err < 0) | ||
307 | goto err_free_skb; | ||
213 | 308 | ||
214 | if (hdr->evt == HCI_EV_VENDOR) { | 309 | if (hdr->evt == HCI_EV_VENDOR) { |
215 | if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT, | 310 | if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT, |
@@ -220,6 +315,13 @@ static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb) | |||
220 | } | 315 | } |
221 | } | 316 | } |
222 | 317 | ||
318 | return 0; | ||
319 | |||
320 | err_free_skb: | ||
321 | kfree_skb(bdev->evt_skb); | ||
322 | bdev->evt_skb = NULL; | ||
323 | |||
324 | err_out: | ||
223 | return err; | 325 | return err; |
224 | } | 326 | } |
225 | 327 | ||
@@ -482,28 +584,79 @@ static int btmtkuart_flush(struct hci_dev *hdev) | |||
482 | return 0; | 584 | return 0; |
483 | } | 585 | } |
484 | 586 | ||
587 | static int btmtkuart_func_query(struct hci_dev *hdev) | ||
588 | { | ||
589 | struct btmtk_hci_wmt_params wmt_params; | ||
590 | int status, err; | ||
591 | u8 param = 0; | ||
592 | |||
593 | /* Query whether the function is enabled */ | ||
594 | wmt_params.op = MTK_WMT_FUNC_CTRL; | ||
595 | wmt_params.flag = 4; | ||
596 | wmt_params.dlen = sizeof(param); | ||
597 | wmt_params.data = ¶m; | ||
598 | wmt_params.status = &status; | ||
599 | |||
600 | err = mtk_hci_wmt_sync(hdev, &wmt_params); | ||
601 | if (err < 0) { | ||
602 | bt_dev_err(hdev, "Failed to query function status (%d)", err); | ||
603 | return err; | ||
604 | } | ||
605 | |||
606 | return status; | ||
607 | } | ||
608 | |||
485 | static int btmtkuart_setup(struct hci_dev *hdev) | 609 | static int btmtkuart_setup(struct hci_dev *hdev) |
486 | { | 610 | { |
487 | struct btmtk_hci_wmt_params wmt_params; | 611 | struct btmtk_hci_wmt_params wmt_params; |
612 | ktime_t calltime, delta, rettime; | ||
613 | struct btmtk_tci_sleep tci_sleep; | ||
614 | unsigned long long duration; | ||
615 | struct sk_buff *skb; | ||
616 | int err, status; | ||
488 | u8 param = 0x1; | 617 | u8 param = 0x1; |
489 | int err = 0; | ||
490 | 618 | ||
491 | /* Setup a firmware which the device definitely requires */ | 619 | calltime = ktime_get(); |
492 | err = mtk_setup_fw(hdev); | ||
493 | if (err < 0) | ||
494 | return err; | ||
495 | 620 | ||
496 | wmt_params.op = MTK_WMT_RST; | 621 | /* Query whether the firmware is already download */ |
497 | wmt_params.flag = 4; | 622 | wmt_params.op = MTK_WMT_SEMAPHORE; |
623 | wmt_params.flag = 1; | ||
498 | wmt_params.dlen = 0; | 624 | wmt_params.dlen = 0; |
499 | wmt_params.data = NULL; | 625 | wmt_params.data = NULL; |
500 | wmt_params.status = NULL; | 626 | wmt_params.status = &status; |
501 | 627 | ||
502 | /* Activate funciton the firmware providing to */ | ||
503 | err = mtk_hci_wmt_sync(hdev, &wmt_params); | 628 | err = mtk_hci_wmt_sync(hdev, &wmt_params); |
504 | if (err < 0) { | 629 | if (err < 0) { |
505 | bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); | 630 | bt_dev_err(hdev, "Failed to query firmware status (%d)", err); |
631 | return err; | ||
632 | } | ||
633 | |||
634 | if (status == BTMTK_WMT_PATCH_DONE) { | ||
635 | bt_dev_info(hdev, "Firmware already downloaded"); | ||
636 | goto ignore_setup_fw; | ||
637 | } | ||
638 | |||
639 | /* Setup a firmware which the device definitely requires */ | ||
640 | err = mtk_setup_fw(hdev); | ||
641 | if (err < 0) | ||
506 | return err; | 642 | return err; |
643 | |||
644 | ignore_setup_fw: | ||
645 | /* Query whether the device is already enabled */ | ||
646 | err = readx_poll_timeout(btmtkuart_func_query, hdev, status, | ||
647 | status < 0 || status != BTMTK_WMT_ON_PROGRESS, | ||
648 | 2000, 5000000); | ||
649 | /* -ETIMEDOUT happens */ | ||
650 | if (err < 0) | ||
651 | return err; | ||
652 | |||
653 | /* The other errors happen in btusb_mtk_func_query */ | ||
654 | if (status < 0) | ||
655 | return status; | ||
656 | |||
657 | if (status == BTMTK_WMT_ON_DONE) { | ||
658 | bt_dev_info(hdev, "function already on"); | ||
659 | goto ignore_func_on; | ||
507 | } | 660 | } |
508 | 661 | ||
509 | /* Enable Bluetooth protocol */ | 662 | /* Enable Bluetooth protocol */ |
@@ -519,6 +672,29 @@ static int btmtkuart_setup(struct hci_dev *hdev) | |||
519 | return err; | 672 | return err; |
520 | } | 673 | } |
521 | 674 | ||
675 | ignore_func_on: | ||
676 | /* Apply the low power environment setup */ | ||
677 | tci_sleep.mode = 0x5; | ||
678 | tci_sleep.duration = cpu_to_le16(0x640); | ||
679 | tci_sleep.host_duration = cpu_to_le16(0x640); | ||
680 | tci_sleep.host_wakeup_pin = 0; | ||
681 | tci_sleep.time_compensation = 0; | ||
682 | |||
683 | skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep, | ||
684 | HCI_INIT_TIMEOUT); | ||
685 | if (IS_ERR(skb)) { | ||
686 | err = PTR_ERR(skb); | ||
687 | bt_dev_err(hdev, "Failed to apply low power setting (%d)", err); | ||
688 | return err; | ||
689 | } | ||
690 | kfree_skb(skb); | ||
691 | |||
692 | rettime = ktime_get(); | ||
693 | delta = ktime_sub(rettime, calltime); | ||
694 | duration = (unsigned long long)ktime_to_ns(delta) >> 10; | ||
695 | |||
696 | bt_dev_info(hdev, "Device setup in %llu usecs", duration); | ||
697 | |||
522 | return 0; | 698 | return 0; |
523 | } | 699 | } |
524 | 700 | ||