blob: ceaa47490680778bcd8de7c3029c09714353ef14 [file] [log] [blame]
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001/*
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-Hartmana1c16ed2010-10-21 11:17:44 -070016#include <linux/types.h>
17#include <linux/sched.h> /* request_irq() */
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070018#include <linux/netdevice.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070019#include <bcmdefs.h>
Brett Rudley7f7c3db2010-10-26 15:23:09 -070020#include <osl.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070021#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 Ptasinskicf2b4482010-09-20 22:33:12 -070026#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 Ptasinskicf2b4482010-09-20 22:33:12 -070053#include <wl_cfg80211.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070054
55extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd);
56extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd);
57
58int sdio_function_init(void);
59void sdio_function_cleanup(void);
60
61/* module param defaults */
Jason Cooper5f782de2010-10-06 10:08:01 -040062static int clockoverride;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070063
64module_param(clockoverride, int, 0644);
65MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
66
67PBCMSDH_SDMMC_INSTANCE gInstance;
68
69/* Maximum number of bcmsdh_sdmmc devices supported by driver */
70#define BCMSDH_SDMMC_MAX_DEVICES 1
71
72extern int bcmsdh_probe(struct device *dev);
73extern int bcmsdh_remove(struct device *dev);
74struct device sdmmc_dev;
75
76static 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 Ptasinskicf2b4482010-09-20 22:33:12 -0700101 wl_cfg80211_sdio_func(func);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700102 sd_trace(("F2 found, calling bcmsdh_probe...\n"));
103 ret = bcmsdh_probe(&sdmmc_dev);
104 }
105
106 return ret;
107}
108
109static 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 */
124static 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
134MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
135
136static 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
143struct sdos_info {
144 sdioh_info_t *sd;
145 spinlock_t lock;
146};
147
148int sdioh_sdmmc_osinit(sdioh_info_t *sd)
149{
150 struct sdos_info *sdos;
151
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200152 sdos = kmalloc(sizeof(struct sdos_info), GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700153 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
162void 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.com182acb32010-10-13 00:09:12 +0200168 kfree(sdos);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700169}
170
171/* Interrupt enable/disable */
172SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
173{
Greg Kroah-Hartman3deea902010-10-05 11:15:47 -0700174 unsigned long flags;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700175 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*/
207int 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 Rudley9249ede2010-11-30 20:09:49 -0800216 memset(&sdmmc_dev, 0, sizeof(sdmmc_dev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700217 error = sdio_register_driver(&bcmsdh_sdmmc_driver);
218
219 return error;
220}
221
222/*
223 * module cleanup
224*/
225extern int bcmsdh_remove(struct device *dev);
226void 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}