2 * Copyright (c) 2010 Broadcom Corporation
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.
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.
18 #include <bcmendian.h>
23 #include <linux/delay.h>
25 #include <asm/paccess.h>
31 #define PCI_CFG_RETRY 10
33 #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognise osh */
34 #define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */
36 typedef struct bcm_mem_link {
37 struct bcm_mem_link *prev;
38 struct bcm_mem_link *next;
41 char file[BCM_MEM_FILENAME_LEN];
51 bcm_mem_link_t *dbgmem_list;
54 /* Global ASSERT type flag */
55 uint32 g_assert_type = 0;
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 */
101 /* When an new error code is added to bcmutils.h, add os
102 * spcecific error translation here as well
104 /* check if BCME_LAST changed since the last time this function was updated */
106 #error "You need to add a OS error translation in the linuxbcmerrormap \
107 for new error code defined in bcmutils.h"
111 /* translate bcmerrors into linux errors */
112 int osl_error(int bcmerror)
116 else if (bcmerror < BCME_LAST)
117 bcmerror = BCME_ERROR;
119 /* Array bounds covered by ASSERT in osl_attach */
120 return linuxbcmerrormap[-bcmerror];
123 osl_t *osl_attach(void *pdev, uint bustype, bool pkttag)
127 osh = kmalloc(sizeof(osl_t), GFP_ATOMIC);
130 bzero(osh, sizeof(osl_t));
132 /* Check that error map has the right number of entries in it */
133 ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1));
135 osh->magic = OS_HANDLE_MAGIC;
138 osh->dbgmem_list = NULL;
140 osh->pub.pkttag = pkttag;
141 osh->bustype = bustype;
146 osh->pub.mmbus = TRUE;
153 osh->pub.mmbus = FALSE;
163 ASSERT(OSL_PKTTAG_SZ <= sizeof(skb->cb));
169 void osl_detach(osl_t * osh)
174 ASSERT(osh->magic == OS_HANDLE_MAGIC);
178 /* Return a new packet. zero out pkttag */
179 void *BCMFASTPATH osl_pktget(osl_t * osh, uint len)
183 if ((skb = dev_alloc_skb(len))) {
187 osh->pub.pktalloced++;
190 return ((void *)skb);
193 /* Free the driver packet. Free the tag if present */
194 void BCMFASTPATH osl_pktfree(osl_t * osh, void *p, bool send)
196 struct sk_buff *skb, *nskb;
199 skb = (struct sk_buff *)p;
202 if (send && osh->pub.tx_fn)
203 osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
205 /* perversion: we use skb->next to chain multi-skb packets */
211 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
214 dev_kfree_skb_any(skb);
216 /* can free immediately (even in_irq()) if destructor
221 osh->pub.pktalloced--;
227 uint32 osl_pci_read_config(osl_t * osh, uint offset, uint size)
230 uint retry = PCI_CFG_RETRY;
232 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
234 /* only 4byte access supported */
238 pci_read_config_dword(osh->pdev, offset, &val);
239 if (val != 0xffffffff)
244 if (retry < PCI_CFG_RETRY)
245 printk("PCI CONFIG READ access to %d required %d retries\n",
246 offset, (PCI_CFG_RETRY - retry));
252 void osl_pci_write_config(osl_t * osh, uint offset, uint size, uint val)
254 uint retry = PCI_CFG_RETRY;
256 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
258 /* only 4byte access supported */
262 pci_write_config_dword(osh->pdev, offset, val);
263 if (offset != PCI_BAR0_WIN)
265 if (osl_pci_read_config(osh, offset, size) == val)
270 if (retry < PCI_CFG_RETRY)
271 printk("PCI CONFIG WRITE access to %d required %d retries\n",
272 offset, (PCI_CFG_RETRY - retry));
276 /* return bus # for the pci device pointed by osh->pdev */
277 uint osl_pci_bus(osl_t * osh)
279 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
281 return ((struct pci_dev *)osh->pdev)->bus->number;
284 /* return slot # for the pci device pointed by osh->pdev */
285 uint osl_pci_slot(osl_t * osh)
287 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
289 return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
293 osl_pcmcia_attr(osl_t * osh, uint offset, char *buf, int size, bool write)
297 void osl_pcmcia_read_attr(osl_t * osh, uint offset, void *buf, int size)
299 osl_pcmcia_attr(osh, offset, (char *)buf, size, FALSE);
302 void osl_pcmcia_write_attr(osl_t * osh, uint offset, void *buf, int size)
304 osl_pcmcia_attr(osh, offset, (char *)buf, size, TRUE);
307 void *osl_malloc(osl_t * osh, uint size)
311 /* only ASSERT if osh is defined */
313 ASSERT(osh->magic == OS_HANDLE_MAGIC);
315 if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) {
321 osh->malloced += size;
326 void osl_mfree(osl_t * osh, void *addr, uint size)
329 ASSERT(osh->magic == OS_HANDLE_MAGIC);
330 osh->malloced -= size;
335 uint osl_malloced(osl_t * osh)
337 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
338 return (osh->malloced);
341 uint osl_malloc_failed(osl_t * osh)
343 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
344 return (osh->failed);
347 uint osl_dma_consistent_align(void)
352 void *osl_dma_alloc_consistent(osl_t * osh, uint size, uint16 align_bits,
353 uint * alloced, ulong * pap)
355 uint16 align = (1 << align_bits);
356 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
358 if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align))
362 return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t *) pap));
365 void osl_dma_free_consistent(osl_t * osh, void *va, uint size, ulong pa)
367 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
369 pci_free_consistent(osh->pdev, size, va, (dma_addr_t) pa);
372 uint BCMFASTPATH osl_dma_map(osl_t * osh, void *va, uint size, int direction)
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));
381 void BCMFASTPATH osl_dma_unmap(osl_t * osh, uint pa, uint size, int direction)
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);
390 #if defined(BCMDBG_ASSERT)
391 void osl_assert(char *exp, char *file, int line)
396 basename = strrchr(file, '/');
405 snprintf(tempbuf, 256,
406 "assertion \"%s\" failed: file \"%s\", line %d\n", exp,
409 /* Print assert message and give it time to be written to /var/log/messages */
410 if (!in_interrupt()) {
412 printk("%s", tempbuf);
413 printk("panic in %d seconds\n", delay);
414 set_current_state(TASK_INTERRUPTIBLE);
415 schedule_timeout(delay * HZ);
418 switch (g_assert_type) {
420 panic("%s", tempbuf);
423 printk("%s", tempbuf);
427 printk("%s", tempbuf);
432 #endif /* BCMDBG_ASSERT */
435 #endif /* defined(BCMDBG_ASSERT) */
437 void osl_delay(uint usec)
449 * The pkttag contents are NOT cloned.
451 void *osl_pktdup(osl_t * osh, void *skb)
455 if ((p = skb_clone((struct sk_buff *)skb, GFP_ATOMIC)) == NULL)
458 /* skb_clone copies skb->cb.. we don't want that */
460 bzero((void *)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ);
462 /* Increment the packet counter */
463 osh->pub.pktalloced++;
468 uint8 osl_readb(osl_t * osh, volatile uint8 * r)
470 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
471 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
473 return (uint8) ((rreg) (ctx, (void *)r, sizeof(uint8)));
476 uint16 osl_readw(osl_t * osh, volatile uint16 * r)
478 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
479 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
481 return (uint16) ((rreg) (ctx, (void *)r, sizeof(uint16)));
484 uint32 osl_readl(osl_t * osh, volatile uint32 * r)
486 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
487 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
489 return (uint32) ((rreg) (ctx, (void *)r, sizeof(uint32)));
492 void osl_writeb(osl_t * osh, volatile uint8 * r, uint8 v)
494 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
495 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
497 ((wreg) (ctx, (void *)r, v, sizeof(uint8)));
500 void osl_writew(osl_t * osh, volatile uint16 * r, uint16 v)
502 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
503 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
505 ((wreg) (ctx, (void *)r, v, sizeof(uint16)));
508 void osl_writel(osl_t * osh, volatile uint32 * r, uint32 v)
510 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
511 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
513 ((wreg) (ctx, (void *)r, v, sizeof(uint32)));
516 /* Linux Kernel: File Operations: end */