update rkwifi bcmdhd to version 1.201.34.1
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / bcmsdh_linux.c
1 /*
2  * SDIO access interface for drivers - linux specific (pci only)
3  *
4  * $Copyright Open Broadcom Corporation$
5  *
6  * $Id: bcmsdh_linux.c 461444 2014-03-12 02:55:28Z $
7  */
8
9 /**
10  * @file bcmsdh_linux.c
11  */
12
13 #define __UNDEF_NO_VERSION__
14
15 #include <typedefs.h>
16 #include <linuxver.h>
17 #include <linux/pci.h>
18 #include <linux/completion.h>
19
20 #include <osl.h>
21 #include <pcicfg.h>
22 #include <bcmdefs.h>
23 #include <bcmdevs.h>
24 #include <linux/irq.h>
25 extern void dhdsdio_isr(void * args);
26 #include <bcmutils.h>
27 #include <dngl_stats.h>
28 #include <dhd.h>
29 #if defined(CONFIG_ARCH_ODIN)
30 #include <linux/platform_data/gpio-odin.h>
31 #endif /* defined(CONFIG_ARCH_ODIN) */
32 #include <dhd_linux.h>
33
34 /* driver info, initialized when bcmsdh_register is called */
35 static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL};
36
37 typedef enum {
38         DHD_INTR_INVALID = 0,
39         DHD_INTR_INBAND,
40         DHD_INTR_HWOOB,
41         DHD_INTR_SWOOB
42 } DHD_HOST_INTR_TYPE;
43
44 /* the BCMSDH module comprises the generic part (bcmsdh.c) and OS specific layer (e.g.
45  * bcmsdh_linux.c). Put all OS specific variables (e.g. irq number and flags) here rather
46  * than in the common structure bcmsdh_info. bcmsdh_info only keeps a handle (os_ctx) to this
47  * structure.
48  */
49 typedef struct bcmsdh_os_info {
50         DHD_HOST_INTR_TYPE      intr_type;
51         int                     oob_irq_num;    /* valid when hardware or software oob in use */
52         unsigned long           oob_irq_flags;  /* valid when hardware or software oob in use */
53         bool                    oob_irq_registered;
54         bool                    oob_irq_enabled;
55         bool                    oob_irq_wake_enabled;
56         spinlock_t              oob_irq_spinlock;
57         bcmsdh_cb_fn_t          oob_irq_handler;
58         void                    *oob_irq_handler_context;
59         void                    *context;       /* context returned from upper layer */
60         void                    *sdioh;         /* handle to lower layer (sdioh) */
61         void                    *dev;           /* handle to the underlying device */
62         bool                    dev_wake_enabled;
63 } bcmsdh_os_info_t;
64
65 /* debugging macros */
66 #define SDLX_MSG(x) printf x
67
68 /**
69  * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
70  */
71 bool
72 bcmsdh_chipmatch(uint16 vendor, uint16 device)
73 {
74         /* Add other vendors and devices as required */
75
76 #ifdef BCMSDIOH_STD
77         /* Check for Arasan host controller */
78         if (vendor == VENDOR_SI_IMAGE) {
79                 return (TRUE);
80         }
81         /* Check for BRCM 27XX Standard host controller */
82         if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) {
83                 return (TRUE);
84         }
85         /* Check for BRCM Standard host controller */
86         if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) {
87                 return (TRUE);
88         }
89         /* Check for TI PCIxx21 Standard host controller */
90         if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) {
91                 return (TRUE);
92         }
93         if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) {
94                 return (TRUE);
95         }
96         /* Ricoh R5C822 Standard SDIO Host */
97         if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) {
98                 return (TRUE);
99         }
100         /* JMicron Standard SDIO Host */
101         if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) {
102                 return (TRUE);
103         }
104
105 #endif /* BCMSDIOH_STD */
106 #ifdef BCMSDIOH_SPI
107         /* This is the PciSpiHost. */
108         if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
109                 printf("Found PCI SPI Host Controller\n");
110                 return (TRUE);
111         }
112
113 #endif /* BCMSDIOH_SPI */
114
115         return (FALSE);
116 }
117
118 void* bcmsdh_probe(osl_t *osh, void *dev, void *sdioh, void *adapter_info, uint bus_type,
119         uint bus_num, uint slot_num)
120 {
121         ulong regs;
122         bcmsdh_info_t *bcmsdh;
123         uint32 vendevid;
124         bcmsdh_os_info_t *bcmsdh_osinfo = NULL;
125
126         bcmsdh = bcmsdh_attach(osh, sdioh, &regs);
127         if (bcmsdh == NULL) {
128                 SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__));
129                 goto err;
130         }
131         bcmsdh_osinfo = MALLOC(osh, sizeof(bcmsdh_os_info_t));
132         if (bcmsdh_osinfo == NULL) {
133                 SDLX_MSG(("%s: failed to allocate bcmsdh_os_info_t\n", __FUNCTION__));
134                 goto err;
135         }
136         bzero((char *)bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
137         bcmsdh->os_cxt = bcmsdh_osinfo;
138         bcmsdh_osinfo->sdioh = sdioh;
139         bcmsdh_osinfo->dev = dev;
140         osl_set_bus_handle(osh, bcmsdh);
141
142 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
143         if (dev && device_init_wakeup(dev, true) == 0)
144                 bcmsdh_osinfo->dev_wake_enabled = TRUE;
145 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
146
147 #if defined(OOB_INTR_ONLY)
148         spin_lock_init(&bcmsdh_osinfo->oob_irq_spinlock);
149         /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */
150         bcmsdh_osinfo->oob_irq_num = wifi_platform_get_irq_number(adapter_info,
151                 &bcmsdh_osinfo->oob_irq_flags);
152         if  (bcmsdh_osinfo->oob_irq_num < 0) {
153                 SDLX_MSG(("%s: Host OOB irq is not defined\n", __FUNCTION__));
154                 goto err;
155         }
156 #endif /* defined(BCMLXSDMMC) */
157
158         /* Read the vendor/device ID from the CIS */
159         vendevid = bcmsdh_query_device(bcmsdh);
160         /* try to attach to the target device */
161         bcmsdh_osinfo->context = drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
162                 slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
163         if (bcmsdh_osinfo->context == NULL) {
164                 SDLX_MSG(("%s: device attach failed\n", __FUNCTION__));
165                 goto err;
166         }
167
168         return bcmsdh;
169
170         /* error handling */
171 err:
172         if (bcmsdh != NULL)
173                 bcmsdh_detach(osh, bcmsdh);
174         if (bcmsdh_osinfo != NULL)
175                 MFREE(osh, bcmsdh_osinfo, sizeof(bcmsdh_os_info_t));
176         return NULL;
177 }
178
179 int bcmsdh_remove(bcmsdh_info_t *bcmsdh)
180 {
181         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
182
183 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
184         if (bcmsdh_osinfo->dev)
185                 device_init_wakeup(bcmsdh_osinfo->dev, false);
186         bcmsdh_osinfo->dev_wake_enabled = FALSE;
187 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
188
189         drvinfo.remove(bcmsdh_osinfo->context);
190         MFREE(bcmsdh->osh, bcmsdh->os_cxt, sizeof(bcmsdh_os_info_t));
191         bcmsdh_detach(bcmsdh->osh, bcmsdh);
192
193         return 0;
194 }
195
196 int bcmsdh_suspend(bcmsdh_info_t *bcmsdh)
197 {
198         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
199
200         if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context))
201                 return -EBUSY;
202         return 0;
203 }
204
205 int bcmsdh_resume(bcmsdh_info_t *bcmsdh)
206 {
207         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
208
209         if (drvinfo.resume)
210                 return drvinfo.resume(bcmsdh_osinfo->context);
211         return 0;
212 }
213
214 extern int bcmsdh_register_client_driver(void);
215 extern void bcmsdh_unregister_client_driver(void);
216 extern int sdio_func_reg_notify(void* semaphore);
217 extern void sdio_func_unreg_notify(void);
218
219 #if defined(BCMLXSDMMC)
220 int bcmsdh_reg_sdio_notify(void* semaphore)
221 {
222         return sdio_func_reg_notify(semaphore);
223 }
224
225 void bcmsdh_unreg_sdio_notify(void)
226 {
227         sdio_func_unreg_notify();
228 }
229 #endif /* defined(BCMLXSDMMC) */
230
231 int
232 bcmsdh_register(bcmsdh_driver_t *driver)
233 {
234         int error = 0;
235
236         drvinfo = *driver;
237         SDLX_MSG(("%s: register client driver\n", __FUNCTION__));
238         error = bcmsdh_register_client_driver();
239         if (error)
240                 SDLX_MSG(("%s: failed %d\n", __FUNCTION__, error));
241
242         return error;
243 }
244
245 void
246 bcmsdh_unregister(void)
247 {
248 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
249                 if (bcmsdh_pci_driver.node.next == NULL)
250                         return;
251 #endif
252
253         bcmsdh_unregister_client_driver();
254 }
255
256 void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh)
257 {
258 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
259         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
260         pm_stay_awake(bcmsdh_osinfo->dev);
261 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
262 }
263
264 void bcmsdh_dev_relax(bcmsdh_info_t *bcmsdh)
265 {
266 #if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36))
267         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
268         pm_relax(bcmsdh_osinfo->dev);
269 #endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */
270 }
271
272 bool bcmsdh_dev_pm_enabled(bcmsdh_info_t *bcmsdh)
273 {
274         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
275
276         return bcmsdh_osinfo->dev_wake_enabled;
277 }
278
279 #if defined(OOB_INTR_ONLY)
280 void bcmsdh_oob_intr_set(bcmsdh_info_t *bcmsdh, bool enable)
281 {
282         unsigned long flags;
283         bcmsdh_os_info_t *bcmsdh_osinfo;
284
285         if (!bcmsdh)
286                 return;
287
288         bcmsdh_osinfo = bcmsdh->os_cxt;
289         spin_lock_irqsave(&bcmsdh_osinfo->oob_irq_spinlock, flags);
290         if (bcmsdh_osinfo->oob_irq_enabled != enable) {
291                 if (enable)
292                         enable_irq(bcmsdh_osinfo->oob_irq_num);
293                 else
294                         disable_irq_nosync(bcmsdh_osinfo->oob_irq_num);
295                 bcmsdh_osinfo->oob_irq_enabled = enable;
296         }
297         spin_unlock_irqrestore(&bcmsdh_osinfo->oob_irq_spinlock, flags);
298 }
299
300 static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
301 {
302         bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)dev_id;
303         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
304
305         bcmsdh_oob_intr_set(bcmsdh, FALSE);
306         bcmsdh_osinfo->oob_irq_handler(bcmsdh_osinfo->oob_irq_handler_context);
307
308         return IRQ_HANDLED;
309 }
310
311 int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler,
312         void* oob_irq_handler_context)
313 {
314         int err = 0;
315         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
316
317         SDLX_MSG(("%s: Enter\n", __FUNCTION__));
318         if (bcmsdh_osinfo->oob_irq_registered) {
319                 SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__));
320                 return -EBUSY;
321         }
322         SDLX_MSG(("%s OOB irq=%d flags=%X\n", __FUNCTION__,
323                 (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags));
324         bcmsdh_osinfo->oob_irq_handler = oob_irq_handler;
325         bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context;
326 #if defined(CONFIG_ARCH_ODIN)
327         err = odin_gpio_sms_request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
328                 bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
329 #else
330         err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq,
331                 bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);
332 #endif /* defined(CONFIG_ARCH_ODIN) */
333         if (err) {
334                 SDLX_MSG(("%s: request_irq failed with %d\n", __FUNCTION__, err));
335                 return err;
336         }
337
338 #if defined(DISABLE_WOWLAN)
339         SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__));
340         err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
341         if (err)
342                 SDLX_MSG(("%s: disable_irq_wake failed with %d\n", __FUNCTION__, err));
343         else
344                 bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
345 #else
346         SDLX_MSG(("%s: enable_irq_wake\n", __FUNCTION__));
347         err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num);
348         if (err)
349                 SDLX_MSG(("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err));
350         else
351                 bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;
352 #endif
353         bcmsdh_osinfo->oob_irq_enabled = TRUE;
354         bcmsdh_osinfo->oob_irq_registered = TRUE;
355         return err;
356 }
357
358 void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh)
359 {
360         int err = 0;
361         bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt;
362
363         SDLX_MSG(("%s: Enter\n", __FUNCTION__));
364         if (!bcmsdh_osinfo->oob_irq_registered) {
365                 SDLX_MSG(("%s: irq is not registered\n", __FUNCTION__));
366                 return;
367         }
368         if (bcmsdh_osinfo->oob_irq_wake_enabled) {
369                 err = disable_irq_wake(bcmsdh_osinfo->oob_irq_num);
370                 if (!err)
371                         bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;
372         }
373         if (bcmsdh_osinfo->oob_irq_enabled) {
374                 disable_irq(bcmsdh_osinfo->oob_irq_num);
375                 bcmsdh_osinfo->oob_irq_enabled = FALSE;
376         }
377         free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh);
378         bcmsdh_osinfo->oob_irq_registered = FALSE;
379 }
380 #endif 
381
382 /* Module parameters specific to each host-controller driver */
383
384 extern uint sd_msglevel;        /* Debug message level */
385 module_param(sd_msglevel, uint, 0);
386
387 extern uint sd_power;   /* 0 = SD Power OFF, 1 = SD Power ON. */
388 module_param(sd_power, uint, 0);
389
390 extern uint sd_clock;   /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */
391 module_param(sd_clock, uint, 0);
392
393 extern uint sd_divisor; /* Divisor (-1 means external clock) */
394 module_param(sd_divisor, uint, 0);
395
396 extern uint sd_sdmode;  /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
397 module_param(sd_sdmode, uint, 0);
398
399 extern uint sd_hiok;    /* Ok to use hi-speed mode */
400 module_param(sd_hiok, uint, 0);
401
402 extern uint sd_f2_blocksize;
403 module_param(sd_f2_blocksize, int, 0);
404
405 #ifdef BCMSDIOH_STD
406 extern int sd_uhsimode;
407 module_param(sd_uhsimode, int, 0);
408 extern uint sd_tuning_period;
409 module_param(sd_tuning_period, uint, 0);
410 extern int sd_delay_value;
411 module_param(sd_delay_value, uint, 0);
412
413 /* SDIO Drive Strength for UHSI mode specific to SDIO3.0 */
414 extern char dhd_sdiod_uhsi_ds_override[2];
415 module_param_string(dhd_sdiod_uhsi_ds_override, dhd_sdiod_uhsi_ds_override, 2, 0);
416
417 #endif
418
419 #ifdef BCMSDH_MODULE
420 EXPORT_SYMBOL(bcmsdh_attach);
421 EXPORT_SYMBOL(bcmsdh_detach);
422 EXPORT_SYMBOL(bcmsdh_intr_query);
423 EXPORT_SYMBOL(bcmsdh_intr_enable);
424 EXPORT_SYMBOL(bcmsdh_intr_disable);
425 EXPORT_SYMBOL(bcmsdh_intr_reg);
426 EXPORT_SYMBOL(bcmsdh_intr_dereg);
427
428 #if defined(DHD_DEBUG)
429 EXPORT_SYMBOL(bcmsdh_intr_pending);
430 #endif
431
432 EXPORT_SYMBOL(bcmsdh_devremove_reg);
433 EXPORT_SYMBOL(bcmsdh_cfg_read);
434 EXPORT_SYMBOL(bcmsdh_cfg_write);
435 EXPORT_SYMBOL(bcmsdh_cis_read);
436 EXPORT_SYMBOL(bcmsdh_reg_read);
437 EXPORT_SYMBOL(bcmsdh_reg_write);
438 EXPORT_SYMBOL(bcmsdh_regfail);
439 EXPORT_SYMBOL(bcmsdh_send_buf);
440 EXPORT_SYMBOL(bcmsdh_recv_buf);
441
442 EXPORT_SYMBOL(bcmsdh_rwdata);
443 EXPORT_SYMBOL(bcmsdh_abort);
444 EXPORT_SYMBOL(bcmsdh_query_device);
445 EXPORT_SYMBOL(bcmsdh_query_iofnum);
446 EXPORT_SYMBOL(bcmsdh_iovar_op);
447 EXPORT_SYMBOL(bcmsdh_register);
448 EXPORT_SYMBOL(bcmsdh_unregister);
449 EXPORT_SYMBOL(bcmsdh_chipmatch);
450 EXPORT_SYMBOL(bcmsdh_reset);
451 EXPORT_SYMBOL(bcmsdh_waitlockfree);
452
453 EXPORT_SYMBOL(bcmsdh_get_dstatus);
454 EXPORT_SYMBOL(bcmsdh_cfg_read_word);
455 EXPORT_SYMBOL(bcmsdh_cfg_write_word);
456 EXPORT_SYMBOL(bcmsdh_cur_sbwad);
457 EXPORT_SYMBOL(bcmsdh_chipinfo);
458
459 #endif /* BCMSDH_MODULE */