• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel
3  *
4  * Copyright (C) 1999-2009, Broadcom Corporation
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  * $Id: bcmsdh_sdmmc_linux.c,v 1.1.2.5.6.10 2009/10/14 04:32:13 Exp $
25  */
26 
27 #include <typedefs.h>
28 #include <bcmutils.h>
29 #include <sdio.h>	/* SDIO Specs */
30 #include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
31 #include <sdiovar.h>	/* to get msglevel bit values */
32 
33 #include <linux/sched.h>	/* request_irq() */
34 
35 #include <linux/mmc/core.h>
36 #include <linux/mmc/card.h>
37 #include <linux/mmc/sdio_func.h>
38 #include <linux/mmc/sdio_ids.h>
39 
40 #if !defined(SDIO_VENDOR_ID_BROADCOM)
41 #define SDIO_VENDOR_ID_BROADCOM		0x02d0
42 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
43 #if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
44 #define SDIO_DEVICE_ID_BROADCOM_4325	0x0000
45 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
46 #if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
47 #define SDIO_DEVICE_ID_BROADCOM_4329	0x4329
48 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
49 
50 #include <bcmsdh_sdmmc.h>
51 
52 #include <dhd_dbg.h>
53 
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 #define DESCRIPTION "bcmsdh_sdmmc Driver"
62 #define AUTHOR "Broadcom Corporation"
63 
64 /* module param defaults */
65 static int clockoverride = 0;
66 
67 module_param(clockoverride, int, 0644);
68 MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
69 
70 PBCMSDH_SDMMC_INSTANCE gInstance;
71 
72 /* Maximum number of bcmsdh_sdmmc devices supported by driver */
73 #define BCMSDH_SDMMC_MAX_DEVICES 1
74 
75 extern int bcmsdh_probe(struct device *dev);
76 extern int bcmsdh_remove(struct device *dev);
77 struct device sdmmc_dev;
78 
79 
bcmsdh_sdmmc_probe(struct sdio_func * func,const struct sdio_device_id * id)80 static int bcmsdh_sdmmc_probe(struct sdio_func *func,
81                               const struct sdio_device_id *id)
82 {
83 	int ret = 0;
84 	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
85 	sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class));
86 	sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
87 	sd_trace(("sdio_device: 0x%04x\n", func->device));
88 	sd_trace(("Function#: 0x%04x\n", func->num));
89 
90 	if (func->num == 1) {
91 		/* Keep a copy of F1's 'func' in F0, just in case... */
92 		gInstance->func[0] = func;
93 		if(func->device == 0x4) { /* 4318 */
94 			gInstance->func[2] = NULL;
95 			sd_trace(("NIC found, calling bcmsdh_probe...\n"));
96 			ret = bcmsdh_probe(&sdmmc_dev);
97 		}
98 	}
99 
100 	gInstance->func[func->num] = func;
101 
102 	if (func->num == 2) {
103 		sd_trace(("F2 found, calling bcmsdh_probe...\n"));
104 		ret = bcmsdh_probe(&sdmmc_dev);
105 	}
106 
107 	return ret;
108 }
109 
bcmsdh_sdmmc_remove(struct sdio_func * func)110 static void bcmsdh_sdmmc_remove(struct sdio_func *func)
111 {
112 	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
113 	sd_info(("sdio_bcmsdh: func->class=%x\n", func->class));
114 	sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
115 	sd_info(("sdio_device: 0x%04x\n", func->device));
116 	sd_info(("Function#: 0x%04x\n", func->num));
117 
118 	if (func->num == 2) {
119 		sd_trace(("F2 found, calling bcmsdh_probe...\n"));
120 		bcmsdh_remove(&sdmmc_dev);
121 	}
122 }
123 
124 
125 /* devices we support, null terminated */
126 static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
127 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
128 	{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
129 	{ SDIO_DEVICE_CLASS(SDIO_CLASS_NONE)		},
130 	{ /* end: all zeroes */				},
131 };
132 
133 MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids);
134 
135 static struct sdio_driver bcmsdh_sdmmc_driver = {
136 	.probe		= bcmsdh_sdmmc_probe,
137 	.remove		= bcmsdh_sdmmc_remove,
138 	.name		= "bcmsdh_sdmmc",
139 	.id_table	= bcmsdh_sdmmc_ids,
140 	};
141 
142 struct sdos_info {
143 	sdioh_info_t *sd;
144 	spinlock_t lock;
145 };
146 
147 
148 int
sdioh_sdmmc_osinit(sdioh_info_t * sd)149 sdioh_sdmmc_osinit(sdioh_info_t *sd)
150 {
151 	struct sdos_info *sdos;
152 
153 	sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info));
154 	sd->sdos_info = (void*)sdos;
155 	if (sdos == NULL)
156 		return BCME_NOMEM;
157 
158 	sdos->sd = sd;
159 	spin_lock_init(&sdos->lock);
160 	return BCME_OK;
161 }
162 
163 void
sdioh_sdmmc_osfree(sdioh_info_t * sd)164 sdioh_sdmmc_osfree(sdioh_info_t *sd)
165 {
166 	struct sdos_info *sdos;
167 	ASSERT(sd && sd->sdos_info);
168 
169 	sdos = (struct sdos_info *)sd->sdos_info;
170 	MFREE(sd->osh, sdos, sizeof(struct sdos_info));
171 }
172 #if defined(OOB_INTR_ONLY)
173 int
sdioh_mmc_irq(int irq)174 sdioh_mmc_irq(int irq)
175 {
176 	return (MSM_GPIO_TO_INT(irq));
177 }
178 #endif /* defined(OOB_INTR_ONLY) */
179 
180 /* Interrupt enable/disable */
181 SDIOH_API_RC
sdioh_interrupt_set(sdioh_info_t * sd,bool enable)182 sdioh_interrupt_set(sdioh_info_t *sd, bool enable)
183 {
184 	ulong flags;
185 	struct sdos_info *sdos;
186 
187 	sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling"));
188 
189 	sdos = (struct sdos_info *)sd->sdos_info;
190 	ASSERT(sdos);
191 
192 #if !defined(OOB_INTR_ONLY)
193 	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
194 		sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__));
195 		return SDIOH_API_RC_FAIL;
196 	}
197 #endif /* !defined(OOB_INTR_ONLY) */
198 
199 	/* Ensure atomicity for enable/disable calls */
200 	spin_lock_irqsave(&sdos->lock, flags);
201 
202 	sd->client_intr_enabled = enable;
203 	if (enable) {
204 		sdioh_sdmmc_devintr_on(sd);
205 	} else {
206 		sdioh_sdmmc_devintr_off(sd);
207 	}
208 
209 	spin_unlock_irqrestore(&sdos->lock, flags);
210 
211 	return SDIOH_API_RC_SUCCESS;
212 }
213 
214 
215 #ifdef BCMSDH_MODULE
216 static int __init
bcmsdh_module_init(void)217 bcmsdh_module_init(void)
218 {
219 	int error = 0;
220 	sdio_function_init();
221 	return error;
222 }
223 
224 static void __exit
bcmsdh_module_cleanup(void)225 bcmsdh_module_cleanup(void)
226 {
227 	sdio_function_cleanup();
228 }
229 
230 module_init(bcmsdh_module_init);
231 module_exit(bcmsdh_module_cleanup);
232 
233 MODULE_LICENSE("GPL v2");
234 MODULE_DESCRIPTION(DESCRIPTION);
235 MODULE_AUTHOR(AUTHOR);
236 
237 #endif /* BCMSDH_MODULE */
238 /*
239  * module init
240 */
sdio_function_init(void)241 int sdio_function_init(void)
242 {
243 	int error = 0;
244 	sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__));
245 
246 	gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL);
247 	if (!gInstance)
248 		return -ENOMEM;
249 
250 	bzero(&sdmmc_dev, sizeof(sdmmc_dev));
251 	error = sdio_register_driver(&bcmsdh_sdmmc_driver);
252 
253 
254 	return error;
255 }
256 
257 /*
258  * module cleanup
259 */
260 extern int bcmsdh_remove(struct device *dev);
sdio_function_cleanup(void)261 void sdio_function_cleanup(void)
262 {
263 	sd_trace(("%s Enter\n", __FUNCTION__));
264 
265 
266 	sdio_unregister_driver(&bcmsdh_sdmmc_driver);
267 
268 	if (gInstance)
269 		kfree(gInstance);
270 }
271