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