aboutsummaryrefslogtreecommitdiff
path: root/drivers/sdio/function/wlan/ar6000/hif/hif.c
blob: f5a098672a1294c2c37c45bc8e90d5f1186acae8 (plain)
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
/*
 * @file: hif.c
 *
 * @abstract: HIF layer reference implementation for Atheros SDIO stack
 *
 * @notice: Copyright (c) 2004-2006 Atheros Communications Inc.
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation;
 *
 *  Software distributed under the License is distributed on an "AS
 *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  rights and limitations under the License.
 *
 *
 *
 */

#include "hif_internal.h"

/* ------ Static Variables ------ */

/* ------ Global Variable Declarations ------- */
SD_PNP_INFO Ids[] = {
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0xB,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0xA,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0x9,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0x8,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6002_BASE | 0x0,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
        .SDIO_ManufacturerID = MANUFACTURER_ID_AR6002_BASE | 0x1,
        .SDIO_ManufacturerCode = MANUFACTURER_CODE,
        .SDIO_FunctionClass = FUNCTION_CLASS,
        .SDIO_FunctionNo = 1
    },
    {
    }                      //list is null termintaed
};

TARGET_FUNCTION_CONTEXT FunctionContext = {
    .function.Version    = CT_SDIO_STACK_VERSION_CODE,
    .function.pName      = "sdio_wlan",
    .function.MaxDevices = 1,
    .function.NumDevices = 0,
    .function.pIds       = Ids,
    .function.pProbe     = hifDeviceInserted,
    .function.pRemove    = hifDeviceRemoved,
    .function.pSuspend   = NULL,
    .function.pResume    = NULL,
    .function.pWake      = NULL,
    .function.pContext   = &FunctionContext,
};

HIF_DEVICE hifDevice[HIF_MAX_DEVICES];
HTC_CALLBACKS htcCallbacks;
BUS_REQUEST busRequest[BUS_REQUEST_MAX_NUM];
static BUS_REQUEST *s_busRequestFreeQueue = NULL;
OS_CRITICALSECTION lock;
extern A_UINT32 onebitmode;
extern A_UINT32 busspeedlow;
extern A_UINT32 debughif;

#ifdef DEBUG
#define ATH_DEBUG_ERROR 1
#define ATH_DEBUG_WARN  2
#define ATH_DEBUG_TRACE 3
#define _AR_DEBUG_PRINTX_ARG(arg...) arg
#define AR_DEBUG_PRINTF(lvl, args)\
    {if (lvl <= debughif)\
        A_PRINTF(KERN_ALERT _AR_DEBUG_PRINTX_ARG args);\
    }
#else
#define AR_DEBUG_PRINTF(lvl, args)
#endif

static BUS_REQUEST *hifAllocateBusRequest(void);
static void hifFreeBusRequest(BUS_REQUEST *busrequest);
static THREAD_RETURN insert_helper_func(POSKERNEL_HELPER pHelper);
static void ResetAllCards(void);

/* ------ Functions ------ */
int HIFInit(HTC_CALLBACKS *callbacks)
{
    SDIO_STATUS status;
    DBG_ASSERT(callbacks != NULL);

    /* Store the callback and event handlers */
    htcCallbacks.deviceInsertedHandler = callbacks->deviceInsertedHandler;
    htcCallbacks.deviceRemovedHandler = callbacks->deviceRemovedHandler;
    htcCallbacks.deviceSuspendHandler = callbacks->deviceSuspendHandler;
    htcCallbacks.deviceResumeHandler = callbacks->deviceResumeHandler;
    htcCallbacks.deviceWakeupHandler = callbacks->deviceWakeupHandler;
    htcCallbacks.rwCompletionHandler = callbacks->rwCompletionHandler;
    htcCallbacks.dsrHandler = callbacks->dsrHandler;

    CriticalSectionInit(&lock);

    /* Register with bus driver core */
    status = SDIO_RegisterFunction(&FunctionContext.function);
    DBG_ASSERT(SDIO_SUCCESS(status));

    return(0);
}

A_STATUS
HIFReadWrite(HIF_DEVICE *device,
             A_UINT32 address,
             A_UCHAR *buffer,
             A_UINT32 length,
             A_UINT32 request,
             void *context)
{
    A_UINT8 rw;
    A_UINT8 mode;
    A_UINT8 funcNo;
    A_UINT8 opcode;
    A_UINT16 count;
    SDREQUEST *sdrequest;
    SDIO_STATUS sdiostatus;
    BUS_REQUEST *busrequest;
    A_STATUS    status = A_OK;

    DBG_ASSERT(device != NULL);
    DBG_ASSERT(device->handle != NULL);

    AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));

    do {
        busrequest = hifAllocateBusRequest();
        if (busrequest == NULL) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF Unable to allocate bus request\n"));
            status = A_NO_RESOURCE;
            break;
        }

        sdrequest = busrequest->request;
        busrequest->context = context;

        sdrequest->pDataBuffer = buffer;
        if (request & HIF_SYNCHRONOUS) {
            sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS;
            sdrequest->pCompleteContext = NULL;
            sdrequest->pCompletion = NULL;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Synchronous\n"));
        } else if (request & HIF_ASYNCHRONOUS) {
            sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS |
                               SDREQ_FLAGS_TRANS_ASYNC;
            sdrequest->pCompleteContext = busrequest;
            sdrequest->pCompletion = hifRWCompletionHandler;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Asynchronous\n"));
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Invalid execution mode: 0x%08x\n", request));
            status = A_EINVAL;
            break;
        }

        if (request & HIF_EXTENDED_IO) {
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Command type: CMD53\n"));
            sdrequest->Command = CMD53;
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Invalid command type: 0x%08x\n", request));
            status = A_EINVAL;
            break;
        }

        if (request & HIF_BLOCK_BASIS) {
            mode = CMD53_BLOCK_BASIS;
            sdrequest->BlockLen = HIF_MBOX_BLOCK_SIZE;
            sdrequest->BlockCount = length / HIF_MBOX_BLOCK_SIZE;
            count = sdrequest->BlockCount;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                            ("Block mode (BlockLen: %d, BlockCount: %d)\n",
                            sdrequest->BlockLen, sdrequest->BlockCount));
        } else if (request & HIF_BYTE_BASIS) {
            mode = CMD53_BYTE_BASIS;
            sdrequest->BlockLen = length;
            sdrequest->BlockCount = 1;
            count = sdrequest->BlockLen;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                            ("Byte mode (BlockLen: %d, BlockCount: %d)\n",
                            sdrequest->BlockLen, sdrequest->BlockCount));
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Invalid data mode: 0x%08x\n", request));
            status = A_EINVAL;
            break;
        }

#if 0
        /* useful for checking register accesses */
        if (length & 0x3) {
            A_PRINTF(KERN_ALERT"HIF (%s) is not a multiple of 4 bytes, addr:0x%X, len:%d\n",
                                request & HIF_WRITE ? "write":"read", address, length);
        }
#endif

        if ((address >= HIF_MBOX_START_ADDR(0)) &&
            (address <= HIF_MBOX_END_ADDR(3)))
        {

            DBG_ASSERT(length <= HIF_MBOX_WIDTH);

            /*
             * Mailbox write. Adjust the address so that the last byte
             * falls on the EOM address.
             */
            address += (HIF_MBOX_WIDTH - length);
        }



        if (request & HIF_WRITE) {
            rw = CMD53_WRITE;
            sdrequest->Flags |= SDREQ_FLAGS_DATA_WRITE;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Write\n"));
        } else if (request & HIF_READ) {
            rw = CMD53_READ;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Read\n"));
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Invalid direction: 0x%08x\n", request));
            status = A_EINVAL;
            break;
        }

        if (request & HIF_FIXED_ADDRESS) {
            opcode = CMD53_FIXED_ADDRESS;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Fixed\n"));
        } else if (request & HIF_INCREMENTAL_ADDRESS) {
            opcode = CMD53_INCR_ADDRESS;
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Incremental\n"));
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Invalid address mode: 0x%08x\n", request));
            status = A_EINVAL;
            break;
        }

        funcNo = SDDEVICE_GET_SDIO_FUNCNO(device->handle);
        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Function number: %d\n", funcNo));
        SDIO_SET_CMD53_ARG(sdrequest->Argument, rw, funcNo,
                           mode, opcode, address, count);

        /* Send the command out */
        sdiostatus = SDDEVICE_CALL_REQUEST_FUNC(device->handle, sdrequest);

        if (!SDIO_SUCCESS(sdiostatus)) {
            status = A_ERROR;
        }

    } while (FALSE);

    if (A_FAILED(status) || (request & HIF_SYNCHRONOUS)) {
        if (busrequest != NULL) {
            hifFreeBusRequest(busrequest);
        }
    }

    if (A_FAILED(status) && (request & HIF_ASYNCHRONOUS)) {
            /* call back async handler on failure */
        htcCallbacks.rwCompletionHandler(context, status);
    }

    return status;
}

A_STATUS
HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode,
                   void *config, A_UINT32 configLen)
{
    A_UINT32 count;

    switch(opcode) {
        case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
            ((A_UINT32 *)config)[0] = HIF_MBOX0_BLOCK_SIZE;
            ((A_UINT32 *)config)[1] = HIF_MBOX1_BLOCK_SIZE;
            ((A_UINT32 *)config)[2] = HIF_MBOX2_BLOCK_SIZE;
            ((A_UINT32 *)config)[3] = HIF_MBOX3_BLOCK_SIZE;
            break;

        case HIF_DEVICE_GET_MBOX_ADDR:
            for (count = 0; count < 4; count ++) {
                ((A_UINT32 *)config)[count] = HIF_MBOX_START_ADDR(count);
            }
            break;
        case HIF_DEVICE_GET_IRQ_PROC_MODE:
                /* the SDIO stack allows the interrupts to be processed either way, ASYNC or SYNC */
            *((HIF_DEVICE_IRQ_PROCESSING_MODE *)config) = HIF_DEVICE_IRQ_ASYNC_SYNC;
            break;
        default:
            AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
                            ("Unsupported configuration opcode: %d\n", opcode));
            return A_ERROR;
    }

    return A_OK;
}

void
HIFShutDownDevice(HIF_DEVICE *device)
{
    A_UINT8 data;
    A_UINT32 count;
    SDIO_STATUS status;
    SDCONFIG_BUS_MODE_DATA busSettings;
    SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;

    if (device != NULL) {
        DBG_ASSERT(device->handle != NULL);

        /* Remove the allocated current if any */
        status = SDLIB_IssueConfig(device->handle,
                                   SDCONFIG_FUNC_FREE_SLOT_CURRENT, NULL, 0);
        DBG_ASSERT(SDIO_SUCCESS(status));

        /* Disable the card */
        fData.EnableFlags = SDCONFIG_DISABLE_FUNC;
        fData.TimeOut = 1;
        status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ENABLE_DISABLE,
                                   &fData, sizeof(fData));
        DBG_ASSERT(SDIO_SUCCESS(status));

        /* Perform a soft I/O reset */
        data = SDIO_IO_RESET;
        status = SDLIB_IssueCMD52(device->handle, 0, SDIO_IO_ABORT_REG,
                                  &data, 1, 1);
        DBG_ASSERT(SDIO_SUCCESS(status));

        /*
         * WAR - Codetelligence driver does not seem to shutdown correctly in 1
         * bit mode. By default it configures the HC in the 4 bit. Its later in
         * our driver that we switch to 1 bit mode. If we try to shutdown, the
         * driver hangs so we revert to 4 bit mode, to be transparent to the
         * underlying bus driver.
         */
        if (onebitmode) {
            ZERO_OBJECT(busSettings);
            busSettings.BusModeFlags = SDDEVICE_GET_BUSMODE_FLAGS(device->handle);
            SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
                                   SDCONFIG_BUS_WIDTH_4_BIT);

            /* Issue config request to change the bus width to 4 bit */
            status = SDLIB_IssueConfig(device->handle, SDCONFIG_BUS_MODE_CTRL,
                                       &busSettings,
                                       sizeof(SDCONFIG_BUS_MODE_DATA));
            DBG_ASSERT(SDIO_SUCCESS(status));
        }

        /* Free the bus requests */
        for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
            SDDeviceFreeRequest(device->handle, busRequest[count].request);
        }
        /* Clean up the queue */
        s_busRequestFreeQueue = NULL;
    } else {
            /* since we are unloading the driver anyways, reset all cards in case the SDIO card
             * is externally powered and we are unloading the SDIO stack.  This avoids the problem when
             * the SDIO stack is reloaded and attempts are made to re-enumerate a card that is already
             * enumerated */
        ResetAllCards();
        /* Unregister with bus driver core */
        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                        ("Unregistering with the bus driver\n"));
        status = SDIO_UnregisterFunction(&FunctionContext.function);
        DBG_ASSERT(SDIO_SUCCESS(status));
    }
}

void
hifRWCompletionHandler(SDREQUEST *request)
{
    A_STATUS status;
    void *context;
    BUS_REQUEST *busrequest;

    if (SDIO_SUCCESS(request->Status)) {
        status = A_OK;
    } else {
        status = A_ERROR;
    }

    DBG_ASSERT(status == A_OK);
    busrequest = (BUS_REQUEST *) request->pCompleteContext;
    context = (void *) busrequest->context;
        /* free the request before calling the callback, in case the
         * callback submits another request, this guarantees that
         * there is at least 1 free request available everytime the callback
         * is invoked */
    hifFreeBusRequest(busrequest);
    htcCallbacks.rwCompletionHandler(context, status);
}

void
hifIRQHandler(void *context)
{
    A_STATUS status;
    HIF_DEVICE *device;

    device = (HIF_DEVICE *)context;
    AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
    status = htcCallbacks.dsrHandler(device->htc_handle);
    DBG_ASSERT(status == A_OK);
}

BOOL
hifDeviceInserted(SDFUNCTION *function, SDDEVICE *handle)
{
    BOOL enabled;
    A_UINT8 data;
    A_UINT32 count;
    HIF_DEVICE *device;
    SDIO_STATUS status;
    A_UINT16 maxBlocks;
    A_UINT16 maxBlockSize;
    SDCONFIG_BUS_MODE_DATA busSettings;
    SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
    TARGET_FUNCTION_CONTEXT *functionContext;
    SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent;
    SD_BUSCLOCK_RATE                currentBusClock;

    DBG_ASSERT(function != NULL);
    DBG_ASSERT(handle != NULL);

    device = addHifDevice(handle);
    AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
    functionContext =  (TARGET_FUNCTION_CONTEXT *)function->pContext;

    /*
     * Issue commands to get the manufacturer ID and stuff and compare it
     * against the rev Id derived from the ID registered during the
     * initialization process. Report the device only in the case there
     * is a match. In the case od SDIO, the bus driver has already queried
     * these details so we just need to use their data structures to get the
     * relevant values. Infact, the driver has already matched it against
     * the Ids that we registered with it so we dont need to the step here.
     */

    /* Configure the SDIO Bus Width */
    if (onebitmode) {
        data = SDIO_BUS_WIDTH_1_BIT;
        status = SDLIB_IssueCMD52(handle, 0, SDIO_BUS_IF_REG, &data, 1, 1);
        if (!SDIO_SUCCESS(status)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Unable to set the bus width to 1 bit\n"));
            return FALSE;
        }
    }

    /* Get current bus flags */
    ZERO_OBJECT(busSettings);

    busSettings.BusModeFlags = SDDEVICE_GET_BUSMODE_FLAGS(handle);
    if (onebitmode) {
        SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
                               SDCONFIG_BUS_WIDTH_1_BIT);
    }

        /* get the current operating clock, the bus driver sets us up based
         * on what our CIS reports and what the host controller can handle
         * we can use this to determine whether we want to drop our clock rate
         * down */
    currentBusClock = SDDEVICE_GET_OPER_CLOCK(handle);
    busSettings.ClockRate = currentBusClock;

    AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                        ("HIF currently running at: %d \n",currentBusClock));

        /* see if HIF wants to run at a lower clock speed, we may already be
         * at that lower clock speed */
    if (currentBusClock > (SDIO_CLOCK_FREQUENCY_DEFAULT >> busspeedlow)) {
        busSettings.ClockRate = SDIO_CLOCK_FREQUENCY_DEFAULT >> busspeedlow;
        AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
                        ("HIF overriding clock to %d \n",busSettings.ClockRate));
    }

    /* Issue config request to override clock rate */
    status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_CHANGE_BUS_MODE, &busSettings,
                               sizeof(SDCONFIG_BUS_MODE_DATA));
    if (!SDIO_SUCCESS(status)) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                        ("Unable to configure the host clock\n"));
        return FALSE;
    } else {
        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                        ("Configured clock: %d, Maximum clock: %d\n",
                        busSettings.ActualClockRate,
                        SDDEVICE_GET_MAX_CLOCK(handle)));
    }

    /*
     * Check if the target supports block mode. This result of this check
     * can be used to implement the HIFReadWrite API.
     */
    if (SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle)) {
        /* Limit block size to operational block limit or card function
           capability */
        maxBlockSize = min(SDDEVICE_GET_OPER_BLOCK_LEN(handle),
                           SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle));

        /* check if the card support multi-block transfers */
        if (!(SDDEVICE_GET_SDIOCARD_CAPS(handle) & SDIO_CAPS_MULTI_BLOCK)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Byte basis only\n"));

            /* Limit block size to max byte basis */
            maxBlockSize =  min(maxBlockSize,
                                (A_UINT16)SDIO_MAX_LENGTH_BYTE_BASIS);
            maxBlocks = 1;
        } else {
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Multi-block capable\n"));
            maxBlocks = SDDEVICE_GET_OPER_BLOCKS(handle);
            status = SDLIB_SetFunctionBlockSize(handle, HIF_MBOX_BLOCK_SIZE);
            if (!SDIO_SUCCESS(status)) {
                AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                                ("Failed to set block size. Err:%d\n", status));
                return FALSE;
            }
        }

        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                        ("Bytes Per Block: %d bytes, Block Count:%d \n",
                        maxBlockSize, maxBlocks));
    } else {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                        ("Function does not support Block Mode!\n"));
        return FALSE;
    }

    /* Allocate the slot current */
    status = SDLIB_GetDefaultOpCurrent(handle, &slotCurrent.SlotCurrent);
    if (SDIO_SUCCESS(status)) {
        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Allocating Slot current: %d mA\n",
                                slotCurrent.SlotCurrent));
        status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ALLOC_SLOT_CURRENT,
                                   &slotCurrent, sizeof(slotCurrent));
        if (!SDIO_SUCCESS(status)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                            ("Failed to allocate slot current %d\n", status));
            return FALSE;
        }
    }

    /* Enable the dragon function */
    count = 0;
    enabled = FALSE;
    fData.TimeOut = 1;
    fData.EnableFlags = SDCONFIG_ENABLE_FUNC;
    while ((count++ < SDWLAN_ENABLE_DISABLE_TIMEOUT) && !enabled)
    {
        /* Enable dragon */
        status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ENABLE_DISABLE,
                                   &fData, sizeof(fData));
        if (!SDIO_SUCCESS(status)) {
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                            ("Attempting to enable the card again\n"));
            continue;
        }

        /* Mark the status as enabled */
        enabled = TRUE;
    }

    /* Check if we were succesful in enabling the target */
    if (!enabled) {
        AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
                        ("Failed to communicate with the target\n"));
        return FALSE;
    }

    /* Allocate the bus requests to be used later */
    A_MEMZERO(busRequest, sizeof(busRequest));
    for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
        if ((busRequest[count].request = SDDeviceAllocRequest(handle)) == NULL){
            AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("Unable to allocate memory\n"));
            /* TODO: Free the memory that has already been allocated */
            return FALSE;
        }
        hifFreeBusRequest(&busRequest[count]);

        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                ("0x%08x = busRequest[%d].request = 0x%08x\n",
				(unsigned int) &busRequest[count], count,
				(unsigned int) busRequest[count].request));
    }

        /* Schedule a worker to handle device inserted, this is a temporary workaround
         * to fix a deadlock if the device fails to intialize in the insertion handler
         * The failure causes the instance to shutdown the HIF layer and unregister the
         * function driver within the busdriver probe context which can deadlock
         *
         * NOTE: we cannot use the default work queue because that would block
         * SD bus request processing for all synchronous I/O. We must use a kernel
         * thread that is creating using the helper library.
         * */

    if (SDIO_SUCCESS(SDLIB_OSCreateHelper(&device->insert_helper,
                         insert_helper_func,
                         device))) {
        device->helper_started = TRUE;
    }

    return TRUE;
}

static THREAD_RETURN insert_helper_func(POSKERNEL_HELPER pHelper)
{

    /*
     * Adding a wait of around a second before we issue the very first
     * command to dragon. During the process of loading/unloading the
     * driver repeatedly it was observed that we get a data timeout
     * while accessing function 1 registers in the chip. The theory at
     * this point is that some initialization delay in dragon is
     * causing the SDIO state in dragon core to be not ready even after
     * the ready bit indicates that function 1 is ready. Accomodating
     * for this behavior by adding some delay in the driver before it
     * issues the first command after switching on dragon. Need to
     * investigate this a bit more - TODO
     */

    A_MDELAY(1000);
        /* Inform HTC */
    if ((htcCallbacks.deviceInsertedHandler(SD_GET_OS_HELPER_CONTEXT(pHelper))) != A_OK) {
        AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device rejected\n"));
    }

    return 0;
}

void
HIFAckInterrupt(HIF_DEVICE *device)
{
    SDIO_STATUS status;
    DBG_ASSERT(device != NULL);
    DBG_ASSERT(device->handle != NULL);

    /* Acknowledge our function IRQ */
    status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ACK_IRQ,
                               NULL, 0);
    DBG_ASSERT(SDIO_SUCCESS(status));
}

void
HIFUnMaskInterrupt(HIF_DEVICE *device)
{
    SDIO_STATUS status;

    DBG_ASSERT(device != NULL);
    DBG_ASSERT(device->handle != NULL);

    /* Register the IRQ Handler */
    SDDEVICE_SET_IRQ_HANDLER(device->handle, hifIRQHandler, device);

    /* Unmask our function IRQ */
    status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_UNMASK_IRQ,
                               NULL, 0);
    DBG_ASSERT(SDIO_SUCCESS(status));
}

void HIFMaskInterrupt(HIF_DEVICE *device)
{
    SDIO_STATUS status;
    DBG_ASSERT(device != NULL);
    DBG_ASSERT(device->handle != NULL);

    /* Mask our function IRQ */
    status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_MASK_IRQ,
                               NULL, 0);
    DBG_ASSERT(SDIO_SUCCESS(status));

    /* Unregister the IRQ Handler */
    SDDEVICE_SET_IRQ_HANDLER(device->handle, NULL, NULL);
}

static BUS_REQUEST *hifAllocateBusRequest(void)
{
    BUS_REQUEST *busrequest;

    /* Acquire lock */
    CriticalSectionAcquire(&lock);

    /* Remove first in list */
    if((busrequest = s_busRequestFreeQueue) != NULL)
    {
        s_busRequestFreeQueue = busrequest->next;
    }

    /* Release lock */
    CriticalSectionRelease(&lock);

    return busrequest;
}

static void
hifFreeBusRequest(BUS_REQUEST *busrequest)
{
    DBG_ASSERT(busrequest != NULL);

    /* Acquire lock */
    CriticalSectionAcquire(&lock);

    /* Insert first in list */
    busrequest->next = s_busRequestFreeQueue;
    s_busRequestFreeQueue = busrequest;

    /* Release lock */
    CriticalSectionRelease(&lock);
}

void
hifDeviceRemoved(SDFUNCTION *function, SDDEVICE *handle)
{
    A_STATUS status;
    HIF_DEVICE *device;
    DBG_ASSERT(function != NULL);
    DBG_ASSERT(handle != NULL);

    device = getHifDevice(handle);
    status = htcCallbacks.deviceRemovedHandler(device->htc_handle, A_OK);

        /* cleanup the helper thread */
    if (device->helper_started) {
        SDLIB_OSDeleteHelper(&device->insert_helper);
        device->helper_started = FALSE;
    }

    delHifDevice(handle);
    DBG_ASSERT(status == A_OK);
}

HIF_DEVICE *
addHifDevice(SDDEVICE *handle)
{
    DBG_ASSERT(handle != NULL);
    hifDevice[0].handle = handle;
    return &hifDevice[0];
}

HIF_DEVICE *
getHifDevice(SDDEVICE *handle)
{
    DBG_ASSERT(handle != NULL);
    return &hifDevice[0];
}

void
delHifDevice(SDDEVICE *handle)
{
    DBG_ASSERT(handle != NULL);
    hifDevice[0].handle = NULL;
}

static void ResetAllCards(void)
{
    UINT8       data;
    SDIO_STATUS status;
    int         i;

    data = SDIO_IO_RESET;

    /* set the I/O CARD reset bit:
     * NOTE: we are exploiting a "feature" of the SDIO core that resets the core when you
     * set the RES bit in the SDIO_IO_ABORT register.  This bit however "normally" resets the
     * I/O functions leaving the SDIO core in the same state (as per SDIO spec).
     * In this design, this reset can be used to reset the SDIO core itself */
    for (i = 0; i < HIF_MAX_DEVICES; i++) {
        if (hifDevice[i].handle != NULL) {
            AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
                        ("Issuing I/O Card reset for instance: %d \n",i));
                /* set the I/O Card reset bit */
            status = SDLIB_IssueCMD52(hifDevice[i].handle,
                                      0,                    /* function 0 space */
                                      SDIO_IO_ABORT_REG,
                                      &data,
                                      1,                    /* 1 byte */
                                      TRUE);                /* write */
        }
    }

}

void HIFSetHandle(void *hif_handle, void *handle)
{
    HIF_DEVICE *device = (HIF_DEVICE *) hif_handle;

    device->htc_handle = handle;

    return;
}