xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/bcmsdh_linux.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * SDIO access interface for drivers - linux specific (pci only)
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2020, Broadcom.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
7*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
8*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
9*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10*4882a593Smuzhiyun  * following added to such license:
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
13*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
14*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
15*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
16*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
17*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
18*4882a593Smuzhiyun  * modifications of the software.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  * $Id$
24*4882a593Smuzhiyun  */
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /**
27*4882a593Smuzhiyun  * @file bcmsdh_linux.c
28*4882a593Smuzhiyun  */
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define __UNDEF_NO_VERSION__
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #include <typedefs.h>
33*4882a593Smuzhiyun #include <linuxver.h>
34*4882a593Smuzhiyun #include <linux/pci.h>
35*4882a593Smuzhiyun #include <linux/completion.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #include <osl.h>
38*4882a593Smuzhiyun #include <pcicfg.h>
39*4882a593Smuzhiyun #include <bcmdefs.h>
40*4882a593Smuzhiyun #include <bcmdevs.h>
41*4882a593Smuzhiyun #include <linux/irq.h>
42*4882a593Smuzhiyun extern void dhdsdio_isr(void * args);
43*4882a593Smuzhiyun #include <bcmutils.h>
44*4882a593Smuzhiyun #include <dngl_stats.h>
45*4882a593Smuzhiyun #include <dhd.h>
46*4882a593Smuzhiyun #if defined(CONFIG_ARCH_ODIN)
47*4882a593Smuzhiyun #include <linux/platform_data/gpio-odin.h>
48*4882a593Smuzhiyun #endif /* defined(CONFIG_ARCH_ODIN) */
49*4882a593Smuzhiyun #include <dhd_linux.h>
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /* driver info, initialized when bcmsdh_register is called */
52*4882a593Smuzhiyun static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun typedef enum {
55*4882a593Smuzhiyun 	DHD_INTR_INVALID = 0,
56*4882a593Smuzhiyun 	DHD_INTR_INBAND,
57*4882a593Smuzhiyun 	DHD_INTR_HWOOB,
58*4882a593Smuzhiyun 	DHD_INTR_SWOOB
59*4882a593Smuzhiyun } DHD_HOST_INTR_TYPE;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer (e.g.
62*4882a593Smuzhiyun  * bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and flags) here rather
63*4882a593Smuzhiyun  * than in the common structure bcmsdh_info. bcmsdh_info only keeps a handle (os_ctx) to this
64*4882a593Smuzhiyun  * structure.
65*4882a593Smuzhiyun  */
66*4882a593Smuzhiyun typedef struct bcmsdh_os_info {
67*4882a593Smuzhiyun 	DHD_HOST_INTR_TYPE	intr_type;
68*4882a593Smuzhiyun 	int			oob_irq_num;	/* valid when hardware or software oob in use */
69*4882a593Smuzhiyun 	unsigned long		oob_irq_flags;	/* valid when hardware or software oob in use */
70*4882a593Smuzhiyun 	bool			oob_irq_registered;
71*4882a593Smuzhiyun 	bool			oob_irq_enabled;
72*4882a593Smuzhiyun 	bool			oob_irq_wake_enabled;
73*4882a593Smuzhiyun 	spinlock_t		oob_irq_spinlock;
74*4882a593Smuzhiyun 	bcmsdh_cb_fn_t		oob_irq_handler;
75*4882a593Smuzhiyun 	void			*oob_irq_handler_context;
76*4882a593Smuzhiyun 	void			*context;	/* context returned from upper layer */
77*4882a593Smuzhiyun 	void			*sdioh;		/* handle to lower layer (sdioh) */
78*4882a593Smuzhiyun 	void			*dev;		/* handle to the underlying device */
79*4882a593Smuzhiyun 	bool			dev_wake_enabled;
80*4882a593Smuzhiyun } bcmsdh_os_info_t;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun /* debugging macros */
83*4882a593Smuzhiyun #ifdef BCMDBG_ERR
84*4882a593Smuzhiyun #define SDLX_ERR(x) printf x
85*4882a593Smuzhiyun #define SDLX_MSG(x)	printf x
86*4882a593Smuzhiyun #else
87*4882a593Smuzhiyun #define SDLX_ERR(x) printf x
88*4882a593Smuzhiyun #define SDLX_MSG(x)	printf x
89*4882a593Smuzhiyun #endif /* BCMDBG_ERR */
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun /**
92*4882a593Smuzhiyun  * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
93*4882a593Smuzhiyun  */
94*4882a593Smuzhiyun bool
bcmsdh_chipmatch(uint16 vendor,uint16 device)95*4882a593Smuzhiyun bcmsdh_chipmatch(uint16 vendor, uint16 device)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	/* Add other vendors and devices as required */
98*4882a593Smuzhiyun #ifdef BCMINTERNAL
99*4882a593Smuzhiyun #ifdef BCMSDIOH_BCM
100*4882a593Smuzhiyun 	if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
101*4882a593Smuzhiyun 		return (TRUE);
102*4882a593Smuzhiyun 	}
103*4882a593Smuzhiyun 	if (device == BCM_SDIOH_ID && vendor == VENDOR_BROADCOM) {
104*4882a593Smuzhiyun 		return (TRUE);
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 	if (device == BCM4710_DEVICE_ID && vendor == VENDOR_BROADCOM) {
107*4882a593Smuzhiyun 		return (TRUE);
108*4882a593Smuzhiyun 	}
109*4882a593Smuzhiyun 	/* For now still accept the old devid */
110*4882a593Smuzhiyun 	if (device == 0x4380 && vendor == VENDOR_BROADCOM) {
111*4882a593Smuzhiyun 		return (TRUE);
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun #endif /* BCMSDIOH_BCM */
114*4882a593Smuzhiyun #endif /* BCMINTERNAL */
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun #ifdef BCMSDIOH_STD
117*4882a593Smuzhiyun 	/* Check for Arasan host controller */
118*4882a593Smuzhiyun 	if (vendor == VENDOR_SI_IMAGE) {
119*4882a593Smuzhiyun 		return (TRUE);
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 	/* Check for BRCM 27XX Standard host controller */
122*4882a593Smuzhiyun 	if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
123*4882a593Smuzhiyun 		return (TRUE);
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 	/* Check for BRCM Standard host controller */
126*4882a593Smuzhiyun 	if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
127*4882a593Smuzhiyun 		return (TRUE);
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 	/* Check for TI PCIxx21 Standard host controller */
130*4882a593Smuzhiyun 	if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
131*4882a593Smuzhiyun 		return (TRUE);
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 	if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
134*4882a593Smuzhiyun 		return (TRUE);
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 	/* Ricoh R5C822 Standard SDIO Host */
137*4882a593Smuzhiyun 	if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
138*4882a593Smuzhiyun 		return (TRUE);
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 	/* JMicron Standard SDIO Host */
141*4882a593Smuzhiyun 	if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
142*4882a593Smuzhiyun 		return (TRUE);
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun #ifdef BCMINTERNAL
146*4882a593Smuzhiyun 	/* Check for Jinvani (C-Guys) host controller */
147*4882a593Smuzhiyun 	if (device == JINVANI_SDIOH_ID && vendor == VENDOR_JINVANI) {
148*4882a593Smuzhiyun 		return (TRUE);
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun #endif /* BCMINTERNAL */
151*4882a593Smuzhiyun #endif /* BCMSDIOH_STD */
152*4882a593Smuzhiyun #ifdef BCMSDIOH_SPI
153*4882a593Smuzhiyun 	/* This is the PciSpiHost. */
154*4882a593Smuzhiyun 	if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
155*4882a593Smuzhiyun 		printf("Found PCI SPI Host Controller\n");
156*4882a593Smuzhiyun 		return (TRUE);
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun #ifdef BCMINTERNAL
160*4882a593Smuzhiyun 	/* This is the SPI Host for QT. */
161*4882a593Smuzhiyun 	if (device == BCM_SPIH_ID && vendor == VENDOR_BROADCOM) {
162*4882a593Smuzhiyun 		printf("Found SPI Host Controller\n");
163*4882a593Smuzhiyun 		return (TRUE);
164*4882a593Smuzhiyun 	}
165*4882a593Smuzhiyun #endif /* BCMINTERNAL */
166*4882a593Smuzhiyun #endif /* BCMSDIOH_SPI */
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun #ifdef BCMINTERNAL
169*4882a593Smuzhiyun 	/*
170*4882a593Smuzhiyun 	 * XXX - This is a hack to get the GPL SdioLinux driver to load on Arasan/x86
171*4882a593Smuzhiyun 	 * This is accomplished by installing a PciSpiHost into the system alongside the
172*4882a593Smuzhiyun 	 * Arasan controller.  The PciSpiHost is just used to get BCMSDH loaded.
173*4882a593Smuzhiyun 	 */
174*4882a593Smuzhiyun #ifdef BCMSDH_FD
175*4882a593Smuzhiyun 	if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
176*4882a593Smuzhiyun 		printf("Found SdioLinux Host Controller\n");
177*4882a593Smuzhiyun 		return (TRUE);
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun #endif /* BCMSDH_FD */
180*4882a593Smuzhiyun #endif /* BCMINTERNAL */
181*4882a593Smuzhiyun 	return (FALSE);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
bcmsdh_probe(osl_t * osh,void * dev,void * sdioh,void * adapter_info,uint bus_type,uint bus_num,uint slot_num)184*4882a593Smuzhiyun void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
185*4882a593Smuzhiyun 	uint bus_num, uint slot_num)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	ulong regs;
188*4882a593Smuzhiyun 	bcmsdh_info_t *bcmsdh;
189*4882a593Smuzhiyun 	uint32 vendevid;
190*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = NULL;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	bcmsdh = bcmsdh_attach(osh, sdioh, &regs);
193*4882a593Smuzhiyun 	if (bcmsdh == NULL) {
194*4882a593Smuzhiyun 		SDLX_ERR(("%s: bcmsdh_attach failed\n", __FUNCTION__));
195*4882a593Smuzhiyun 		goto err;
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 	bcmsdh_osinfo = MALLOC(osh, sizeof(bcmsdh_os_info_t));
198*4882a593Smuzhiyun 	if (bcmsdh_osinfo == NULL) {
199*4882a593Smuzhiyun 		SDLX_ERR(("%s: failed to allocate bcmsdh_os_info_t\n", __FUNCTION__));
200*4882a593Smuzhiyun 		goto err;
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 	bzero((char *)bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
203*4882a593Smuzhiyun 	bcmsdh->os_cxt = bcmsdh_osinfo;
204*4882a593Smuzhiyun 	bcmsdh_osinfo->sdioh = sdioh;
205*4882a593Smuzhiyun 	bcmsdh_osinfo->dev = dev;
206*4882a593Smuzhiyun 	osl_set_bus_handle(osh, bcmsdh);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
209*4882a593Smuzhiyun 	if (dev && device_init_wakeup(dev, true) == 0)
210*4882a593Smuzhiyun 		bcmsdh_osinfo->dev_wake_enabled = TRUE;
211*4882a593Smuzhiyun #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun #if defined(OOB_INTR_ONLY)
214*4882a593Smuzhiyun 	spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
215*4882a593Smuzhiyun 	/* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
216*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter_info,
217*4882a593Smuzhiyun 		&bcmsdh_osinfo->oob_irq_flags);
218*4882a593Smuzhiyun 	if  (bcmsdh_osinfo->oob_irq_num < 0) {
219*4882a593Smuzhiyun 		SDLX_ERR(("%s: Host OOB irq is not defined\n", __FUNCTION__));
220*4882a593Smuzhiyun 		goto err;
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun #endif /* defined(BCMLXSDMMC) */
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/* Read the vendor/device ID from the CIS */
225*4882a593Smuzhiyun 	vendevid = bcmsdh_query_device(bcmsdh);
226*4882a593Smuzhiyun 	/* try to attach to the target device */
227*4882a593Smuzhiyun 	bcmsdh_osinfo->context = drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
228*4882a593Smuzhiyun 		slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
229*4882a593Smuzhiyun 	if (bcmsdh_osinfo->context == NULL) {
230*4882a593Smuzhiyun 		SDLX_ERR(("%s: device attach failed\n", __FUNCTION__));
231*4882a593Smuzhiyun 		goto err;
232*4882a593Smuzhiyun 	}
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	return bcmsdh;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	/* error handling */
237*4882a593Smuzhiyun err:
238*4882a593Smuzhiyun 	if (bcmsdh != NULL)
239*4882a593Smuzhiyun 		bcmsdh_detach(osh, bcmsdh);
240*4882a593Smuzhiyun 	if (bcmsdh_osinfo != NULL)
241*4882a593Smuzhiyun 		MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
242*4882a593Smuzhiyun 	return NULL;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
bcmsdh_remove(bcmsdh_info_t * bcmsdh)245*4882a593Smuzhiyun int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
250*4882a593Smuzhiyun 	if (bcmsdh_osinfo->dev)
251*4882a593Smuzhiyun 		device_init_wakeup(bcmsdh_osinfo->dev, false);
252*4882a593Smuzhiyun 	bcmsdh_osinfo->dev_wake_enabled = FALSE;
253*4882a593Smuzhiyun #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	drvinfo.remove(bcmsdh_osinfo->context);
256*4882a593Smuzhiyun 	MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
257*4882a593Smuzhiyun 	bcmsdh_detach(bcmsdh->osh, bcmsdh);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	return 0;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun #ifdef DHD_WAKE_STATUS
bcmsdh_get_total_wake(bcmsdh_info_t * bcmsdh)263*4882a593Smuzhiyun int bcmsdh_get_total_wake(bcmsdh_info_t *bcmsdh)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	return bcmsdh->total_wake_count;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun 
bcmsdh_set_get_wake(bcmsdh_info_t * bcmsdh,int flag)268*4882a593Smuzhiyun int bcmsdh_set_get_wake(bcmsdh_info_t *bcmsdh, int flag)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun #if defined(OOB_INTR_ONLY)
271*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
272*4882a593Smuzhiyun 	unsigned long flags;
273*4882a593Smuzhiyun #endif
274*4882a593Smuzhiyun 	int ret = 0;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun #if defined(OOB_INTR_ONLY)
277*4882a593Smuzhiyun 	spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	ret = bcmsdh->pkt_wake;
280*4882a593Smuzhiyun 	bcmsdh->total_wake_count += flag;
281*4882a593Smuzhiyun 	bcmsdh->pkt_wake = flag;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
284*4882a593Smuzhiyun #endif
285*4882a593Smuzhiyun 	return ret;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun #endif /* DHD_WAKE_STATUS */
288*4882a593Smuzhiyun 
bcmsdh_suspend(bcmsdh_info_t * bcmsdh)289*4882a593Smuzhiyun int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context))
294*4882a593Smuzhiyun 		return -EBUSY;
295*4882a593Smuzhiyun 	return 0;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun 
bcmsdh_resume(bcmsdh_info_t * bcmsdh)298*4882a593Smuzhiyun int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (drvinfo.resume)
303*4882a593Smuzhiyun 		return drvinfo.resume(bcmsdh_osinfo->context);
304*4882a593Smuzhiyun 	return 0;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun extern int bcmsdh_register_client_driver(void);
308*4882a593Smuzhiyun extern void bcmsdh_unregister_client_driver(void);
309*4882a593Smuzhiyun extern int sdio_func_reg_notify(void* semaphore);
310*4882a593Smuzhiyun extern void sdio_func_unreg_notify(void);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun #if defined(BCMLXSDMMC)
bcmsdh_reg_sdio_notify(void * semaphore)313*4882a593Smuzhiyun int bcmsdh_reg_sdio_notify(void* semaphore)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	return sdio_func_reg_notify(semaphore);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun 
bcmsdh_unreg_sdio_notify(void)318*4882a593Smuzhiyun void bcmsdh_unreg_sdio_notify(void)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun 	sdio_func_unreg_notify();
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun #endif /* defined(BCMLXSDMMC) */
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun int
bcmsdh_register(bcmsdh_driver_t * driver)325*4882a593Smuzhiyun bcmsdh_register(bcmsdh_driver_t *driver)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	int error = 0;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	drvinfo = *driver;
330*4882a593Smuzhiyun 	SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
331*4882a593Smuzhiyun 	error = bcmsdh_register_client_driver();
332*4882a593Smuzhiyun 	if (error)
333*4882a593Smuzhiyun 		SDLX_ERR(("%s: failed %d\n", __FUNCTION__, error));
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	return error;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun void
bcmsdh_unregister(void)339*4882a593Smuzhiyun bcmsdh_unregister(void)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
342*4882a593Smuzhiyun 		if (bcmsdh_pci_driver.node.next == NULL)
343*4882a593Smuzhiyun 			return;
344*4882a593Smuzhiyun #endif
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	bcmsdh_unregister_client_driver();
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun 
bcmsdh_get_dev(bcmsdh_info_t * sdh)349*4882a593Smuzhiyun void *bcmsdh_get_dev(bcmsdh_info_t *sdh)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = sdh->os_cxt;
352*4882a593Smuzhiyun 	return bcmsdh_osinfo->dev;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
bcmsdh_dev_pm_stay_awake(bcmsdh_info_t * bcmsdh)355*4882a593Smuzhiyun void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
358*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
359*4882a593Smuzhiyun 	pm_stay_awake(bcmsdh_osinfo->dev);
360*4882a593Smuzhiyun #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
bcmsdh_dev_relax(bcmsdh_info_t * bcmsdh)363*4882a593Smuzhiyun void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
366*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
367*4882a593Smuzhiyun 	pm_relax(bcmsdh_osinfo->dev);
368*4882a593Smuzhiyun #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
bcmsdh_dev_pm_enabled(bcmsdh_info_t * bcmsdh)371*4882a593Smuzhiyun bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	return bcmsdh_osinfo->dev_wake_enabled;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID)
bcmsdh_get_oob_intr_num(bcmsdh_info_t * bcmsdh)379*4882a593Smuzhiyun int bcmsdh_get_oob_intr_num(bcmsdh_info_t *bcmsdh)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	return bcmsdh_osinfo->oob_irq_num;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
bcmsdh_oob_intr_set(bcmsdh_info_t * bcmsdh,bool enable)386*4882a593Smuzhiyun void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
387*4882a593Smuzhiyun {
388*4882a593Smuzhiyun 	unsigned long flags;
389*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	if (!bcmsdh)
392*4882a593Smuzhiyun 		return;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	bcmsdh_osinfo = bcmsdh->os_cxt;
395*4882a593Smuzhiyun 	spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
396*4882a593Smuzhiyun 	if (bcmsdh_osinfo->oob_irq_enabled != enable) {
397*4882a593Smuzhiyun 		if (enable)
398*4882a593Smuzhiyun 			enable_irq(bcmsdh_osinfo->oob_irq_num);
399*4882a593Smuzhiyun 		else
400*4882a593Smuzhiyun 			disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
401*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_enabled = enable;
402*4882a593Smuzhiyun 	}
403*4882a593Smuzhiyun 	spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun #ifdef ENABLE_WAKEUP_PKT_DUMP
407*4882a593Smuzhiyun extern volatile bool dhd_mmc_suspend;
408*4882a593Smuzhiyun extern volatile bool dhd_mmc_wake;
409*4882a593Smuzhiyun #endif /* ENABLE_WAKEUP_PKT_DUMP */
410*4882a593Smuzhiyun 
wlan_oob_irq(int irq,void * dev_id)411*4882a593Smuzhiyun static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
412*4882a593Smuzhiyun {
413*4882a593Smuzhiyun 	bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
414*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun #ifndef BCMSPI_ANDROID
417*4882a593Smuzhiyun 	bcmsdh_oob_intr_set(bcmsdh, FALSE);
418*4882a593Smuzhiyun #endif /* !BCMSPI_ANDROID */
419*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun #ifdef ENABLE_WAKEUP_PKT_DUMP
422*4882a593Smuzhiyun 	if (dhd_mmc_suspend) {
423*4882a593Smuzhiyun 		dhd_mmc_wake = TRUE;
424*4882a593Smuzhiyun 	}
425*4882a593Smuzhiyun #endif /* ENABLE_WAKEUP_PKT_DUMP */
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	return IRQ_HANDLED;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun 
bcmsdh_oob_intr_register(bcmsdh_info_t * bcmsdh,bcmsdh_cb_fn_t oob_irq_handler,void * oob_irq_handler_context)430*4882a593Smuzhiyun int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler,
431*4882a593Smuzhiyun 	void* oob_irq_handler_context)
432*4882a593Smuzhiyun {
433*4882a593Smuzhiyun 	int err = 0;
434*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (bcmsdh_osinfo->oob_irq_registered) {
437*4882a593Smuzhiyun 		SDLX_ERR(("%s: irq is already registered\n", __FUNCTION__));
438*4882a593Smuzhiyun 		return -EBUSY;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun #ifdef HW_OOB
441*4882a593Smuzhiyun 	SDLX_MSG(("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
442*4882a593Smuzhiyun 		(int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
443*4882a593Smuzhiyun #else
444*4882a593Smuzhiyun 	SDLX_MSG(("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__,
445*4882a593Smuzhiyun 		(int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
446*4882a593Smuzhiyun #endif
447*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
448*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
449*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_enabled = TRUE;
450*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_registered = TRUE;
451*4882a593Smuzhiyun #if defined(CONFIG_ARCH_ODIN)
452*4882a593Smuzhiyun 	err = odin_gpio_sms_request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
453*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
454*4882a593Smuzhiyun #else
455*4882a593Smuzhiyun 	err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
456*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
457*4882a593Smuzhiyun #endif /* defined(CONFIG_ARCH_ODIN) */
458*4882a593Smuzhiyun 	if (err) {
459*4882a593Smuzhiyun 		SDLX_ERR(("%s: request_irq failed with %d\n", __FUNCTION__, err));
460*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_enabled = FALSE;
461*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_registered = FALSE;
462*4882a593Smuzhiyun 		return err;
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun #if defined(DISABLE_WOWLAN)
466*4882a593Smuzhiyun 	SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__));
467*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
468*4882a593Smuzhiyun #else
469*4882a593Smuzhiyun #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
470*4882a593Smuzhiyun 	if (device_may_wakeup(bcmsdh_osinfo->dev)) {
471*4882a593Smuzhiyun #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
472*4882a593Smuzhiyun 		err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
473*4882a593Smuzhiyun 		if (err)
474*4882a593Smuzhiyun 			SDLX_ERR(("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err));
475*4882a593Smuzhiyun 		else
476*4882a593Smuzhiyun 			bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
477*4882a593Smuzhiyun #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
480*4882a593Smuzhiyun #endif
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	return 0;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
bcmsdh_oob_intr_unregister(bcmsdh_info_t * bcmsdh)485*4882a593Smuzhiyun void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun 	int err = 0;
488*4882a593Smuzhiyun 	bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	SDLX_MSG(("%s: Enter\n", __FUNCTION__));
491*4882a593Smuzhiyun 	if (!bcmsdh_osinfo->oob_irq_registered) {
492*4882a593Smuzhiyun 		SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
493*4882a593Smuzhiyun 		return;
494*4882a593Smuzhiyun 	}
495*4882a593Smuzhiyun 	if (bcmsdh_osinfo->oob_irq_wake_enabled) {
496*4882a593Smuzhiyun #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
497*4882a593Smuzhiyun 		if (device_may_wakeup(bcmsdh_osinfo->dev)) {
498*4882a593Smuzhiyun #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
499*4882a593Smuzhiyun 			err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
500*4882a593Smuzhiyun 			if (!err)
501*4882a593Smuzhiyun 				bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
502*4882a593Smuzhiyun #if defined(CONFIG_ARCH_RHEA) || defined(CONFIG_ARCH_CAPRI)
503*4882a593Smuzhiyun 		}
504*4882a593Smuzhiyun #endif /* CONFIG_ARCH_RHEA || CONFIG_ARCH_CAPRI */
505*4882a593Smuzhiyun 	}
506*4882a593Smuzhiyun 	if (bcmsdh_osinfo->oob_irq_enabled) {
507*4882a593Smuzhiyun 		disable_irq(bcmsdh_osinfo->oob_irq_num);
508*4882a593Smuzhiyun 		bcmsdh_osinfo->oob_irq_enabled = FALSE;
509*4882a593Smuzhiyun 	}
510*4882a593Smuzhiyun 	free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
511*4882a593Smuzhiyun 	bcmsdh_osinfo->oob_irq_registered = FALSE;
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun /* Module parameters specific to each host-controller driver */
516*4882a593Smuzhiyun /* XXX Need to move these to where they really belong! */
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun extern uint sd_msglevel;	/* Debug message level */
519*4882a593Smuzhiyun module_param(sd_msglevel, uint, 0);
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun extern uint sd_power;	/* 0 = SD Power OFF, 1 = SD Power ON. */
522*4882a593Smuzhiyun module_param(sd_power, uint, 0);
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun extern uint sd_clock;	/* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
525*4882a593Smuzhiyun module_param(sd_clock, uint, 0);
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun extern uint sd_divisor;	/* Divisor (-1 means external clock) */
528*4882a593Smuzhiyun module_param(sd_divisor, uint, 0);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun extern uint sd_sdmode;	/* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
531*4882a593Smuzhiyun module_param(sd_sdmode, uint, 0);
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun extern uint sd_hiok;	/* Ok to use hi-speed mode */
534*4882a593Smuzhiyun module_param(sd_hiok, uint, 0);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun extern uint sd_f2_blocksize;
537*4882a593Smuzhiyun module_param(sd_f2_blocksize, int, 0);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun extern uint sd_f1_blocksize;
540*4882a593Smuzhiyun module_param(sd_f1_blocksize, int, 0);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun #ifdef BCMSDIOH_STD
543*4882a593Smuzhiyun extern int sd_uhsimode;
544*4882a593Smuzhiyun module_param(sd_uhsimode, int, 0);
545*4882a593Smuzhiyun extern uint sd_tuning_period;
546*4882a593Smuzhiyun module_param(sd_tuning_period, uint, 0);
547*4882a593Smuzhiyun extern int sd_delay_value;
548*4882a593Smuzhiyun module_param(sd_delay_value, uint, 0);
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
551*4882a593Smuzhiyun extern char dhd_sdiod_uhsi_ds_override[2];
552*4882a593Smuzhiyun module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2, 0);
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun #endif
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun #ifdef BCMSDH_MODULE
557*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_attach);
558*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_detach);
559*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_query);
560*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_enable);
561*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_disable);
562*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_reg);
563*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_dereg);
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun #if defined(DHD_DEBUG) || defined(BCMDBG)
566*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_intr_pending);
567*4882a593Smuzhiyun #endif
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun #if defined (BT_OVER_SDIO)
570*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_btsdio_interface_init);
571*4882a593Smuzhiyun #endif /* defined (BT_OVER_SDIO) */
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_devremove_reg);
574*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cfg_read);
575*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cfg_write);
576*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cis_read);
577*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_reg_read);
578*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_reg_write);
579*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_regfail);
580*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_send_buf);
581*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_recv_buf);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_rwdata);
584*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_abort);
585*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_query_device);
586*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_query_iofnum);
587*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_iovar_op);
588*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_register);
589*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_unregister);
590*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_chipmatch);
591*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_reset);
592*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_waitlockfree);
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_get_dstatus);
595*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cfg_read_word);
596*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cfg_write_word);
597*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_cur_sbwad);
598*4882a593Smuzhiyun EXPORT_SYMBOL(bcmsdh_chipinfo);
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun #endif /* BCMSDH_MODULE */
601