-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathhci_bcm4377.c
2514 lines (2130 loc) · 71.4 KB
/
hci_bcm4377.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Bluetooth HCI driver for Broadcom 4377/4378/4387 devices attached via PCIe
*
* Copyright (C) The Asahi Linux Contributors
*/
#include <linux/async.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/dmi.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/pci.h>
#include <linux/printk.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
enum bcm4377_chip {
BCM4377 = 0,
BCM4378,
BCM4387,
};
#define BCM4377_DEVICE_ID 0x5fa0
#define BCM4378_DEVICE_ID 0x5f69
#define BCM4387_DEVICE_ID 0x5f71
#define BCM4377_TIMEOUT 1000
/*
* These devices only support DMA transactions inside a 32bit window
* (possibly to avoid 64 bit arithmetic). The window size cannot exceed
* 0xffffffff but is always aligned down to the previous 0x200 byte boundary
* which effectively limits the window to [start, start+0xfffffe00].
* We just limit the DMA window to [0, 0xfffffe00] to make sure we don't
* run into this limitation.
*/
#define BCM4377_DMA_MASK 0xfffffe00
#define BCM4377_PCIECFG_BAR0_WINDOW1 0x80
#define BCM4377_PCIECFG_BAR0_WINDOW2 0x70
#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1 0x74
#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW2 0x78
#define BCM4377_PCIECFG_BAR2_WINDOW 0x84
#define BCM4377_PCIECFG_BAR0_CORE2_WINDOW1_DEFAULT 0x18011000
#define BCM4377_PCIECFG_BAR2_WINDOW_DEFAULT 0x19000000
#define BCM4377_PCIECFG_SUBSYSTEM_CTRL 0x88
#define BCM4377_BAR0_FW_DOORBELL 0x140
#define BCM4377_BAR0_RTI_CONTROL 0x144
#define BCM4377_BAR0_SLEEP_CONTROL 0x150
#define BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE 0
#define BCM4377_BAR0_SLEEP_CONTROL_AWAKE 2
#define BCM4377_BAR0_SLEEP_CONTROL_QUIESCE 3
#define BCM4377_BAR0_DOORBELL 0x174
#define BCM4377_BAR0_DOORBELL_VALUE GENMASK(31, 16)
#define BCM4377_BAR0_DOORBELL_IDX GENMASK(15, 8)
#define BCM4377_BAR0_DOORBELL_RING BIT(5)
#define BCM4377_BAR0_HOST_WINDOW_LO 0x590
#define BCM4377_BAR0_HOST_WINDOW_HI 0x594
#define BCM4377_BAR0_HOST_WINDOW_SIZE 0x598
#define BCM4377_BAR2_BOOTSTAGE 0x200454
#define BCM4377_BAR2_FW_LO 0x200478
#define BCM4377_BAR2_FW_HI 0x20047c
#define BCM4377_BAR2_FW_SIZE 0x200480
#define BCM4377_BAR2_CONTEXT_ADDR_LO 0x20048c
#define BCM4377_BAR2_CONTEXT_ADDR_HI 0x200450
#define BCM4377_BAR2_RTI_STATUS 0x20045c
#define BCM4377_BAR2_RTI_WINDOW_LO 0x200494
#define BCM4377_BAR2_RTI_WINDOW_HI 0x200498
#define BCM4377_BAR2_RTI_WINDOW_SIZE 0x20049c
#define BCM4377_OTP_SIZE 0xe0
#define BCM4377_OTP_SYS_VENDOR 0x15
#define BCM4377_OTP_CIS 0x80
#define BCM4377_OTP_VENDOR_HDR 0x00000008
#define BCM4377_OTP_MAX_PARAM_LEN 16
#define BCM4377_N_TRANSFER_RINGS 9
#define BCM4377_N_COMPLETION_RINGS 6
#define BCM4377_MAX_RING_SIZE 256
#define BCM4377_MSGID_GENERATION GENMASK(15, 8)
#define BCM4377_MSGID_ID GENMASK(7, 0)
#define BCM4377_RING_N_ENTRIES 128
#define BCM4377_CONTROL_MSG_SIZE 0x34
#define BCM4377_XFER_RING_MAX_INPLACE_PAYLOAD_SIZE (4 * 0xff)
#define MAX_ACL_PAYLOAD_SIZE (HCI_MAX_FRAME_SIZE + HCI_ACL_HDR_SIZE)
#define MAX_SCO_PAYLOAD_SIZE (HCI_MAX_SCO_SIZE + HCI_SCO_HDR_SIZE)
#define MAX_EVENT_PAYLOAD_SIZE (HCI_MAX_EVENT_SIZE + HCI_EVENT_HDR_SIZE)
enum bcm4377_otp_params_type {
BCM4377_OTP_BOARD_PARAMS,
BCM4377_OTP_CHIP_PARAMS
};
enum bcm4377_transfer_ring_id {
BCM4377_XFER_RING_CONTROL = 0,
BCM4377_XFER_RING_HCI_H2D = 1,
BCM4377_XFER_RING_HCI_D2H = 2,
BCM4377_XFER_RING_SCO_H2D = 3,
BCM4377_XFER_RING_SCO_D2H = 4,
BCM4377_XFER_RING_ACL_H2D = 5,
BCM4377_XFER_RING_ACL_D2H = 6,
};
enum bcm4377_completion_ring_id {
BCM4377_ACK_RING_CONTROL = 0,
BCM4377_ACK_RING_HCI_ACL = 1,
BCM4377_EVENT_RING_HCI_ACL = 2,
BCM4377_ACK_RING_SCO = 3,
BCM4377_EVENT_RING_SCO = 4,
};
enum bcm4377_doorbell {
BCM4377_DOORBELL_CONTROL = 0,
BCM4377_DOORBELL_HCI_H2D = 1,
BCM4377_DOORBELL_HCI_D2H = 2,
BCM4377_DOORBELL_ACL_H2D = 3,
BCM4377_DOORBELL_ACL_D2H = 4,
BCM4377_DOORBELL_SCO = 6,
};
/*
* Transfer ring entry
*
* flags: Flags to indicate if the payload is appended or mapped
* len: Payload length
* payload: Optional payload DMA address
* id: Message id to recognize the answer in the completion ring entry
*/
struct bcm4377_xfer_ring_entry {
#define BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED BIT(0)
#define BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER BIT(1)
u8 flags;
__le16 len;
u8 _unk0;
__le64 payload;
__le16 id;
u8 _unk1[2];
} __packed;
static_assert(sizeof(struct bcm4377_xfer_ring_entry) == 0x10);
/*
* Completion ring entry
*
* flags: Flags to indicate if the payload is appended or mapped. If the payload
* is mapped it can be found in the buffer of the corresponding transfer
* ring message.
* ring_id: Transfer ring ID which required this message
* msg_id: Message ID specified in transfer ring entry
* len: Payload length
*/
struct bcm4377_completion_ring_entry {
u8 flags;
u8 _unk0;
__le16 ring_id;
__le16 msg_id;
__le32 len;
u8 _unk1[6];
} __packed;
static_assert(sizeof(struct bcm4377_completion_ring_entry) == 0x10);
enum bcm4377_control_message_type {
BCM4377_CONTROL_MSG_CREATE_XFER_RING = 1,
BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING = 2,
BCM4377_CONTROL_MSG_DESTROY_XFER_RING = 3,
BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING = 4,
};
/*
* Control message used to create a completion ring
*
* msg_type: Must be BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING
* header_size: Unknown, but probably reserved space in front of the entry
* footer_size: Number of 32 bit words reserved for payloads after the entry
* id/id_again: Completion ring index
* ring_iova: DMA address of the ring buffer
* n_elements: Number of elements inside the ring buffer
* msi: MSI index, doesn't work for all rings though and should be zero
* intmod_delay: Unknown delay
* intmod_bytes: Unknown
*/
struct bcm4377_create_completion_ring_msg {
u8 msg_type;
u8 header_size;
u8 footer_size;
u8 _unk0;
__le16 id;
__le16 id_again;
__le64 ring_iova;
__le16 n_elements;
__le32 unk;
u8 _unk1[6];
__le16 msi;
__le16 intmod_delay;
__le32 intmod_bytes;
__le16 _unk2;
__le32 _unk3;
u8 _unk4[10];
} __packed;
static_assert(sizeof(struct bcm4377_create_completion_ring_msg) ==
BCM4377_CONTROL_MSG_SIZE);
/*
* Control ring message used to destroy a completion ring
*
* msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_COMPLETION_RING
* ring_id: Completion ring to be destroyed
*/
struct bcm4377_destroy_completion_ring_msg {
u8 msg_type;
u8 _pad0;
__le16 ring_id;
u8 _pad1[48];
} __packed;
static_assert(sizeof(struct bcm4377_destroy_completion_ring_msg) ==
BCM4377_CONTROL_MSG_SIZE);
/*
* Control message used to create a transfer ring
*
* msg_type: Must be BCM4377_CONTROL_MSG_CREATE_XFER_RING
* header_size: Number of 32 bit words reserved for unknown content before the
* entry
* footer_size: Number of 32 bit words reserved for payloads after the entry
* ring_id/ring_id_again: Transfer ring index
* ring_iova: DMA address of the ring buffer
* n_elements: Number of elements inside the ring buffer
* completion_ring_id: Completion ring index for acknowledgements and events
* doorbell: Doorbell index used to notify device of new entries
* flags: Transfer ring flags
* - virtual: set if there is no associated shared memory and only the
* corresponding completion ring is used
* - sync: only set for the SCO rings
*/
struct bcm4377_create_transfer_ring_msg {
u8 msg_type;
u8 header_size;
u8 footer_size;
u8 _unk0;
__le16 ring_id;
__le16 ring_id_again;
__le64 ring_iova;
u8 _unk1[8];
__le16 n_elements;
__le16 completion_ring_id;
__le16 doorbell;
#define BCM4377_XFER_RING_FLAG_VIRTUAL BIT(7)
#define BCM4377_XFER_RING_FLAG_SYNC BIT(8)
__le16 flags;
u8 _unk2[20];
} __packed;
static_assert(sizeof(struct bcm4377_create_transfer_ring_msg) ==
BCM4377_CONTROL_MSG_SIZE);
/*
* Control ring message used to destroy a transfer ring
*
* msg_type: Must be BCM4377_CONTROL_MSG_DESTROY_XFER_RING
* ring_id: Transfer ring to be destroyed
*/
struct bcm4377_destroy_transfer_ring_msg {
u8 msg_type;
u8 _pad0;
__le16 ring_id;
u8 _pad1[48];
} __packed;
static_assert(sizeof(struct bcm4377_destroy_transfer_ring_msg) ==
BCM4377_CONTROL_MSG_SIZE);
/*
* "Converged IPC" context struct used to make the device aware of all other
* shared memory structures. A pointer to this structure is configured inside a
* MMIO register.
*
* version: Protocol version, must be 2.
* size: Size of this structure, must be 0x68.
* enabled_caps: Enabled capabilities. Unknown bitfield but should be 2.
* peripheral_info_addr: DMA address for a 0x20 buffer to which the device will
* write unknown contents
* {completion,xfer}_ring_{tails,heads}_addr: DMA pointers to ring heads/tails
* n_completion_rings: Number of completion rings, the firmware only works if
* this is set to BCM4377_N_COMPLETION_RINGS.
* n_xfer_rings: Number of transfer rings, the firmware only works if
* this is set to BCM4377_N_TRANSFER_RINGS.
* control_completion_ring_addr: Control completion ring buffer DMA address
* control_xfer_ring_addr: Control transfer ring buffer DMA address
* control_xfer_ring_n_entries: Number of control transfer ring entries
* control_completion_ring_n_entries: Number of control completion ring entries
* control_xfer_ring_doorbell: Control transfer ring doorbell
* control_completion_ring_doorbell: Control completion ring doorbell,
* must be set to 0xffff
* control_xfer_ring_msi: Control completion ring MSI index, must be 0
* control_completion_ring_msi: Control completion ring MSI index, must be 0.
* control_xfer_ring_header_size: Number of 32 bit words reserved in front of
* every control transfer ring entry
* control_xfer_ring_footer_size: Number of 32 bit words reserved after every
* control transfer ring entry
* control_completion_ring_header_size: Number of 32 bit words reserved in front
* of every control completion ring entry
* control_completion_ring_footer_size: Number of 32 bit words reserved after
* every control completion ring entry
* scratch_pad: Optional scratch pad DMA address
* scratch_pad_size: Scratch pad size
*/
struct bcm4377_context {
__le16 version;
__le16 size;
__le32 enabled_caps;
__le64 peripheral_info_addr;
/* ring heads and tails */
__le64 completion_ring_heads_addr;
__le64 xfer_ring_tails_addr;
__le64 completion_ring_tails_addr;
__le64 xfer_ring_heads_addr;
__le16 n_completion_rings;
__le16 n_xfer_rings;
/* control ring configuration */
__le64 control_completion_ring_addr;
__le64 control_xfer_ring_addr;
__le16 control_xfer_ring_n_entries;
__le16 control_completion_ring_n_entries;
__le16 control_xfer_ring_doorbell;
__le16 control_completion_ring_doorbell;
__le16 control_xfer_ring_msi;
__le16 control_completion_ring_msi;
u8 control_xfer_ring_header_size;
u8 control_xfer_ring_footer_size;
u8 control_completion_ring_header_size;
u8 control_completion_ring_footer_size;
__le16 _unk0;
__le16 _unk1;
__le64 scratch_pad;
__le32 scratch_pad_size;
__le32 _unk3;
} __packed;
static_assert(sizeof(struct bcm4377_context) == 0x68);
#define BCM4378_CALIBRATION_CHUNK_SIZE 0xe6
struct bcm4378_hci_send_calibration_cmd {
u8 unk;
__le16 blocks_left;
u8 data[BCM4378_CALIBRATION_CHUNK_SIZE];
} __packed;
#define BCM4378_PTB_CHUNK_SIZE 0xcf
struct bcm4378_hci_send_ptb_cmd {
__le16 blocks_left;
u8 data[BCM4378_PTB_CHUNK_SIZE];
} __packed;
/*
* Shared memory structure used to store the ring head and tail pointers.
*/
struct bcm4377_ring_state {
__le16 completion_ring_head[BCM4377_N_COMPLETION_RINGS];
__le16 completion_ring_tail[BCM4377_N_COMPLETION_RINGS];
__le16 xfer_ring_head[BCM4377_N_TRANSFER_RINGS];
__le16 xfer_ring_tail[BCM4377_N_TRANSFER_RINGS];
};
/*
* A transfer ring can be used in two configurations:
* 1) Send control or HCI messages to the device which are then acknowledged
* in the corresponding completion ring
* 2) Receiving HCI frames from the devices. In this case the transfer ring
* itself contains empty messages that are acknowledged once data is
* available from the device. If the payloads fit inside the footers
* of the completion ring the transfer ring can be configured to be
* virtual such that it has no ring buffer.
*
* ring_id: ring index hardcoded in the firmware
* doorbell: doorbell index to notify device of new entries
* payload_size: optional in-place payload size
* mapped_payload_size: optional out-of-place payload size
* completion_ring: index of corresponding completion ring
* n_entries: number of entries inside this ring
* generation: ring generation; incremented on hci_open to detect stale messages
* sync: set to true for SCO rings
* virtual: set to true if this ring has no entries and is just required to
* setup a corresponding completion ring for device->host messages
* d2h_buffers_only: set to true if this ring is only used to provide large
* buffers used by device->host messages in the completion
* ring
* allow_wait: allow to wait for messages to be acknowledged
* enabled: true once the ring has been created and can be used
* ring: ring buffer for entries (struct bcm4377_xfer_ring_entry)
* ring_dma: DMA address for ring entry buffer
* payloads: payload buffer for mapped_payload_size payloads
* payloads_dma:DMA address for payload buffer
* events: pointer to array of completions if waiting is allowed
* msgids: bitmap to keep track of used message ids
* lock: Spinlock to protect access to ring structurs used in the irq handler
*/
struct bcm4377_transfer_ring {
enum bcm4377_transfer_ring_id ring_id;
enum bcm4377_doorbell doorbell;
size_t payload_size;
size_t mapped_payload_size;
u8 completion_ring;
u16 n_entries;
u8 generation;
bool sync;
bool virtual;
bool d2h_buffers_only;
bool allow_wait;
bool enabled;
void *ring;
dma_addr_t ring_dma;
void *payloads;
dma_addr_t payloads_dma;
struct completion **events;
DECLARE_BITMAP(msgids, BCM4377_MAX_RING_SIZE);
spinlock_t lock;
};
/*
* A completion ring can be either used to either acknowledge messages sent in
* the corresponding transfer ring or to receive messages associated with the
* transfer ring. When used to receive messages the transfer ring either
* has no ring buffer and is only advanced ("virtual transfer ring") or it
* only contains empty DMA buffers to be used for the payloads.
*
* ring_id: completion ring id, hardcoded in firmware
* payload_size: optional payload size after each entry
* delay: unknown delay
* n_entries: number of entries in this ring
* enabled: true once the ring has been created and can be used
* ring: ring buffer for entries (struct bcm4377_completion_ring_entry)
* ring_dma: DMA address of ring buffer
* transfer_rings: bitmap of corresponding transfer ring ids
*/
struct bcm4377_completion_ring {
enum bcm4377_completion_ring_id ring_id;
u16 payload_size;
u16 delay;
u16 n_entries;
bool enabled;
void *ring;
dma_addr_t ring_dma;
unsigned long transfer_rings;
};
struct bcm4377_data;
/*
* Chip-specific configuration struct
*
* id: Chip id (e.g. 0x4377 for BCM4377)
* otp_offset: Offset to the start of the OTP inside BAR0
* bar0_window1: Backplane address mapped to the first window in BAR0
* bar0_window2: Backplane address mapped to the second window in BAR0
* bar0_core2_window2: Optional backplane address mapped to the second core's
* second window in BAR0
* has_bar0_core2_window2: Set to true if this chip requires the second core's
* second window to be configured
* clear_pciecfg_subsystem_ctrl_bit19: Set to true if bit 19 in the
* vendor-specific subsystem control
* register has to be cleared
* disable_aspm: Set to true if ASPM must be disabled due to hardware errata
* broken_ext_scan: Set to true if the chip erroneously claims to support
* extended scanning
* broken_mws_transport_config: Set to true if the chip erroneously claims to
* support MWS Transport Configuration
* send_calibration: Optional callback to send calibration data
* send_ptb: Callback to send "PTB" regulatory/calibration data
*/
struct bcm4377_hw {
unsigned int id;
u32 otp_offset;
u32 bar0_window1;
u32 bar0_window2;
u32 bar0_core2_window2;
unsigned long has_bar0_core2_window2 : 1;
unsigned long clear_pciecfg_subsystem_ctrl_bit19 : 1;
unsigned long disable_aspm : 1;
unsigned long broken_ext_scan : 1;
unsigned long broken_mws_transport_config : 1;
int (*send_calibration)(struct bcm4377_data *bcm4377);
int (*send_ptb)(struct bcm4377_data *bcm4377,
const struct firmware *fw);
};
static const struct bcm4377_hw bcm4377_hw_variants[];
static const struct dmi_system_id bcm4377_dmi_board_table[];
/*
* Private struct associated with each device containing global state
*
* pdev: Pointer to associated struct pci_dev
* hdev: Pointer to associated strucy hci_dev
* bar0: iomem pointing to BAR0
* bar1: iomem pointing to BAR2
* bootstage: Current value of the bootstage
* rti_status: Current "RTI" status value
* hw: Pointer to chip-specific struct bcm4377_hw
* taurus_cal_blob: "Taurus" calibration blob used for some chips
* taurus_cal_size: "Taurus" calibration blob size
* taurus_beamforming_cal_blob: "Taurus" beamforming calibration blob used for
* some chips
* taurus_beamforming_cal_size: "Taurus" beamforming calibration blob size
* stepping: Chip stepping read from OTP; used for firmware selection
* vendor: Antenna vendor read from OTP; used for firmware selection
* board_type: Board type from FDT or DMI match; used for firmware selection
* event: Event for changed bootstage or rti_status; used for booting firmware
* ctx: "Converged IPC" context
* ctx_dma: "Converged IPC" context DMA address
* ring_state: Shared memory buffer containing ring head and tail indexes
* ring_state_dma: DMA address for ring_state
* {control,hci_acl,sco}_ack_ring: Completion rings used to acknowledge messages
* {hci_acl,sco}_event_ring: Completion rings used for device->host messages
* control_h2d_ring: Transfer ring used for control messages
* {hci,sco,acl}_h2d_ring: Transfer ring used to transfer HCI frames
* {hci,sco,acl}_d2h_ring: Transfer ring used to receive HCI frames in the
* corresponding completion ring
*/
struct bcm4377_data {
struct pci_dev *pdev;
struct hci_dev *hdev;
void __iomem *bar0;
void __iomem *bar2;
u32 bootstage;
u32 rti_status;
const struct bcm4377_hw *hw;
const void *taurus_cal_blob;
int taurus_cal_size;
const void *taurus_beamforming_cal_blob;
int taurus_beamforming_cal_size;
char stepping[BCM4377_OTP_MAX_PARAM_LEN];
char vendor[BCM4377_OTP_MAX_PARAM_LEN];
const char *board_type;
struct completion event;
struct bcm4377_context *ctx;
dma_addr_t ctx_dma;
struct bcm4377_ring_state *ring_state;
dma_addr_t ring_state_dma;
/*
* The HCI and ACL rings have to be merged because this structure is
* hardcoded in the firmware.
*/
struct bcm4377_completion_ring control_ack_ring;
struct bcm4377_completion_ring hci_acl_ack_ring;
struct bcm4377_completion_ring hci_acl_event_ring;
struct bcm4377_completion_ring sco_ack_ring;
struct bcm4377_completion_ring sco_event_ring;
struct bcm4377_transfer_ring control_h2d_ring;
struct bcm4377_transfer_ring hci_h2d_ring;
struct bcm4377_transfer_ring hci_d2h_ring;
struct bcm4377_transfer_ring sco_h2d_ring;
struct bcm4377_transfer_ring sco_d2h_ring;
struct bcm4377_transfer_ring acl_h2d_ring;
struct bcm4377_transfer_ring acl_d2h_ring;
};
static void bcm4377_ring_doorbell(struct bcm4377_data *bcm4377, u8 doorbell,
u16 val)
{
u32 db = 0;
db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_VALUE, val);
db |= FIELD_PREP(BCM4377_BAR0_DOORBELL_IDX, doorbell);
db |= BCM4377_BAR0_DOORBELL_RING;
dev_dbg(&bcm4377->pdev->dev, "write %d to doorbell #%d (0x%x)\n", val,
doorbell, db);
iowrite32(db, bcm4377->bar0 + BCM4377_BAR0_DOORBELL);
}
static int bcm4377_extract_msgid(struct bcm4377_data *bcm4377,
struct bcm4377_transfer_ring *ring,
u16 raw_msgid, u8 *msgid)
{
u8 generation = FIELD_GET(BCM4377_MSGID_GENERATION, raw_msgid);
*msgid = FIELD_GET(BCM4377_MSGID_ID, raw_msgid);
if (generation != ring->generation) {
dev_warn(
&bcm4377->pdev->dev,
"invalid message generation %d should be %d in entry for ring %d\n",
generation, ring->generation, ring->ring_id);
return -EINVAL;
}
if (*msgid >= ring->n_entries) {
dev_warn(&bcm4377->pdev->dev,
"invalid message id in entry for ring %d: %d > %d\n",
ring->ring_id, *msgid, ring->n_entries);
return -EINVAL;
}
return 0;
}
static void bcm4377_handle_event(struct bcm4377_data *bcm4377,
struct bcm4377_transfer_ring *ring,
u16 raw_msgid, u8 entry_flags, u8 type,
void *payload, size_t len)
{
struct sk_buff *skb;
u16 head;
u8 msgid;
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
if (!ring->enabled) {
dev_warn(&bcm4377->pdev->dev,
"event for disabled transfer ring %d\n",
ring->ring_id);
goto out;
}
if (ring->d2h_buffers_only &&
entry_flags & BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED) {
if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid))
goto out;
if (len > ring->mapped_payload_size) {
dev_warn(
&bcm4377->pdev->dev,
"invalid payload len in event for ring %d: %zu > %zu\n",
ring->ring_id, len, ring->mapped_payload_size);
goto out;
}
payload = ring->payloads + msgid * ring->mapped_payload_size;
}
skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!skb)
goto out;
memcpy(skb_put(skb, len), payload, len);
hci_skb_pkt_type(skb) = type;
hci_recv_frame(bcm4377->hdev, skb);
out:
head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
head = (head + 1) % ring->n_entries;
bcm4377->ring_state->xfer_ring_head[ring->ring_id] = cpu_to_le16(head);
bcm4377_ring_doorbell(bcm4377, ring->doorbell, head);
spin_unlock_irqrestore(&ring->lock, flags);
}
static void bcm4377_handle_ack(struct bcm4377_data *bcm4377,
struct bcm4377_transfer_ring *ring,
u16 raw_msgid)
{
unsigned long flags;
u8 msgid;
spin_lock_irqsave(&ring->lock, flags);
if (bcm4377_extract_msgid(bcm4377, ring, raw_msgid, &msgid))
goto unlock;
if (!test_bit(msgid, ring->msgids)) {
dev_warn(
&bcm4377->pdev->dev,
"invalid message id in ack for ring %d: %d is not used\n",
ring->ring_id, msgid);
goto unlock;
}
if (ring->allow_wait && ring->events[msgid]) {
complete(ring->events[msgid]);
ring->events[msgid] = NULL;
}
bitmap_release_region(ring->msgids, msgid, ring->n_entries);
unlock:
spin_unlock_irqrestore(&ring->lock, flags);
}
static void bcm4377_handle_completion(struct bcm4377_data *bcm4377,
struct bcm4377_completion_ring *ring,
u16 pos)
{
struct bcm4377_completion_ring_entry *entry;
u16 msg_id, transfer_ring;
size_t entry_size, data_len;
void *data;
if (pos >= ring->n_entries) {
dev_warn(&bcm4377->pdev->dev,
"invalid offset %d for completion ring %d\n", pos,
ring->ring_id);
return;
}
entry_size = sizeof(*entry) + ring->payload_size;
entry = ring->ring + pos * entry_size;
data = ring->ring + pos * entry_size + sizeof(*entry);
data_len = le32_to_cpu(entry->len);
msg_id = le16_to_cpu(entry->msg_id);
transfer_ring = le16_to_cpu(entry->ring_id);
if ((ring->transfer_rings & BIT(transfer_ring)) == 0) {
dev_warn(
&bcm4377->pdev->dev,
"invalid entry at offset %d for transfer ring %d in completion ring %d\n",
pos, transfer_ring, ring->ring_id);
return;
}
dev_dbg(&bcm4377->pdev->dev,
"entry in completion ring %d for transfer ring %d with msg_id %d\n",
ring->ring_id, transfer_ring, msg_id);
switch (transfer_ring) {
case BCM4377_XFER_RING_CONTROL:
bcm4377_handle_ack(bcm4377, &bcm4377->control_h2d_ring, msg_id);
break;
case BCM4377_XFER_RING_HCI_H2D:
bcm4377_handle_ack(bcm4377, &bcm4377->hci_h2d_ring, msg_id);
break;
case BCM4377_XFER_RING_SCO_H2D:
bcm4377_handle_ack(bcm4377, &bcm4377->sco_h2d_ring, msg_id);
break;
case BCM4377_XFER_RING_ACL_H2D:
bcm4377_handle_ack(bcm4377, &bcm4377->acl_h2d_ring, msg_id);
break;
case BCM4377_XFER_RING_HCI_D2H:
bcm4377_handle_event(bcm4377, &bcm4377->hci_d2h_ring, msg_id,
entry->flags, HCI_EVENT_PKT, data,
data_len);
break;
case BCM4377_XFER_RING_SCO_D2H:
bcm4377_handle_event(bcm4377, &bcm4377->sco_d2h_ring, msg_id,
entry->flags, HCI_SCODATA_PKT, data,
data_len);
break;
case BCM4377_XFER_RING_ACL_D2H:
bcm4377_handle_event(bcm4377, &bcm4377->acl_d2h_ring, msg_id,
entry->flags, HCI_ACLDATA_PKT, data,
data_len);
break;
default:
dev_warn(
&bcm4377->pdev->dev,
"entry in completion ring %d for unknown transfer ring %d with msg_id %d\n",
ring->ring_id, transfer_ring, msg_id);
}
}
static void bcm4377_poll_completion_ring(struct bcm4377_data *bcm4377,
struct bcm4377_completion_ring *ring)
{
u16 tail;
__le16 *heads = bcm4377->ring_state->completion_ring_head;
__le16 *tails = bcm4377->ring_state->completion_ring_tail;
if (!ring->enabled)
return;
tail = le16_to_cpu(tails[ring->ring_id]);
dev_dbg(&bcm4377->pdev->dev,
"completion ring #%d: head: %d, tail: %d\n", ring->ring_id,
le16_to_cpu(heads[ring->ring_id]), tail);
while (tail != le16_to_cpu(READ_ONCE(heads[ring->ring_id]))) {
/*
* ensure the CPU doesn't speculate through the comparison.
* otherwise it might already read the (empty) queue entry
* before the updated head has been loaded and checked.
*/
dma_rmb();
bcm4377_handle_completion(bcm4377, ring, tail);
tail = (tail + 1) % ring->n_entries;
tails[ring->ring_id] = cpu_to_le16(tail);
}
}
static irqreturn_t bcm4377_irq(int irq, void *data)
{
struct bcm4377_data *bcm4377 = data;
u32 bootstage, rti_status;
bootstage = ioread32(bcm4377->bar2 + BCM4377_BAR2_BOOTSTAGE);
rti_status = ioread32(bcm4377->bar2 + BCM4377_BAR2_RTI_STATUS);
if (bootstage != bcm4377->bootstage ||
rti_status != bcm4377->rti_status) {
dev_dbg(&bcm4377->pdev->dev,
"bootstage = %d -> %d, rti state = %d -> %d\n",
bcm4377->bootstage, bootstage, bcm4377->rti_status,
rti_status);
complete(&bcm4377->event);
bcm4377->bootstage = bootstage;
bcm4377->rti_status = rti_status;
}
if (rti_status > 2)
dev_err(&bcm4377->pdev->dev, "RTI status is %d\n", rti_status);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->control_ack_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_event_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->hci_acl_ack_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_ack_ring);
bcm4377_poll_completion_ring(bcm4377, &bcm4377->sco_event_ring);
return IRQ_HANDLED;
}
static int bcm4377_enqueue(struct bcm4377_data *bcm4377,
struct bcm4377_transfer_ring *ring, void *data,
size_t len, bool wait)
{
unsigned long flags;
struct bcm4377_xfer_ring_entry *entry;
void *payload;
size_t offset;
u16 head, tail, new_head;
u16 raw_msgid;
int ret, msgid;
DECLARE_COMPLETION_ONSTACK(event);
if (len > ring->payload_size && len > ring->mapped_payload_size) {
dev_warn(
&bcm4377->pdev->dev,
"payload len %zu is too large for ring %d (max is %zu or %zu)\n",
len, ring->ring_id, ring->payload_size,
ring->mapped_payload_size);
return -EINVAL;
}
if (wait && !ring->allow_wait)
return -EINVAL;
if (ring->virtual)
return -EINVAL;
spin_lock_irqsave(&ring->lock, flags);
head = le16_to_cpu(bcm4377->ring_state->xfer_ring_head[ring->ring_id]);
tail = le16_to_cpu(bcm4377->ring_state->xfer_ring_tail[ring->ring_id]);
new_head = (head + 1) % ring->n_entries;
if (new_head == tail) {
dev_warn(&bcm4377->pdev->dev,
"can't send message because ring %d is full\n",
ring->ring_id);
ret = -EINVAL;
goto out;
}
msgid = bitmap_find_free_region(ring->msgids, ring->n_entries, 0);
if (msgid < 0) {
dev_warn(&bcm4377->pdev->dev,
"can't find message id for ring %d\n", ring->ring_id);
ret = -EINVAL;
goto out;
}
raw_msgid = FIELD_PREP(BCM4377_MSGID_GENERATION, ring->generation);
raw_msgid |= FIELD_PREP(BCM4377_MSGID_ID, msgid);
offset = head * (sizeof(*entry) + ring->payload_size);
entry = ring->ring + offset;
memset(entry, 0, sizeof(*entry));
entry->id = cpu_to_le16(raw_msgid);
entry->len = cpu_to_le16(len);
if (len <= ring->payload_size) {
entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_IN_FOOTER;
payload = ring->ring + offset + sizeof(*entry);
} else {
entry->flags = BCM4377_XFER_RING_FLAG_PAYLOAD_MAPPED;
entry->payload = cpu_to_le64(ring->payloads_dma +
msgid * ring->mapped_payload_size);
payload = ring->payloads + msgid * ring->mapped_payload_size;
}
memcpy(payload, data, len);
if (wait)
ring->events[msgid] = &event;
/*
* The 4377 chips stop responding to any commands as soon as they
* have been idle for a while. Poking the sleep control register here
* makes them come alive again.
*/
iowrite32(BCM4377_BAR0_SLEEP_CONTROL_AWAKE,
bcm4377->bar0 + BCM4377_BAR0_SLEEP_CONTROL);
dev_dbg(&bcm4377->pdev->dev,
"updating head for transfer queue #%d to %d\n", ring->ring_id,
new_head);
bcm4377->ring_state->xfer_ring_head[ring->ring_id] =
cpu_to_le16(new_head);
if (!ring->sync)
bcm4377_ring_doorbell(bcm4377, ring->doorbell, new_head);
ret = 0;
out:
spin_unlock_irqrestore(&ring->lock, flags);
if (ret == 0 && wait) {
ret = wait_for_completion_interruptible_timeout(
&event, BCM4377_TIMEOUT);
if (ret == 0)
ret = -ETIMEDOUT;
else if (ret > 0)
ret = 0;
spin_lock_irqsave(&ring->lock, flags);
ring->events[msgid] = NULL;
spin_unlock_irqrestore(&ring->lock, flags);
}
return ret;
}
static int bcm4377_create_completion_ring(struct bcm4377_data *bcm4377,
struct bcm4377_completion_ring *ring)
{
struct bcm4377_create_completion_ring_msg msg;
int ret;
if (ring->enabled) {
dev_warn(&bcm4377->pdev->dev,
"completion ring %d already enabled\n", ring->ring_id);
return 0;
}
memset(ring->ring, 0,
ring->n_entries * (sizeof(struct bcm4377_completion_ring_entry) +
ring->payload_size));
memset(&msg, 0, sizeof(msg));
msg.msg_type = BCM4377_CONTROL_MSG_CREATE_COMPLETION_RING;
msg.id = cpu_to_le16(ring->ring_id);
msg.id_again = cpu_to_le16(ring->ring_id);
msg.ring_iova = cpu_to_le64(ring->ring_dma);
msg.n_elements = cpu_to_le16(ring->n_entries);
msg.intmod_bytes = cpu_to_le32(0xffffffff);
msg.unk = cpu_to_le32(0xffffffff);
msg.intmod_delay = cpu_to_le16(ring->delay);
msg.footer_size = ring->payload_size / 4;
ret = bcm4377_enqueue(bcm4377, &bcm4377->control_h2d_ring, &msg,
sizeof(msg), true);
if (!ret)
ring->enabled = true;
return ret;