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