2 * Linux OS Independent Layer
4 * Copyright 2007, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
17 #include <bcmendian.h>
21 #include "linux_osl.h"
23 #include <linux/delay.h>
25 #include <asm/paccess.h>
29 #define PCI_CFG_RETRY 10
31 #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognise osh */
32 #define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */
34 typedef struct bcm_mem_link
36 struct bcm_mem_link *prev;
37 struct bcm_mem_link *next;
40 char file[BCM_MEM_FILENAME_LEN];
52 bcm_mem_link_t *dbgmem_list;
53 #ifdef BCMDBG_PKT /* pkt logging for debugging */
54 pktlist_info_t pktlist;
55 #endif /* BCMDBG_PKT */
59 static int16 linuxbcmerrormap[] = { 0, /* 0 */
60 -EINVAL, /* BCME_ERROR */
61 -EINVAL, /* BCME_BADARG */
62 -EINVAL, /* BCME_BADOPTION */
63 -EINVAL, /* BCME_NOTUP */
64 -EINVAL, /* BCME_NOTDOWN */
65 -EINVAL, /* BCME_NOTAP */
66 -EINVAL, /* BCME_NOTSTA */
67 -EINVAL, /* BCME_BADKEYIDX */
68 -EINVAL, /* BCME_RADIOOFF */
69 -EINVAL, /* BCME_NOTBANDLOCKED */
70 -EINVAL, /* BCME_NOCLK */
71 -EINVAL, /* BCME_BADRATESET */
72 -EINVAL, /* BCME_BADBAND */
73 -E2BIG, /* BCME_BUFTOOSHORT */
74 -E2BIG, /* BCME_BUFTOOLONG */
75 -EBUSY, /* BCME_BUSY */
76 -EINVAL, /* BCME_NOTASSOCIATED */
77 -EINVAL, /* BCME_BADSSIDLEN */
78 -EINVAL, /* BCME_OUTOFRANGECHAN */
79 -EINVAL, /* BCME_BADCHAN */
80 -EFAULT, /* BCME_BADADDR */
81 -ENOMEM, /* BCME_NORESOURCE */
82 -EOPNOTSUPP, /* BCME_UNSUPPORTED */
83 -EMSGSIZE, /* BCME_BADLENGTH */
84 -EINVAL, /* BCME_NOTREADY */
85 -EPERM, /* BCME_NOTPERMITTED */
86 -ENOMEM, /* BCME_NOMEM */
87 -EINVAL, /* BCME_ASSOCIATED */
88 -ERANGE, /* BCME_RANGE */
89 -EINVAL, /* BCME_NOTFOUND */
90 -EINVAL, /* BCME_WME_NOT_ENABLED */
91 -EINVAL, /* BCME_TSPEC_NOTFOUND */
92 -EINVAL, /* BCME_ACM_NOTSUPPORTED */
93 -EINVAL, /* BCME_NOT_WME_ASSOCIATION */
94 -EIO, /* BCME_SDIO_ERROR */
95 -ENODEV, /* BCME_DONGLE_DOWN */
96 -EINVAL /* BCME_VERSION */
97 /* When an new error code is added to bcmutils.h, add os
98 * spcecific error translation here as well
100 /* check if BCME_LAST changed since the last time this function was updated */
102 #error "You need to add a OS error translation in the linuxbcmerrormap \
103 for new error code defined in bcmuitls.h"
104 #endif /* BCME_LAST != -37 */
107 /* translate bcmerrors into linux errors */
109 osl_error (int bcmerror)
113 else if (bcmerror < BCME_LAST)
114 bcmerror = BCME_ERROR;
116 /* Array bounds covered by ASSERT in osl_attach */
117 return linuxbcmerrormap[-bcmerror];
121 osl_attach (void *pdev, uint bustype, bool pkttag)
125 osh = kmalloc (sizeof (osl_t), GFP_ATOMIC);
128 bzero (osh, sizeof (osl_t));
130 /* Check that error map has the right number of entries in it */
131 ASSERT (ABS (BCME_LAST) == (ARRAYSIZE (linuxbcmerrormap) - 1));
133 osh->magic = OS_HANDLE_MAGIC;
136 osh->dbgmem_list = NULL;
138 osh->pub.pkttag = pkttag;
139 osh->bustype = bustype;
146 osh->pub.mmbus = TRUE;
160 ASSERT (OSL_PKTTAG_SZ <= sizeof (skb->cb));
167 osl_detach (osl_t * osh)
172 ASSERT (osh->magic == OS_HANDLE_MAGIC);
176 /* Return a new packet. zero out pkttag */
178 osl_pktget (osl_t * osh, uint len)
182 if ((skb = dev_alloc_skb (len)))
188 pktlist_add (&(osh->pktlist), (void *) skb);
189 #endif /* BCMDBG_PKT */
191 osh->pub.pktalloced++;
194 return ((void *) skb);
197 /* Free the driver packet. Free the tag if present */
199 osl_pktfree (osl_t * osh, void *p, bool send)
201 struct sk_buff *skb, *nskb;
203 skb = (struct sk_buff *) p;
205 if (send && osh->pub.tx_fn)
206 osh->pub.tx_fn (osh->pub.tx_ctx, p, 0);
208 /* perversion: we use skb->next to chain multi-skb packets */
215 pktlist_remove (&(osh->pktlist), (void *) skb);
216 #endif /* BCMDBG_PKT */
220 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if destructor exists
222 dev_kfree_skb_any (skb);
226 /* can free immediately (even in_irq()) if destructor does not exist */
230 osh->pub.pktalloced--;
237 osl_pci_read_config (osl_t * osh, uint offset, uint size)
240 uint retry = PCI_CFG_RETRY;
242 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
244 /* only 4byte access supported */
249 pci_read_config_dword (osh->pdev, offset, &val);
250 if (val != 0xffffffff)
256 if (retry < PCI_CFG_RETRY)
257 printk ("PCI CONFIG READ access to %d required %d retries\n", offset,
258 (PCI_CFG_RETRY - retry));
265 osl_pci_write_config (osl_t * osh, uint offset, uint size, uint val)
267 uint retry = PCI_CFG_RETRY;
269 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
271 /* only 4byte access supported */
276 pci_write_config_dword (osh->pdev, offset, val);
277 if (offset != PCI_BAR0_WIN)
279 if (osl_pci_read_config (osh, offset, size) == val)
285 if (retry < PCI_CFG_RETRY)
286 printk ("PCI CONFIG WRITE access to %d required %d retries\n", offset,
287 (PCI_CFG_RETRY - retry));
291 /* return bus # for the pci device pointed by osh->pdev */
293 osl_pci_bus (osl_t * osh)
295 ASSERT (osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
297 return ((struct pci_dev *) osh->pdev)->bus->number;
300 /* return slot # for the pci device pointed by osh->pdev */
302 osl_pci_slot (osl_t * osh)
304 ASSERT (osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
306 return PCI_SLOT (((struct pci_dev *) osh->pdev)->devfn);
310 osl_pcmcia_attr (osl_t * osh, uint offset, char *buf, int size, bool write)
315 osl_pcmcia_read_attr (osl_t * osh, uint offset, void *buf, int size)
317 osl_pcmcia_attr (osh, offset, (char *) buf, size, FALSE);
321 osl_pcmcia_write_attr (osl_t * osh, uint offset, void *buf, int size)
323 osl_pcmcia_attr (osh, offset, (char *) buf, size, TRUE);
330 osl_debug_malloc (osl_t * osh, uint size, int line, char *file)
338 (bcm_mem_link_t *) osl_malloc (osh,
339 sizeof (bcm_mem_link_t) + size)) ==
346 basename = strrchr (file, '/');
354 strncpy (p->file, basename, BCM_MEM_FILENAME_LEN);
355 p->file[BCM_MEM_FILENAME_LEN - 1] = '\0';
357 /* link this block */
359 p->next = osh->dbgmem_list;
362 osh->dbgmem_list = p;
368 osl_debug_mfree (osl_t * osh, void *addr, uint size, int line, char *file)
371 (bcm_mem_link_t *) ((int8 *) addr - sizeof (bcm_mem_link_t));
373 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
378 ("osl_debug_mfree: double free on addr %p size %d at line %d file %s\n",
379 addr, size, line, file);
387 ("osl_debug_mfree: dealloc size %d does not match alloc size %d on addr %p"
388 " at line %d file %s\n", size, p->size, addr, line, file);
389 ASSERT (p->size == size);
393 /* unlink this block */
395 p->prev->next = p->next;
397 p->next->prev = p->prev;
398 if (osh->dbgmem_list == p)
399 osh->dbgmem_list = p->next;
400 p->next = p->prev = NULL;
402 osl_mfree (osh, p, size + sizeof (bcm_mem_link_t));
406 osl_debug_memdump (osl_t * osh, struct bcmstrbuf *b)
410 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
412 bcm_bprintf (b, " Address\tSize\tFile:line\n");
413 for (p = osh->dbgmem_list; p; p = p->next)
414 bcm_bprintf (b, "0x%08x\t%5d\t%s:%d\n",
415 (uintptr) p + sizeof (bcm_mem_link_t), p->size, p->file,
421 #endif /* BCMDBG_MEM */
424 osl_malloc (osl_t * osh, uint size)
428 /* only ASSERT if osh is defined */
430 ASSERT (osh->magic == OS_HANDLE_MAGIC);
432 if ((addr = kmalloc (size, GFP_ATOMIC)) == NULL)
439 osh->malloced += size;
445 osl_mfree (osl_t * osh, void *addr, uint size)
449 ASSERT (osh->magic == OS_HANDLE_MAGIC);
450 osh->malloced -= size;
456 osl_malloced (osl_t * osh)
458 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
459 return (osh->malloced);
463 osl_malloc_failed (osl_t * osh)
465 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
466 return (osh->failed);
470 osl_dma_alloc_consistent (osl_t * osh, uint size, ulong * pap)
472 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
474 return (pci_alloc_consistent (osh->pdev, size, (dma_addr_t *) pap));
478 osl_dma_free_consistent (osl_t * osh, void *va, uint size, ulong pa)
480 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
482 pci_free_consistent (osh->pdev, size, va, (dma_addr_t) pa);
486 osl_dma_map (osl_t * osh, void *va, uint size, int direction)
490 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
491 dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE;
492 return (pci_map_single (osh->pdev, va, size, dir));
496 osl_dma_unmap (osl_t * osh, uint pa, uint size, int direction)
500 ASSERT ((osh && (osh->magic == OS_HANDLE_MAGIC)));
501 dir = (direction == DMA_TX) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE;
502 pci_unmap_single (osh->pdev, (uint32) pa, size, dir);
505 #if defined(BINOSL) || defined(BCMDBG_ASSERT)
507 osl_assert (char *exp, char *file, int line)
511 sprintf (tempbuf, "assertion \"%s\" failed: file \"%s\", line %d\n", exp,
515 #endif /* BCMDBG_ASSERT || BINOSL */
518 osl_delay (uint usec)
524 d = MIN (usec, 1000);
531 * The pkttag contents are NOT cloned.
534 osl_pktdup (osl_t * osh, void *skb)
538 if ((p = skb_clone ((struct sk_buff *) skb, GFP_ATOMIC)) == NULL)
541 /* skb_clone copies skb->cb.. we don't want that */
543 bzero ((void *) ((struct sk_buff *) p)->cb, OSL_PKTTAG_SZ);
545 /* Increment the packet counter */
546 osh->pub.pktalloced++;
548 pktlist_add (&(osh->pktlist), (void *) p);
549 #endif /* BCMDBG_PKT */
554 osl_pktalloced (osl_t * osh)
556 return (osh->pub.pktalloced);
561 osl_pktlist_dump (osl_t * osh, char *buf)
563 pktlist_dump (&(osh->pktlist), buf);
568 osl_pktlist_add (osl_t * osh, void *p)
570 pktlist_add (&(osh->pktlist), p);
574 osl_pktlist_remove (osl_t * osh, void *p)
576 pktlist_remove (&(osh->pktlist), p);
578 #endif /* BCMDBG_PKT */
581 * BINOSL selects the slightly slower function-call-based binary compatible osl.
586 osl_printf (const char *format, ...)
592 /* sprintf into a local buffer because there *is* no "vprintk()".. */
593 va_start (args, format);
594 len = vsnprintf (buf, 1024, format, args);
597 if (len > sizeof (buf))
599 printk ("osl_printf: buffer overrun\n");
603 return (printk (buf));
607 osl_sprintf (char *buf, const char *format, ...)
612 va_start (args, format);
613 rc = vsprintf (buf, format, args);
619 osl_strcmp (const char *s1, const char *s2)
621 return (strcmp (s1, s2));
625 osl_strncmp (const char *s1, const char *s2, uint n)
627 return (strncmp (s1, s2, n));
631 osl_strlen (const char *s)
637 osl_strcpy (char *d, const char *s)
639 return (strcpy (d, s));
643 osl_strncpy (char *d, const char *s, uint n)
645 return (strncpy (d, s, n));
649 bcopy (const void *src, void *dst, int len)
651 memcpy (dst, src, len);
655 bcmp (const void *b1, const void *b2, int len)
657 return (memcmp (b1, b2, len));
661 bzero (void *b, int len)
663 memset (b, '\0', len);
667 osl_readl (volatile uint32 * r)
673 osl_readw (volatile uint16 * r)
679 osl_readb (volatile uint8 * r)
685 osl_writel (uint32 v, volatile uint32 * r)
691 osl_writew (uint16 v, volatile uint16 * r)
697 osl_writeb (uint8 v, volatile uint8 * r)
703 osl_uncached (void *va)
706 return ((void *) KSEG1ADDR (va));
708 return ((void *) va);
718 cycles = read_c0_count () * 2;
719 #elif defined(__i386__)
723 #endif /* defined(mips) */
728 osl_reg_map (uint32 pa, uint size)
730 return (ioremap_nocache ((unsigned long) pa, (unsigned long) size));
734 osl_reg_unmap (void *va)
740 osl_busprobe (uint32 * val, uint32 addr)
743 return get_dbe (*val, (uint32 *) addr);
745 *val = readl ((uint32 *) (uintptr) addr);
751 osl_pktshared (void *skb)
753 return (((struct sk_buff *) skb)->cloned);
757 osl_pktdata (osl_t * osh, void *skb)
759 return (((struct sk_buff *) skb)->data);
763 osl_pktlen (osl_t * osh, void *skb)
765 return (((struct sk_buff *) skb)->len);
769 osl_pktheadroom (osl_t * osh, void *skb)
771 return (uint) skb_headroom ((struct sk_buff *) skb);
775 osl_pkttailroom (osl_t * osh, void *skb)
777 return (uint) skb_tailroom ((struct sk_buff *) skb);
781 osl_pktnext (osl_t * osh, void *skb)
783 return (((struct sk_buff *) skb)->next);
787 osl_pktsetnext (void *skb, void *x)
789 ((struct sk_buff *) skb)->next = (struct sk_buff *) x;
793 osl_pktsetlen (osl_t * osh, void *skb, uint len)
795 __skb_trim ((struct sk_buff *) skb, len);
799 osl_pktpush (osl_t * osh, void *skb, int bytes)
801 return (skb_push ((struct sk_buff *) skb, bytes));
805 osl_pktpull (osl_t * osh, void *skb, int bytes)
807 return (skb_pull ((struct sk_buff *) skb, bytes));
811 osl_pkttag (void *skb)
813 return ((void *) (((struct sk_buff *) skb)->cb));
817 osl_pktlink (void *skb)
819 return (((struct sk_buff *) skb)->prev);
823 osl_pktsetlink (void *skb, void *x)
825 ((struct sk_buff *) skb)->prev = (struct sk_buff *) x;
829 osl_pktprio (void *skb)
831 return (((struct sk_buff *) skb)->priority);
835 osl_pktsetprio (void *skb, uint x)
837 ((struct sk_buff *) skb)->priority = x;
840 /* Convert a driver packet to native(OS) packet
841 * In the process, packettag is zeroed out before sending up
842 * IP code depends on skb->cb to be setup correctly with various options
843 * In our case, that means it should be 0
846 osl_pkt_tonative (osl_t * osh, void *pkt)
848 struct sk_buff *nskb;
851 bzero ((void *) ((struct sk_buff *) pkt)->cb, OSL_PKTTAG_SZ);
853 /* Decrement the packet counter */
854 for (nskb = (struct sk_buff *) pkt; nskb; nskb = nskb->next)
857 pktlist_remove (&(osh->pktlist), (void *) nskb);
858 #endif /* BCMDBG_PKT */
859 osh->pub.pktalloced--;
862 return (struct sk_buff *) pkt;
865 /* Convert a native(OS) packet to driver packet.
866 * In the process, native packet is destroyed, there is no copying
867 * Also, a packettag is zeroed out
870 osl_pkt_frmnative (osl_t * osh, struct sk_buff *skb)
872 struct sk_buff *nskb;
875 bzero ((void *) skb->cb, OSL_PKTTAG_SZ);
877 /* Increment the packet counter */
878 for (nskb = skb; nskb; nskb = nskb->next)
881 pktlist_add (&(osh->pktlist), (void *) nskb);
882 #endif /* BCMDBG_PKT */
883 osh->pub.pktalloced++;