.. | .. |
---|
1 | | -/* SPDX-License-Identifier: GPL-2.0 */ |
---|
2 | 1 | /* |
---|
3 | 2 | * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel |
---|
4 | 3 | * |
---|
5 | | - * Copyright (C) 1999-2019, Broadcom Corporation |
---|
6 | | - * |
---|
| 4 | + * Portions of this code are copyright (c) 2022 Cypress Semiconductor Corporation |
---|
| 5 | + * |
---|
| 6 | + * Copyright (C) 1999-2017, Broadcom Corporation |
---|
| 7 | + * |
---|
7 | 8 | * Unless you and Broadcom execute a separate written software license |
---|
8 | 9 | * agreement governing use of this software, this software is licensed to you |
---|
9 | 10 | * under the terms of the GNU General Public License version 2 (the "GPL"), |
---|
10 | 11 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
---|
11 | 12 | * following added to such license: |
---|
12 | | - * |
---|
| 13 | + * |
---|
13 | 14 | * As a special exception, the copyright holders of this software give you |
---|
14 | 15 | * permission to link this software with independent modules, and to copy and |
---|
15 | 16 | * distribute the resulting executable under terms of your choice, provided that |
---|
.. | .. |
---|
17 | 18 | * the license of that module. An independent module is a module which is not |
---|
18 | 19 | * derived from this software. The special exception does not apply to any |
---|
19 | 20 | * modifications of the software. |
---|
20 | | - * |
---|
| 21 | + * |
---|
21 | 22 | * Notwithstanding the above, under no circumstances may you combine this |
---|
22 | 23 | * software in any way with any other Broadcom software provided under a license |
---|
23 | 24 | * other than the GPL, without Broadcom's express prior written consent. |
---|
.. | .. |
---|
25 | 26 | * |
---|
26 | 27 | * <<Broadcom-WL-IPTag/Proprietary,Open:>> |
---|
27 | 28 | * |
---|
28 | | - * $Id: bcmsdh_sdmmc.c 712404 2019-03-20 06:20:25Z $ |
---|
| 29 | + * $Id: bcmsdh_sdmmc.c 690631 2017-03-17 04:27:33Z $ |
---|
29 | 30 | */ |
---|
30 | 31 | #include <typedefs.h> |
---|
31 | 32 | |
---|
.. | .. |
---|
39 | 40 | #include <sdiovar.h> /* ioctl/iovars */ |
---|
40 | 41 | |
---|
41 | 42 | #include <linux/mmc/core.h> |
---|
| 43 | +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 8)) |
---|
| 44 | +#include <drivers/mmc/core/host.h> |
---|
| 45 | +#else |
---|
42 | 46 | #include <linux/mmc/host.h> |
---|
| 47 | +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 0)) */ |
---|
43 | 48 | #include <linux/mmc/card.h> |
---|
44 | 49 | #include <linux/mmc/sdio_func.h> |
---|
45 | 50 | #include <linux/mmc/sdio_ids.h> |
---|
46 | 51 | |
---|
47 | 52 | #include <dngl_stats.h> |
---|
48 | 53 | #include <dhd.h> |
---|
| 54 | +#include <dhd_dbg.h> |
---|
49 | 55 | |
---|
50 | 56 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) |
---|
51 | 57 | #include <linux/suspend.h> |
---|
52 | 58 | extern volatile bool dhd_mmc_suspend; |
---|
53 | | -#endif |
---|
| 59 | +#endif // endif |
---|
54 | 60 | #include "bcmsdh_sdmmc.h" |
---|
| 61 | + |
---|
| 62 | +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 0)) || (LINUX_VERSION_CODE >= \ |
---|
| 63 | + KERNEL_VERSION(4, 4, 0)) |
---|
| 64 | +static inline void |
---|
| 65 | +mmc_host_clk_hold(struct mmc_host *host) |
---|
| 66 | +{ |
---|
| 67 | + BCM_REFERENCE(host); |
---|
| 68 | + return; |
---|
| 69 | +} |
---|
| 70 | + |
---|
| 71 | +static inline void |
---|
| 72 | +mmc_host_clk_release(struct mmc_host *host) |
---|
| 73 | +{ |
---|
| 74 | + BCM_REFERENCE(host); |
---|
| 75 | + return; |
---|
| 76 | +} |
---|
| 77 | + |
---|
| 78 | +static inline unsigned int |
---|
| 79 | +mmc_host_clk_rate(struct mmc_host *host) |
---|
| 80 | +{ |
---|
| 81 | + return host->ios.clock; |
---|
| 82 | +} |
---|
| 83 | +#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 0) */ |
---|
55 | 84 | |
---|
56 | 85 | #ifndef BCMSDH_MODULE |
---|
57 | 86 | extern int sdio_function_init(void); |
---|
.. | .. |
---|
63 | 92 | static void IRQHandlerF2(struct sdio_func *func); |
---|
64 | 93 | #endif /* !defined(OOB_INTR_ONLY) */ |
---|
65 | 94 | static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); |
---|
66 | | - |
---|
67 | | -#if defined(ANDROID_SDIO_RESET) |
---|
68 | | -extern int sdio_reset_comm(struct mmc_card *card); |
---|
69 | | -#else |
---|
| 95 | +#if defined(OEM_ANDROID) && !defined(CONFIG_SOC_S5E5515) |
---|
70 | 96 | static int sdio_reset_comm(struct mmc_card *card) |
---|
71 | 97 | { |
---|
72 | 98 | return 0; |
---|
73 | 99 | } |
---|
74 | | -#endif /* ANDROID_SDIO_RESET */ |
---|
| 100 | +#else |
---|
| 101 | +extern int sdio_reset_comm(struct mmc_card *card); |
---|
| 102 | +#endif /* OEM_ANDROID */ |
---|
75 | 103 | |
---|
76 | 104 | #define DEFAULT_SDIO_F2_BLKSIZE 512 |
---|
77 | 105 | #ifndef CUSTOM_SDIO_F2_BLKSIZE |
---|
78 | 106 | #define CUSTOM_SDIO_F2_BLKSIZE DEFAULT_SDIO_F2_BLKSIZE |
---|
79 | | -#endif |
---|
| 107 | +#endif // endif |
---|
80 | 108 | |
---|
81 | 109 | #define DEFAULT_SDIO_F1_BLKSIZE 64 |
---|
82 | 110 | #ifndef CUSTOM_SDIO_F1_BLKSIZE |
---|
83 | 111 | #define CUSTOM_SDIO_F1_BLKSIZE DEFAULT_SDIO_F1_BLKSIZE |
---|
84 | | -#endif |
---|
| 112 | +#endif // endif |
---|
85 | 113 | |
---|
86 | 114 | #define MAX_IO_RW_EXTENDED_BLK 511 |
---|
87 | 115 | |
---|
88 | 116 | uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */ |
---|
89 | 117 | uint sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE; |
---|
90 | 118 | uint sd_f1_blocksize = CUSTOM_SDIO_F1_BLKSIZE; |
---|
| 119 | + |
---|
| 120 | +#if defined(BT_OVER_SDIO) |
---|
| 121 | +uint sd_f3_blocksize = 64; |
---|
| 122 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
| 123 | + |
---|
91 | 124 | uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */ |
---|
92 | 125 | |
---|
93 | 126 | uint sd_power = 1; /* Default to SD Slot powered ON */ |
---|
94 | 127 | uint sd_clock = 1; /* Default to SD Clock turned ON */ |
---|
95 | 128 | uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */ |
---|
96 | | -uint sd_msglevel = 0x01; |
---|
| 129 | +uint sd_msglevel = SDH_ERROR_VAL; |
---|
97 | 130 | uint sd_use_dma = TRUE; |
---|
| 131 | + |
---|
| 132 | + |
---|
98 | 133 | |
---|
99 | 134 | #ifndef CUSTOM_RXCHAIN |
---|
100 | 135 | #define CUSTOM_RXCHAIN 0 |
---|
101 | | -#endif |
---|
102 | | - |
---|
103 | | -/* Enables host to dongle glomming. Also increases the |
---|
104 | | - * dma buffer size. This will increase the rx throughput |
---|
105 | | - * as there will be lesser CMD53 transactions |
---|
106 | | - */ |
---|
107 | | -#ifdef BCMSDIOH_TXGLOM |
---|
108 | | -uint sd_txglom = 1; |
---|
109 | | -module_param(sd_txglom, uint, 0664); |
---|
110 | | -#endif /* BCMSDIOH_TXGLOM */ |
---|
| 136 | +#endif // endif |
---|
111 | 137 | |
---|
112 | 138 | DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait); |
---|
113 | 139 | DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait); |
---|
114 | 140 | DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait); |
---|
115 | 141 | DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait); |
---|
116 | 142 | |
---|
| 143 | +#if !defined(ARCH_DMA_MINALIGN) |
---|
| 144 | +#define ARCH_DMA_MINALIGN 128 |
---|
| 145 | +#endif /* !defined(ARCH_DMA_MINALIGN) */ |
---|
117 | 146 | #define DMA_ALIGN_MASK 0x03 |
---|
118 | 147 | #define MMC_SDIO_ABORT_RETRY_LIMIT 5 |
---|
119 | 148 | |
---|
120 | 149 | int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data); |
---|
| 150 | + |
---|
| 151 | +#if defined(BT_OVER_SDIO) |
---|
| 152 | +extern |
---|
| 153 | +void sdioh_sdmmc_card_enable_func_f3(sdioh_info_t *sd, struct sdio_func *func) |
---|
| 154 | +{ |
---|
| 155 | + sd->func[3] = func; |
---|
| 156 | + sd_info(("%s sd->func[3] %p\n", __FUNCTION__, sd->func[3])); |
---|
| 157 | +} |
---|
| 158 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
| 159 | + |
---|
| 160 | +void sdmmc_set_clock_rate(sdioh_info_t *sd, uint hz); |
---|
| 161 | +uint sdmmc_get_clock_rate(sdioh_info_t *sd); |
---|
| 162 | +void sdmmc_set_clock_divisor(sdioh_info_t *sd, uint sd_div); |
---|
121 | 163 | |
---|
122 | 164 | static int |
---|
123 | 165 | sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd) |
---|
.. | .. |
---|
182 | 224 | sd->func[0] = &sd->fake_func0; |
---|
183 | 225 | sd->func[1] = func->card->sdio_func[0]; |
---|
184 | 226 | sd->func[2] = func->card->sdio_func[1]; |
---|
| 227 | + |
---|
| 228 | +#if defined(BT_OVER_SDIO) |
---|
| 229 | + sd->func[3] = NULL; |
---|
| 230 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
| 231 | + |
---|
| 232 | + |
---|
185 | 233 | sd->num_funcs = 2; |
---|
186 | 234 | sd->sd_blockmode = TRUE; |
---|
187 | 235 | sd->use_client_ints = TRUE; |
---|
.. | .. |
---|
212 | 260 | goto fail; |
---|
213 | 261 | } |
---|
214 | 262 | |
---|
| 263 | + sd->sd_clk_rate = sdmmc_get_clock_rate(sd); |
---|
| 264 | + DHD_ERROR(("%s: sd clock rate = %u\n", __FUNCTION__, sd->sd_clk_rate)); |
---|
| 265 | + |
---|
215 | 266 | sdioh_sdmmc_card_enablefuncs(sd); |
---|
216 | 267 | |
---|
217 | 268 | sd_trace(("%s: Done\n", __FUNCTION__)); |
---|
.. | .. |
---|
221 | 272 | MFREE(sd->osh, sd, sizeof(sdioh_info_t)); |
---|
222 | 273 | return NULL; |
---|
223 | 274 | } |
---|
224 | | - |
---|
225 | 275 | |
---|
226 | 276 | extern SDIOH_API_RC |
---|
227 | 277 | sdioh_detach(osl_t *osh, sdioh_info_t *sd) |
---|
.. | .. |
---|
275 | 325 | /* Enable F1 and F2 interrupts, clear master enable */ |
---|
276 | 326 | reg &= ~INTR_CTL_MASTER_EN; |
---|
277 | 327 | reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); |
---|
| 328 | +#if defined(BT_OVER_SDIO) |
---|
| 329 | + reg |= (INTR_CTL_FUNC3_EN); |
---|
| 330 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
278 | 331 | sdio_writeb(sd->func[0], reg, SDIOD_CCCR_INTEN, &err); |
---|
279 | 332 | sdio_release_host(sd->func[0]); |
---|
280 | 333 | |
---|
.. | .. |
---|
305 | 358 | return SDIOH_API_RC_FAIL; |
---|
306 | 359 | } |
---|
307 | 360 | reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); |
---|
| 361 | +#if defined(BT_OVER_SDIO) |
---|
| 362 | + reg &= ~INTR_CTL_FUNC3_EN; |
---|
| 363 | +#endif // endif |
---|
308 | 364 | /* Disable master interrupt with the last function interrupt */ |
---|
309 | 365 | if (!(reg & 0xFE)) |
---|
310 | 366 | reg = 0; |
---|
.. | .. |
---|
397 | 453 | { |
---|
398 | 454 | return (0); |
---|
399 | 455 | } |
---|
400 | | -#endif |
---|
| 456 | +#endif // endif |
---|
401 | 457 | |
---|
402 | 458 | uint |
---|
403 | 459 | sdioh_query_iofnum(sdioh_info_t *sd) |
---|
.. | .. |
---|
426 | 482 | }; |
---|
427 | 483 | |
---|
428 | 484 | const bcm_iovar_t sdioh_iovars[] = { |
---|
429 | | - {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, |
---|
430 | | - {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 }, |
---|
431 | | - {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ |
---|
432 | | - {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, |
---|
433 | | - {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, |
---|
434 | | - {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, |
---|
435 | | - {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, |
---|
436 | | - {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, |
---|
437 | | - {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, |
---|
438 | | - {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, |
---|
439 | | - {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, |
---|
440 | | - {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, |
---|
441 | | - {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, |
---|
442 | | - {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 }, |
---|
443 | | - {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 }, |
---|
444 | | - {NULL, 0, 0, 0, 0 } |
---|
| 485 | + {"sd_msglevel", IOV_MSGLEVEL, 0, 0, IOVT_UINT32, 0 }, |
---|
| 486 | + {"sd_blockmode", IOV_BLOCKMODE, 0, 0, IOVT_BOOL, 0 }, |
---|
| 487 | + {"sd_blocksize", IOV_BLOCKSIZE, 0, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ |
---|
| 488 | + {"sd_dma", IOV_DMA, 0, 0, IOVT_BOOL, 0 }, |
---|
| 489 | + {"sd_ints", IOV_USEINTS, 0, 0, IOVT_BOOL, 0 }, |
---|
| 490 | + {"sd_numints", IOV_NUMINTS, 0, 0, IOVT_UINT32, 0 }, |
---|
| 491 | + {"sd_numlocalints", IOV_NUMLOCALINTS, 0, 0, IOVT_UINT32, 0 }, |
---|
| 492 | + {"sd_divisor", IOV_DIVISOR, 0, 0, IOVT_UINT32, 0 }, |
---|
| 493 | + {"sd_power", IOV_POWER, 0, 0, IOVT_UINT32, 0 }, |
---|
| 494 | + {"sd_clock", IOV_CLOCK, 0, 0, IOVT_UINT32, 0 }, |
---|
| 495 | + {"sd_mode", IOV_SDMODE, 0, 0, IOVT_UINT32, 100}, |
---|
| 496 | + {"sd_highspeed", IOV_HISPEED, 0, 0, IOVT_UINT32, 0 }, |
---|
| 497 | + {"sd_rxchain", IOV_RXCHAIN, 0, 0, IOVT_BOOL, 0 }, |
---|
| 498 | + {NULL, 0, 0, 0, 0, 0 } |
---|
445 | 499 | }; |
---|
446 | 500 | |
---|
447 | 501 | int |
---|
.. | .. |
---|
549 | 603 | /* Now set it */ |
---|
550 | 604 | si->client_block_size[func] = blksize; |
---|
551 | 605 | |
---|
| 606 | +#ifdef USE_DYNAMIC_F2_BLKSIZE |
---|
| 607 | + if (si->func[func] == NULL) { |
---|
| 608 | + sd_err(("%s: SDIO Device not present\n", __FUNCTION__)); |
---|
| 609 | + bcmerror = BCME_NORESOURCE; |
---|
| 610 | + break; |
---|
| 611 | + } |
---|
| 612 | + sdio_claim_host(si->func[func]); |
---|
| 613 | + bcmerror = sdio_set_block_size(si->func[func], blksize); |
---|
| 614 | + if (bcmerror) |
---|
| 615 | + sd_err(("%s: Failed to set F%d blocksize to %d(%d)\n", |
---|
| 616 | + __FUNCTION__, func, blksize, bcmerror)); |
---|
| 617 | + sdio_release_host(si->func[func]); |
---|
| 618 | +#endif /* USE_DYNAMIC_F2_BLKSIZE */ |
---|
552 | 619 | break; |
---|
553 | 620 | } |
---|
554 | 621 | |
---|
.. | .. |
---|
586 | 653 | break; |
---|
587 | 654 | |
---|
588 | 655 | case IOV_SVAL(IOV_DIVISOR): |
---|
589 | | - sd_divisor = int_val; |
---|
| 656 | + /* set the clock to divisor, if value is non-zero & power of 2 */ |
---|
| 657 | + if (int_val && !(int_val & (int_val - 1))) { |
---|
| 658 | + sd_divisor = int_val; |
---|
| 659 | + sdmmc_set_clock_divisor(si, sd_divisor); |
---|
| 660 | + } else { |
---|
| 661 | + DHD_ERROR(("%s: Invalid sd_divisor value, should be power of 2!\n", |
---|
| 662 | + __FUNCTION__)); |
---|
| 663 | + } |
---|
590 | 664 | break; |
---|
591 | 665 | |
---|
592 | 666 | case IOV_GVAL(IOV_POWER): |
---|
.. | .. |
---|
634 | 708 | int_val = (int32)0; |
---|
635 | 709 | bcopy(&int_val, arg, val_size); |
---|
636 | 710 | break; |
---|
637 | | - |
---|
638 | | - case IOV_GVAL(IOV_HOSTREG): |
---|
639 | | - { |
---|
640 | | - sdreg_t *sd_ptr = (sdreg_t *)params; |
---|
641 | | - |
---|
642 | | - if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { |
---|
643 | | - sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); |
---|
644 | | - bcmerror = BCME_BADARG; |
---|
645 | | - break; |
---|
646 | | - } |
---|
647 | | - |
---|
648 | | - sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__, |
---|
649 | | - (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), |
---|
650 | | - sd_ptr->offset)); |
---|
651 | | - if (sd_ptr->offset & 1) |
---|
652 | | - int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */ |
---|
653 | | - else if (sd_ptr->offset & 2) |
---|
654 | | - int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */ |
---|
655 | | - else |
---|
656 | | - int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */ |
---|
657 | | - |
---|
658 | | - bcopy(&int_val, arg, sizeof(int_val)); |
---|
659 | | - break; |
---|
660 | | - } |
---|
661 | | - |
---|
662 | | - case IOV_SVAL(IOV_HOSTREG): |
---|
663 | | - { |
---|
664 | | - sdreg_t *sd_ptr = (sdreg_t *)params; |
---|
665 | | - |
---|
666 | | - if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { |
---|
667 | | - sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); |
---|
668 | | - bcmerror = BCME_BADARG; |
---|
669 | | - break; |
---|
670 | | - } |
---|
671 | | - |
---|
672 | | - sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value, |
---|
673 | | - (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), |
---|
674 | | - sd_ptr->offset)); |
---|
675 | | - break; |
---|
676 | | - } |
---|
677 | | - |
---|
678 | | - case IOV_GVAL(IOV_DEVREG): |
---|
679 | | - { |
---|
680 | | - sdreg_t *sd_ptr = (sdreg_t *)params; |
---|
681 | | - uint8 data = 0; |
---|
682 | | - |
---|
683 | | - if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { |
---|
684 | | - bcmerror = BCME_SDIO_ERROR; |
---|
685 | | - break; |
---|
686 | | - } |
---|
687 | | - |
---|
688 | | - int_val = (int)data; |
---|
689 | | - bcopy(&int_val, arg, sizeof(int_val)); |
---|
690 | | - break; |
---|
691 | | - } |
---|
692 | | - |
---|
693 | | - case IOV_SVAL(IOV_DEVREG): |
---|
694 | | - { |
---|
695 | | - sdreg_t *sd_ptr = (sdreg_t *)params; |
---|
696 | | - uint8 data = (uint8)sd_ptr->value; |
---|
697 | | - |
---|
698 | | - if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { |
---|
699 | | - bcmerror = BCME_SDIO_ERROR; |
---|
700 | | - break; |
---|
701 | | - } |
---|
702 | | - break; |
---|
703 | | - } |
---|
704 | | - |
---|
705 | 711 | default: |
---|
706 | 712 | bcmerror = BCME_UNSUPPORTED; |
---|
707 | 713 | break; |
---|
.. | .. |
---|
806 | 812 | int err_ret = 0; |
---|
807 | 813 | #if defined(MMC_SDIO_ABORT) |
---|
808 | 814 | int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; |
---|
809 | | -#endif |
---|
| 815 | +#endif // endif |
---|
810 | 816 | |
---|
811 | 817 | sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr)); |
---|
812 | 818 | |
---|
.. | .. |
---|
818 | 824 | * as a special case. |
---|
819 | 825 | */ |
---|
820 | 826 | if (regaddr == SDIOD_CCCR_IOEN) { |
---|
| 827 | +#if defined(BT_OVER_SDIO) |
---|
| 828 | + do { |
---|
| 829 | + if (sd->func[3]) { |
---|
| 830 | + sd_info(("bcmsdh_sdmmc F3: *byte 0x%x\n", *byte)); |
---|
| 831 | + |
---|
| 832 | + if (*byte & SDIO_FUNC_ENABLE_3) { |
---|
| 833 | + sdio_claim_host(sd->func[3]); |
---|
| 834 | + |
---|
| 835 | + /* Set Function 3 Block Size */ |
---|
| 836 | + err_ret = sdio_set_block_size(sd->func[3], |
---|
| 837 | + sd_f3_blocksize); |
---|
| 838 | + if (err_ret) { |
---|
| 839 | + sd_err(("F3 blocksize set err%d\n", |
---|
| 840 | + err_ret)); |
---|
| 841 | + } |
---|
| 842 | + |
---|
| 843 | + /* Enable Function 3 */ |
---|
| 844 | + sd_info(("bcmsdh_sdmmc F3: enable F3 fn %p\n", |
---|
| 845 | + sd->func[3])); |
---|
| 846 | + err_ret = sdio_enable_func(sd->func[3]); |
---|
| 847 | + if (err_ret) { |
---|
| 848 | + sd_err(("bcmsdh_sdmmc: enable F3 err:%d\n", |
---|
| 849 | + err_ret)); |
---|
| 850 | + } |
---|
| 851 | + |
---|
| 852 | + sdio_release_host(sd->func[3]); |
---|
| 853 | + |
---|
| 854 | + break; |
---|
| 855 | + } else if (*byte & SDIO_FUNC_DISABLE_3) { |
---|
| 856 | + sdio_claim_host(sd->func[3]); |
---|
| 857 | + |
---|
| 858 | + /* Disable Function 3 */ |
---|
| 859 | + sd_info(("bcmsdh_sdmmc F3: disable F3 fn %p\n", |
---|
| 860 | + sd->func[3])); |
---|
| 861 | + err_ret = sdio_disable_func(sd->func[3]); |
---|
| 862 | + if (err_ret) { |
---|
| 863 | + sd_err(("bcmsdh_sdmmc: Disable F3 err:%d\n", |
---|
| 864 | + err_ret)); |
---|
| 865 | + } |
---|
| 866 | + sdio_release_host(sd->func[3]); |
---|
| 867 | + sd->func[3] = NULL; |
---|
| 868 | + |
---|
| 869 | + break; |
---|
| 870 | + } |
---|
| 871 | + } |
---|
| 872 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
821 | 873 | if (sd->func[2]) { |
---|
822 | 874 | sdio_claim_host(sd->func[2]); |
---|
823 | 875 | if (*byte & SDIO_FUNC_ENABLE_2) { |
---|
.. | .. |
---|
837 | 889 | } |
---|
838 | 890 | sdio_release_host(sd->func[2]); |
---|
839 | 891 | } |
---|
840 | | - } |
---|
| 892 | +#if defined(BT_OVER_SDIO) |
---|
| 893 | + } while (0); |
---|
| 894 | +#endif /* defined (BT_OVER_SDIO) */ |
---|
| 895 | + } |
---|
841 | 896 | #if defined(MMC_SDIO_ABORT) |
---|
842 | 897 | /* to allow abort command through F1 */ |
---|
843 | 898 | else if (regaddr == SDIOD_CCCR_IOABORT) { |
---|
.. | .. |
---|
908 | 963 | int err_ret = SDIOH_API_RC_FAIL; |
---|
909 | 964 | #if defined(MMC_SDIO_ABORT) |
---|
910 | 965 | int sdio_abort_retry = MMC_SDIO_ABORT_RETRY_LIMIT; |
---|
911 | | -#endif |
---|
| 966 | +#endif // endif |
---|
912 | 967 | |
---|
913 | 968 | if (func == 0) { |
---|
914 | 969 | sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__)); |
---|
.. | .. |
---|
973 | 1028 | return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); |
---|
974 | 1029 | } |
---|
975 | 1030 | |
---|
| 1031 | +#ifdef BCMSDIOH_TXGLOM |
---|
976 | 1032 | static SDIOH_API_RC |
---|
977 | 1033 | sdioh_request_packet_chain(sdioh_info_t *sd, uint fix_inc, uint write, uint func, |
---|
978 | 1034 | uint addr, void *pkt) |
---|
.. | .. |
---|
1031 | 1087 | * a restriction on max tx/glom count (based on host->max_segs). |
---|
1032 | 1088 | */ |
---|
1033 | 1089 | if (sg_count >= ARRAYSIZE(sd->sg_list)) { |
---|
1034 | | - sd_err(("%s: sg list entries exceed limit\n", __FUNCTION__)); |
---|
| 1090 | + sd_err(("%s: sg list entries(%u) exceed limit(%u)," |
---|
| 1091 | + " sd blk_size=%u\n", |
---|
| 1092 | + __FUNCTION__, sg_count, ARRAYSIZE(sd->sg_list), blk_size)); |
---|
1035 | 1093 | return (SDIOH_API_RC_FAIL); |
---|
1036 | 1094 | } |
---|
1037 | 1095 | pdata += pkt_offset; |
---|
.. | .. |
---|
1043 | 1101 | * DMA descriptor, use multiple sg buffers when xfer_size is bigger than |
---|
1044 | 1102 | * max_seg_size |
---|
1045 | 1103 | */ |
---|
1046 | | - if (sg_data_size > host->max_seg_size) |
---|
| 1104 | + if (sg_data_size > host->max_seg_size) { |
---|
1047 | 1105 | sg_data_size = host->max_seg_size; |
---|
| 1106 | + } |
---|
1048 | 1107 | sg_set_buf(&sd->sg_list[sg_count++], pdata, sg_data_size); |
---|
1049 | 1108 | |
---|
1050 | 1109 | ttl_len += sg_data_size; |
---|
.. | .. |
---|
1095 | 1154 | sd_trace(("%s: Exit\n", __FUNCTION__)); |
---|
1096 | 1155 | return SDIOH_API_RC_SUCCESS; |
---|
1097 | 1156 | } |
---|
| 1157 | +#endif /* BCMSDIOH_TXGLOM */ |
---|
1098 | 1158 | |
---|
1099 | 1159 | static SDIOH_API_RC |
---|
1100 | 1160 | sdioh_buffer_tofrom_bus(sdioh_info_t *sd, uint fix_inc, uint write, uint func, |
---|
.. | .. |
---|
1139 | 1199 | return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); |
---|
1140 | 1200 | } |
---|
1141 | 1201 | |
---|
1142 | | -static SDIOH_API_RC |
---|
1143 | | -sdioh_buffer_tofrom_bus_ext(sdioh_info_t *sd, uint fix_inc, uint write, uint func, |
---|
1144 | | - uint addr, void *pkt) |
---|
1145 | | -{ |
---|
1146 | | - SDIOH_API_RC status; |
---|
1147 | | - void *pnext; |
---|
1148 | | - int blk_size; |
---|
1149 | | - uint ttl_len = 0, total_len=0; |
---|
1150 | | - uint8 *buffer = NULL; |
---|
1151 | | - |
---|
1152 | | - sd_trace(("%s: Enter\n", __FUNCTION__)); |
---|
1153 | | - blk_size = sd->client_block_size[func]; |
---|
1154 | | - |
---|
1155 | | - /* at least 4 bytes alignment of skb buff is guaranteed */ |
---|
1156 | | - for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) |
---|
1157 | | - ttl_len += PKTLEN(sd->osh, pnext); |
---|
1158 | | - /* Packet alignment */ |
---|
1159 | | - if (!write || ttl_len < 32) |
---|
1160 | | - ttl_len = (ttl_len + 3) & 0xFFFFFFFC; |
---|
1161 | | - else if ((ttl_len > blk_size) && (ttl_len % blk_size)) |
---|
1162 | | - { |
---|
1163 | | - if (func == SDIO_FUNC_2) |
---|
1164 | | - { |
---|
1165 | | - sd_info(("%s: [%s] dhd_sdio must align %d bytes" |
---|
1166 | | - " packet larger than a %d bytes blk size by a blk size\n", |
---|
1167 | | - __FUNCTION__, write ? "W" : "R", ttl_len, blk_size)); |
---|
1168 | | - } |
---|
1169 | | - ttl_len += blk_size - (ttl_len % blk_size); |
---|
1170 | | - } |
---|
1171 | | - buffer = (uint8 *)MALLOC(sd->osh, ttl_len); |
---|
1172 | | - if(!buffer) |
---|
1173 | | - return SDIOH_API_RC_FAIL; |
---|
1174 | | - |
---|
1175 | | - pnext = pkt; |
---|
1176 | | - while (pnext != NULL) |
---|
1177 | | - { |
---|
1178 | | - bcopy(PKTDATA(sd->osh, pnext), buffer + total_len, PKTLEN(sd->osh, pnext)); |
---|
1179 | | - total_len += PKTLEN(sd->osh, pnext); |
---|
1180 | | - pnext = PKTNEXT(sd->osh, pnext); |
---|
1181 | | - } |
---|
1182 | | - |
---|
1183 | | - status = sdioh_buffer_tofrom_bus(sd, fix_inc, write, func, addr, buffer, total_len); |
---|
1184 | | - |
---|
1185 | | - if (buffer) |
---|
1186 | | - MFREE(sd->osh, buffer, ttl_len); |
---|
1187 | | - |
---|
1188 | | - return status; |
---|
1189 | | -} |
---|
1190 | | - |
---|
1191 | 1202 | /* |
---|
1192 | 1203 | * This function takes a buffer or packet, and fixes everything up so that in the |
---|
1193 | 1204 | * end, a DMA-able packet is created. |
---|
.. | .. |
---|
1211 | 1222 | DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); |
---|
1212 | 1223 | |
---|
1213 | 1224 | if (pkt) { |
---|
| 1225 | +#ifdef BCMSDIOH_TXGLOM |
---|
| 1226 | + /* packet chain, only used for tx/rx glom, all packets length |
---|
| 1227 | + * are aligned, total length is a block multiple |
---|
| 1228 | + */ |
---|
1214 | 1229 | if (PKTNEXT(sd->osh, pkt)) |
---|
1215 | | - { |
---|
1216 | | - /* packet chain, only used for tx/rx glom, all packets length |
---|
1217 | | - * are aligned, total length is a block multiple |
---|
1218 | | - */ |
---|
1219 | | - if (sd->func[func]->card->host->max_segs > 1 /* Multi-desc mode for scatter-gather DMA */ |
---|
1220 | | - && (!write || sd_txglom) ) /* tx will check sd_txglom */ |
---|
1221 | | - return sdioh_request_packet_chain(sd, fix_inc, write, func, addr, pkt); |
---|
1222 | | - if (write && sd_txglom) /* Copy mode only in Tx */ |
---|
1223 | | - return sdioh_buffer_tofrom_bus_ext(sd, fix_inc, write, func, addr, pkt); |
---|
1224 | | - } |
---|
| 1230 | + return sdioh_request_packet_chain(sd, fix_inc, write, func, addr, pkt); |
---|
| 1231 | +#endif /* BCMSDIOH_TXGLOM */ |
---|
1225 | 1232 | /* non-glom mode, ignore the buffer parameter and use the packet pointer |
---|
1226 | 1233 | * (this shouldn't happen) |
---|
1227 | 1234 | */ |
---|
.. | .. |
---|
1232 | 1239 | ASSERT(buffer); |
---|
1233 | 1240 | |
---|
1234 | 1241 | /* buffer and length are aligned, use it directly so we can avoid memory copy */ |
---|
1235 | | - if (((ulong)buffer & DMA_ALIGN_MASK) == 0 && (buf_len & DMA_ALIGN_MASK) == 0) |
---|
| 1242 | + if (((ulong)buffer & (ARCH_DMA_MINALIGN - 1)) == 0 && (buf_len & DMA_ALIGN_MASK) == 0) |
---|
1236 | 1243 | return sdioh_buffer_tofrom_bus(sd, fix_inc, write, func, addr, buffer, buf_len); |
---|
1237 | 1244 | |
---|
1238 | 1245 | sd_trace(("%s: [%d] doing memory copy buf=%p, len=%d\n", |
---|
.. | .. |
---|
1315 | 1322 | sd_data(("%s: byte read data=0x%02x\n", |
---|
1316 | 1323 | __FUNCTION__, *data)); |
---|
1317 | 1324 | } else { |
---|
1318 | | - sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize); |
---|
| 1325 | + if (sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize)) { |
---|
| 1326 | + return BCME_SDIO_ERROR; |
---|
| 1327 | + } |
---|
1319 | 1328 | if (regsize == 2) |
---|
1320 | 1329 | *data &= 0xffff; |
---|
1321 | 1330 | |
---|
.. | .. |
---|
1386 | 1395 | } |
---|
1387 | 1396 | #endif /* NOTUSED */ |
---|
1388 | 1397 | |
---|
| 1398 | +#if defined(OEM_ANDROID) || defined(OEM_EMBEDDED_LINUX) |
---|
| 1399 | +static int sdio_sw_reset(sdioh_info_t *sd) |
---|
| 1400 | +{ |
---|
| 1401 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) |
---|
| 1402 | + struct mmc_host *host = sd->func[0]->card->host; |
---|
| 1403 | +#endif |
---|
| 1404 | + int err = 0; |
---|
| 1405 | + |
---|
| 1406 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) |
---|
| 1407 | + printf("%s: Enter\n", __FUNCTION__); |
---|
| 1408 | + sdio_claim_host(sd->func[0]); |
---|
| 1409 | + err = mmc_sw_reset(host); |
---|
| 1410 | + sdio_release_host(sd->func[0]); |
---|
| 1411 | +#else |
---|
| 1412 | + err = sdio_reset_comm(sd->func[0]->card); |
---|
| 1413 | +#endif |
---|
| 1414 | + |
---|
| 1415 | + if (err) |
---|
| 1416 | + sd_err(("%s Failed, error = %d\n", __FUNCTION__, err)); |
---|
| 1417 | + |
---|
| 1418 | + return err; |
---|
| 1419 | +} |
---|
| 1420 | +#endif |
---|
| 1421 | + |
---|
1389 | 1422 | int |
---|
1390 | 1423 | sdioh_start(sdioh_info_t *sd, int stage) |
---|
1391 | 1424 | { |
---|
| 1425 | +#if defined(OEM_ANDROID) || defined(OEM_EMBEDDED_LINUX) |
---|
1392 | 1426 | int ret; |
---|
1393 | 1427 | |
---|
1394 | 1428 | if (!sd) { |
---|
.. | .. |
---|
1401 | 1435 | sdio access will come in way |
---|
1402 | 1436 | */ |
---|
1403 | 1437 | if (sd->func[0]) { |
---|
1404 | | - if (stage == 0) { |
---|
| 1438 | + if (stage == 0) { |
---|
1405 | 1439 | /* Since the power to the chip is killed, we will have |
---|
1406 | 1440 | re enumerate the device again. Set the block size |
---|
1407 | 1441 | and enable the fucntion 1 for in preparation for |
---|
1408 | 1442 | downloading the code |
---|
1409 | 1443 | */ |
---|
1410 | | - /* sdio_reset_comm() For Android kernel only |
---|
1411 | | - has been fixed in latest kernel/msm.git for Linux |
---|
| 1444 | + /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux |
---|
1412 | 1445 | 2.6.27. The implementation prior to that is buggy, and needs broadcom's |
---|
1413 | 1446 | patch for it |
---|
1414 | 1447 | */ |
---|
1415 | | - if ((ret = sdio_reset_comm(sd->func[0]->card))) { |
---|
| 1448 | + if ((ret = sdio_sw_reset(sd))) { |
---|
1416 | 1449 | sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); |
---|
1417 | 1450 | return ret; |
---|
1418 | 1451 | } |
---|
.. | .. |
---|
1465 | 1498 | #else /* defined(OOB_INTR_ONLY) */ |
---|
1466 | 1499 | #if defined(HW_OOB) |
---|
1467 | 1500 | sdioh_enable_func_intr(sd); |
---|
1468 | | -#endif |
---|
| 1501 | +#endif // endif |
---|
1469 | 1502 | bcmsdh_oob_intr_set(sd->bcmsdh, TRUE); |
---|
1470 | 1503 | #endif /* !defined(OOB_INTR_ONLY) */ |
---|
1471 | 1504 | } |
---|
1472 | 1505 | } |
---|
1473 | 1506 | else |
---|
1474 | 1507 | sd_err(("%s Failed\n", __FUNCTION__)); |
---|
| 1508 | +#endif /* defined(OEM_ANDROID) || defined(OEM_EMBEDDED_LINUX) */ |
---|
1475 | 1509 | |
---|
1476 | 1510 | return (0); |
---|
1477 | 1511 | } |
---|
.. | .. |
---|
1479 | 1513 | int |
---|
1480 | 1514 | sdioh_stop(sdioh_info_t *sd) |
---|
1481 | 1515 | { |
---|
| 1516 | +#if defined(OEM_ANDROID) || defined(OEM_EMBEDDED_LINUX) |
---|
1482 | 1517 | /* MSM7201A Android sdio stack has bug with interrupt |
---|
1483 | 1518 | So internaly within SDIO stack they are polling |
---|
1484 | 1519 | which cause issue when device is turned off. So |
---|
.. | .. |
---|
1496 | 1531 | #else /* defined(OOB_INTR_ONLY) */ |
---|
1497 | 1532 | #if defined(HW_OOB) |
---|
1498 | 1533 | sdioh_disable_func_intr(sd); |
---|
1499 | | -#endif |
---|
| 1534 | +#endif // endif |
---|
1500 | 1535 | bcmsdh_oob_intr_set(sd->bcmsdh, FALSE); |
---|
1501 | 1536 | #endif /* !defined(OOB_INTR_ONLY) */ |
---|
1502 | 1537 | } |
---|
1503 | 1538 | else |
---|
1504 | 1539 | sd_err(("%s Failed\n", __FUNCTION__)); |
---|
| 1540 | +#endif /* defined(OEM_ANDROID) || defined(OEM_EMBEDDED_LINUX) */ |
---|
1505 | 1541 | return (0); |
---|
1506 | 1542 | } |
---|
1507 | 1543 | |
---|
.. | .. |
---|
1510 | 1546 | { |
---|
1511 | 1547 | return (1); |
---|
1512 | 1548 | } |
---|
1513 | | - |
---|
1514 | 1549 | |
---|
1515 | 1550 | SDIOH_API_RC |
---|
1516 | 1551 | sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio) |
---|
.. | .. |
---|
1535 | 1570 | { |
---|
1536 | 1571 | return SDIOH_API_RC_FAIL; |
---|
1537 | 1572 | } |
---|
| 1573 | + |
---|
| 1574 | +uint |
---|
| 1575 | +sdmmc_get_clock_rate(sdioh_info_t *sd) |
---|
| 1576 | +{ |
---|
| 1577 | + struct sdio_func *sdio_func = sd->func[0]; |
---|
| 1578 | + struct mmc_host *host = sdio_func->card->host; |
---|
| 1579 | +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 6)) |
---|
| 1580 | + return mmc_host_clk_rate(host); |
---|
| 1581 | +#else |
---|
| 1582 | + return host->ios.clock; |
---|
| 1583 | +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 6)) */ |
---|
| 1584 | +} |
---|
| 1585 | + |
---|
| 1586 | +void |
---|
| 1587 | +sdmmc_set_clock_rate(sdioh_info_t *sd, uint hz) |
---|
| 1588 | +{ |
---|
| 1589 | + struct sdio_func *sdio_func = sd->func[0]; |
---|
| 1590 | + struct mmc_host *host = sdio_func->card->host; |
---|
| 1591 | + struct mmc_ios *ios = &host->ios; |
---|
| 1592 | + |
---|
| 1593 | +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 6)) |
---|
| 1594 | + mmc_host_clk_hold(host); |
---|
| 1595 | +#endif // endif |
---|
| 1596 | + DHD_INFO(("%s: Before change: sd clock rate is %u\n", __FUNCTION__, ios->clock)); |
---|
| 1597 | + if (hz < host->f_min) { |
---|
| 1598 | + DHD_ERROR(("%s: Intended rate is below min rate, setting to min\n", __FUNCTION__)); |
---|
| 1599 | + hz = host->f_min; |
---|
| 1600 | + } |
---|
| 1601 | + |
---|
| 1602 | + if (hz > host->f_max) { |
---|
| 1603 | + DHD_ERROR(("%s: Intended rate exceeds max rate, setting to max\n", __FUNCTION__)); |
---|
| 1604 | + hz = host->f_max; |
---|
| 1605 | + } |
---|
| 1606 | + ios->clock = hz; |
---|
| 1607 | + host->ops->set_ios(host, ios); |
---|
| 1608 | + DHD_ERROR(("%s: After change: sd clock rate is %u\n", __FUNCTION__, ios->clock)); |
---|
| 1609 | +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 6)) |
---|
| 1610 | + mmc_host_clk_release(host); |
---|
| 1611 | +#endif // endif |
---|
| 1612 | +} |
---|
| 1613 | + |
---|
| 1614 | +void |
---|
| 1615 | +sdmmc_set_clock_divisor(sdioh_info_t *sd, uint sd_div) |
---|
| 1616 | +{ |
---|
| 1617 | + uint hz; |
---|
| 1618 | + uint old_div = sdmmc_get_clock_rate(sd); |
---|
| 1619 | + if (old_div == sd_div) { |
---|
| 1620 | + return; |
---|
| 1621 | + } |
---|
| 1622 | + |
---|
| 1623 | + hz = sd->sd_clk_rate / sd_div; |
---|
| 1624 | + sdmmc_set_clock_rate(sd, hz); |
---|
| 1625 | +} |
---|