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