1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
4 *
5 * Copyright (C) 1999-2017, Broadcom Corporation
6 *
7 * Unless you and Broadcom execute a separate written software license
8 * agreement governing use of this software, this software is licensed to you
9 * under the terms of the GNU General Public License version 2 (the "GPL"),
10 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11 * following added to such license:
12 *
13 * As a special exception, the copyright holders of this software give you
14 * permission to link this software with independent modules, and to copy and
15 * distribute the resulting executable under terms of your choice, provided that
16 * you also meet, for each linked independent module, the terms and conditions of
17 * the license of that module. An independent module is a module which is not
18 * derived from this software. The special exception does not apply to any
19 * modifications of the software.
20 *
21 * Notwithstanding the above, under no circumstances may you combine this
22 * software in any way with any other Broadcom software provided under a license
23 * other than the GPL, without Broadcom's express prior written consent.
24 *
25 *
26 * <<Broadcom-WL-IPTag/Proprietary,Open:>>
27 *
28 * $Id: bcmsdh_sdmmc.c 710913 2017-07-14 10:17:51Z $
29 */
30 #include <typedefs.h>
31
32 #include <bcmdevs.h>
33 #include <bcmendian.h>
34 #include <bcmutils.h>
35 #include <osl.h>
36 #include <sdio.h> /* SDIO Device and Protocol Specs */
37 #include <sdioh.h> /* Standard SDIO Host Controller Specification */
38 #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
39 #include <sdiovar.h> /* ioctl/iovars */
40
41 #include <linux/mmc/core.h>
42 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 0))
43 #include <drivers/mmc/core/host.h>
44 void
mmc_host_clk_hold(struct mmc_host * host)45 mmc_host_clk_hold(struct mmc_host *host)
46 {
47 BCM_REFERENCE(host);
48 return;
49 }
50
51 void
mmc_host_clk_release(struct mmc_host * host)52 mmc_host_clk_release(struct mmc_host *host)
53 {
54 BCM_REFERENCE(host);
55 return;
56 }
57 #elif (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 8))
58 #include <drivers/mmc/core/host.h>
59 #else
60 #include <linux/mmc/host.h>
61 #endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 0)) */
62 #include <linux/mmc/card.h>
63 #include <linux/mmc/sdio_func.h>
64 #include <linux/mmc/sdio_ids.h>
65
66 #include <dngl_stats.h>
67 #include <dhd.h>
68 #include <dhd_dbg.h>
69
70 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
71 #include <linux/suspend.h>
72 extern volatile bool dhd_mmc_suspend;
73 #endif
74 #include "bcmsdh_sdmmc.h"
75
76 #ifndef BCMSDH_MODULE
77 extern int sdio_function_init(void);
78 extern void sdio_function_cleanup(void);
79 #endif /* BCMSDH_MODULE */
80
81 #if !defined(OOB_INTR_ONLY)
82 static void IRQHandler(struct sdio_func *func);
83 static void IRQHandlerF2(struct sdio_func *func);
84 #endif /* !defined(OOB_INTR_ONLY) */
85 static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr);
86 #if defined(ENABLE_INSMOD_NO_FW_LOAD) && !defined(BUS_POWER_RESTORE)
87 extern int sdio_reset_comm(struct mmc_card *card);
88 #endif
89 #ifdef GLOBAL_SDMMC_INSTANCE
90 extern PBCMSDH_SDMMC_INSTANCE gInstance;
91 #endif
92
93 #define DEFAULT_SDIO_F2_BLKSIZE 512
94 #ifndef CUSTOM_SDIO_F2_BLKSIZE
95 #define CUSTOM_SDIO_F2_BLKSIZE DEFAULT_SDIO_F2_BLKSIZE
96 #endif
97
98 #define DEFAULT_SDIO_F1_BLKSIZE 64
99 #ifndef CUSTOM_SDIO_F1_BLKSIZE
100 #define CUSTOM_SDIO_F1_BLKSIZE DEFAULT_SDIO_F1_BLKSIZE
101 #endif
102
103 #define MAX_IO_RW_EXTENDED_BLK 511
104
105 uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */
106 uint sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE;
107 uint sd_f1_blocksize = CUSTOM_SDIO_F1_BLKSIZE;
108
109 #if defined(BT_OVER_SDIO)
110 uint sd_f3_blocksize = 64;
111 #endif /* defined (BT_OVER_SDIO) */
112
113 uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */
114
115 uint sd_power = 1; /* Default to SD Slot powered ON */
116 uint sd_clock = 1; /* Default to SD Clock turned ON */
117 uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */
118 uint sd_msglevel = 0x01;
119 uint sd_use_dma = TRUE;
120
121 #ifndef CUSTOM_RXCHAIN
122 #define CUSTOM_RXCHAIN 0
123 #endif
124
125 DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
126 DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
127 DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
128 DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
129
130 #define DMA_ALIGN_MASK 0x03
131 #define MMC_SDIO_ABORT_RETRY_LIMIT 5
132
133 int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data);
134
135 void sdmmc_set_clock_rate(sdioh_info_t *sd, uint hz);
136 uint sdmmc_get_clock_rate(sdioh_info_t *sd);
137 void sdmmc_set_clock_divisor(sdioh_info_t *sd, uint sd_div);
138 #if defined(BT_OVER_SDIO)
139 extern
sdioh_sdmmc_card_enable_func_f3(sdioh_info_t * sd,struct sdio_func * func)140 void sdioh_sdmmc_card_enable_func_f3(sdioh_info_t *sd, struct sdio_func *func)
141 {
142 sd->func[3] = func;
143 sd_info(("%s sd->func[3] %p\n", __FUNCTION__, sd->func[3]));
144 }
145 #endif /* defined (BT_OVER_SDIO) */
146
147 static int
sdioh_sdmmc_card_enablefuncs(sdioh_info_t * sd)148 sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd)
149 {
150 int err_ret;
151 uint32 fbraddr;
152 uint8 func;
153
154 sd_trace(("%s\n", __FUNCTION__));
155
156 /* Get the Card's common CIS address */
157 sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0);
158 sd->func_cis_ptr[0] = sd->com_cis_ptr;
159 sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
160
161 /* Get the Card's function CIS (for each function) */
162 for (fbraddr = SDIOD_FBR_STARTADDR, func = 1;
163 func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
164 sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr);
165 sd_info(("%s: Function %d CIS Ptr = 0x%x\n",
166 __FUNCTION__, func, sd->func_cis_ptr[func]));
167 }
168
169 sd->func_cis_ptr[0] = sd->com_cis_ptr;
170 sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr));
171
172 /* Enable Function 1 */
173 sdio_claim_host(sd->func[1]);
174 err_ret = sdio_enable_func(sd->func[1]);
175 sdio_release_host(sd->func[1]);
176 if (err_ret) {
177 sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x\n", err_ret));
178 }
179
180 return FALSE;
181 }
182
183 /*
184 * Public entry points & extern's
185 */
186 extern sdioh_info_t *
sdioh_attach(osl_t * osh,struct sdio_func * func)187 sdioh_attach(osl_t *osh, struct sdio_func *func)
188 {
189 sdioh_info_t *sd = NULL;
190 int err_ret;
191
192 sd_trace(("%s\n", __FUNCTION__));
193
194 if (func == NULL) {
195 sd_err(("%s: sdio function device is NULL\n", __FUNCTION__));
196 return NULL;
197 }
198
199 if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
200 sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh)));
201 return NULL;
202 }
203 bzero((char *)sd, sizeof(sdioh_info_t));
204 sd->osh = osh;
205 sd->fake_func0.num = 0;
206 sd->fake_func0.card = func->card;
207 sd->func[0] = &sd->fake_func0;
208 #ifdef GLOBAL_SDMMC_INSTANCE
209 if (func->num == 2)
210 sd->func[1] = gInstance->func[1];
211 #else
212 sd->func[1] = func->card->sdio_func[0];
213 #endif
214 sd->func[2] = func->card->sdio_func[1];
215 #ifdef GLOBAL_SDMMC_INSTANCE
216 sd->func[func->num] = func;
217 #endif
218
219 #if defined(BT_OVER_SDIO)
220 sd->func[3] = NULL;
221 #endif /* defined (BT_OVER_SDIO) */
222
223 sd->num_funcs = 2;
224 sd->sd_blockmode = TRUE;
225 sd->use_client_ints = TRUE;
226 sd->client_block_size[0] = 64;
227 sd->use_rxchain = CUSTOM_RXCHAIN;
228 if (sd->func[1] == NULL || sd->func[2] == NULL) {
229 sd_err(("%s: func 1 or 2 is null \n", __FUNCTION__));
230 goto fail;
231 }
232 sdio_set_drvdata(sd->func[1], sd);
233
234 sdio_claim_host(sd->func[1]);
235 sd->client_block_size[1] = sd_f1_blocksize;
236 err_ret = sdio_set_block_size(sd->func[1], sd_f1_blocksize);
237 sdio_release_host(sd->func[1]);
238 if (err_ret) {
239 sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize(%d)\n", err_ret));
240 goto fail;
241 }
242
243 sdio_claim_host(sd->func[2]);
244 sd->client_block_size[2] = sd_f2_blocksize;
245 DHD_PRINT("%s: set sd_f2_blocksize %d\n", __FUNCTION__, sd_f2_blocksize);
246 err_ret = sdio_set_block_size(sd->func[2], sd_f2_blocksize);
247 sdio_release_host(sd->func[2]);
248 if (err_ret) {
249 sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d(%d)\n",
250 sd_f2_blocksize, err_ret));
251 goto fail;
252 }
253
254 sd->sd_clk_rate = sdmmc_get_clock_rate(sd);
255 DHD_PRINT("%s: sd clock rate = %u\n", __FUNCTION__, sd->sd_clk_rate);
256 sdioh_sdmmc_card_enablefuncs(sd);
257
258 sd_trace(("%s: Done\n", __FUNCTION__));
259 return sd;
260
261 fail:
262 MFREE(sd->osh, sd, sizeof(sdioh_info_t));
263 return NULL;
264 }
265
266
267 extern SDIOH_API_RC
sdioh_detach(osl_t * osh,sdioh_info_t * sd)268 sdioh_detach(osl_t *osh, sdioh_info_t *sd)
269 {
270 sd_trace(("%s\n", __FUNCTION__));
271
272 if (sd) {
273
274 /* Disable Function 2 */
275 if (sd->func[2]) {
276 sdio_claim_host(sd->func[2]);
277 sdio_disable_func(sd->func[2]);
278 sdio_release_host(sd->func[2]);
279 }
280
281 /* Disable Function 1 */
282 if (sd->func[1]) {
283 sdio_claim_host(sd->func[1]);
284 sdio_disable_func(sd->func[1]);
285 sdio_release_host(sd->func[1]);
286 }
287
288 sd->func[1] = NULL;
289 sd->func[2] = NULL;
290
291 MFREE(sd->osh, sd, sizeof(sdioh_info_t));
292 }
293 return SDIOH_API_RC_SUCCESS;
294 }
295
296 #if defined(OOB_INTR_ONLY) && defined(HW_OOB)
297
298 extern SDIOH_API_RC
sdioh_enable_func_intr(sdioh_info_t * sd)299 sdioh_enable_func_intr(sdioh_info_t *sd)
300 {
301 uint8 reg;
302 int err;
303
304 if (sd->func[0] == NULL) {
305 sd_err(("%s: function 0 pointer is NULL\n", __FUNCTION__));
306 return SDIOH_API_RC_FAIL;
307 }
308
309 sdio_claim_host(sd->func[0]);
310 reg = sdio_readb(sd->func[0], SDIOD_CCCR_INTEN, &err);
311 if (err) {
312 sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
313 sdio_release_host(sd->func[0]);
314 return SDIOH_API_RC_FAIL;
315 }
316 /* Enable F1 and F2 interrupts, clear master enable */
317 reg &= ~INTR_CTL_MASTER_EN;
318 reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN);
319 #if defined(BT_OVER_SDIO)
320 reg |= (INTR_CTL_FUNC3_EN);
321 #endif /* defined (BT_OVER_SDIO) */
322 sdio_writeb(sd->func[0], reg, SDIOD_CCCR_INTEN, &err);
323 sdio_release_host(sd->func[0]);
324
325 if (err) {
326 sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
327 return SDIOH_API_RC_FAIL;
328 }
329
330 return SDIOH_API_RC_SUCCESS;
331 }
332
333 extern SDIOH_API_RC
sdioh_disable_func_intr(sdioh_info_t * sd)334 sdioh_disable_func_intr(sdioh_info_t *sd)
335 {
336 uint8 reg;
337 int err;
338
339 if (sd->func[0] == NULL) {
340 sd_err(("%s: function 0 pointer is NULL\n", __FUNCTION__));
341 return SDIOH_API_RC_FAIL;
342 }
343
344 sdio_claim_host(sd->func[0]);
345 reg = sdio_readb(sd->func[0], SDIOD_CCCR_INTEN, &err);
346 if (err) {
347 sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
348 sdio_release_host(sd->func[0]);
349 return SDIOH_API_RC_FAIL;
350 }
351 reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN);
352 #if defined(BT_OVER_SDIO)
353 reg &= ~INTR_CTL_FUNC3_EN;
354 #endif
355 /* Disable master interrupt with the last function interrupt */
356 if (!(reg & 0xFE))
357 reg = 0;
358 sdio_writeb(sd->func[0], reg, SDIOD_CCCR_INTEN, &err);
359 sdio_release_host(sd->func[0]);
360
361 if (err) {
362 sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err));
363 return SDIOH_API_RC_FAIL;
364 }
365
366 return SDIOH_API_RC_SUCCESS;
367 }
368 #endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
369
370 /* Configure callback to client when we recieve client interrupt */
371 extern SDIOH_API_RC
sdioh_interrupt_register(sdioh_info_t * sd,sdioh_cb_fn_t fn,void * argh)372 sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
373 {
374 sd_trace(("%s: Entering\n", __FUNCTION__));
375 if (fn == NULL) {
376 sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__));
377 return SDIOH_API_RC_FAIL;
378 }
379 #if !defined(OOB_INTR_ONLY)
380 sd->intr_handler = fn;
381 sd->intr_handler_arg = argh;
382 sd->intr_handler_valid = TRUE;
383
384 /* register and unmask irq */
385 if (sd->func[2]) {
386 sdio_claim_host(sd->func[2]);
387 sdio_claim_irq(sd->func[2], IRQHandlerF2);
388 sdio_release_host(sd->func[2]);
389 }
390
391 if (sd->func[1]) {
392 sdio_claim_host(sd->func[1]);
393 sdio_claim_irq(sd->func[1], IRQHandler);
394 sdio_release_host(sd->func[1]);
395 }
396 #elif defined(HW_OOB)
397 sdioh_enable_func_intr(sd);
398 #endif /* !defined(OOB_INTR_ONLY) */
399
400 return SDIOH_API_RC_SUCCESS;
401 }
402
403 extern SDIOH_API_RC
sdioh_interrupt_deregister(sdioh_info_t * sd)404 sdioh_interrupt_deregister(sdioh_info_t *sd)
405 {
406 sd_trace(("%s: Entering\n", __FUNCTION__));
407
408 #if !defined(OOB_INTR_ONLY)
409 if (sd->func[1]) {
410 /* register and unmask irq */
411 sdio_claim_host(sd->func[1]);
412 sdio_release_irq(sd->func[1]);
413 sdio_release_host(sd->func[1]);
414 }
415
416 if (sd->func[2]) {
417 /* Claim host controller F2 */
418 sdio_claim_host(sd->func[2]);
419 sdio_release_irq(sd->func[2]);
420 /* Release host controller F2 */
421 sdio_release_host(sd->func[2]);
422 }
423
424 sd->intr_handler_valid = FALSE;
425 sd->intr_handler = NULL;
426 sd->intr_handler_arg = NULL;
427 #elif defined(HW_OOB)
428 if (dhd_download_fw_on_driverload)
429 sdioh_disable_func_intr(sd);
430 #endif /* !defined(OOB_INTR_ONLY) */
431 return SDIOH_API_RC_SUCCESS;
432 }
433
434 extern SDIOH_API_RC
sdioh_interrupt_query(sdioh_info_t * sd,bool * onoff)435 sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
436 {
437 sd_trace(("%s: Entering\n", __FUNCTION__));
438 *onoff = sd->client_intr_enabled;
439 return SDIOH_API_RC_SUCCESS;
440 }
441
442 #if defined(DHD_DEBUG)
443 extern bool
sdioh_interrupt_pending(sdioh_info_t * sd)444 sdioh_interrupt_pending(sdioh_info_t *sd)
445 {
446 return (0);
447 }
448 #endif
449
450 uint
sdioh_query_iofnum(sdioh_info_t * sd)451 sdioh_query_iofnum(sdioh_info_t *sd)
452 {
453 return sd->num_funcs;
454 }
455
456 /* IOVar table */
457 enum {
458 IOV_MSGLEVEL = 1,
459 IOV_BLOCKMODE,
460 IOV_BLOCKSIZE,
461 IOV_DMA,
462 IOV_USEINTS,
463 IOV_NUMINTS,
464 IOV_NUMLOCALINTS,
465 IOV_HOSTREG,
466 IOV_DEVREG,
467 IOV_DIVISOR,
468 IOV_SDMODE,
469 IOV_HISPEED,
470 IOV_HCIREGS,
471 IOV_POWER,
472 IOV_CLOCK,
473 IOV_RXCHAIN
474 };
475
476 const bcm_iovar_t sdioh_iovars[] = {
477 {"sd_msglevel", IOV_MSGLEVEL, 0, 0, IOVT_UINT32, 0 },
478 {"sd_blockmode", IOV_BLOCKMODE, 0, 0, IOVT_BOOL, 0 },
479 {"sd_blocksize", IOV_BLOCKSIZE, 0, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */
480 {"sd_dma", IOV_DMA, 0, 0, IOVT_BOOL, 0 },
481 {"sd_ints", IOV_USEINTS, 0, 0, IOVT_BOOL, 0 },
482 {"sd_numints", IOV_NUMINTS, 0, 0, IOVT_UINT32, 0 },
483 {"sd_numlocalints", IOV_NUMLOCALINTS, 0, 0, IOVT_UINT32, 0 },
484 {"sd_divisor", IOV_DIVISOR, 0, 0, IOVT_UINT32, 0 },
485 {"sd_power", IOV_POWER, 0, 0, IOVT_UINT32, 0 },
486 {"sd_clock", IOV_CLOCK, 0, 0, IOVT_UINT32, 0 },
487 {"sd_mode", IOV_SDMODE, 0, 0, IOVT_UINT32, 100},
488 {"sd_highspeed", IOV_HISPEED, 0, 0, IOVT_UINT32, 0 },
489 {"sd_rxchain", IOV_RXCHAIN, 0, 0, IOVT_BOOL, 0 },
490 {NULL, 0, 0, 0, 0, 0 }
491 };
492
493 int
sdioh_iovar_op(sdioh_info_t * si,const char * name,void * params,int plen,void * arg,int len,bool set)494 sdioh_iovar_op(sdioh_info_t *si, const char *name,
495 void *params, int plen, void *arg, int len, bool set)
496 {
497 const bcm_iovar_t *vi = NULL;
498 int bcmerror = 0;
499 int val_size;
500 int32 int_val = 0;
501 bool bool_val;
502 uint32 actionid;
503
504 ASSERT(name);
505 ASSERT(len >= 0);
506
507 /* Get must have return space; Set does not take qualifiers */
508 ASSERT(set || (arg && len));
509 ASSERT(!set || (!params && !plen));
510
511 sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
512
513 if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
514 bcmerror = BCME_UNSUPPORTED;
515 goto exit;
516 }
517
518 if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0)
519 goto exit;
520
521 /* Set up params so get and set can share the convenience variables */
522 if (params == NULL) {
523 params = arg;
524 plen = len;
525 }
526
527 if (vi->type == IOVT_VOID)
528 val_size = 0;
529 else if (vi->type == IOVT_BUFFER)
530 val_size = len;
531 else
532 val_size = sizeof(int);
533
534 if (plen >= (int)sizeof(int_val))
535 bcopy(params, &int_val, sizeof(int_val));
536
537 bool_val = (int_val != 0) ? TRUE : FALSE;
538 BCM_REFERENCE(bool_val);
539
540 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
541 switch (actionid) {
542 case IOV_GVAL(IOV_MSGLEVEL):
543 int_val = (int32)sd_msglevel;
544 bcopy(&int_val, arg, val_size);
545 break;
546
547 case IOV_SVAL(IOV_MSGLEVEL):
548 sd_msglevel = int_val;
549 break;
550
551 case IOV_GVAL(IOV_BLOCKMODE):
552 int_val = (int32)si->sd_blockmode;
553 bcopy(&int_val, arg, val_size);
554 break;
555
556 case IOV_SVAL(IOV_BLOCKMODE):
557 si->sd_blockmode = (bool)int_val;
558 /* Haven't figured out how to make non-block mode with DMA */
559 break;
560
561 case IOV_GVAL(IOV_BLOCKSIZE):
562 if ((uint32)int_val > si->num_funcs) {
563 bcmerror = BCME_BADARG;
564 break;
565 }
566 int_val = (int32)si->client_block_size[int_val];
567 bcopy(&int_val, arg, val_size);
568 break;
569
570 case IOV_SVAL(IOV_BLOCKSIZE):
571 {
572 uint func = ((uint32)int_val >> 16);
573 uint blksize = (uint16)int_val;
574 uint maxsize;
575
576 if (func > si->num_funcs) {
577 bcmerror = BCME_BADARG;
578 break;
579 }
580
581 switch (func) {
582 case 0: maxsize = 32; break;
583 case 1: maxsize = BLOCK_SIZE_4318; break;
584 case 2: maxsize = BLOCK_SIZE_4328; break;
585 default: maxsize = 0;
586 }
587 if (blksize > maxsize) {
588 bcmerror = BCME_BADARG;
589 break;
590 }
591 if (!blksize) {
592 blksize = maxsize;
593 }
594
595 /* Now set it */
596 si->client_block_size[func] = blksize;
597
598 #ifdef USE_DYNAMIC_F2_BLKSIZE
599 if (si->func[func] == NULL) {
600 sd_err(("%s: SDIO Device not present\n", __FUNCTION__));
601 bcmerror = BCME_NORESOURCE;
602 break;
603 }
604 sdio_claim_host(si->func[func]);
605 bcmerror = sdio_set_block_size(si->func[func], blksize);
606 if (bcmerror)
607 sd_err(("%s: Failed to set F%d blocksize to %d(%d)\n",
608 __FUNCTION__, func, blksize, bcmerror));
609 sdio_release_host(si->func[func]);
610 #endif /* USE_DYNAMIC_F2_BLKSIZE */
611 break;
612 }
613
614 case IOV_GVAL(IOV_RXCHAIN):
615 int_val = (int32)si->use_rxchain;
616 bcopy(&int_val, arg, val_size);
617 break;
618
619 case IOV_GVAL(IOV_DMA):
620 int_val = (int32)si->sd_use_dma;
621 bcopy(&int_val, arg, val_size);
622 break;
623
624 case IOV_SVAL(IOV_DMA):
625 si->sd_use_dma = (bool)int_val;
626 break;
627
628 case IOV_GVAL(IOV_USEINTS):
629 int_val = (int32)si->use_client_ints;
630 bcopy(&int_val, arg, val_size);
631 break;
632
633 case IOV_SVAL(IOV_USEINTS):
634 si->use_client_ints = (bool)int_val;
635 if (si->use_client_ints)
636 si->intmask |= CLIENT_INTR;
637 else
638 si->intmask &= ~CLIENT_INTR;
639
640 break;
641
642 case IOV_GVAL(IOV_DIVISOR):
643 int_val = (uint32)sd_divisor;
644 bcopy(&int_val, arg, val_size);
645 break;
646
647 case IOV_SVAL(IOV_DIVISOR):
648 /* set the clock to divisor, if value is non-zero & power of 2 */
649 if (int_val && !(int_val & (int_val - 1))) {
650 sd_divisor = int_val;
651 sdmmc_set_clock_divisor(si, sd_divisor);
652 } else {
653 DHD_ERROR(("%s: Invalid sd_divisor value, should be power of 2!\n",
654 __FUNCTION__));
655 }
656 break;
657
658 case IOV_GVAL(IOV_POWER):
659 int_val = (uint32)sd_power;
660 bcopy(&int_val, arg, val_size);
661 break;
662
663 case IOV_SVAL(IOV_POWER):
664 sd_power = int_val;
665 break;
666
667 case IOV_GVAL(IOV_CLOCK):
668 int_val = (uint32)sd_clock;
669 bcopy(&int_val, arg, val_size);
670 break;
671
672 case IOV_SVAL(IOV_CLOCK):
673 sd_clock = int_val;
674 break;
675
676 case IOV_GVAL(IOV_SDMODE):
677 int_val = (uint32)sd_sdmode;
678 bcopy(&int_val, arg, val_size);
679 break;
680
681 case IOV_SVAL(IOV_SDMODE):
682 sd_sdmode = int_val;
683 break;
684
685 case IOV_GVAL(IOV_HISPEED):
686 int_val = (uint32)sd_hiok;
687 bcopy(&int_val, arg, val_size);
688 break;
689
690 case IOV_SVAL(IOV_HISPEED):
691 sd_hiok = int_val;
692 break;
693
694 case IOV_GVAL(IOV_NUMINTS):
695 int_val = (int32)si->intrcount;
696 bcopy(&int_val, arg, val_size);
697 break;
698
699 case IOV_GVAL(IOV_NUMLOCALINTS):
700 int_val = (int32)0;
701 bcopy(&int_val, arg, val_size);
702 break;
703 default:
704 bcmerror = BCME_UNSUPPORTED;
705 break;
706 }
707 exit:
708
709 return bcmerror;
710 }
711
712 #if (defined(OOB_INTR_ONLY) && defined(HW_OOB)) || defined(FORCE_WOWLAN)
713
714 SDIOH_API_RC
sdioh_enable_hw_oob_intr(sdioh_info_t * sd,bool enable)715 sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable)
716 {
717 SDIOH_API_RC status;
718 uint8 data;
719
720 if (enable)
721 #ifdef HW_OOB_LOW_LEVEL
722 data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
723 #else
724 data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI;
725 #endif
726 else
727 data = SDIO_SEPINT_ACT_HI; /* disable hw oob interrupt */
728
729 status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data);
730 return status;
731 }
732 #endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */
733
734 extern SDIOH_API_RC
sdioh_cfg_read(sdioh_info_t * sd,uint fnc_num,uint32 addr,uint8 * data)735 sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
736 {
737 SDIOH_API_RC status;
738 /* No lock needed since sdioh_request_byte does locking */
739 status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
740 return status;
741 }
742
743 extern SDIOH_API_RC
sdioh_cfg_write(sdioh_info_t * sd,uint fnc_num,uint32 addr,uint8 * data)744 sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
745 {
746 /* No lock needed since sdioh_request_byte does locking */
747 SDIOH_API_RC status;
748 status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
749 return status;
750 }
751
752 static int
sdioh_sdmmc_get_cisaddr(sdioh_info_t * sd,uint32 regaddr)753 sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr)
754 {
755 /* read 24 bits and return valid 17 bit addr */
756 int i;
757 uint32 scratch, regdata;
758 uint8 *ptr = (uint8 *)&scratch;
759 for (i = 0; i < 3; i++) {
760 if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, ®data)) != SUCCESS)
761 sd_err(("%s: Can't read!\n", __FUNCTION__));
762
763 *ptr++ = (uint8) regdata;
764 regaddr++;
765 }
766
767 /* Only the lower 17-bits are valid */
768 scratch = ltoh32(scratch);
769 scratch &= 0x0001FFFF;
770 return (scratch);
771 }
772
773 extern SDIOH_API_RC
sdioh_cis_read(sdioh_info_t * sd,uint func,uint8 * cisd,uint32 length)774 sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
775 {
776 uint32 count;
777 int offset;
778 uint32 foo;
779 uint8 *cis = cisd;
780
781 sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
782
783 if (!sd->func_cis_ptr[func]) {
784 bzero(cis, length);
785 sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func));
786 return SDIOH_API_RC_FAIL;
787 }
788
789 sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func]));
790
791 for (count = 0; count < length; count++) {
792 offset = sd->func_cis_ptr[func] + count;
793 if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) {
794 sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
795 return SDIOH_API_RC_FAIL;
796 }
797
798 *cis = (uint8)(foo & 0xff);
799 cis++;
800 }
801
802 return SDIOH_API_RC_SUCCESS;
803 }
804
805 extern SDIOH_API_RC
sdioh_cisaddr_read(sdioh_info_t * sd,uint func,uint8 * cisd,uint32 offset)806 sdioh_cisaddr_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 offset)
807 {
808 uint32 foo;
809
810 sd_trace(("%s: Func = %d\n", __FUNCTION__, func));
811
812 if (!sd->func_cis_ptr[func]) {
813 sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func));
814 return SDIOH_API_RC_FAIL;
815 }
816
817 sd_trace(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func]));
818
819 if (sdioh_sdmmc_card_regread (sd, 0, sd->func_cis_ptr[func]+offset, 1, &foo) < 0) {
820 sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
821 return SDIOH_API_RC_FAIL;
822 }
823
824 *cisd = (uint8)(foo & 0xff);
825
826 return SDIOH_API_RC_SUCCESS;
827 }
828
829 extern SDIOH_API_RC
sdioh_request_byte(sdioh_info_t * sd,uint rw,uint func,uint regaddr,uint8 * byte)830 sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
831 {
832 int err_ret = 0;
833 #if defined(MMC_SDIO_ABORT)
834 int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT;
835 #endif
836 struct osl_timespec now, before;
837
838 #ifdef DHD_LOAD_CHIPALIVE
839 if (sd->sdmmc_sleep)
840 return SDIOH_API_RC_SUCCESS;
841 #endif
842
843 if (sd_msglevel & SDH_COST_VAL)
844 osl_do_gettimeofday(&before);
845
846 sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr));
847
848 DHD_PM_RESUME_WAIT(sdioh_request_byte_wait);
849 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
850 if(rw) { /* CMD52 Write */
851 if (func == 0) {
852 /* Can only directly write to some F0 registers. Handle F2 enable
853 * as a special case.
854 */
855 if (regaddr == SDIOD_CCCR_IOEN) {
856 #if defined(BT_OVER_SDIO)
857 do {
858 if (sd->func[3]) {
859 sd_info(("bcmsdh_sdmmc F3: *byte 0x%x\n", *byte));
860
861 if (*byte & SDIO_FUNC_ENABLE_3) {
862 sdio_claim_host(sd->func[3]);
863
864 /* Set Function 3 Block Size */
865 err_ret = sdio_set_block_size(sd->func[3],
866 sd_f3_blocksize);
867 if (err_ret) {
868 sd_err(("F3 blocksize set err%d\n",
869 err_ret));
870 }
871
872 /* Enable Function 3 */
873 sd_info(("bcmsdh_sdmmc F3: enable F3 fn %p\n",
874 sd->func[3]));
875 err_ret = sdio_enable_func(sd->func[3]);
876 if (err_ret) {
877 sd_err(("bcmsdh_sdmmc: enable F3 err:%d\n",
878 err_ret));
879 }
880
881 sdio_release_host(sd->func[3]);
882
883 break;
884 } else if (*byte & SDIO_FUNC_DISABLE_3) {
885 sdio_claim_host(sd->func[3]);
886
887 /* Disable Function 3 */
888 sd_info(("bcmsdh_sdmmc F3: disable F3 fn %p\n",
889 sd->func[3]));
890 err_ret = sdio_disable_func(sd->func[3]);
891 if (err_ret) {
892 sd_err(("bcmsdh_sdmmc: Disable F3 err:%d\n",
893 err_ret));
894 }
895 sdio_release_host(sd->func[3]);
896 sd->func[3] = NULL;
897
898 break;
899 }
900 }
901 #endif /* defined (BT_OVER_SDIO) */
902 if (sd->func[2]) {
903 sdio_claim_host(sd->func[2]);
904 if (*byte & SDIO_FUNC_ENABLE_2) {
905 /* Enable Function 2 */
906 err_ret = sdio_enable_func(sd->func[2]);
907 if (err_ret) {
908 sd_err(("bcmsdh_sdmmc: enable F2 failed:%d\n",
909 err_ret));
910 }
911 } else {
912 /* Disable Function 2 */
913 err_ret = sdio_disable_func(sd->func[2]);
914 if (err_ret) {
915 sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d\n",
916 err_ret));
917 }
918 }
919 sdio_release_host(sd->func[2]);
920 }
921 #if defined(BT_OVER_SDIO)
922 } while (0);
923 #endif /* defined (BT_OVER_SDIO) */
924 }
925 #if defined(MMC_SDIO_ABORT)
926 /* to allow abort command through F1 */
927 else if (regaddr == SDIOD_CCCR_IOABORT) {
928 while (sdio_abort_retry--) {
929 if (sd->func[func]) {
930 sdio_claim_host(sd->func[func]);
931 /*
932 * this sdio_f0_writeb() can be replaced with
933 * another api depending upon MMC driver change.
934 * As of this time, this is temporaray one
935 */
936 sdio_writeb(sd->func[func],
937 *byte, regaddr, &err_ret);
938 sdio_release_host(sd->func[func]);
939 }
940 if (!err_ret)
941 break;
942 }
943 }
944 #endif /* MMC_SDIO_ABORT */
945 /* to allow abort command through F1 */
946 #if defined(SDIO_ISR_THREAD)
947 else if (regaddr == SDIOD_CCCR_INTR_EXTN) {
948 while (sdio_abort_retry--) {
949 if (sd->func[func]) {
950 sdio_claim_host(sd->func[func]);
951 /*
952 * this sdio_f0_writeb() can be replaced with
953 * another api depending upon MMC driver change.
954 * As of this time, this is temporaray one
955 */
956 sdio_writeb(sd->func[func],
957 *byte, regaddr, &err_ret);
958 sdio_release_host(sd->func[func]);
959 }
960 if (!err_ret)
961 break;
962 }
963 }
964 #endif
965 else if (regaddr < 0xF0) {
966 sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr));
967 } else {
968 /* Claim host controller, perform F0 write, and release */
969 if (sd->func[func]) {
970 sdio_claim_host(sd->func[func]);
971 sdio_f0_writeb(sd->func[func],
972 *byte, regaddr, &err_ret);
973 sdio_release_host(sd->func[func]);
974 }
975 }
976 } else {
977 /* Claim host controller, perform Fn write, and release */
978 if (sd->func[func]) {
979 sdio_claim_host(sd->func[func]);
980 sdio_writeb(sd->func[func], *byte, regaddr, &err_ret);
981 sdio_release_host(sd->func[func]);
982 }
983 }
984 } else { /* CMD52 Read */
985 /* Claim host controller, perform Fn read, and release */
986 if (sd->func[func]) {
987 sdio_claim_host(sd->func[func]);
988 if (func == 0) {
989 *byte = sdio_f0_readb(sd->func[func], regaddr, &err_ret);
990 } else {
991 *byte = sdio_readb(sd->func[func], regaddr, &err_ret);
992 }
993 sdio_release_host(sd->func[func]);
994 }
995 }
996
997 if (err_ret) {
998 if ((regaddr == 0x1001F) && ((err_ret == -ETIMEDOUT) || (err_ret == -EILSEQ))) {
999 } else {
1000 sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
1001 rw ? "Write" : "Read", func, regaddr, *byte, err_ret));
1002 }
1003 }
1004
1005 if (sd_msglevel & SDH_COST_VAL) {
1006 osl_do_gettimeofday(&now);
1007 sd_cost(("%s: rw=%d len=1 cost=%lds %luus\n", __FUNCTION__,
1008 rw, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000));
1009 }
1010
1011 return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
1012 }
1013
1014 uint
sdioh_set_mode(sdioh_info_t * sd,uint mode)1015 sdioh_set_mode(sdioh_info_t *sd, uint mode)
1016 {
1017 if (mode == SDPCM_TXGLOM_CPY)
1018 sd->txglom_mode = mode;
1019 else if (mode == SDPCM_TXGLOM_MDESC)
1020 sd->txglom_mode = mode;
1021
1022 return (sd->txglom_mode);
1023 }
1024
1025 #ifdef PKT_STATICS
1026 uint32
sdioh_get_spend_time(sdioh_info_t * sd)1027 sdioh_get_spend_time(sdioh_info_t *sd)
1028 {
1029 return (sd->sdio_spent_time_us);
1030 }
1031 #endif
1032
1033 #ifdef DHD_LOAD_CHIPALIVE
1034 bool
sdioh_get_sdmmc_sleep(sdioh_info_t * sd)1035 sdioh_get_sdmmc_sleep(sdioh_info_t *sd)
1036 {
1037 return sd->sdmmc_sleep;
1038 }
1039
1040 void
sdioh_set_sdmmc_sleep(sdioh_info_t * sd,bool sleep)1041 sdioh_set_sdmmc_sleep(sdioh_info_t *sd, bool sleep)
1042 {
1043 sd->sdmmc_sleep = sleep;
1044 }
1045 #endif
1046
1047 extern SDIOH_API_RC
sdioh_request_word(sdioh_info_t * sd,uint cmd_type,uint rw,uint func,uint addr,uint32 * word,uint nbytes)1048 sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
1049 uint32 *word, uint nbytes)
1050 {
1051 int err_ret = SDIOH_API_RC_FAIL;
1052 int err_ret2 = SDIOH_API_RC_SUCCESS; // terence 20130621: prevent dhd_dpc in dead lock
1053 #if defined(MMC_SDIO_ABORT)
1054 int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT;
1055 #endif
1056 struct osl_timespec now, before;
1057
1058 #ifdef DHD_LOAD_CHIPALIVE
1059 if (sd->sdmmc_sleep)
1060 return SDIOH_API_RC_SUCCESS;
1061 #endif
1062
1063 if (sd_msglevel & SDH_COST_VAL)
1064 osl_do_gettimeofday(&before);
1065
1066 if (func == 0) {
1067 sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__));
1068 return SDIOH_API_RC_FAIL;
1069 }
1070
1071 sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
1072 __FUNCTION__, cmd_type, rw, func, addr, nbytes));
1073
1074 DHD_PM_RESUME_WAIT(sdioh_request_word_wait);
1075 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
1076 /* Claim host controller */
1077 sdio_claim_host(sd->func[func]);
1078
1079 if(rw) { /* CMD52 Write */
1080 if (nbytes == 4) {
1081 sdio_writel(sd->func[func], *word, addr, &err_ret);
1082 } else if (nbytes == 2) {
1083 sdio_writew(sd->func[func], (*word & 0xFFFF), addr, &err_ret);
1084 } else {
1085 sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
1086 }
1087 } else { /* CMD52 Read */
1088 if (nbytes == 4) {
1089 *word = sdio_readl(sd->func[func], addr, &err_ret);
1090 } else if (nbytes == 2) {
1091 *word = sdio_readw(sd->func[func], addr, &err_ret) & 0xFFFF;
1092 } else {
1093 sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes));
1094 }
1095 }
1096
1097 /* Release host controller */
1098 sdio_release_host(sd->func[func]);
1099
1100 if (err_ret) {
1101 #if defined(MMC_SDIO_ABORT)
1102 /* Any error on CMD53 transaction should abort that function using function 0. */
1103 while (sdio_abort_retry--) {
1104 if (sd->func[0]) {
1105 sdio_claim_host(sd->func[0]);
1106 /*
1107 * this sdio_f0_writeb() can be replaced with another api
1108 * depending upon MMC driver change.
1109 * As of this time, this is temporaray one
1110 */
1111 sdio_writeb(sd->func[0],
1112 func, SDIOD_CCCR_IOABORT, &err_ret2);
1113 sdio_release_host(sd->func[0]);
1114 }
1115 if (!err_ret2)
1116 break;
1117 }
1118 if (err_ret)
1119 #endif /* MMC_SDIO_ABORT */
1120 {
1121 sd_err(("bcmsdh_sdmmc: Failed to %s word F%d:@0x%05x=%02x, Err: 0x%08x\n",
1122 rw ? "Write" : "Read", func, addr, *word, err_ret));
1123 }
1124 }
1125
1126 if (sd_msglevel & SDH_COST_VAL) {
1127 osl_do_gettimeofday(&now);
1128 sd_cost(("%s: rw=%d, len=%d cost=%lds %luus\n", __FUNCTION__,
1129 rw, nbytes, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000));
1130 }
1131
1132 return (((err_ret == 0)&&(err_ret2 == 0)) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
1133 }
1134
1135 #ifdef BCMSDIOH_TXGLOM
1136 static SDIOH_API_RC
sdioh_request_packet_chain(sdioh_info_t * sd,uint fix_inc,uint write,uint func,uint addr,void * pkt)1137 sdioh_request_packet_chain(sdioh_info_t *sd, uint fix_inc, uint write, uint func,
1138 uint addr, void *pkt)
1139 {
1140 bool fifo = (fix_inc == SDIOH_DATA_FIX);
1141 int err_ret = 0;
1142 void *pnext;
1143 uint ttl_len, pkt_offset;
1144 uint blk_num;
1145 uint blk_size;
1146 uint max_blk_count;
1147 uint max_req_size;
1148 struct mmc_request mmc_req;
1149 struct mmc_command mmc_cmd;
1150 struct mmc_data mmc_dat;
1151 uint32 sg_count;
1152 struct sdio_func *sdio_func = sd->func[func];
1153 struct mmc_host *host = sdio_func->card->host;
1154 uint8 *localbuf = NULL;
1155 uint local_plen = 0;
1156 uint pkt_len = 0;
1157 struct osl_timespec now, before;
1158
1159 sd_trace(("%s: Enter\n", __FUNCTION__));
1160 ASSERT(pkt);
1161 DHD_PM_RESUME_WAIT(sdioh_request_packet_wait);
1162 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
1163
1164 #ifndef PKT_STATICS
1165 if (sd_msglevel & SDH_COST_VAL)
1166 #endif
1167 osl_do_gettimeofday(&before);
1168
1169 blk_size = sd->client_block_size[func];
1170 max_blk_count = min(host->max_blk_count, (uint)MAX_IO_RW_EXTENDED_BLK);
1171 max_req_size = min(max_blk_count * blk_size, host->max_req_size);
1172
1173 pkt_offset = 0;
1174 pnext = pkt;
1175
1176 ttl_len = 0;
1177 sg_count = 0;
1178 if(sd->txglom_mode == SDPCM_TXGLOM_MDESC) {
1179 while (pnext != NULL) {
1180 ttl_len = 0;
1181 sg_count = 0;
1182 memset(&mmc_req, 0, sizeof(struct mmc_request));
1183 memset(&mmc_cmd, 0, sizeof(struct mmc_command));
1184 memset(&mmc_dat, 0, sizeof(struct mmc_data));
1185 sg_init_table(sd->sg_list, ARRAYSIZE(sd->sg_list));
1186
1187 /* Set up scatter-gather DMA descriptors. this loop is to find out the max
1188 * data we can transfer with one command 53. blocks per command is limited by
1189 * host max_req_size and 9-bit max block number. when the total length of this
1190 * packet chain is bigger than max_req_size, use multiple SD_IO_RW_EXTENDED
1191 * commands (each transfer is still block aligned)
1192 */
1193 while (pnext != NULL && ttl_len < max_req_size) {
1194 int pkt_len;
1195 int sg_data_size;
1196 uint8 *pdata = (uint8*)PKTDATA(sd->osh, pnext);
1197
1198 ASSERT(pdata != NULL);
1199 pkt_len = PKTLEN(sd->osh, pnext);
1200 sd_trace(("%s[%d] data=%p, len=%d\n", __FUNCTION__, write, pdata, pkt_len));
1201 /* sg_count is unlikely larger than the array size, and this is
1202 * NOT something we can handle here, but in case it happens, PLEASE put
1203 * a restriction on max tx/glom count (based on host->max_segs).
1204 */
1205 if (sg_count >= ARRAYSIZE(sd->sg_list)) {
1206 sd_err(("%s: sg list entries exceed limit %d\n", __FUNCTION__, sg_count));
1207 return (SDIOH_API_RC_FAIL);
1208 }
1209 pdata += pkt_offset;
1210
1211 sg_data_size = pkt_len - pkt_offset;
1212 if (sg_data_size > max_req_size - ttl_len)
1213 sg_data_size = max_req_size - ttl_len;
1214 /* some platforms put a restriction on the data size of each scatter-gather
1215 * DMA descriptor, use multiple sg buffers when xfer_size is bigger than
1216 * max_seg_size
1217 */
1218 if (sg_data_size > host->max_seg_size)
1219 sg_data_size = host->max_seg_size;
1220 sg_set_buf(&sd->sg_list[sg_count++], pdata, sg_data_size);
1221
1222 ttl_len += sg_data_size;
1223 pkt_offset += sg_data_size;
1224 if (pkt_offset == pkt_len) {
1225 pnext = PKTNEXT(sd->osh, pnext);
1226 pkt_offset = 0;
1227 }
1228 }
1229
1230 if (ttl_len % blk_size != 0) {
1231 sd_err(("%s, data length %d not aligned to block size %d\n",
1232 __FUNCTION__, ttl_len, blk_size));
1233 return SDIOH_API_RC_FAIL;
1234 }
1235 blk_num = ttl_len / blk_size;
1236 mmc_dat.sg = sd->sg_list;
1237 mmc_dat.sg_len = sg_count;
1238 mmc_dat.blksz = blk_size;
1239 mmc_dat.blocks = blk_num;
1240 mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
1241 mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */
1242 mmc_cmd.arg = write ? 1<<31 : 0;
1243 mmc_cmd.arg |= (func & 0x7) << 28;
1244 mmc_cmd.arg |= 1<<27;
1245 mmc_cmd.arg |= fifo ? 0 : 1<<26;
1246 mmc_cmd.arg |= (addr & 0x1FFFF) << 9;
1247 mmc_cmd.arg |= blk_num & 0x1FF;
1248 mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
1249 mmc_req.cmd = &mmc_cmd;
1250 mmc_req.data = &mmc_dat;
1251 if (!fifo)
1252 addr += ttl_len;
1253
1254 sdio_claim_host(sdio_func);
1255 mmc_set_data_timeout(&mmc_dat, sdio_func->card);
1256 mmc_wait_for_req(host, &mmc_req);
1257 sdio_release_host(sdio_func);
1258
1259 err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error;
1260 if (0 != err_ret) {
1261 sd_err(("%s:CMD53 %s failed with code %d\n",
1262 __FUNCTION__, write ? "write" : "read", err_ret));
1263 return SDIOH_API_RC_FAIL;
1264 }
1265 }
1266 } else if(sd->txglom_mode == SDPCM_TXGLOM_CPY) {
1267 for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) {
1268 ttl_len += PKTLEN(sd->osh, pnext);
1269 }
1270 /* Claim host controller */
1271 sdio_claim_host(sd->func[func]);
1272 for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) {
1273 uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext);
1274 pkt_len = PKTLEN(sd->osh, pnext);
1275
1276 if (!localbuf) {
1277 localbuf = (uint8 *)MALLOC(sd->osh, ttl_len);
1278 if (localbuf == NULL) {
1279 sd_err(("%s: %s TXGLOM: localbuf malloc FAILED\n",
1280 __FUNCTION__, (write) ? "TX" : "RX"));
1281 goto txglomfail;
1282 }
1283 }
1284
1285 bcopy(buf, (localbuf + local_plen), pkt_len);
1286 local_plen += pkt_len;
1287 if (PKTNEXT(sd->osh, pnext))
1288 continue;
1289
1290 buf = localbuf;
1291 pkt_len = local_plen;
1292 txglomfail:
1293 /* Align Patch */
1294 if (!write || pkt_len < 32)
1295 pkt_len = (pkt_len + 3) & 0xFFFFFFFC;
1296 else if (pkt_len % blk_size)
1297 pkt_len += blk_size - (pkt_len % blk_size);
1298
1299 if ((write) && (!fifo))
1300 err_ret = sdio_memcpy_toio(sd->func[func], addr, buf, pkt_len);
1301 else if (write)
1302 err_ret = sdio_memcpy_toio(sd->func[func], addr, buf, pkt_len);
1303 else if (fifo)
1304 err_ret = sdio_readsb(sd->func[func], buf, addr, pkt_len);
1305 else
1306 err_ret = sdio_memcpy_fromio(sd->func[func], buf, addr, pkt_len);
1307
1308 if (err_ret)
1309 sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n",
1310 __FUNCTION__,
1311 (write) ? "TX" : "RX",
1312 pnext, sg_count, addr, pkt_len, err_ret));
1313 else
1314 sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n",
1315 __FUNCTION__,
1316 (write) ? "TX" : "RX",
1317 pnext, sg_count, addr, pkt_len));
1318
1319 if (!fifo)
1320 addr += pkt_len;
1321 sg_count ++;
1322 }
1323 sdio_release_host(sd->func[func]);
1324 } else {
1325 sd_err(("%s: set to wrong glom mode %d\n", __FUNCTION__, sd->txglom_mode));
1326 return SDIOH_API_RC_FAIL;
1327 }
1328
1329 if (localbuf)
1330 MFREE(sd->osh, localbuf, ttl_len);
1331
1332 #ifndef PKT_STATICS
1333 if (sd_msglevel & SDH_COST_VAL)
1334 #endif
1335 {
1336 osl_do_gettimeofday(&now);
1337 sd_cost(("%s: rw=%d, ttl_len=%d, cost=%lds %luus\n", __FUNCTION__,
1338 write, ttl_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000));
1339 }
1340
1341 #ifdef PKT_STATICS
1342 if (write && (func == 2))
1343 sd->sdio_spent_time_us = osl_do_gettimediff(&now, &before);
1344 #endif
1345
1346 sd_trace(("%s: Exit\n", __FUNCTION__));
1347 return SDIOH_API_RC_SUCCESS;
1348 }
1349 #endif /* BCMSDIOH_TXGLOM */
1350
1351 static SDIOH_API_RC
sdioh_buffer_tofrom_bus(sdioh_info_t * sd,uint fix_inc,uint write,uint func,uint addr,uint8 * buf,uint len)1352 sdioh_buffer_tofrom_bus(sdioh_info_t *sd, uint fix_inc, uint write, uint func,
1353 uint addr, uint8 *buf, uint len)
1354 {
1355 bool fifo = (fix_inc == SDIOH_DATA_FIX);
1356 int err_ret = 0;
1357 struct osl_timespec now, before;
1358
1359 sd_trace(("%s: Enter\n", __FUNCTION__));
1360 ASSERT(buf);
1361
1362 if (sd_msglevel & SDH_COST_VAL)
1363 osl_do_gettimeofday(&before);
1364
1365 /* NOTE:
1366 * For all writes, each packet length is aligned to 32 (or 4)
1367 * bytes in dhdsdio_txpkt_preprocess, and for glom the last packet length
1368 * is aligned to block boundary. If you want to align each packet to
1369 * a custom size, please do it in dhdsdio_txpkt_preprocess, NOT here
1370 *
1371 * For reads, the alignment is doen in sdioh_request_buffer.
1372 *
1373 */
1374 sdio_claim_host(sd->func[func]);
1375
1376 if ((write) && (!fifo))
1377 err_ret = sdio_memcpy_toio(sd->func[func], addr, buf, len);
1378 else if (write)
1379 err_ret = sdio_memcpy_toio(sd->func[func], addr, buf, len);
1380 else if (fifo)
1381 err_ret = sdio_readsb(sd->func[func], buf, addr, len);
1382 else
1383 err_ret = sdio_memcpy_fromio(sd->func[func], buf, addr, len);
1384
1385 sdio_release_host(sd->func[func]);
1386
1387 if (err_ret)
1388 sd_err(("%s: %s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=%d\n", __FUNCTION__,
1389 (write) ? "TX" : "RX", buf, addr, len, err_ret));
1390 else
1391 sd_trace(("%s: %s xfr'd %p, addr=0x%05x, len=%d\n", __FUNCTION__,
1392 (write) ? "TX" : "RX", buf, addr, len));
1393
1394 sd_trace(("%s: Exit\n", __FUNCTION__));
1395
1396 if (sd_msglevel & SDH_COST_VAL) {
1397 osl_do_gettimeofday(&now);
1398 sd_cost(("%s: rw=%d, len=%d cost=%lds %luus\n", __FUNCTION__,
1399 write, len, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000));
1400 }
1401
1402 return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
1403 }
1404
1405
1406 /*
1407 * This function takes a buffer or packet, and fixes everything up so that in the
1408 * end, a DMA-able packet is created.
1409 *
1410 * A buffer does not have an associated packet pointer, and may or may not be aligned.
1411 * A packet may consist of a single packet, or a packet chain. If it is a packet chain,
1412 * then all the packets in the chain must be properly aligned. If the packet data is not
1413 * aligned, then there may only be one packet, and in this case, it is copied to a new
1414 * aligned packet.
1415 *
1416 */
1417 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 buf_len,uint8 * buffer,void * pkt)1418 sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func,
1419 uint addr, uint reg_width, uint buf_len, uint8 *buffer, void *pkt)
1420 {
1421 SDIOH_API_RC status;
1422 void *tmppkt;
1423 struct osl_timespec now, before;
1424
1425 sd_trace(("%s: Enter\n", __FUNCTION__));
1426 DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait);
1427 DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL);
1428
1429 if (sd_msglevel & SDH_COST_VAL)
1430 osl_do_gettimeofday(&before);
1431
1432 if (pkt) {
1433 #ifdef BCMSDIOH_TXGLOM
1434 /* packet chain, only used for tx/rx glom, all packets length
1435 * are aligned, total length is a block multiple
1436 */
1437 if (PKTNEXT(sd->osh, pkt))
1438 return sdioh_request_packet_chain(sd, fix_inc, write, func, addr, pkt);
1439 #endif /* BCMSDIOH_TXGLOM */
1440 /* non-glom mode, ignore the buffer parameter and use the packet pointer
1441 * (this shouldn't happen)
1442 */
1443 buffer = PKTDATA(sd->osh, pkt);
1444 buf_len = PKTLEN(sd->osh, pkt);
1445 }
1446
1447 ASSERT(buffer);
1448
1449 /* buffer and length are aligned, use it directly so we can avoid memory copy */
1450 if (((ulong)buffer & DMA_ALIGN_MASK) == 0 && (buf_len & DMA_ALIGN_MASK) == 0)
1451 return sdioh_buffer_tofrom_bus(sd, fix_inc, write, func, addr, buffer, buf_len);
1452
1453 sd_trace(("%s: [%d] doing memory copy buf=%p, len=%d\n",
1454 __FUNCTION__, write, buffer, buf_len));
1455
1456 /* otherwise, a memory copy is needed as the input buffer is not aligned */
1457 tmppkt = PKTGET_STATIC(sd->osh, buf_len + DEFAULT_SDIO_F2_BLKSIZE, write ? TRUE : FALSE);
1458 if (tmppkt == NULL) {
1459 sd_err(("%s: PKTGET failed: len %d\n", __FUNCTION__, buf_len));
1460 return SDIOH_API_RC_FAIL;
1461 }
1462
1463 if (write)
1464 bcopy(buffer, PKTDATA(sd->osh, tmppkt), buf_len);
1465
1466 status = sdioh_buffer_tofrom_bus(sd, fix_inc, write, func, addr,
1467 PKTDATA(sd->osh, tmppkt), ROUNDUP(buf_len, (DMA_ALIGN_MASK+1)));
1468
1469 if (!write)
1470 bcopy(PKTDATA(sd->osh, tmppkt), buffer, buf_len);
1471
1472 PKTFREE_STATIC(sd->osh, tmppkt, write ? TRUE : FALSE);
1473
1474 if (sd_msglevel & SDH_COST_VAL) {
1475 osl_do_gettimeofday(&now);
1476 sd_cost(("%s: len=%d cost=%lds %luus\n", __FUNCTION__,
1477 buf_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000 - before.tv_nsec/1000));
1478 }
1479
1480 return status;
1481 }
1482
1483 /* this function performs "abort" for both of host & device */
1484 extern int
sdioh_abort(sdioh_info_t * sd,uint func)1485 sdioh_abort(sdioh_info_t *sd, uint func)
1486 {
1487 #if defined(MMC_SDIO_ABORT)
1488 char t_func = (char) func;
1489 #endif /* defined(MMC_SDIO_ABORT) */
1490 sd_trace(("%s: Enter\n", __FUNCTION__));
1491
1492 #if defined(MMC_SDIO_ABORT)
1493 /* issue abort cmd52 command through F1 */
1494 sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func);
1495 #endif /* defined(MMC_SDIO_ABORT) */
1496
1497 sd_trace(("%s: Exit\n", __FUNCTION__));
1498 return SDIOH_API_RC_SUCCESS;
1499 }
1500
1501 /* Reset and re-initialize the device */
sdioh_sdio_reset(sdioh_info_t * si)1502 int sdioh_sdio_reset(sdioh_info_t *si)
1503 {
1504 sd_trace(("%s: Enter\n", __FUNCTION__));
1505 sd_trace(("%s: Exit\n", __FUNCTION__));
1506 return SDIOH_API_RC_SUCCESS;
1507 }
1508
1509 /* Disable device interrupt */
1510 void
sdioh_sdmmc_devintr_off(sdioh_info_t * sd)1511 sdioh_sdmmc_devintr_off(sdioh_info_t *sd)
1512 {
1513 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
1514 sd->intmask &= ~CLIENT_INTR;
1515 }
1516
1517 /* Enable device interrupt */
1518 void
sdioh_sdmmc_devintr_on(sdioh_info_t * sd)1519 sdioh_sdmmc_devintr_on(sdioh_info_t *sd)
1520 {
1521 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
1522 sd->intmask |= CLIENT_INTR;
1523 }
1524
1525 /* Read client card reg */
1526 int
sdioh_sdmmc_card_regread(sdioh_info_t * sd,int func,uint32 regaddr,int regsize,uint32 * data)1527 sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
1528 {
1529
1530 if ((func == 0) || (regsize == 1)) {
1531 uint8 temp = 0;
1532
1533 sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
1534 *data = temp;
1535 *data &= 0xff;
1536 sd_data(("%s: byte read data=0x%02x\n",
1537 __FUNCTION__, *data));
1538 } else {
1539 if (sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize)) {
1540 return BCME_SDIO_ERROR;
1541 }
1542
1543 if (regsize == 2)
1544 *data &= 0xffff;
1545
1546 sd_data(("%s: word read data=0x%08x\n",
1547 __FUNCTION__, *data));
1548 }
1549
1550 return SUCCESS;
1551 }
1552
1553 #if !defined(OOB_INTR_ONLY)
1554 /* bcmsdh_sdmmc interrupt handler */
IRQHandler(struct sdio_func * func)1555 static void IRQHandler(struct sdio_func *func)
1556 {
1557 sdioh_info_t *sd;
1558
1559 sd = sdio_get_drvdata(func);
1560
1561 ASSERT(sd != NULL);
1562 sdio_release_host(sd->func[0]);
1563
1564 if (sd->use_client_ints) {
1565 sd->intrcount++;
1566 ASSERT(sd->intr_handler);
1567 ASSERT(sd->intr_handler_arg);
1568 (sd->intr_handler)(sd->intr_handler_arg);
1569 } else {
1570 sd_err(("bcmsdh_sdmmc: ***IRQHandler\n"));
1571
1572 sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
1573 __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
1574 }
1575
1576 sdio_claim_host(sd->func[0]);
1577 }
1578
1579 /* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */
IRQHandlerF2(struct sdio_func * func)1580 static void IRQHandlerF2(struct sdio_func *func)
1581 {
1582 sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n"));
1583 }
1584 #endif /* !defined(OOB_INTR_ONLY) */
1585
1586 #ifdef NOTUSED
1587 /* Write client card reg */
1588 static int
sdioh_sdmmc_card_regwrite(sdioh_info_t * sd,int func,uint32 regaddr,int regsize,uint32 data)1589 sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
1590 {
1591
1592 if ((func == 0) || (regsize == 1)) {
1593 uint8 temp;
1594
1595 temp = data & 0xff;
1596 sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
1597 sd_data(("%s: byte write data=0x%02x\n",
1598 __FUNCTION__, data));
1599 } else {
1600 if (regsize == 2)
1601 data &= 0xffff;
1602
1603 sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize);
1604
1605 sd_data(("%s: word write data=0x%08x\n",
1606 __FUNCTION__, data));
1607 }
1608
1609 return SUCCESS;
1610 }
1611 #endif /* NOTUSED */
1612
1613 int
sdioh_start(sdioh_info_t * sd,int stage)1614 sdioh_start(sdioh_info_t *sd, int stage)
1615 {
1616 int ret;
1617
1618 if (!sd) {
1619 sd_err(("%s Failed, sd is NULL\n", __FUNCTION__));
1620 return (0);
1621 }
1622
1623 /* Need to do this stages as we can't enable the interrupt till
1624 downloading of the firmware is complete, other wise polling
1625 sdio access will come in way
1626 */
1627 if (sd->func[0]) {
1628 if (stage == 0) {
1629 /* Since the power to the chip is killed, we will have
1630 re enumerate the device again. Set the block size
1631 and enable the fucntion 1 for in preparation for
1632 downloading the code
1633 */
1634 /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux
1635 2.6.27. The implementation prior to that is buggy, and needs broadcom's
1636 patch for it
1637 */
1638 #ifdef DHD_LOAD_CHIPALIVE
1639 if (!dhd_chip_alive) {
1640 #endif
1641 #if defined(ENABLE_INSMOD_NO_FW_LOAD) && !defined(BUS_POWER_RESTORE)
1642 if ((ret = sdio_reset_comm(sd->func[0]->card))) {
1643 sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret));
1644 return ret;
1645 } else
1646 #endif
1647 {
1648 sd->num_funcs = 2;
1649 sd->sd_blockmode = TRUE;
1650 sd->use_client_ints = TRUE;
1651 sd->client_block_size[0] = 64;
1652
1653 if (sd->func[1]) {
1654 /* Claim host controller */
1655 sdio_claim_host(sd->func[1]);
1656
1657 sd->client_block_size[1] = 64;
1658 ret = sdio_set_block_size(sd->func[1], 64);
1659 if (ret) {
1660 sd_err(("bcmsdh_sdmmc: Failed to set F1 "
1661 "blocksize(%d)\n", ret));
1662 }
1663
1664 /* Release host controller F1 */
1665 sdio_release_host(sd->func[1]);
1666 }
1667
1668 if (sd->func[2]) {
1669 /* Claim host controller F2 */
1670 sdio_claim_host(sd->func[2]);
1671
1672 sd->client_block_size[2] = sd_f2_blocksize;
1673 printf("%s: set sd_f2_blocksize %d\n", __FUNCTION__, sd_f2_blocksize);
1674 ret = sdio_set_block_size(sd->func[2], sd_f2_blocksize);
1675 if (ret) {
1676 sd_err(("bcmsdh_sdmmc: Failed to set F2 "
1677 "blocksize to %d(%d)\n", sd_f2_blocksize, ret));
1678 }
1679
1680 /* Release host controller F2 */
1681 sdio_release_host(sd->func[2]);
1682 }
1683
1684 sdioh_sdmmc_card_enablefuncs(sd);
1685 }
1686 #ifdef DHD_LOAD_CHIPALIVE
1687 }
1688 #endif
1689 } else {
1690 #if !defined(OOB_INTR_ONLY)
1691 sdio_claim_host(sd->func[0]);
1692 if (sd->func[2])
1693 sdio_claim_irq(sd->func[2], IRQHandlerF2);
1694 if (sd->func[1])
1695 sdio_claim_irq(sd->func[1], IRQHandler);
1696 sdio_release_host(sd->func[0]);
1697 #else /* defined(OOB_INTR_ONLY) */
1698 #if defined(HW_OOB)
1699 sdioh_enable_func_intr(sd);
1700 #endif
1701 bcmsdh_oob_intr_set(sd->bcmsdh, TRUE);
1702 #endif /* !defined(OOB_INTR_ONLY) */
1703 }
1704 }
1705 else
1706 sd_err(("%s Failed\n", __FUNCTION__));
1707
1708 return (0);
1709 }
1710
1711 int
sdioh_stop(sdioh_info_t * sd)1712 sdioh_stop(sdioh_info_t *sd)
1713 {
1714 /* MSM7201A Android sdio stack has bug with interrupt
1715 So internaly within SDIO stack they are polling
1716 which cause issue when device is turned off. So
1717 unregister interrupt with SDIO stack to stop the
1718 polling
1719 */
1720 if (sd->func[0]) {
1721 #if !defined(OOB_INTR_ONLY)
1722 sdio_claim_host(sd->func[0]);
1723 if (sd->func[1])
1724 sdio_release_irq(sd->func[1]);
1725 if (sd->func[2])
1726 sdio_release_irq(sd->func[2]);
1727 sdio_release_host(sd->func[0]);
1728 #else /* defined(OOB_INTR_ONLY) */
1729 #if defined(HW_OOB)
1730 sdioh_disable_func_intr(sd);
1731 #endif
1732 bcmsdh_oob_intr_set(sd->bcmsdh, FALSE);
1733 #endif /* !defined(OOB_INTR_ONLY) */
1734 }
1735 else
1736 sd_err(("%s Failed\n", __FUNCTION__));
1737 return (0);
1738 }
1739
1740 int
sdioh_waitlockfree(sdioh_info_t * sd)1741 sdioh_waitlockfree(sdioh_info_t *sd)
1742 {
1743 return (1);
1744 }
1745
1746
1747 SDIOH_API_RC
sdioh_gpioouten(sdioh_info_t * sd,uint32 gpio)1748 sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio)
1749 {
1750 return SDIOH_API_RC_FAIL;
1751 }
1752
1753 SDIOH_API_RC
sdioh_gpioout(sdioh_info_t * sd,uint32 gpio,bool enab)1754 sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab)
1755 {
1756 return SDIOH_API_RC_FAIL;
1757 }
1758
1759 bool
sdioh_gpioin(sdioh_info_t * sd,uint32 gpio)1760 sdioh_gpioin(sdioh_info_t *sd, uint32 gpio)
1761 {
1762 return FALSE;
1763 }
1764
1765 SDIOH_API_RC
sdioh_gpio_init(sdioh_info_t * sd)1766 sdioh_gpio_init(sdioh_info_t *sd)
1767 {
1768 return SDIOH_API_RC_FAIL;
1769 }
1770
1771 uint
sdmmc_get_clock_rate(sdioh_info_t * sd)1772 sdmmc_get_clock_rate(sdioh_info_t *sd)
1773 {
1774 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) || (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
1775 return 0;
1776 #else
1777 struct sdio_func *sdio_func = sd->func[0];
1778 struct mmc_host *host = sdio_func->card->host;
1779 return mmc_host_clk_rate(host);
1780 #endif
1781 }
1782
1783
1784 void
sdmmc_set_clock_rate(sdioh_info_t * sd,uint hz)1785 sdmmc_set_clock_rate(sdioh_info_t *sd, uint hz)
1786 {
1787 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) || (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
1788 return;
1789 #else
1790 struct sdio_func *sdio_func = sd->func[0];
1791 struct mmc_host *host = sdio_func->card->host;
1792 struct mmc_ios *ios = &host->ios;
1793
1794 mmc_host_clk_hold(host);
1795 DHD_INFO(("%s: Before change: sd clock rate is %u\n", __FUNCTION__, ios->clock));
1796 if (hz < host->f_min) {
1797 DHD_ERROR(("%s: Intended rate is below min rate, setting to min\n", __FUNCTION__));
1798 hz = host->f_min;
1799 }
1800
1801 if (hz > host->f_max) {
1802 DHD_ERROR(("%s: Intended rate exceeds max rate, setting to max\n", __FUNCTION__));
1803 hz = host->f_max;
1804 }
1805 ios->clock = hz;
1806 host->ops->set_ios(host, ios);
1807 DHD_ERROR(("%s: After change: sd clock rate is %u\n", __FUNCTION__, ios->clock));
1808 mmc_host_clk_release(host);
1809 #endif
1810 }
1811
1812 void
sdmmc_set_clock_divisor(sdioh_info_t * sd,uint sd_div)1813 sdmmc_set_clock_divisor(sdioh_info_t *sd, uint sd_div)
1814 {
1815 uint hz;
1816 uint old_div = sdmmc_get_clock_rate(sd);
1817 if (old_div == sd_div) {
1818 return;
1819 }
1820
1821 hz = sd->sd_clk_rate / sd_div;
1822 sdmmc_set_clock_rate(sd, hz);
1823 }
1824