Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[pandora-kernel.git] / drivers / staging / brcm80211 / util / linux_osl.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 #include <linux/delay.h>
18 #include <linux/fs.h>
19 #ifdef mips
20 #include <asm/paccess.h>
21 #endif                          /* mips */
22 #include <bcmendian.h>
23 #include <linuxver.h>
24 #include <bcmdefs.h>
25 #include <osl.h>
26 #include <bcmutils.h>
27 #include <pcicfg.h>
28
29
30 #define PCI_CFG_RETRY           10
31
32 #define OS_HANDLE_MAGIC         0x1234abcd      /* Magic # to recognise osh */
33 #define BCM_MEM_FILENAME_LEN    24      /* Mem. filename length */
34
35 struct osl_info {
36         osl_pubinfo_t pub;
37         uint magic;
38         void *pdev;
39         uint failed;
40         uint bustype;
41 };
42
43 /* Global ASSERT type flag */
44 u32 g_assert_type;
45
46 #ifdef BRCM_FULLMAC
47 static s16 linuxbcmerrormap[] = { 0,    /* 0 */
48         -EINVAL,                /* BCME_ERROR */
49         -EINVAL,                /* BCME_BADARG */
50         -EINVAL,                /* BCME_BADOPTION */
51         -EINVAL,                /* BCME_NOTUP */
52         -EINVAL,                /* BCME_NOTDOWN */
53         -EINVAL,                /* BCME_NOTAP */
54         -EINVAL,                /* BCME_NOTSTA */
55         -EINVAL,                /* BCME_BADKEYIDX */
56         -EINVAL,                /* BCME_RADIOOFF */
57         -EINVAL,                /* BCME_NOTBANDLOCKED */
58         -EINVAL,                /* BCME_NOCLK */
59         -EINVAL,                /* BCME_BADRATESET */
60         -EINVAL,                /* BCME_BADBAND */
61         -E2BIG,                 /* BCME_BUFTOOSHORT */
62         -E2BIG,                 /* BCME_BUFTOOLONG */
63         -EBUSY,                 /* BCME_BUSY */
64         -EINVAL,                /* BCME_NOTASSOCIATED */
65         -EINVAL,                /* BCME_BADSSIDLEN */
66         -EINVAL,                /* BCME_OUTOFRANGECHAN */
67         -EINVAL,                /* BCME_BADCHAN */
68         -EFAULT,                /* BCME_BADADDR */
69         -ENOMEM,                /* BCME_NORESOURCE */
70         -EOPNOTSUPP,            /* BCME_UNSUPPORTED */
71         -EMSGSIZE,              /* BCME_BADLENGTH */
72         -EINVAL,                /* BCME_NOTREADY */
73         -EPERM,                 /* BCME_NOTPERMITTED */
74         -ENOMEM,                /* BCME_NOMEM */
75         -EINVAL,                /* BCME_ASSOCIATED */
76         -ERANGE,                /* BCME_RANGE */
77         -EINVAL,                /* BCME_NOTFOUND */
78         -EINVAL,                /* BCME_WME_NOT_ENABLED */
79         -EINVAL,                /* BCME_TSPEC_NOTFOUND */
80         -EINVAL,                /* BCME_ACM_NOTSUPPORTED */
81         -EINVAL,                /* BCME_NOT_WME_ASSOCIATION */
82         -EIO,                   /* BCME_SDIO_ERROR */
83         -ENODEV,                /* BCME_DONGLE_DOWN */
84         -EINVAL,                /* BCME_VERSION */
85         -EIO,                   /* BCME_TXFAIL */
86         -EIO,                   /* BCME_RXFAIL */
87         -EINVAL,                /* BCME_NODEVICE */
88         -EINVAL,                /* BCME_NMODE_DISABLED */
89         -ENODATA,               /* BCME_NONRESIDENT */
90
91 /* When an new error code is added to bcmutils.h, add os
92  * spcecific error translation here as well
93  */
94 /* check if BCME_LAST changed since the last time this function was updated */
95 #if BCME_LAST != -42
96 #error "You need to add a OS error translation in the linuxbcmerrormap \
97         for new error code defined in bcmutils.h"
98 #endif
99 };
100
101 /* translate bcmerrors into linux errors */
102 int osl_error(int bcmerror)
103 {
104         if (bcmerror > 0)
105                 bcmerror = 0;
106         else if (bcmerror < BCME_LAST)
107                 bcmerror = BCME_ERROR;
108
109         /* Array bounds covered by ASSERT in osl_attach */
110         return linuxbcmerrormap[-bcmerror];
111 }
112 #endif /* BRCM_FULLMAC */
113
114 osl_t *osl_attach(void *pdev, uint bustype, bool pkttag)
115 {
116         osl_t *osh;
117
118         osh = kmalloc(sizeof(osl_t), GFP_ATOMIC);
119         ASSERT(osh);
120
121         bzero(osh, sizeof(osl_t));
122
123 #ifdef BRCM_FULLMAC
124         /* Check that error map has the right number of entries in it */
125         ASSERT(ABS(BCME_LAST) == (ARRAY_SIZE(linuxbcmerrormap) - 1));
126 #endif /* BRCM_FULLMAC */
127
128         osh->magic = OS_HANDLE_MAGIC;
129         osh->failed = 0;
130         osh->pdev = pdev;
131         osh->pub.pkttag = pkttag;
132         osh->bustype = bustype;
133
134         switch (bustype) {
135         case PCI_BUS:
136         case SI_BUS:
137         case PCMCIA_BUS:
138                 osh->pub.mmbus = true;
139                 break;
140         case JTAG_BUS:
141         case SDIO_BUS:
142         case USB_BUS:
143         case SPI_BUS:
144         case RPC_BUS:
145                 osh->pub.mmbus = false;
146                 break;
147         default:
148                 ASSERT(false);
149                 break;
150         }
151
152 #if defined(BCMDBG) && !defined(BRCM_FULLMAC)
153         if (pkttag) {
154                 struct sk_buff *skb;
155                 ASSERT(OSL_PKTTAG_SZ <= sizeof(skb->cb));
156         }
157 #endif
158         return osh;
159 }
160
161 void osl_detach(osl_t *osh)
162 {
163         if (osh == NULL)
164                 return;
165
166         ASSERT(osh->magic == OS_HANDLE_MAGIC);
167         kfree(osh);
168 }
169
170 /* Return a new packet. zero out pkttag */
171 void *BCMFASTPATH osl_pktget(osl_t *osh, uint len)
172 {
173         struct sk_buff *skb;
174
175         skb = dev_alloc_skb(len);
176         if (skb) {
177                 skb_put(skb, len);
178                 skb->priority = 0;
179
180                 osh->pub.pktalloced++;
181         }
182
183         return (void *)skb;
184 }
185
186 /* Free the driver packet. Free the tag if present */
187 void BCMFASTPATH osl_pktfree(osl_t *osh, void *p, bool send)
188 {
189         struct sk_buff *skb, *nskb;
190         int nest = 0;
191
192         skb = (struct sk_buff *)p;
193         ASSERT(skb);
194
195         if (send && osh->pub.tx_fn)
196                 osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
197
198         /* perversion: we use skb->next to chain multi-skb packets */
199         while (skb) {
200                 nskb = skb->next;
201                 skb->next = NULL;
202
203                 if (skb->destructor)
204                         /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
205                          * destructor exists
206                          */
207                         dev_kfree_skb_any(skb);
208                 else
209                         /* can free immediately (even in_irq()) if destructor
210                          * does not exist
211                          */
212                         dev_kfree_skb(skb);
213
214                 osh->pub.pktalloced--;
215                 nest++;
216                 skb = nskb;
217         }
218 }
219
220 u32 osl_pci_read_config(osl_t *osh, uint offset, uint size)
221 {
222         uint val = 0;
223         uint retry = PCI_CFG_RETRY;
224
225         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
226
227         /* only 4byte access supported */
228         ASSERT(size == 4);
229
230         do {
231                 pci_read_config_dword(osh->pdev, offset, &val);
232                 if (val != 0xffffffff)
233                         break;
234         } while (retry--);
235
236 #ifdef BCMDBG
237         if (retry < PCI_CFG_RETRY)
238                 printk("PCI CONFIG READ access to %d required %d retries\n",
239                        offset, (PCI_CFG_RETRY - retry));
240 #endif                          /* BCMDBG */
241
242         return val;
243 }
244
245 void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
246 {
247         uint retry = PCI_CFG_RETRY;
248
249         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
250
251         /* only 4byte access supported */
252         ASSERT(size == 4);
253
254         do {
255                 pci_write_config_dword(osh->pdev, offset, val);
256                 if (offset != PCI_BAR0_WIN)
257                         break;
258                 if (osl_pci_read_config(osh, offset, size) == val)
259                         break;
260         } while (retry--);
261
262 #if defined(BCMDBG) && !defined(BRCM_FULLMAC)
263         if (retry < PCI_CFG_RETRY)
264                 printk("PCI CONFIG WRITE access to %d required %d retries\n",
265                        offset, (PCI_CFG_RETRY - retry));
266 #endif                          /* BCMDBG */
267 }
268
269 /* return bus # for the pci device pointed by osh->pdev */
270 uint osl_pci_bus(osl_t *osh)
271 {
272         ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
273
274         return ((struct pci_dev *)osh->pdev)->bus->number;
275 }
276
277 /* return slot # for the pci device pointed by osh->pdev */
278 uint osl_pci_slot(osl_t *osh)
279 {
280         ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
281
282         return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
283 }
284
285 uint osl_dma_consistent_align(void)
286 {
287         return PAGE_SIZE;
288 }
289
290 void *osl_dma_alloc_consistent(osl_t *osh, uint size, u16 align_bits,
291                                uint *alloced, unsigned long *pap)
292 {
293         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
294
295         if (align_bits) {
296                 u16 align = (1 << align_bits);
297                 if (!IS_ALIGNED(DMA_CONSISTENT_ALIGN, align))
298                         size += align;
299                 *alloced = size;
300         }
301         return pci_alloc_consistent(osh->pdev, size, (dma_addr_t *) pap);
302 }
303
304 void osl_dma_free_consistent(osl_t *osh, void *va, uint size, unsigned long pa)
305 {
306         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
307
308         pci_free_consistent(osh->pdev, size, va, (dma_addr_t) pa);
309 }
310
311 uint BCMFASTPATH osl_dma_map(osl_t *osh, void *va, uint size, int direction)
312 {
313         int dir;
314
315         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
316         dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE;
317         return pci_map_single(osh->pdev, va, size, dir);
318 }
319
320 void BCMFASTPATH osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
321 {
322         int dir;
323
324         ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
325         dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE;
326         pci_unmap_single(osh->pdev, (u32) pa, size, dir);
327 }
328
329 #if defined(BCMDBG_ASSERT)
330 void osl_assert(char *exp, char *file, int line)
331 {
332         char tempbuf[256];
333         char *basename;
334
335         basename = strrchr(file, '/');
336         /* skip the '/' */
337         if (basename)
338                 basename++;
339
340         if (!basename)
341                 basename = file;
342
343 #ifdef BCMDBG_ASSERT
344         snprintf(tempbuf, 256,
345                  "assertion \"%s\" failed: file \"%s\", line %d\n", exp,
346                  basename, line);
347
348         /* Print assert message and give it time to be written to /var/log/messages */
349         if (!in_interrupt()) {
350                 const int delay = 3;
351                 printk(KERN_ERR "%s", tempbuf);
352                 printk(KERN_ERR "panic in %d seconds\n", delay);
353                 set_current_state(TASK_INTERRUPTIBLE);
354                 schedule_timeout(delay * HZ);
355         }
356
357         switch (g_assert_type) {
358         case 0:
359                 panic(KERN_ERR "%s", tempbuf);
360                 break;
361         case 1:
362                 printk(KERN_ERR "%s", tempbuf);
363                 BUG();
364                 break;
365         case 2:
366                 printk(KERN_ERR "%s", tempbuf);
367                 break;
368         default:
369                 break;
370         }
371 #endif                          /* BCMDBG_ASSERT */
372
373 }
374 #endif                          /* defined(BCMDBG_ASSERT) */
375
376 #if defined(BCMSDIO) && !defined(BRCM_FULLMAC)
377 u8 osl_readb(osl_t *osh, volatile u8 *r)
378 {
379         osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
380         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
381
382         return (u8) ((rreg) (ctx, (void *)r, sizeof(u8)));
383 }
384
385 u16 osl_readw(osl_t *osh, volatile u16 *r)
386 {
387         osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
388         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
389
390         return (u16) ((rreg) (ctx, (void *)r, sizeof(u16)));
391 }
392
393 u32 osl_readl(osl_t *osh, volatile u32 *r)
394 {
395         osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
396         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
397
398         return (u32) ((rreg) (ctx, (void *)r, sizeof(u32)));
399 }
400
401 void osl_writeb(osl_t *osh, volatile u8 *r, u8 v)
402 {
403         osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
404         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
405
406         ((wreg) (ctx, (void *)r, v, sizeof(u8)));
407 }
408
409 void osl_writew(osl_t *osh, volatile u16 *r, u16 v)
410 {
411         osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
412         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
413
414         ((wreg) (ctx, (void *)r, v, sizeof(u16)));
415 }
416
417 void osl_writel(osl_t *osh, volatile u32 *r, u32 v)
418 {
419         osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
420         void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
421
422         ((wreg) (ctx, (void *)r, v, sizeof(u32)));
423 }
424 #endif  /* BCMSDIO */