OSDN Git Service

d24b5e7d753cd2f808a9fb75d429696b2748b4a9
[uclinux-h8/linux.git] / drivers / staging / brcm80211 / brcmfmac / bcmsdh_linux.c
1 /*
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  */
16
17 /**
18  * @file bcmsdh_linux.c
19  */
20
21 #define __UNDEF_NO_VERSION__
22
23 #include <linux/netdevice.h>
24 #include <linux/pci.h>
25 #include <linux/completion.h>
26
27 #include <osl.h>
28 #include <pcicfg.h>
29 #include <bcmdefs.h>
30 #include <bcmdevs.h>
31
32 #if defined(OOB_INTR_ONLY)
33 #include <linux/irq.h>
34 extern void dhdsdio_isr(void *args);
35 #include <bcmutils.h>
36 #include <dngl_stats.h>
37 #include <dhd.h>
38 #endif                          /* defined(OOB_INTR_ONLY) */
39 #if defined(CONFIG_MACH_SANDGATE2G) || defined(CONFIG_MACH_LOGICPD_PXA270)
40 #if !defined(BCMPLATFORM_BUS)
41 #define BCMPLATFORM_BUS
42 #endif                          /* !defined(BCMPLATFORM_BUS) */
43
44 #include <linux/platform_device.h>
45 #endif                          /* CONFIG_MACH_SANDGATE2G */
46
47 /**
48  * SDIO Host Controller info
49  */
50 typedef struct bcmsdh_hc bcmsdh_hc_t;
51
52 struct bcmsdh_hc {
53         bcmsdh_hc_t *next;
54 #ifdef BCMPLATFORM_BUS
55         struct device *dev;     /* platform device handle */
56 #else
57         struct pci_dev *dev;    /* pci device handle */
58 #endif                          /* BCMPLATFORM_BUS */
59         struct osl_info *osh;
60         void *regs;             /* SDIO Host Controller address */
61         bcmsdh_info_t *sdh;     /* SDIO Host Controller handle */
62         void *ch;
63         unsigned int oob_irq;
64         unsigned long oob_flags;        /* OOB Host specifiction
65                                         as edge and etc */
66         bool oob_irq_registered;
67 #if defined(OOB_INTR_ONLY)
68         spinlock_t irq_lock;
69 #endif
70 };
71 static bcmsdh_hc_t *sdhcinfo;
72
73 /* driver info, initialized when bcmsdh_register is called */
74 static bcmsdh_driver_t drvinfo = { NULL, NULL };
75
76 /* debugging macros */
77 #define SDLX_MSG(x)
78
79 /**
80  * Checks to see if vendor and device IDs match a supported SDIO Host Controller.
81  */
82 bool bcmsdh_chipmatch(u16 vendor, u16 device)
83 {
84         /* Add other vendors and devices as required */
85
86 #ifdef BCMSDIOH_STD
87         /* Check for Arasan host controller */
88         if (vendor == VENDOR_SI_IMAGE)
89                 return true;
90
91         /* Check for BRCM 27XX Standard host controller */
92         if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM)
93                 return true;
94
95         /* Check for BRCM Standard host controller */
96         if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM)
97                 return true;
98
99         /* Check for TI PCIxx21 Standard host controller */
100         if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI)
101                 return true;
102
103         if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI)
104                 return true;
105
106         /* Ricoh R5C822 Standard SDIO Host */
107         if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH)
108                 return true;
109
110         /* JMicron Standard SDIO Host */
111         if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON)
112                 return true;
113 #endif                          /* BCMSDIOH_STD */
114 #ifdef BCMSDIOH_SPI
115         /* This is the PciSpiHost. */
116         if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) {
117                 printf("Found PCI SPI Host Controller\n");
118                 return true;
119         }
120 #endif                          /* BCMSDIOH_SPI */
121
122         return false;
123 }
124
125 #if defined(BCMPLATFORM_BUS)
126 #if defined(BCMLXSDMMC)
127 /* forward declarations */
128 int bcmsdh_probe(struct device *dev);
129 EXPORT_SYMBOL(bcmsdh_probe);
130
131 int bcmsdh_remove(struct device *dev);
132 EXPORT_SYMBOL(bcmsdh_remove);
133
134 #else
135 /* forward declarations */
136 static int __devinit bcmsdh_probe(struct device *dev);
137 static int __devexit bcmsdh_remove(struct device *dev);
138 #endif                          /* BCMLXSDMMC */
139
140 #ifndef BCMLXSDMMC
141 static
142 #endif                          /* BCMLXSDMMC */
143 int bcmsdh_probe(struct device *dev)
144 {
145         struct osl_info *osh = NULL;
146         bcmsdh_hc_t *sdhc = NULL;
147         unsigned long regs = 0;
148         bcmsdh_info_t *sdh = NULL;
149 #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
150         struct platform_device *pdev;
151         struct resource *r;
152 #endif                          /* BCMLXSDMMC */
153         int irq = 0;
154         u32 vendevid;
155         unsigned long irq_flags = 0;
156
157 #if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS)
158         pdev = to_platform_device(dev);
159         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
160         irq = platform_get_irq(pdev, 0);
161         if (!r || irq == NO_IRQ)
162                 return -ENXIO;
163 #endif                          /* BCMLXSDMMC */
164
165 #if defined(OOB_INTR_ONLY)
166 #ifdef HW_OOB
167         irq_flags =
168             IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
169             IORESOURCE_IRQ_SHAREABLE;
170 #else
171         irq_flags = IRQF_TRIGGER_FALLING;
172 #endif                          /* HW_OOB */
173         irq = dhd_customer_oob_irq_map(&irq_flags);
174         if (irq < 0) {
175                 SDLX_MSG(("%s: Host irq is not defined\n", __func__));
176                 return 1;
177         }
178 #endif                          /* defined(OOB_INTR_ONLY) */
179         /* allocate SDIO Host Controller state info */
180         osh = osl_attach(dev, PCI_BUS);
181         if (!osh) {
182                 SDLX_MSG(("%s: osl_attach failed\n", __func__));
183                 goto err;
184         }
185         sdhc = kzalloc(sizeof(bcmsdh_hc_t), GFP_ATOMIC);
186         if (!sdhc) {
187                 SDLX_MSG(("%s: out of memory\n", __func__));
188                 goto err;
189         }
190         sdhc->osh = osh;
191
192         sdhc->dev = (void *)dev;
193
194 #ifdef BCMLXSDMMC
195         sdh = bcmsdh_attach(osh, (void *)0, (void **)&regs, irq);
196         if (!sdh) {
197                 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__));
198                 goto err;
199         }
200 #else
201         sdh = bcmsdh_attach(osh, (void *)r->start, (void **)&regs, irq);
202         if (!sdh) {
203                 SDLX_MSG(("%s: bcmsdh_attach failed\n", __func__));
204                 goto err;
205         }
206 #endif                          /* BCMLXSDMMC */
207         sdhc->sdh = sdh;
208         sdhc->oob_irq = irq;
209         sdhc->oob_flags = irq_flags;
210         sdhc->oob_irq_registered = false;       /* to make sure.. */
211 #if defined(OOB_INTR_ONLY)
212         spin_lock_init(&sdhc->irq_lock);
213 #endif
214
215         /* chain SDIO Host Controller info together */
216         sdhc->next = sdhcinfo;
217         sdhcinfo = sdhc;
218         /* Read the vendor/device ID from the CIS */
219         vendevid = bcmsdh_query_device(sdh);
220
221         /* try to attach to the target device */
222         sdhc->ch = drvinfo.attach((vendevid >> 16), (vendevid & 0xFFFF),
223                                 0, 0, 0, 0, (void *)regs, NULL, sdh);
224         if (!sdhc->ch) {
225                 SDLX_MSG(("%s: device attach failed\n", __func__));
226                 goto err;
227         }
228
229         return 0;
230
231         /* error handling */
232 err:
233         if (sdhc) {
234                 if (sdhc->sdh)
235                         bcmsdh_detach(sdhc->osh, sdhc->sdh);
236                 kfree(sdhc);
237         }
238         if (osh)
239                 osl_detach(osh);
240         return -ENODEV;
241 }
242
243 #ifndef BCMLXSDMMC
244 static
245 #endif                          /* BCMLXSDMMC */
246 int bcmsdh_remove(struct device *dev)
247 {
248         bcmsdh_hc_t *sdhc, *prev;
249         struct osl_info *osh;
250
251         sdhc = sdhcinfo;
252         drvinfo.detach(sdhc->ch);
253         bcmsdh_detach(sdhc->osh, sdhc->sdh);
254         /* find the SDIO Host Controller state for this pdev
255                  and take it out from the list */
256         for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
257                 if (sdhc->dev == (void *)dev) {
258                         if (prev)
259                                 prev->next = sdhc->next;
260                         else
261                                 sdhcinfo = NULL;
262                         break;
263                 }
264                 prev = sdhc;
265         }
266         if (!sdhc) {
267                 SDLX_MSG(("%s: failed\n", __func__));
268                 return 0;
269         }
270
271         /* release SDIO Host Controller info */
272         osh = sdhc->osh;
273         kfree(sdhc);
274         osl_detach(osh);
275
276 #if !defined(BCMLXSDMMC)
277         dev_set_drvdata(dev, NULL);
278 #endif                          /* !defined(BCMLXSDMMC) */
279
280         return 0;
281 }
282 #endif                          /* BCMPLATFORM_BUS */
283
284 extern int sdio_function_init(void);
285
286 int bcmsdh_register(bcmsdh_driver_t *driver)
287 {
288         drvinfo = *driver;
289
290         SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
291         return sdio_function_init();
292 }
293
294 extern void sdio_function_cleanup(void);
295
296 void bcmsdh_unregister(void)
297 {
298         sdio_function_cleanup();
299 }
300
301 #if defined(OOB_INTR_ONLY)
302 void bcmsdh_oob_intr_set(bool enable)
303 {
304         static bool curstate = 1;
305         unsigned long flags;
306
307         spin_lock_irqsave(&sdhcinfo->irq_lock, flags);
308         if (curstate != enable) {
309                 if (enable)
310                         enable_irq(sdhcinfo->oob_irq);
311                 else
312                         disable_irq_nosync(sdhcinfo->oob_irq);
313                 curstate = enable;
314         }
315         spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags);
316 }
317
318 static irqreturn_t wlan_oob_irq(int irq, void *dev_id)
319 {
320         dhd_pub_t *dhdp;
321
322         dhdp = (dhd_pub_t *) dev_get_drvdata(sdhcinfo->dev);
323
324         bcmsdh_oob_intr_set(0);
325
326         if (dhdp == NULL) {
327                 SDLX_MSG(("Out of band GPIO interrupt fired way too early\n"));
328                 return IRQ_HANDLED;
329         }
330
331         WAKE_LOCK_TIMEOUT(dhdp, WAKE_LOCK_TMOUT, 25);
332
333         dhdsdio_isr((void *)dhdp->bus);
334
335         return IRQ_HANDLED;
336 }
337
338 int bcmsdh_register_oob_intr(void *dhdp)
339 {
340         int error = 0;
341
342         SDLX_MSG(("%s Enter\n", __func__));
343
344         sdhcinfo->oob_flags =
345             IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL |
346             IORESOURCE_IRQ_SHAREABLE;
347         dev_set_drvdata(sdhcinfo->dev, dhdp);
348
349         if (!sdhcinfo->oob_irq_registered) {
350                 SDLX_MSG(("%s IRQ=%d Type=%X\n", __func__,
351                           (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags));
352                 /* Refer to customer Host IRQ docs about
353                          proper irqflags definition */
354                 error =
355                     request_irq(sdhcinfo->oob_irq, wlan_oob_irq,
356                                 sdhcinfo->oob_flags, "bcmsdh_sdmmc", NULL);
357                 if (error)
358                         return -ENODEV;
359
360                 set_irq_wake(sdhcinfo->oob_irq, 1);
361                 sdhcinfo->oob_irq_registered = true;
362         }
363
364         return 0;
365 }
366
367 void bcmsdh_unregister_oob_intr(void)
368 {
369         SDLX_MSG(("%s: Enter\n", __func__));
370
371         set_irq_wake(sdhcinfo->oob_irq, 0);
372         disable_irq(sdhcinfo->oob_irq); /* just in case.. */
373         free_irq(sdhcinfo->oob_irq, NULL);
374         sdhcinfo->oob_irq_registered = false;
375 }
376 #endif                          /* defined(OOB_INTR_ONLY) */
377 /* Module parameters specific to each host-controller driver */
378
379 extern uint sd_msglevel;        /* Debug message level */
380 module_param(sd_msglevel, uint, 0);
381
382 extern uint sd_power;           /* 0 = SD Power OFF,
383                                          1 = SD Power ON. */
384 module_param(sd_power, uint, 0);
385
386 extern uint sd_clock;           /* SD Clock Control, 0 = SD Clock OFF,
387                                  1 = SD Clock ON */
388 module_param(sd_clock, uint, 0);
389
390 extern uint sd_divisor;         /* Divisor (-1 means external clock) */
391 module_param(sd_divisor, uint, 0);
392
393 extern uint sd_sdmode;          /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */
394 module_param(sd_sdmode, uint, 0);
395
396 extern uint sd_hiok;            /* Ok to use hi-speed mode */
397 module_param(sd_hiok, uint, 0);
398
399 extern uint sd_f2_blocksize;
400 module_param(sd_f2_blocksize, int, 0);