Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2010 Broadcom Corporation |
| 3 | * |
| 4 | * Permission to use, copy, modify, and/or distribute this software for any |
| 5 | * purpose with or without fee is hereby granted, provided that the above |
| 6 | * copyright notice and this permission notice appear in all copies. |
| 7 | * |
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 15 | */ |
Greg Kroah-Hartman | a1c16ed | 2010-10-21 11:17:44 -0700 | [diff] [blame^] | 16 | #include <linux/types.h> |
| 17 | #include <linux/sched.h> /* request_irq() */ |
| 18 | #include <bcmdefs.h> |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 19 | #include <bcmutils.h> |
| 20 | #include <sdio.h> /* SDIO Specs */ |
| 21 | #include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */ |
| 22 | #include <sdiovar.h> /* to get msglevel bit values */ |
| 23 | |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 24 | #include <linux/mmc/core.h> |
| 25 | #include <linux/mmc/card.h> |
| 26 | #include <linux/mmc/sdio_func.h> |
| 27 | #include <linux/mmc/sdio_ids.h> |
| 28 | |
| 29 | #if !defined(SDIO_VENDOR_ID_BROADCOM) |
| 30 | #define SDIO_VENDOR_ID_BROADCOM 0x02d0 |
| 31 | #endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ |
| 32 | |
| 33 | #define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 |
| 34 | |
| 35 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) |
| 36 | #define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ |
| 37 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ |
| 38 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4325) |
| 39 | #define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 |
| 40 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ |
| 41 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4329) |
| 42 | #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 |
| 43 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ |
| 44 | #if !defined(SDIO_DEVICE_ID_BROADCOM_4319) |
| 45 | #define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 |
| 46 | #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ |
| 47 | |
| 48 | #include <bcmsdh_sdmmc.h> |
| 49 | |
| 50 | #include <dhd_dbg.h> |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 51 | #include <wl_cfg80211.h> |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 52 | |
| 53 | extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); |
| 54 | extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); |
| 55 | |
| 56 | int sdio_function_init(void); |
| 57 | void sdio_function_cleanup(void); |
| 58 | |
| 59 | /* module param defaults */ |
Jason Cooper | 5f782de | 2010-10-06 10:08:01 -0400 | [diff] [blame] | 60 | static int clockoverride; |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 61 | |
| 62 | module_param(clockoverride, int, 0644); |
| 63 | MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); |
| 64 | |
| 65 | PBCMSDH_SDMMC_INSTANCE gInstance; |
| 66 | |
| 67 | /* Maximum number of bcmsdh_sdmmc devices supported by driver */ |
| 68 | #define BCMSDH_SDMMC_MAX_DEVICES 1 |
| 69 | |
| 70 | extern int bcmsdh_probe(struct device *dev); |
| 71 | extern int bcmsdh_remove(struct device *dev); |
| 72 | struct device sdmmc_dev; |
| 73 | |
| 74 | static int bcmsdh_sdmmc_probe(struct sdio_func *func, |
| 75 | const struct sdio_device_id *id) |
| 76 | { |
| 77 | int ret = 0; |
| 78 | static struct sdio_func sdio_func_0; |
| 79 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); |
| 80 | sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class)); |
| 81 | sd_trace(("sdio_vendor: 0x%04x\n", func->vendor)); |
| 82 | sd_trace(("sdio_device: 0x%04x\n", func->device)); |
| 83 | sd_trace(("Function#: 0x%04x\n", func->num)); |
| 84 | |
| 85 | if (func->num == 1) { |
| 86 | sdio_func_0.num = 0; |
| 87 | sdio_func_0.card = func->card; |
| 88 | gInstance->func[0] = &sdio_func_0; |
| 89 | if (func->device == 0x4) { /* 4318 */ |
| 90 | gInstance->func[2] = NULL; |
| 91 | sd_trace(("NIC found, calling bcmsdh_probe...\n")); |
| 92 | ret = bcmsdh_probe(&sdmmc_dev); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | gInstance->func[func->num] = func; |
| 97 | |
| 98 | if (func->num == 2) { |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 99 | wl_cfg80211_sdio_func(func); |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 100 | sd_trace(("F2 found, calling bcmsdh_probe...\n")); |
| 101 | ret = bcmsdh_probe(&sdmmc_dev); |
| 102 | } |
| 103 | |
| 104 | return ret; |
| 105 | } |
| 106 | |
| 107 | static void bcmsdh_sdmmc_remove(struct sdio_func *func) |
| 108 | { |
| 109 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); |
| 110 | sd_info(("sdio_bcmsdh: func->class=%x\n", func->class)); |
| 111 | sd_info(("sdio_vendor: 0x%04x\n", func->vendor)); |
| 112 | sd_info(("sdio_device: 0x%04x\n", func->device)); |
| 113 | sd_info(("Function#: 0x%04x\n", func->num)); |
| 114 | |
| 115 | if (func->num == 2) { |
| 116 | sd_trace(("F2 found, calling bcmsdh_remove...\n")); |
| 117 | bcmsdh_remove(&sdmmc_dev); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /* devices we support, null terminated */ |
| 122 | static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { |
| 123 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT)}, |
| 124 | {SDIO_DEVICE |
| 125 | (SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)}, |
| 126 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325)}, |
| 127 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, |
| 128 | {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319)}, |
| 129 | { /* end: all zeroes */ }, |
| 130 | }; |
| 131 | |
| 132 | MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); |
| 133 | |
| 134 | static struct sdio_driver bcmsdh_sdmmc_driver = { |
| 135 | .probe = bcmsdh_sdmmc_probe, |
| 136 | .remove = bcmsdh_sdmmc_remove, |
| 137 | .name = "brcmfmac", |
| 138 | .id_table = bcmsdh_sdmmc_ids, |
| 139 | }; |
| 140 | |
| 141 | struct sdos_info { |
| 142 | sdioh_info_t *sd; |
| 143 | spinlock_t lock; |
| 144 | }; |
| 145 | |
| 146 | int sdioh_sdmmc_osinit(sdioh_info_t *sd) |
| 147 | { |
| 148 | struct sdos_info *sdos; |
| 149 | |
mike.rapoport@gmail.com | 5fcc1fc | 2010-10-13 00:09:10 +0200 | [diff] [blame] | 150 | sdos = kmalloc(sizeof(struct sdos_info), GFP_ATOMIC); |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 151 | sd->sdos_info = (void *)sdos; |
| 152 | if (sdos == NULL) |
| 153 | return BCME_NOMEM; |
| 154 | |
| 155 | sdos->sd = sd; |
| 156 | spin_lock_init(&sdos->lock); |
| 157 | return BCME_OK; |
| 158 | } |
| 159 | |
| 160 | void sdioh_sdmmc_osfree(sdioh_info_t *sd) |
| 161 | { |
| 162 | struct sdos_info *sdos; |
| 163 | ASSERT(sd && sd->sdos_info); |
| 164 | |
| 165 | sdos = (struct sdos_info *)sd->sdos_info; |
mike.rapoport@gmail.com | 182acb3 | 2010-10-13 00:09:12 +0200 | [diff] [blame] | 166 | kfree(sdos); |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | /* Interrupt enable/disable */ |
| 170 | SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *sd, bool enable) |
| 171 | { |
Greg Kroah-Hartman | 3deea90 | 2010-10-05 11:15:47 -0700 | [diff] [blame] | 172 | unsigned long flags; |
Henry Ptasinski | cf2b448 | 2010-09-20 22:33:12 -0700 | [diff] [blame] | 173 | struct sdos_info *sdos; |
| 174 | |
| 175 | sd_trace(("%s: %s\n", __func__, enable ? "Enabling" : "Disabling")); |
| 176 | |
| 177 | sdos = (struct sdos_info *)sd->sdos_info; |
| 178 | ASSERT(sdos); |
| 179 | |
| 180 | #if !defined(OOB_INTR_ONLY) |
| 181 | if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { |
| 182 | sd_err(("%s: no handler registered, will not enable\n", |
| 183 | __func__)); |
| 184 | return SDIOH_API_RC_FAIL; |
| 185 | } |
| 186 | #endif /* !defined(OOB_INTR_ONLY) */ |
| 187 | |
| 188 | /* Ensure atomicity for enable/disable calls */ |
| 189 | spin_lock_irqsave(&sdos->lock, flags); |
| 190 | |
| 191 | sd->client_intr_enabled = enable; |
| 192 | if (enable) |
| 193 | sdioh_sdmmc_devintr_on(sd); |
| 194 | else |
| 195 | sdioh_sdmmc_devintr_off(sd); |
| 196 | |
| 197 | spin_unlock_irqrestore(&sdos->lock, flags); |
| 198 | |
| 199 | return SDIOH_API_RC_SUCCESS; |
| 200 | } |
| 201 | |
| 202 | /* |
| 203 | * module init |
| 204 | */ |
| 205 | int sdio_function_init(void) |
| 206 | { |
| 207 | int error = 0; |
| 208 | sd_trace(("bcmsdh_sdmmc: %s Enter\n", __func__)); |
| 209 | |
| 210 | gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); |
| 211 | if (!gInstance) |
| 212 | return -ENOMEM; |
| 213 | |
| 214 | bzero(&sdmmc_dev, sizeof(sdmmc_dev)); |
| 215 | error = sdio_register_driver(&bcmsdh_sdmmc_driver); |
| 216 | |
| 217 | return error; |
| 218 | } |
| 219 | |
| 220 | /* |
| 221 | * module cleanup |
| 222 | */ |
| 223 | extern int bcmsdh_remove(struct device *dev); |
| 224 | void sdio_function_cleanup(void) |
| 225 | { |
| 226 | sd_trace(("%s Enter\n", __func__)); |
| 227 | |
| 228 | sdio_unregister_driver(&bcmsdh_sdmmc_driver); |
| 229 | |
| 230 | kfree(gInstance); |
| 231 | } |