-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdhd_sdio.c
8524 lines (7292 loc) · 239 KB
/
dhd_sdio.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
/*
* DHD Bus Module for SDIO
*
* Copyright (C) 1999-2013, Broadcom Corporation
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
* $Id: dhd_sdio.c 388269 2013-02-28 19:21:57Z $
*/
#include <typedefs.h>
#include <osl.h>
#include <bcmsdh.h>
#ifdef BCMEMBEDIMAGE
#include BCMEMBEDIMAGE
#endif /* BCMEMBEDIMAGE */
#include <bcmdefs.h>
#include <bcmutils.h>
#include <bcmendian.h>
#include <bcmdevs.h>
#include <siutils.h>
#include <hndpmu.h>
#include <hndsoc.h>
#include <bcmsdpcm.h>
#if defined(DHD_DEBUG)
#include <hndrte_armtrap.h>
#include <hndrte_cons.h>
#endif /* defined(DHD_DEBUG) */
#include <sbchipc.h>
#include <sbhnddma.h>
#include <sdio.h>
#include <sbsdio.h>
#include <sbsdpcmdev.h>
#include <bcmsdpcm.h>
#include <bcmsdbus.h>
#include <proto/ethernet.h>
#include <proto/802.1d.h>
#include <proto/802.11.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_bus.h>
#include <dhd_proto.h>
#include <dhd_dbg.h>
#include <dhdioctl.h>
#include <sdiovar.h>
bool dhd_mp_halting(dhd_pub_t *dhdp);
extern void bcmsdh_waitfor_iodrain(void *sdh);
extern void bcmsdh_reject_ioreqs(void *sdh, bool reject);
extern bool bcmsdh_fatal_error(void *sdh);
#ifndef DHDSDIO_MEM_DUMP_FNAME
#define DHDSDIO_MEM_DUMP_FNAME "mem_dump"
#endif
#define QLEN 256 /* bulk rx and tx queue lengths */
#define FCHI (QLEN - 10)
#define FCLOW (FCHI / 2)
#define PRIOMASK 7
#define TXRETRIES 2 /* # of retries for tx frames */
#ifndef DHD_RXBOUND
#define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */
#endif
#ifndef DHD_TXBOUND
#define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */
#endif
#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */
#define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */
#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */
#ifndef DHD_FIRSTREAD
#define DHD_FIRSTREAD 32
#endif
#if !ISPOWEROF2(DHD_FIRSTREAD)
#error DHD_FIRSTREAD is not a power of 2!
#endif
#ifdef BCMSDIOH_TXGLOM
/* Total length of TX frame header for dongle protocol */
#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_HWEXT_LEN + SDPCM_SWHEADER_LEN)
/* Total length of RX frame for dongle protocol */
#else
/* Total length of TX frame header for dongle protocol */
#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
#endif
#define SDPCM_HDRLEN_RX (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
#ifdef SDTEST
#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
#else
#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN)
#endif
/* Space for header read, limit for data packets */
#ifndef MAX_HDR_READ
#define MAX_HDR_READ 32
#endif
#if !ISPOWEROF2(MAX_HDR_READ)
#error MAX_HDR_READ is not a power of 2!
#endif
#define MAX_RX_DATASZ 2048
/* Maximum milliseconds to wait for F2 to come up */
#define DHD_WAIT_F2RDY 3000
/* Bump up limit on waiting for HT to account for first startup;
* if the image is doing a CRC calculation before programming the PMU
* for HT availability, it could take a couple hundred ms more, so
* max out at a 1 second (1000000us).
*/
#if (PMU_MAX_TRANSITION_DLY <= 1000000)
#undef PMU_MAX_TRANSITION_DLY
#define PMU_MAX_TRANSITION_DLY 1000000
#endif
/* Value for ChipClockCSR during initial setup */
#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ)
#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
/* Flags for SDH calls */
#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
/* Packet free applicable unconditionally for sdio and sdspi. Conditional if
* bufpool was present for gspi bus.
*/
#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \
PKTFREE(bus->dhd->osh, pkt, FALSE);
DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
#if defined(OOB_INTR_ONLY)
extern void bcmsdh_set_irq(int flag);
#endif
#ifdef PROP_TXSTATUS
extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success);
extern void dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
DEFINE_MUTEX(_dhd_sdio_mutex_lock_);
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */
#ifdef DHD_DEBUG
/* Device console log buffer state */
#define CONSOLE_LINE_MAX 192
#define CONSOLE_BUFFER_MAX 2024
typedef struct dhd_console {
uint count; /* Poll interval msec counter */
uint log_addr; /* Log struct address (fixed) */
hndrte_log_t log; /* Log struct (host copy) */
uint bufsize; /* Size of log buffer */
uint8 *buf; /* Log buffer (host copy) */
uint last; /* Last buffer read index */
} dhd_console_t;
#endif /* DHD_DEBUG */
#define REMAP_ENAB(bus) ((bus)->remap)
#define REMAP_ISADDR(bus, a) (((a) >= ((bus)->orig_ramsize)) && ((a) < ((bus)->ramsize)))
#define KSO_ENAB(bus) ((bus)->kso)
#define SR_ENAB(bus) ((bus)->_srenab)
#define SLPAUTO_ENAB(bus) ((SR_ENAB(bus)) && ((bus)->_slpauto))
#define MIN_RSRC_ADDR (SI_ENUM_BASE + 0x618)
#define MIN_RSRC_SR 0x3
#define CORE_CAPEXT_ADDR (SI_ENUM_BASE + 0x64c)
#define CORE_CAPEXT_SR_SUPPORTED_MASK (1 << 1)
#define RCTL_MACPHY_DISABLE_MASK (1 << 26)
#define RCTL_LOGIC_DISABLE_MASK (1 << 27)
#define OOB_WAKEUP_ENAB(bus) ((bus)->_oobwakeup)
#define GPIO_DEV_SRSTATE 16 /* Host gpio17 mapped to device gpio0 SR state */
#define GPIO_DEV_SRSTATE_TIMEOUT 320000 /* 320ms */
#define GPIO_DEV_WAKEUP 17 /* Host gpio17 mapped to device gpio1 wakeup */
#define CC_CHIPCTRL2_GPIO1_WAKEUP (1 << 0)
#define CC_CHIPCTRL3_SR_ENG_ENABLE (1 << 2)
#define OVERFLOW_BLKSZ512_WM 48
#define OVERFLOW_BLKSZ512_MES 80
#define CC_PMUCC3 (0x3)
/* Private data for SDIO bus interaction */
typedef struct dhd_bus {
dhd_pub_t *dhd;
bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */
si_t *sih; /* Handle for SI calls */
char *vars; /* Variables (from CIS and/or other) */
uint varsz; /* Size of variables buffer */
uint32 sbaddr; /* Current SB window pointer (-1, invalid) */
sdpcmd_regs_t *regs; /* Registers for SDIO core */
uint sdpcmrev; /* SDIO core revision */
uint armrev; /* CPU core revision */
uint ramrev; /* SOCRAM core revision */
uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */
uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
uint32 srmemsize; /* Size of SRMEM */
uint32 bus; /* gSPI or SDIO bus */
uint32 hostintmask; /* Copy of Host Interrupt Mask */
uint32 intstatus; /* Intstatus bits (events) pending */
bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
bool fcstate; /* State of dongle flow-control */
uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */
char *fw_path; /* module_param: path to firmware image */
char *nv_path; /* module_param: path to nvram vars file */
const char *nvram_params; /* user specified nvram params. */
uint blocksize; /* Block size of SDIO transfers */
uint roundup; /* Max roundup limit */
struct pktq txq; /* Queue length used for flow-control */
uint8 flowcontrol; /* per prio flow control bitmask */
uint8 tx_seq; /* Transmit sequence number (next) */
uint8 tx_max; /* Maximum transmit sequence allowed */
uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
uint16 nextlen; /* Next Read Len from last header */
uint8 rx_seq; /* Receive sequence number (expected) */
bool rxskip; /* Skip receive (awaiting NAK ACK) */
void *glomd; /* Packet containing glomming descriptor */
void *glom; /* Packet chain for glommed superframe */
uint glomerr; /* Glom packet read errors */
uint8 *rxbuf; /* Buffer for receiving control packets */
uint rxblen; /* Allocated length of rxbuf */
uint8 *rxctl; /* Aligned pointer into rxbuf */
uint8 *databuf; /* Buffer for receiving big glom packet */
uint8 *dataptr; /* Aligned pointer into databuf */
uint rxlen; /* Length of valid data in buffer */
uint8 sdpcm_ver; /* Bus protocol reported by dongle */
bool intr; /* Use interrupts */
bool poll; /* Use polling */
bool ipend; /* Device interrupt is pending */
bool intdis; /* Interrupts disabled by isr */
uint intrcount; /* Count of device interrupt callbacks */
uint lastintrs; /* Count as of last watchdog timer */
uint spurious; /* Count of spurious interrupts */
uint pollrate; /* Ticks between device polls */
uint polltick; /* Tick counter */
uint pollcnt; /* Count of active polls */
#ifdef DHD_DEBUG
dhd_console_t console; /* Console output polling support */
uint console_addr; /* Console address from shared struct */
#endif /* DHD_DEBUG */
uint regfails; /* Count of R_REG/W_REG failures */
uint clkstate; /* State of sd and backplane clock(s) */
bool activity; /* Activity flag for clock down */
int32 idletime; /* Control for activity timeout */
int32 idlecount; /* Activity timeout counter */
int32 idleclock; /* How to set bus driver when idle */
int32 sd_divisor; /* Speed control to bus driver */
int32 sd_mode; /* Mode control to bus driver */
int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
bool use_rxchain; /* If dhd should use PKT chains */
bool sleeping; /* Is SDIO bus sleeping? */
uint rxflow_mode; /* Rx flow control mode */
bool rxflow; /* Is rx flow control on */
uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */
bool alp_only; /* Don't use HT clock (ALP only) */
/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
bool usebufpool;
#ifdef SDTEST
/* external loopback */
bool ext_loop;
uint8 loopid;
/* pktgen configuration */
uint pktgen_freq; /* Ticks between bursts */
uint pktgen_count; /* Packets to send each burst */
uint pktgen_print; /* Bursts between count displays */
uint pktgen_total; /* Stop after this many */
uint pktgen_minlen; /* Minimum packet data len */
uint pktgen_maxlen; /* Maximum packet data len */
uint pktgen_mode; /* Configured mode: tx, rx, or echo */
uint pktgen_stop; /* Number of tx failures causing stop */
/* active pktgen fields */
uint pktgen_tick; /* Tick counter for bursts */
uint pktgen_ptick; /* Burst counter for printing */
uint pktgen_sent; /* Number of test packets generated */
uint pktgen_rcvd; /* Number of test packets received */
uint pktgen_prev_time; /* Time at which previous stats where printed */
uint pktgen_prev_sent; /* Number of test packets generated when
* previous stats were printed
*/
uint pktgen_prev_rcvd; /* Number of test packets received when
* previous stats were printed
*/
uint pktgen_fail; /* Number of failed send attempts */
uint16 pktgen_len; /* Length of next packet to send */
#define PKTGEN_RCV_IDLE (0)
#define PKTGEN_RCV_ONGOING (1)
uint16 pktgen_rcv_state; /* receive state */
uint pktgen_rcvd_rcvsession; /* test pkts rcvd per rcv session. */
#endif /* SDTEST */
/* Some additional counters */
uint tx_sderrs; /* Count of tx attempts with sd errors */
uint fcqueued; /* Tx packets that got queued */
uint rxrtx; /* Count of rtx requests (NAK to dongle) */
uint rx_toolong; /* Receive frames too long to receive */
uint rxc_errors; /* SDIO errors when reading control frames */
uint rx_hdrfail; /* SDIO errors on header reads */
uint rx_badhdr; /* Bad received headers (roosync?) */
uint rx_badseq; /* Mismatched rx sequence number */
uint fc_rcvd; /* Number of flow-control events received */
uint fc_xoff; /* Number which turned on flow-control */
uint fc_xon; /* Number which turned off flow-control */
uint rxglomfail; /* Failed deglom attempts */
uint rxglomframes; /* Number of glom frames (superframes) */
uint rxglompkts; /* Number of packets from glom frames */
uint f2rxhdrs; /* Number of header reads */
uint f2rxdata; /* Number of frame data reads */
uint f2txdata; /* Number of f2 frame writes */
uint f1regdata; /* Number of f1 register accesses */
uint8 *ctrl_frame_buf;
uint32 ctrl_frame_len;
bool ctrl_frame_stat;
uint32 rxint_mode; /* rx interrupt mode */
bool remap; /* Contiguous 1MB RAM: 512K socram + 512K devram
* Available with socram rev 16
* Remap region not DMA-able
*/
bool kso;
bool _slpauto;
bool _oobwakeup;
bool _srenab;
bool readframes;
bool reqbussleep;
uint32 resetinstr;
uint32 dongle_ram_base;
#ifdef BCMSDIOH_TXGLOM
void *glom_pkt_arr[SDPCM_MAXGLOM_SIZE]; /* Array of pkts for glomming */
uint16 glom_cnt; /* Number of pkts in the glom array */
uint16 glom_total_len; /* Total length of pkts in glom array */
bool glom_enable; /* Flag to indicate whether tx glom is enabled/disabled */
uint8 glom_mode; /* Glom mode - 0-copy mode, 1 - Multi-descriptor mode */
uint32 glomsize; /* Glom size limitation */
#endif
} dhd_bus_t;
/* clkstate */
#define CLK_NONE 0
#define CLK_SDONLY 1
#define CLK_PENDING 2 /* Not used yet */
#define CLK_AVAIL 3
#define DHD_NOPMU(dhd) (FALSE)
#ifdef DHD_DEBUG
static int qcount[NUMPRIO];
static int tx_packets[NUMPRIO];
#endif /* DHD_DEBUG */
/* Deferred transmit */
const uint dhd_deferred_tx = 1;
extern uint dhd_watchdog_ms;
extern void dhd_os_wd_timer(void *bus, uint wdtick);
/* Tx/Rx bounds */
uint dhd_txbound;
uint dhd_rxbound;
uint dhd_txminmax = DHD_TXMINMAX;
/* override the RAM size if possible */
#define DONGLE_MIN_RAMSIZE (128 *1024)
int dhd_dongle_ramsize;
uint dhd_doflow = TRUE;
uint dhd_dpcpoll = FALSE;
static bool dhd_alignctl;
static bool sd1idle;
static bool retrydata;
#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
#if defined(SDIO_CRC_ERROR_FIX)
static uint watermark = 48;
static uint mesbusyctrl = 80;
#else
static const uint watermark = 8;
static const uint mesbusyctrl = 0;
#endif
static const uint firstread = DHD_FIRSTREAD;
#define HDATLEN (firstread - (SDPCM_HDRLEN))
/* Retry count for register access failures */
static const uint retry_limit = 2;
/* Force even SD lengths (some host controllers mess up on odd bytes) */
static bool forcealign;
#define ALIGNMENT 4
#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
#endif
#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
#define PKTALIGN(osh, p, len, align) \
do { \
uint datalign; \
datalign = (uintptr)PKTDATA((osh), (p)); \
datalign = ROUNDUP(datalign, (align)) - datalign; \
ASSERT(datalign < (align)); \
ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \
if (datalign) \
PKTPULL((osh), (p), datalign); \
PKTSETLEN((osh), (p), (len)); \
} while (0)
/* Limit on rounding up frames */
static const uint max_roundup = 512;
/* Try doing readahead */
static bool dhd_readahead;
/* To check if there's window offered */
#define DATAOK(bus) \
(((uint8)(bus->tx_max - bus->tx_seq) > 1) && \
(((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
/* To check if there's window offered for ctrl frame */
#define TXCTLOK(bus) \
(((uint8)(bus->tx_max - bus->tx_seq) != 0) && \
(((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
/* Number of pkts available in dongle for data RX */
#define DATABUFCNT(bus) \
((uint8)(bus->tx_max - bus->tx_seq) - 1)
/* Macros to get register read/write status */
/* NOTE: these assume a local dhdsdio_bus_t *bus! */
#define R_SDREG(regvar, regaddr, retryvar) \
do { \
retryvar = 0; \
do { \
regvar = R_REG(bus->dhd->osh, regaddr); \
} while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
if (retryvar) { \
bus->regfails += (retryvar-1); \
if (retryvar > retry_limit) { \
DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
__FUNCTION__, __LINE__)); \
regvar = 0; \
} \
} \
} while (0)
#define W_SDREG(regval, regaddr, retryvar) \
do { \
retryvar = 0; \
do { \
W_REG(bus->dhd->osh, regaddr, regval); \
} while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
if (retryvar) { \
bus->regfails += (retryvar-1); \
if (retryvar > retry_limit) \
DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
__FUNCTION__, __LINE__)); \
} \
} while (0)
#define BUS_WAKE(bus) \
do { \
bus->idlecount = 0; \
if ((bus)->sleeping) \
dhdsdio_bussleep((bus), FALSE); \
} while (0);
/*
* pktavail interrupts from dongle to host can be managed in 3 different ways
* whenever there is a packet available in dongle to transmit to host.
*
* Mode 0: Dongle writes the software host mailbox and host is interrupted.
* Mode 1: (sdiod core rev >= 4)
* Device sets a new bit in the intstatus whenever there is a packet
* available in fifo. Host can't clear this specific status bit until all the
* packets are read from the FIFO. No need to ack dongle intstatus.
* Mode 2: (sdiod core rev >= 4)
* Device sets a bit in the intstatus, and host acks this by writing
* one to this bit. Dongle won't generate anymore packet interrupts
* until host reads all the packets from the dongle and reads a zero to
* figure that there are no more packets. No need to disable host ints.
* Need to ack the intstatus.
*/
#define SDIO_DEVICE_HMB_RXINT 0 /* default old way */
#define SDIO_DEVICE_RXDATAINT_MODE_0 1 /* from sdiod rev 4 */
#define SDIO_DEVICE_RXDATAINT_MODE_1 2 /* from sdiod rev 4 */
#define FRAME_AVAIL_MASK(bus) \
((bus->rxint_mode == SDIO_DEVICE_HMB_RXINT) ? I_HMB_FRAME_IND : I_XMTDATA_AVAIL)
#define DHD_BUS SDIO_BUS
#define PKT_AVAILABLE(bus, intstatus) ((intstatus) & (FRAME_AVAIL_MASK(bus)))
#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
#define GSPI_PR55150_BAILOUT
#ifdef SDTEST
static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint count);
#endif
#ifdef DHD_DEBUG
static int dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size);
static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror);
#endif /* DHD_DEBUG */
static int dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap);
static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh);
static void dhdsdio_disconnect(void *ptr);
static bool dhdsdio_chipmatch(uint16 chipid);
static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh,
void * regsva, uint16 devid);
static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh);
static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh);
static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation,
bool reset_flag);
static void dhd_dongle_setramsize(struct dhd_bus *bus, int mem_size);
static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
uint8 *buf, uint nbytes,
void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags,
uint8 *buf, uint nbytes,
void *pkt, bcmsdh_cmplt_fn_t complete, void *handle);
#ifdef BCMSDIOH_TXGLOM
static void dhd_bcmsdh_glom_post(dhd_bus_t *bus, uint8 *frame, void *pkt, uint len);
static void dhd_bcmsdh_glom_clear(dhd_bus_t *bus);
#endif
static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh);
static int _dhdsdio_download_firmware(dhd_bus_t *bus);
static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path);
static int dhdsdio_download_nvram(dhd_bus_t *bus);
#ifdef BCMEMBEDIMAGE
static int dhdsdio_download_code_array(dhd_bus_t *bus);
#endif
static int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep);
static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok);
static uint8 dhdsdio_sleepcsr_get(dhd_bus_t *bus);
#ifdef WLMEDIA_HTSF
#include <htsf.h>
extern uint32 dhd_get_htsf(void *dhd, int ifidx);
#endif /* WLMEDIA_HTSF */
static void
dhd_overflow_war(struct dhd_bus *bus)
{
int err;
uint8 devctl, wm, mes;
/* See .ppt in PR for these recommended values */
if (bus->blocksize == 512) {
wm = OVERFLOW_BLKSZ512_WM;
mes = OVERFLOW_BLKSZ512_MES;
} else {
mes = bus->blocksize/4;
wm = bus->blocksize/4;
}
/* Update watermark */
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, wm, &err);
devctl = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err);
/* Update MES */
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL,
(mes | SBSDIO_MESBUSYCTRL_ENAB), &err);
DHD_INFO(("Apply overflow WAR: 0x%02x 0x%02x 0x%02x\n",
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err),
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, &err),
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_MESBUSYCTRL, &err)));
}
static void
dhd_dongle_setramsize(struct dhd_bus *bus, int mem_size)
{
int32 min_size = DONGLE_MIN_RAMSIZE;
/* Restrict the ramsize to user specified limit */
DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n",
dhd_dongle_ramsize, min_size));
if ((dhd_dongle_ramsize > min_size) &&
(dhd_dongle_ramsize < (int32)bus->orig_ramsize))
bus->ramsize = dhd_dongle_ramsize;
}
static int
dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address)
{
int err = 0;
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
(address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
if (!err)
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
(address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
if (!err)
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
(address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err);
return err;
}
#ifdef USE_OOB_GPIO1
static int
dhdsdio_oobwakeup_init(dhd_bus_t *bus)
{
uint32 val, addr, data;
bcmsdh_gpioouten(bus->sdh, GPIO_DEV_WAKEUP);
addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr);
data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data);
/* Set device for gpio1 wakeup */
bcmsdh_reg_write(bus->sdh, addr, 4, 2);
val = bcmsdh_reg_read(bus->sdh, data, 4);
val |= CC_CHIPCTRL2_GPIO1_WAKEUP;
bcmsdh_reg_write(bus->sdh, data, 4, val);
bus->_oobwakeup = TRUE;
return 0;
}
#endif /* USE_OOB_GPIO1 */
/*
* Query if FW is in SR mode
*/
static bool
dhdsdio_sr_cap(dhd_bus_t *bus)
{
bool cap = FALSE;
uint32 core_capext, addr, data;
if (bus->sih->chip == BCM4324_CHIP_ID) {
addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr);
data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data);
bcmsdh_reg_write(bus->sdh, addr, 4, 3);
core_capext = bcmsdh_reg_read(bus->sdh, data, 4);
} else if (bus->sih->chip == BCM4330_CHIP_ID) {
core_capext = FALSE;
} else if (bus->sih->chip == BCM43362_CHIP_ID) {
core_capext = FALSE;
} else if ((bus->sih->chip == BCM4335_CHIP_ID) ||
(bus->sih->chip == BCM4350_CHIP_ID)) {
core_capext = TRUE;
} else {
core_capext = bcmsdh_reg_read(bus->sdh, CORE_CAPEXT_ADDR, 4);
core_capext = (core_capext & CORE_CAPEXT_SR_SUPPORTED_MASK);
}
if (!(core_capext))
return FALSE;
if (bus->sih->chip == BCM4324_CHIP_ID) {
/* FIX: Should change to query SR control register instead */
cap = TRUE;
} else if (bus->sih->chip == BCM4335_CHIP_ID) {
uint32 enabval = 0;
addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr);
data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data);
bcmsdh_reg_write(bus->sdh, addr, 4, CC_PMUCC3);
enabval = bcmsdh_reg_read(bus->sdh, data, 4);
if ((bus->sih->chip == BCM4350_CHIP_ID) ||
0)
enabval &= CC_CHIPCTRL3_SR_ENG_ENABLE;
if (enabval)
cap = TRUE;
} else {
data = bcmsdh_reg_read(bus->sdh,
SI_ENUM_BASE + OFFSETOF(chipcregs_t, retention_ctl), 4);
if ((data & (RCTL_MACPHY_DISABLE_MASK | RCTL_LOGIC_DISABLE_MASK)) == 0)
cap = TRUE;
}
return cap;
}
static int
dhdsdio_srwar_init(dhd_bus_t *bus)
{
#if !defined(NDISVER) || (NDISVER < 0x0630)
bcmsdh_gpio_init(bus->sdh);
#endif /* !defined(NDISVER) || (NDISVER < 0x0630) */
#ifdef USE_OOB_GPIO1
dhdsdio_oobwakeup_init(bus);
#endif
return 0;
}
static int
dhdsdio_sr_init(dhd_bus_t *bus)
{
uint8 val;
int err = 0;
if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2))
dhdsdio_srwar_init(bus);
val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL);
val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL,
1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT, &err);
val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_WAKEUPCTRL, NULL);
/* Add CMD14 Support */
dhdsdio_devcap_set(bus,
(SDIOD_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | SDIOD_CCCR_BRCM_CARDCAP_CMD14_EXT));
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
SBSDIO_FUNC1_CHIPCLKCSR, SBSDIO_FORCE_HT, &err);
bus->_slpauto = dhd_slpauto ? TRUE : FALSE;
bus->_srenab = TRUE;
return 0;
}
/*
* FIX: Be sure KSO bit is enabled
* Currently, it's defaulting to 0 which should be 1.
*/
static int
dhdsdio_clk_kso_init(dhd_bus_t *bus)
{
uint8 val;
int err = 0;
/* set flag */
bus->kso = TRUE;
/*
* Enable KeepSdioOn (KSO) bit for normal operation
* Default is 0 (4334A0) so set it. Fixed in B0.
*/
val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, NULL);
if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, val, &err);
if (err)
DHD_ERROR(("%s: SBSDIO_FUNC1_SLEEPCSR err: 0x%x\n", __FUNCTION__, err));
}
return 0;
}
#define KSO_DBG(x)
#define KSO_WAIT_US 50
#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
static int
dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on)
{
uint8 wr_val = 0, rd_val, cmp_val, bmask;
int err = 0;
int try_cnt = 0;
KSO_DBG(("%s> op:%s\n", __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR")));
wr_val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
if (on) {
cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
bmask = cmp_val;
msleep(3);
} else {
/* Put device to sleep, turn off KSO */
cmp_val = 0;
bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
}
do {
rd_val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err);
if (((rd_val & bmask) == cmp_val) && !err)
break;
KSO_DBG(("%s> KSO wr/rd retry:%d, ERR:%x \n", __FUNCTION__, try_cnt, err));
OSL_DELAY(KSO_WAIT_US);
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err);
} while (try_cnt++ < MAX_KSO_ATTEMPTS);
if (try_cnt > 2)
KSO_DBG(("%s> op:%s, try_cnt:%d, rd_val:%x, ERR:%x \n",
__FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err));
if (try_cnt > MAX_KSO_ATTEMPTS) {
DHD_ERROR(("%s> op:%s, ERROR: try_cnt:%d, rd_val:%x, ERR:%x \n",
__FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err));
}
return err;
}
static int
dhdsdio_clk_kso_iovar(dhd_bus_t *bus, bool on)
{
int err = 0;
if (on == FALSE) {
BUS_WAKE(bus);
dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
DHD_ERROR(("%s: KSO disable clk: 0x%x\n", __FUNCTION__,
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
SBSDIO_FUNC1_CHIPCLKCSR, &err)));
dhdsdio_clk_kso_enab(bus, FALSE);
} else {
DHD_ERROR(("%s: KSO enable\n", __FUNCTION__));
/* Make sure we have SD bus access */
if (bus->clkstate == CLK_NONE) {
DHD_ERROR(("%s: Request SD clk\n", __FUNCTION__));
dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
}
/* Double-write to be safe in case transition of AOS */
dhdsdio_clk_kso_enab(bus, TRUE);
dhdsdio_clk_kso_enab(bus, TRUE);
OSL_DELAY(4000);
/* Wait for device ready during transition to wake-up */
SPINWAIT(((dhdsdio_sleepcsr_get(bus)) !=
(SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)),
(10000));
DHD_ERROR(("%s: sleepcsr: 0x%x\n", __FUNCTION__,
dhdsdio_sleepcsr_get(bus)));
}
bus->kso = on;
BCM_REFERENCE(err);
return 0;
}
static uint8
dhdsdio_sleepcsr_get(dhd_bus_t *bus)
{
int err = 0;
uint8 val = 0;
val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, &err);
if (err)
DHD_TRACE(("Failed to read SLEEPCSR: %d\n", err));
return val;
}
uint8
dhdsdio_devcap_get(dhd_bus_t *bus)
{
return bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, NULL);
}
static int
dhdsdio_devcap_set(dhd_bus_t *bus, uint8 cap)
{
int err = 0;
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_BRCM_CARDCAP, cap, &err);
if (err)
DHD_ERROR(("%s: devcap set err: 0x%x\n", __FUNCTION__, err));
return 0;
}
static int
dhdsdio_clk_devsleep_iovar(dhd_bus_t *bus, bool on)
{
int err = 0, retry;
uint8 val;
retry = 0;
if (on == TRUE) {
/* Enter Sleep */
/* Be sure we request clk before going to sleep
* so we can wake-up with clk request already set
* else device can go back to sleep immediately
*/
if (!SLPAUTO_ENAB(bus))
dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
else {
val = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err);
if ((val & SBSDIO_CSR_MASK) == 0) {
DHD_ERROR(("%s: No clock before enter sleep:0x%x\n",
__FUNCTION__, val));
/* Reset clock request */
bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
SBSDIO_ALP_AVAIL_REQ, &err);
DHD_ERROR(("%s: clock before sleep:0x%x\n", __FUNCTION__,
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
SBSDIO_FUNC1_CHIPCLKCSR, &err)));
}
}
DHD_TRACE(("%s: clk before sleep: 0x%x\n", __FUNCTION__,
bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
SBSDIO_FUNC1_CHIPCLKCSR, &err)));
#ifdef USE_CMD14
err = bcmsdh_sleep(bus->sdh, TRUE);
#else
err = dhdsdio_clk_kso_enab(bus, FALSE);
if (OOB_WAKEUP_ENAB(bus))
{
#if !defined(NDISVER) || (NDISVER < 0x0630)
err = bcmsdh_gpioout(bus->sdh, GPIO_DEV_WAKEUP, FALSE); /* GPIO_1 is off */
#endif /* !defined(NDISVER) || (NDISVER < 0x0630) */
}
#endif /* USE_CMD14 */
} else {
/* Exit Sleep */
/* Make sure we have SD bus access */
if (bus->clkstate == CLK_NONE) {
DHD_TRACE(("%s: Request SD clk\n", __FUNCTION__));
dhdsdio_clkctl(bus, CLK_SDONLY, FALSE);
}
#if !defined(NDISVER) || (NDISVER < 0x0630)
if ((bus->sih->chip == BCM4334_CHIP_ID) && (bus->sih->chiprev == 2)) {
SPINWAIT((bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) != TRUE),
GPIO_DEV_SRSTATE_TIMEOUT);
if (bcmsdh_gpioin(bus->sdh, GPIO_DEV_SRSTATE) == FALSE) {
DHD_ERROR(("ERROR: GPIO_DEV_SRSTATE still low!\n"));
}
}
#endif
#ifdef USE_CMD14
err = bcmsdh_sleep(bus->sdh, FALSE);
if (SLPAUTO_ENAB(bus) && (err != 0)) {