1 /*
2 * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3 *
4 * Copyright (C) 1999-2010, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: bcmsdh_sdmmc.c,v 1.1.2.5.6.30.4.1 2010/09/02 23:12:21 Exp $
25 */
26 #include <typedefs.h>
27
28 #include <bcmdevs.h>
29 #include <bcmendian.h>
30 #include <bcmutils.h>
31 #include <osl.h>
32 #include <sdio.h> /* SDIO Device and Protocol Specs */
33 #include <sdioh.h> /* SDIO Host Controller Specification */
34 #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
35 #include <sdiovar.h> /* ioctl/iovars */
36
37 #include <linux/mmc/core.h>
38 #include <linux/mmc/sdio_func.h>
39 #include <linux/mmc/sdio_ids.h>
40
41 #include <dngl_stats.h>
42 #include <dhd.h>
43
44 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
45 #include <linux/suspend.h>
46 extern volatile bool dhd_mmc_suspend;
47 #endif
48 #include "bcmsdh_sdmmc.h"
49
50 #ifndef BCMSDH_MODULE
51 extern int sdio_function_init(void);
52 extern void sdio_function_cleanup(void);
53 #endif /* BCMSDH_MODULE */
54
55 #if !defined(OOB_INTR_ONLY)
56 static void IRQHandler(struct sdio_func *func);
57 static void IRQHandlerF2(struct sdio_func *func);
58 #endif /* !defined(OOB_INTR_ONLY) */
59 static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr);
60 extern int sdio_reset_comm(struct mmc_card *card);
61
62 extern PBCMSDH_SDMMC_INSTANCE gInstance;
63
64 uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */
65 uint sd_f2_blocksize = 512; /* Default blocksize */
66
67 uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */
68
69 uint sd_power = 1; /* Default to SD Slot powered ON */
70 uint sd_clock = 1; /* Default to SD Clock turned ON */
71 uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */
72 uint sd_msglevel = 0x01;
73 uint sd_use_dma = TRUE;
74 DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
75 DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
76 DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
77 DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
78
79 #define DMA_ALIGN_MASK 0x03
80
81 int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data);
82
83 static int
sdioh_sdmmc_card_enablefuncs(sdioh_info_t * sd)84 sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd)
85 {
86 int err_ret;
87 uint32 fbraddr;
88 uint8 func;
89
90 sd_trace(("%s\n", __FUNCTION__));
91
92 /* Get the Card's common CIS address */
93 sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
94 sd->func_cis_ptr[0] = sd->com_cis_ptr;
95 sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
96
97 /* Get the Card's function CIS (for each function) */
98 for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
99 func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
100 sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
101 sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
102 __FUNCTION__, func, sd->func_cis_ptr[func]));
103 }
104
105 sd->func_cis_ptr[0] = sd->com_cis_ptr;
106 sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
107
108 /* Enable Function 1 */
109 sdio_claim_host(gInstance->func[1]);
110 err_ret = sdio_enable_func(gInstance->func[1]);
111 sdio_release_host(gInstance->func[1]);
112 if (err_ret) {
113 sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret));
114 }
115
116 return FALSE;
117 }
118
119 /*
120 * Public entry points & extern's
121 */
122 extern sdioh_info_t *
sdioh_attach(osl_t * osh,void * bar0,uint irq)123 sdioh_attach(osl_t *osh, void *bar0, uint irq)
124 {
125 sdioh_info_t *sd;
126 int err_ret;
127
128 sd_trace(("%s\n", __FUNCTION__));
129
130 if (gInstance == NULL) {
131 sd_err(("%s: SDIO Device not present\n", __FUNCTION__));
132 return NULL;
133 }
134
135 if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
136 sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
137 return NULL;
138 }
139 bzero((char *)sd, sizeof(sdioh_info_t));
140 sd->osh = osh;
141 if (sdioh_sdmmc_osinit(sd) != 0) {
142 sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__));
143 MFREE(sd->osh, sd, sizeof(sdioh_info_t));
144 return NULL;
145 }
146
147 sd->num_funcs = 2;
148 sd->sd_blockmode = TRUE;
149 sd->use_client_ints = TRUE;
150 sd->client_block_size[0] = 64;
151
152 gInstance->sd = sd;
153
154 /* Claim host controller */
155 sdio_claim_host(gInstance->func[1]);
156
157 sd->client_block_size[1] = 64;
158 err_ret = sdio_set_block_size(gInstance->func[1], 64);
159 if (err_ret) {
160 sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
161 }
162
163 /* Release host controller F1 */
164 sdio_release_host(gInstance->func[1]);
165
166 if (gInstance->func[2]) {
167 /* Claim host controller F2 */
168 sdio_claim_host(gInstance->func[2]);
169
170 sd->client_block_size[2] = sd_f2_blocksize;
171 err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize);
172 if (err_ret) {
173 sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n",
174 sd_f2_blocksize));
175 }
176
177 /* Release host controller F2 */
178 sdio_release_host(gInstance->func[2]);
179 }
180
181 sdioh_sdmmc_card_enablefuncs(sd);
182
183 sd_trace(("%s: Done\n", __FUNCTION__));
184 return sd;
185 }
186
187
188 extern SDIOH_API_RC
sdioh_detach(osl_t * osh,sdioh_info_t * sd)189 sdioh_detach(osl_t *osh, sdioh_info_t *sd)
190 {
191 sd_trace(("%s\n", __FUNCTION__));
192
193 if (sd) {
194
195 /* Disable Function 2 */
196 sdio_claim_host(gInstance->func[2]);
197 sdio_disable_func(gInstance->func[2]);
198 sdio_release_host(gInstance->func[2]);
199
200 /* Disable Function 1 */
201 sdio_claim_host(gInstance->func[1]);
202 sdio_disable_func(gInstance->func[1]);
203 sdio_release_host(gInstance->func[1]);
204
205 /* deregister irq */
206 sdioh_sdmmc_osfree(sd);
207
208 MFREE(sd->osh, sd, sizeof(sdioh_info_t));
209 }
210 return SDIOH_API_RC_SUCCESS;
211 }
212
213 #if defined(OOB_INTR_ONLY) && defined(HW_OOB)
214
215 extern SDIOH_API_RC
sdioh_enable_func_intr(void)216 sdioh_enable_func_intr(void)
217 {
218 uint8 reg;
219 int err;
220
221 if (gInstance->func[0]) {
222 sdio_claim_host(gInstance->func[0]);
223
224 reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
225 if (err) {
226 sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
227 sdio_release_host(gInstance->func[0]);
228 return SDIOH_API_RC_FAIL;
229 }
230
231 /* Enable F1 and F2 interrupts, set master enable */
232 reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN | INTR_CTL_MASTER_EN);
233
234 sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
235 sdio_release_host(gInstance->func[0]);
236
237 if (err) {
238 sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
239 return SDIOH_API_RC_FAIL;
240 }
241 }
242
243 return SDIOH_API_RC_SUCCESS;
244 }
245
246 extern SDIOH_API_RC
sdioh_disable_func_intr(void)247 sdioh_disable_func_intr(void)
248 {
249 uint8 reg;
250 int err;
251
252 if (gInstance->func[0]) {
253 sdio_claim_host(gInstance->func[0]);
254 reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err);
255 if (err) {
256 sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
257 sdio_release_host(gInstance->func[0]);
258 return SDIOH_API_RC_FAIL;
259 }
260
261 reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN);
262 /* Disable master interrupt with the last function interrupt */
263 if (!(reg & 0xFE))
264 reg = 0;
265 sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err);
266
267 sdio_release_host(gInstance->func[0]);
268 if (err) {
269 sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
270 return SDIOH_API_RC_FAIL;
271 }
272 }
273 return SDIOH_API_RC_SUCCESS;
274 }
275 #endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
276
277 /* Configure callback to client when we recieve client interrupt */
278 extern SDIOH_API_RC
sdioh_interrupt_register(sdioh_info_t * sd,sdioh_cb_fn_t fn,void * argh)279 sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
280 {
281 sd_trace(("%s: Entering\n", __FUNCTION__));
282 if (fn == NULL) {
283 sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__));
284 return SDIOH_API_RC_FAIL;
285 }
286 #if !defined(OOB_INTR_ONLY)
287 sd->intr_handler = fn;
288 sd->intr_handler_arg = argh;
289 sd->intr_handler_valid = TRUE;
290
291 /* register and unmask irq */
292 if (gInstance->func[2]) {
293 sdio_claim_host(gInstance->func[2]);
294 sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
295 sdio_release_host(gInstance->func[2]);
296 }
297
298 if (gInstance->func[1]) {
299 sdio_claim_host(gInstance->func[1]);
300 sdio_claim_irq(gInstance->func[1], IRQHandler);
301 sdio_release_host(gInstance->func[1]);
302 }
303 #elif defined(HW_OOB)
304 sdioh_enable_func_intr();
305 #endif /* defined(OOB_INTR_ONLY) */
306 return SDIOH_API_RC_SUCCESS;
307 }
308
309 extern SDIOH_API_RC
sdioh_interrupt_deregister(sdioh_info_t * sd)310 sdioh_interrupt_deregister(sdioh_info_t *sd)
311 {
312 sd_trace(("%s: Entering\n", __FUNCTION__));
313
314 #if !defined(OOB_INTR_ONLY)
315 if (gInstance->func[1]) {
316 /* register and unmask irq */
317 sdio_claim_host(gInstance->func[1]);
318 sdio_release_irq(gInstance->func[1]);
319 sdio_release_host(gInstance->func[1]);
320 }
321
322 if (gInstance->func[2]) {
323 /* Claim host controller F2 */
324 sdio_claim_host(gInstance->func[2]);
325 sdio_release_irq(gInstance->func[2]);
326 /* Release host controller F2 */
327 sdio_release_host(gInstance->func[2]);
328 }
329
330 sd->intr_handler_valid = FALSE;
331 sd->intr_handler = NULL;
332 sd->intr_handler_arg = NULL;
333 #elif defined(HW_OOB)
334 sdioh_disable_func_intr();
335 #endif /* !defined(OOB_INTR_ONLY) */
336 return SDIOH_API_RC_SUCCESS;
337 }
338
339 extern SDIOH_API_RC
sdioh_interrupt_query(sdioh_info_t * sd,bool * onoff)340 sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
341 {
342 sd_trace(("%s: Entering\n", __FUNCTION__));
343 *onoff = sd->client_intr_enabled;
344 return SDIOH_API_RC_SUCCESS;
345 }
346
347 #if defined(DHD_DEBUG)
348 extern bool
sdioh_interrupt_pending(sdioh_info_t * sd)349 sdioh_interrupt_pending(sdioh_info_t *sd)
350 {
351 return (0);
352 }
353 #endif
354
355 uint
sdioh_query_iofnum(sdioh_info_t * sd)356 sdioh_query_iofnum(sdioh_info_t *sd)
357 {
358 return sd->num_funcs;
359 }
360
361 /* IOVar table */
362 enum {
363 IOV_MSGLEVEL = 1,
364 IOV_BLOCKMODE,
365 IOV_BLOCKSIZE,
366 IOV_DMA,
367 IOV_USEINTS,
368 IOV_NUMINTS,
369 IOV_NUMLOCALINTS,
370 IOV_HOSTREG,
371 IOV_DEVREG,
372 IOV_DIVISOR,
373 IOV_SDMODE,
374 IOV_HISPEED,
375 IOV_HCIREGS,
376 IOV_POWER,
377 IOV_CLOCK,
378 IOV_RXCHAIN
379 };
380
381 const bcm_iovar_t sdioh_iovars[] = {
382 {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
383 {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 },
384 {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
385 {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 },
386 {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 },
387 {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 },
388 {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 },
389 {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
390 {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) },
391 {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 },
392 {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 },
393 {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 },
394 {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100},
395 {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 },
396 {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 },
397 {NULL, 0, 0, 0, 0 }
398 };
399
400 int
sdioh_iovar_op(sdioh_info_t * si,const char * name,void * params,int plen,void * arg,int len,bool set)401 sdioh_iovar_op(sdioh_info_t *si, const char *name,
402 void *params, int plen, void *arg, int len, bool set)
403 {
404 const bcm_iovar_t *vi = NULL;
405 int bcmerror = 0;
406 int val_size;
407 int32 int_val = 0;
408 bool bool_val;
409 uint32 actionid;
410
411 ASSERT(name);
412 ASSERT(len >= 0);
413
414 /* Get must have return space; Set does not take qualifiers */
415 ASSERT(set || (arg && len));
416 ASSERT(!set || (!params && !plen));
417
418 sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
419
420 if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
421 bcmerror = BCME_UNSUPPORTED;
422 goto exit;
423 }
424
425 if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
426 goto exit;
427
428 /* Set up params so get and set can share the convenience variables */
429 if (params == NULL) {
430 params = arg;
431 plen = len;
432 }
433
434 if (vi->type == IOVT_VOID)
435 val_size = 0;
436 else if (vi->type == IOVT_BUFFER)
437 val_size = len;
438 else
439 val_size = sizeof(int);
440
441 if (plen >= (int)sizeof(int_val))
442 bcopy(params, &int_val, sizeof(int_val));
443
444 bool_val = (int_val != 0) ? TRUE : FALSE;
445
446 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
447 switch (actionid) {
448 case IOV_GVAL(IOV_MSGLEVEL):
449 int_val = (int32)sd_msglevel;
450 bcopy(&int_val, arg, val_size);
451 break;
452
453 case IOV_SVAL(IOV_MSGLEVEL):
454 sd_msglevel = int_val;
455 break;
456
457 case IOV_GVAL(IOV_BLOCKMODE):
458 int_val = (int32)si->sd_blockmode;
459 bcopy(&int_val, arg, val_size);
460 break;
461
462 case IOV_SVAL(IOV_BLOCKMODE):
463 si->sd_blockmode = (bool)int_val;
464 /* Haven't figured out how to make non-block mode with DMA */
465 break;
466
467 case IOV_GVAL(IOV_BLOCKSIZE):
468 if ((uint32)int_val > si->num_funcs) {
469 bcmerror = BCME_BADARG;
470 break;
471 }
472 int_val = (int32)si->client_block_size[int_val];
473 bcopy(&int_val, arg, val_size);
474 break;
475
476 case IOV_SVAL(IOV_BLOCKSIZE):
477 {
478 uint func = ((uint32)int_val >> 16);
479 uint blksize = (uint16)int_val;
480 uint maxsize;
481
482 if (func > si->num_funcs) {
483 bcmerror = BCME_BADARG;
484 break;
485 }
486
487 switch (func) {
488 case 0: maxsize = 32; break;
489 case 1: maxsize = BLOCK_SIZE_4318; break;
490 case 2: maxsize = BLOCK_SIZE_4328; break;
491 default: maxsize = 0;
492 }
493 if (blksize > maxsize) {
494 bcmerror = BCME_BADARG;
495 break;
496 }
497 if (!blksize) {
498 blksize = maxsize;
499 }
500
501 /* Now set it */
502 si->client_block_size[func] = blksize;
503
504 break;
505 }
506
507 case IOV_GVAL(IOV_RXCHAIN):
508 int_val = FALSE;
509 bcopy(&int_val, arg, val_size);
510 break;
511
512 case IOV_GVAL(IOV_DMA):
513 int_val = (int32)si->sd_use_dma;
514 bcopy(&int_val, arg, val_size);
515 break;
516
517 case IOV_SVAL(IOV_DMA):
518 si->sd_use_dma = (bool)int_val;
519 break;
520
521 case IOV_GVAL(IOV_USEINTS):
522 int_val = (int32)si->use_client_ints;
523 bcopy(&int_val, arg, val_size);
524 break;
525
526 case IOV_SVAL(IOV_USEINTS):
527 si->use_client_ints = (bool)int_val;
528 if (si->use_client_ints)
529 si->intmask |= CLIENT_INTR;
530 else
531 si->intmask &= ~CLIENT_INTR;
532
533 break;
534
535 case IOV_GVAL(IOV_DIVISOR):
536 int_val = (uint32)sd_divisor;
537 bcopy(&int_val, arg, val_size);
538 break;
539
540 case IOV_SVAL(IOV_DIVISOR):
541 sd_divisor = int_val;
542 break;
543
544 case IOV_GVAL(IOV_POWER):
545 int_val = (uint32)sd_power;
546 bcopy(&int_val, arg, val_size);
547 break;
548
549 case IOV_SVAL(IOV_POWER):
550 sd_power = int_val;
551 break;
552
553 case IOV_GVAL(IOV_CLOCK):
554 int_val = (uint32)sd_clock;
555 bcopy(&int_val, arg, val_size);
556 break;
557
558 case IOV_SVAL(IOV_CLOCK):
559 sd_clock = int_val;
560 break;
561
562 case IOV_GVAL(IOV_SDMODE):
563 int_val = (uint32)sd_sdmode;
564 bcopy(&int_val, arg, val_size);
565 break;
566
567 case IOV_SVAL(IOV_SDMODE):
568 sd_sdmode = int_val;
569 break;
570
571 case IOV_GVAL(IOV_HISPEED):
572 int_val = (uint32)sd_hiok;
573 bcopy(&int_val, arg, val_size);
574 break;
575
576 case IOV_SVAL(IOV_HISPEED):
577 sd_hiok = int_val;
578 break;
579
580 case IOV_GVAL(IOV_NUMINTS):
581 int_val = (int32)si->intrcount;
582 bcopy(&int_val, arg, val_size);
583 break;
584
585 case IOV_GVAL(IOV_NUMLOCALINTS):
586 int_val = (int32)0;
587 bcopy(&int_val, arg, val_size);
588 break;
589
590 case IOV_GVAL(IOV_HOSTREG):
591 {
592 sdreg_t *sd_ptr = (sdreg_t *)params;
593
594 if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
595 sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
596 bcmerror = BCME_BADARG;
597 break;
598 }
599
600 sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__,
601 (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
602 sd_ptr->offset));
603 if (sd_ptr->offset & 1)
604 int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */
605 else if (sd_ptr->offset & 2)
606 int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */
607 else
608 int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */
609
610 bcopy(&int_val, arg, sizeof(int_val));
611 break;
612 }
613
614 case IOV_SVAL(IOV_HOSTREG):
615 {
616 sdreg_t *sd_ptr = (sdreg_t *)params;
617
618 if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) {
619 sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset));
620 bcmerror = BCME_BADARG;
621 break;
622 }
623
624 sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value,
625 (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32),
626 sd_ptr->offset));
627 break;
628 }
629
630 case IOV_GVAL(IOV_DEVREG):
631 {
632 sdreg_t *sd_ptr = (sdreg_t *)params;
633 uint8 data = 0;
634
635 if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
636 bcmerror = BCME_SDIO_ERROR;
637 break;
638 }
639
640 int_val = (int)data;
641 bcopy(&int_val, arg, sizeof(int_val));
642 break;
643 }
644
645 case IOV_SVAL(IOV_DEVREG):
646 {
647 sdreg_t *sd_ptr = (sdreg_t *)params;
648 uint8 data = (uint8)sd_ptr->value;
649
650 if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
651 bcmerror = BCME_SDIO_ERROR;
652 break;
653 }
654 break;
655 }
656
657 default:
658 bcmerror = BCME_UNSUPPORTED;
659 break;
660 }
661 exit:
662
663 return bcmerror;
664 }
665
666 #if defined(OOB_INTR_ONLY) && defined(HW_OOB)
667
668 SDIOH_API_RC
sdioh_enable_hw_oob_intr(sdioh_info_t * sd,bool enable)669 sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable)
670 {
671 SDIOH_API_RC status;
672 uint8 data;
673
674 if (enable)
675 data = 3; /* enable hw oob interrupt */
676 else
677 data = 4; /* disable hw oob interrupt */
678
679 data |= 4; /* Active HIGH */
680
681 status = sdioh_request_byte(sd, SDIOH_WRITE, 0, 0xf2, &data);
682 return status;
683 }
684 #endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
685
686 extern SDIOH_API_RC
sdioh_cfg_read(sdioh_info_t * sd,uint fnc_num,uint32 addr,uint8 * data)687 sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
688 {
689 SDIOH_API_RC status;
690 /* No lock needed since sdioh_request_byte does locking */
691 status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
692 return status;
693 }
694
695 extern SDIOH_API_RC
sdioh_cfg_write(sdioh_info_t * sd,uint fnc_num,uint32 addr,uint8 * data)696 sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
697 {
698 /* No lock needed since sdioh_request_byte does locking */
699 SDIOH_API_RC status;
700 status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
701 return status;
702 }
703
704 static int
sdioh_sdmmc_get_cisaddr(sdioh_info_t * sd,uint32 regaddr)705 sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
706 {
707 /* read 24 bits and return valid 17 bit addr */
708 int i;
709 uint32 scratch, regdata;
710 uint8 *ptr = (uint8 *)&scratch;
711 for (i = 0; i < 3; i++) {
712 if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, ®data)) != SUCCESS)
713 sd_err(("%s: Can't read!\n", __FUNCTION__));
714
715 *ptr++ = (uint8) regdata;
716 regaddr++;
717 }
718
719 /* Only the lower 17-bits are valid */
720 scratch = ltoh32(scratch);
721 scratch &= 0x0001FFFF;
722 return (scratch);
723 }
724
725 extern SDIOH_API_RC
sdioh_cis_read(sdioh_info_t * sd,uint func,uint8 * cisd,uint32 length)726 sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
727 {
728 uint32 count;
729 int offset;
730 uint32 foo;
731 uint8 *cis = cisd;
732
733 sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
734
735 if (!sd->func_cis_ptr[func]) {
736 bzero(cis, length);
737 sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func));
738 return SDIOH_API_RC_FAIL;
739 }
740
741 sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func]));
742
743 for (count = 0; count < length; count++) {
744 offset = sd->func_cis_ptr[func] + count;
745 if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) {
746 sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
747 return SDIOH_API_RC_FAIL;
748 }
749
750 *cis = (uint8)(foo & 0xff);
751 cis++;
752 }
753
754 return SDIOH_API_RC_SUCCESS;
755 }
756
757 extern SDIOH_API_RC
sdioh_request_byte(sdioh_info_t * sd,uint rw,uint func,uint regaddr,uint8 * byte)758 sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
759 {
760 int err_ret;
761
762 sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr));
763
764 DHD_PM_RESUME_WAIT(sdioh_request_byte_wait);
765 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
766 if(rw) { /* CMD52 Write */
767 if (func == 0) {
768 /* Can only directly write to some F0 registers. Handle F2 enable
769 * as a special case.
770 */
771 if (regaddr == SDIOD_CCCR_IOEN) {
772 if (gInstance->func[2]) {
773 sdio_claim_host(gInstance->func[2]);
774 if (*byte & SDIO_FUNC_ENABLE_2) {
775 /* Enable Function 2 */
776 err_ret = sdio_enable_func(gInstance->func[2]);
777 if (err_ret) {
778 sd_err(("bcmsdh_sdmmc: enable F2 failed:%d",
779 err_ret));
780 }
781 } else {
782 /* Disable Function 2 */
783 err_ret = sdio_disable_func(gInstance->func[2]);
784 if (err_ret) {
785 sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d",
786 err_ret));
787 }
788 }
789 sdio_release_host(gInstance->func[2]);
790 }
791 }
792 #if defined(MMC_SDIO_ABORT)
793 /* to allow abort command through F1 */
794 else if (regaddr == SDIOD_CCCR_IOABORT) {
795 sdio_claim_host(gInstance->func[func]);
796 /*
797 * this sdio_f0_writeb() can be replaced with another api
798 * depending upon MMC driver change.
799 * As of this time, this is temporaray one
800 */
801 sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
802 sdio_release_host(gInstance->func[func]);
803 }
804 #endif /* MMC_SDIO_ABORT */
805 else if (regaddr < 0xF0) {
806 sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr));
807 } else {
808 /* Claim host controller, perform F0 write, and release */
809 sdio_claim_host(gInstance->func[func]);
810 sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
811 sdio_release_host(gInstance->func[func]);
812 }
813 } else {
814 /* Claim host controller, perform Fn write, and release */
815 sdio_claim_host(gInstance->func[func]);
816 sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret);
817 sdio_release_host(gInstance->func[func]);
818 }
819 } else { /* CMD52 Read */
820 /* Claim host controller, perform Fn read, and release */
821 sdio_claim_host(gInstance->func[func]);
822
823 if (func == 0) {
824 *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret);
825 } else {
826 *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret);
827 }
828
829 sdio_release_host(gInstance->func[func]);
830 }
831
832 if (err_ret) {
833 sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
834 rw ? "Write" : "Read", func, regaddr, *byte, err_ret));
835 }
836
837 return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
838 }
839
840 extern SDIOH_API_RC
sdioh_request_word(sdioh_info_t * sd,uint cmd_type,uint rw,uint func,uint addr,uint32 * word,uint nbytes)841 sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
842 uint32 *word, uint nbytes)
843 {
844 int err_ret = SDIOH_API_RC_FAIL;
845
846 if (func == 0) {
847 sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__));
848 return SDIOH_API_RC_FAIL;
849 }
850
851 sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
852 __FUNCTION__, cmd_type, rw, func, addr, nbytes));
853
854 DHD_PM_RESUME_WAIT(sdioh_request_word_wait);
855 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
856 /* Claim host controller */
857 sdio_claim_host(gInstance->func[func]);
858
859 if(rw) { /* CMD52 Write */
860 if (nbytes == 4) {
861 sdio_writel(gInstance->func[func], *word, addr, &err_ret);
862 } else if (nbytes == 2) {
863 sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret);
864 } else {
865 sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
866 }
867 } else { /* CMD52 Read */
868 if (nbytes == 4) {
869 *word = sdio_readl(gInstance->func[func], addr, &err_ret);
870 } else if (nbytes == 2) {
871 *word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF;
872 } else {
873 sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
874 }
875 }
876
877 /* Release host controller */
878 sdio_release_host(gInstance->func[func]);
879
880 if (err_ret) {
881 sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x",
882 rw ? "Write" : "Read", err_ret));
883 }
884
885 return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
886 }
887
888 static SDIOH_API_RC
sdioh_request_packet(sdioh_info_t * sd,uint fix_inc,uint write,uint func,uint addr,void * pkt)889 sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func,
890 uint addr, void *pkt)
891 {
892 bool fifo = (fix_inc == SDIOH_DATA_FIX);
893 uint32 SGCount = 0;
894 int err_ret = 0;
895
896 void *pnext;
897
898 sd_trace(("%s: Enter\n", __FUNCTION__));
899
900 ASSERT(pkt);
901 DHD_PM_RESUME_WAIT(sdioh_request_packet_wait);
902 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
903
904 /* Claim host controller */
905 sdio_claim_host(gInstance->func[func]);
906 for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) {
907 uint pkt_len = PKTLEN(sd->osh, pnext);
908 pkt_len += 3;
909 pkt_len &= 0xFFFFFFFC;
910
911 #ifdef CONFIG_MMC_MSM7X00A
912 if ((pkt_len % 64) == 32) {
913 sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__));
914 pkt_len += 32;
915 }
916 #endif /* CONFIG_MMC_MSM7X00A */
917 /* Make sure the packet is aligned properly. If it isn't, then this
918 * is the fault of sdioh_request_buffer() which is supposed to give
919 * us something we can work with.
920 */
921 ASSERT(((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) == 0);
922
923 if ((write) && (!fifo)) {
924 err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
925 ((uint8*)PKTDATA(sd->osh, pnext)),
926 pkt_len);
927 } else if (write) {
928 err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
929 ((uint8*)PKTDATA(sd->osh, pnext)),
930 pkt_len);
931 } else if (fifo) {
932 err_ret = sdio_readsb(gInstance->func[func],
933 ((uint8*)PKTDATA(sd->osh, pnext)),
934 addr,
935 pkt_len);
936 } else {
937 err_ret = sdio_memcpy_fromio(gInstance->func[func],
938 ((uint8*)PKTDATA(sd->osh, pnext)),
939 addr,
940 pkt_len);
941 }
942
943 if (err_ret) {
944 sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
945 __FUNCTION__,
946 (write) ? "TX" : "RX",
947 pnext, SGCount, addr, pkt_len, err_ret));
948 } else {
949 sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n",
950 __FUNCTION__,
951 (write) ? "TX" : "RX",
952 pnext, SGCount, addr, pkt_len));
953 }
954
955 if (!fifo) {
956 addr += pkt_len;
957 }
958 SGCount ++;
959
960 }
961
962 /* Release host controller */
963 sdio_release_host(gInstance->func[func]);
964
965 sd_trace(("%s: Exit\n", __FUNCTION__));
966 return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
967 }
968
969
970 /*
971 * This function takes a buffer or packet, and fixes everything up so that in the
972 * end, a DMA-able packet is created.
973 *
974 * A buffer does not have an associated packet pointer, and may or may not be aligned.
975 * A packet may consist of a single packet, or a packet chain. If it is a packet chain,
976 * then all the packets in the chain must be properly aligned. If the packet data is not
977 * aligned, then there may only be one packet, and in this case, it is copied to a new
978 * aligned packet.
979 *
980 */
981 extern SDIOH_API_RC
sdioh_request_buffer(sdioh_info_t * sd,uint pio_dma,uint fix_inc,uint write,uint func,uint addr,uint reg_width,uint buflen_u,uint8 * buffer,void * pkt)982 sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func,
983 uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
984 {
985 SDIOH_API_RC Status;
986 void *mypkt = NULL;
987
988 sd_trace(("%s: Enter\n", __FUNCTION__));
989
990 DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait);
991 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
992 /* Case 1: we don't have a packet. */
993 if (pkt == NULL) {
994 sd_data(("%s: Creating new %s Packet, len=%d\n",
995 __FUNCTION__, write ? "TX" : "RX", buflen_u));
996 #ifdef DHD_USE_STATIC_BUF
997 if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) {
998 #else
999 if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) {
1000 #endif /* DHD_USE_STATIC_BUF */
1001 sd_err(("%s: PKTGET failed: len %d\n",
1002 __FUNCTION__, buflen_u));
1003 return SDIOH_API_RC_FAIL;
1004 }
1005
1006 /* For a write, copy the buffer data into the packet. */
1007 if (write) {
1008 bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u);
1009 }
1010
1011 Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
1012
1013 /* For a read, copy the packet data back to the buffer. */
1014 if (!write) {
1015 bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u);
1016 }
1017 #ifdef DHD_USE_STATIC_BUF
1018 PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
1019 #else
1020 PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
1021 #endif /* DHD_USE_STATIC_BUF */
1022 } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) {
1023 /* Case 2: We have a packet, but it is unaligned. */
1024
1025 /* In this case, we cannot have a chain. */
1026 ASSERT(PKTNEXT(sd->osh, pkt) == NULL);
1027
1028 sd_data(("%s: Creating aligned %s Packet, len=%d\n",
1029 __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt)));
1030 #ifdef DHD_USE_STATIC_BUF
1031 if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
1032 #else
1033 if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) {
1034 #endif /* DHD_USE_STATIC_BUF */
1035 sd_err(("%s: PKTGET failed: len %d\n",
1036 __FUNCTION__, PKTLEN(sd->osh, pkt)));
1037 return SDIOH_API_RC_FAIL;
1038 }
1039
1040 /* For a write, copy the buffer data into the packet. */
1041 if (write) {
1042 bcopy(PKTDATA(sd->osh, pkt),
1043 PKTDATA(sd->osh, mypkt),
1044 PKTLEN(sd->osh, pkt));
1045 }
1046
1047 Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt);
1048
1049 /* For a read, copy the packet data back to the buffer. */
1050 if (!write) {
1051 bcopy(PKTDATA(sd->osh, mypkt),
1052 PKTDATA(sd->osh, pkt),
1053 PKTLEN(sd->osh, mypkt));
1054 }
1055 #ifdef DHD_USE_STATIC_BUF
1056 PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE);
1057 #else
1058 PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE);
1059 #endif /* DHD_USE_STATIC_BUF */
1060 } else { /* case 3: We have a packet and it is aligned. */
1061 sd_data(("%s: Aligned %s Packet, direct DMA\n",
1062 __FUNCTION__, write ? "Tx" : "Rx"));
1063 Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt);
1064 }
1065
1066 return (Status);
1067 }
1068
1069 /* this function performs "abort" for both of host & device */
1070 extern int
1071 sdioh_abort(sdioh_info_t *sd, uint func)
1072 {
1073 #if defined(MMC_SDIO_ABORT)
1074 char t_func = (char) func;
1075 #endif /* defined(MMC_SDIO_ABORT) */
1076 sd_trace(("%s: Enter\n", __FUNCTION__));
1077
1078 #if defined(MMC_SDIO_ABORT)
1079 /* issue abort cmd52 command through F1 */
1080 sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func);
1081 #endif /* defined(MMC_SDIO_ABORT) */
1082
1083 sd_trace(("%s: Exit\n", __FUNCTION__));
1084 return SDIOH_API_RC_SUCCESS;
1085 }
1086
1087 /* Reset and re-initialize the device */
1088 int sdioh_sdio_reset(sdioh_info_t *si)
1089 {
1090 sd_trace(("%s: Enter\n", __FUNCTION__));
1091 sd_trace(("%s: Exit\n", __FUNCTION__));
1092 return SDIOH_API_RC_SUCCESS;
1093 }
1094
1095 /* Disable device interrupt */
1096 void
1097 sdioh_sdmmc_devintr_off(sdioh_info_t *sd)
1098 {
1099 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
1100 sd->intmask &= ~CLIENT_INTR;
1101 }
1102
1103 /* Enable device interrupt */
1104 void
1105 sdioh_sdmmc_devintr_on(sdioh_info_t *sd)
1106 {
1107 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
1108 sd->intmask |= CLIENT_INTR;
1109 }
1110
1111 /* Read client card reg */
1112 int
1113 sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
1114 {
1115
1116 if ((func == 0) || (regsize == 1)) {
1117 uint8 temp = 0;
1118
1119 sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
1120 *data = temp;
1121 *data &= 0xff;
1122 sd_data(("%s: byte read data=0x%02x\n",
1123 __FUNCTION__, *data));
1124 } else {
1125 sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize);
1126 if (regsize == 2)
1127 *data &= 0xffff;
1128
1129 sd_data(("%s: word read data=0x%08x\n",
1130 __FUNCTION__, *data));
1131 }
1132
1133 return SUCCESS;
1134 }
1135
1136 #if !defined(OOB_INTR_ONLY)
1137 /* bcmsdh_sdmmc interrupt handler */
1138 static void IRQHandler(struct sdio_func *func)
1139 {
1140 sdioh_info_t *sd;
1141
1142 sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n"));
1143 sd = gInstance->sd;
1144
1145 ASSERT(sd != NULL);
1146 sdio_release_host(gInstance->func[0]);
1147
1148 if (sd->use_client_ints) {
1149 sd->intrcount++;
1150 ASSERT(sd->intr_handler);
1151 ASSERT(sd->intr_handler_arg);
1152 (sd->intr_handler)(sd->intr_handler_arg);
1153 } else {
1154 sd_err(("bcmsdh_sdmmc: ***IRQHandler\n"));
1155
1156 sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
1157 __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
1158 }
1159
1160 sdio_claim_host(gInstance->func[0]);
1161 }
1162
1163 /* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */
1164 static void IRQHandlerF2(struct sdio_func *func)
1165 {
1166 sdioh_info_t *sd;
1167
1168 sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n"));
1169
1170 sd = gInstance->sd;
1171
1172 ASSERT(sd != NULL);
1173 }
1174 #endif /* !defined(OOB_INTR_ONLY) */
1175
1176 #ifdef NOTUSED
1177 /* Write client card reg */
1178 static int
1179 sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
1180 {
1181
1182 if ((func == 0) || (regsize == 1)) {
1183 uint8 temp;
1184
1185 temp = data & 0xff;
1186 sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
1187 sd_data(("%s: byte write data=0x%02x\n",
1188 __FUNCTION__, data));
1189 } else {
1190 if (regsize == 2)
1191 data &= 0xffff;
1192
1193 sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize);
1194
1195 sd_data(("%s: word write data=0x%08x\n",
1196 __FUNCTION__, data));
1197 }
1198
1199 return SUCCESS;
1200 }
1201 #endif /* NOTUSED */
1202
1203 int
1204 sdioh_start(sdioh_info_t *si, int stage)
1205 {
1206 int ret;
1207 sdioh_info_t *sd = gInstance->sd;
1208
1209 /* Need to do this stages as we can't enable the interrupt till
1210 downloading of the firmware is complete, other wise polling
1211 sdio access will come in way
1212 */
1213 if (gInstance->func[0]) {
1214 if (stage == 0) {
1215 /* Since the power to the chip is killed, we will have
1216 re enumerate the device again. Set the block size
1217 and enable the fucntion 1 for in preparation for
1218 downloading the code
1219 */
1220 /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux
1221 2.6.27. The implementation prior to that is buggy, and needs broadcom's
1222 patch for it
1223 */
1224 if ((ret = sdio_reset_comm(gInstance->func[0]->card)))
1225 sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret));
1226 else {
1227 sd->num_funcs = 2;
1228 sd->sd_blockmode = TRUE;
1229 sd->use_client_ints = TRUE;
1230 sd->client_block_size[0] = 64;
1231
1232 /* Claim host controller */
1233 sdio_claim_host(gInstance->func[1]);
1234
1235 sd->client_block_size[1] = 64;
1236 if (sdio_set_block_size(gInstance->func[1], 64)) {
1237 sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n"));
1238 }
1239
1240 /* Release host controller F1 */
1241 sdio_release_host(gInstance->func[1]);
1242
1243 if (gInstance->func[2]) {
1244 /* Claim host controller F2 */
1245 sdio_claim_host(gInstance->func[2]);
1246
1247 sd->client_block_size[2] = sd_f2_blocksize;
1248 if (sdio_set_block_size(gInstance->func[2],
1249 sd_f2_blocksize)) {
1250 sd_err(("bcmsdh_sdmmc: Failed to set F2 "
1251 "blocksize to %d\n", sd_f2_blocksize));
1252 }
1253
1254 /* Release host controller F2 */
1255 sdio_release_host(gInstance->func[2]);
1256 }
1257
1258 sdioh_sdmmc_card_enablefuncs(sd);
1259 }
1260 } else {
1261 #if !defined(OOB_INTR_ONLY)
1262 sdio_claim_host(gInstance->func[0]);
1263 sdio_claim_irq(gInstance->func[2], IRQHandlerF2);
1264 sdio_claim_irq(gInstance->func[1], IRQHandler);
1265 sdio_release_host(gInstance->func[0]);
1266 #else /* defined(OOB_INTR_ONLY) */
1267 #if defined(HW_OOB)
1268 sdioh_enable_func_intr();
1269 #endif
1270 bcmsdh_oob_intr_set(TRUE);
1271 #endif /* !defined(OOB_INTR_ONLY) */
1272 }
1273 }
1274 else
1275 sd_err(("%s Failed\n", __FUNCTION__));
1276
1277 return (0);
1278 }
1279
1280 int
1281 sdioh_stop(sdioh_info_t *si)
1282 {
1283 /* MSM7201A Android sdio stack has bug with interrupt
1284 So internaly within SDIO stack they are polling
1285 which cause issue when device is turned off. So
1286 unregister interrupt with SDIO stack to stop the
1287 polling
1288 */
1289 if (gInstance->func[0]) {
1290 #if !defined(OOB_INTR_ONLY)
1291 sdio_claim_host(gInstance->func[0]);
1292 sdio_release_irq(gInstance->func[1]);
1293 sdio_release_irq(gInstance->func[2]);
1294 sdio_release_host(gInstance->func[0]);
1295 #else /* defined(OOB_INTR_ONLY) */
1296 #if defined(HW_OOB)
1297 sdioh_disable_func_intr();
1298 #endif
1299 bcmsdh_oob_intr_set(FALSE);
1300 #endif /* !defined(OOB_INTR_ONLY) */
1301 }
1302 else
1303 sd_err(("%s Failed\n", __FUNCTION__));
1304 return (0);
1305 }
1306