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.
17 #include <linux/delay.h>
20 #include <asm/paccess.h>
22 #include <bcmendian.h>
30 #define PCI_CFG_RETRY 10
32 #define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognise osh */
33 #define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */
43 /* Global ASSERT type flag */
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 */
91 /* When an new error code is added to bcmutils.h, add os
92 * spcecific error translation here as well
94 /* check if BCME_LAST changed since the last time this function was updated */
96 #error "You need to add a OS error translation in the linuxbcmerrormap \
97 for new error code defined in bcmutils.h"
101 /* translate bcmerrors into linux errors */
102 int osl_error(int bcmerror)
106 else if (bcmerror < BCME_LAST)
107 bcmerror = BCME_ERROR;
109 /* Array bounds covered by ASSERT in osl_attach */
110 return linuxbcmerrormap[-bcmerror];
112 #endif /* BRCM_FULLMAC */
114 osl_t *osl_attach(void *pdev, uint bustype, bool pkttag)
118 osh = kmalloc(sizeof(osl_t), GFP_ATOMIC);
121 bzero(osh, sizeof(osl_t));
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 */
128 osh->magic = OS_HANDLE_MAGIC;
131 osh->pub.pkttag = pkttag;
132 osh->bustype = bustype;
138 osh->pub.mmbus = true;
145 osh->pub.mmbus = false;
152 #if defined(BCMDBG) && !defined(BRCM_FULLMAC)
155 ASSERT(OSL_PKTTAG_SZ <= sizeof(skb->cb));
161 void osl_detach(osl_t *osh)
166 ASSERT(osh->magic == OS_HANDLE_MAGIC);
170 /* Return a new packet. zero out pkttag */
171 void *BCMFASTPATH osl_pktget(osl_t *osh, uint len)
175 skb = dev_alloc_skb(len);
180 osh->pub.pktalloced++;
186 /* Free the driver packet. Free the tag if present */
187 void BCMFASTPATH osl_pktfree(osl_t *osh, void *p, bool send)
189 struct sk_buff *skb, *nskb;
192 skb = (struct sk_buff *)p;
195 if (send && osh->pub.tx_fn)
196 osh->pub.tx_fn(osh->pub.tx_ctx, p, 0);
198 /* perversion: we use skb->next to chain multi-skb packets */
204 /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if
207 dev_kfree_skb_any(skb);
209 /* can free immediately (even in_irq()) if destructor
214 osh->pub.pktalloced--;
220 u32 osl_pci_read_config(osl_t *osh, uint offset, uint size)
223 uint retry = PCI_CFG_RETRY;
225 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
227 /* only 4byte access supported */
231 pci_read_config_dword(osh->pdev, offset, &val);
232 if (val != 0xffffffff)
237 if (retry < PCI_CFG_RETRY)
238 printk("PCI CONFIG READ access to %d required %d retries\n",
239 offset, (PCI_CFG_RETRY - retry));
245 void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val)
247 uint retry = PCI_CFG_RETRY;
249 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
251 /* only 4byte access supported */
255 pci_write_config_dword(osh->pdev, offset, val);
256 if (offset != PCI_BAR0_WIN)
258 if (osl_pci_read_config(osh, offset, size) == val)
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));
269 /* return bus # for the pci device pointed by osh->pdev */
270 uint osl_pci_bus(osl_t *osh)
272 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
274 return ((struct pci_dev *)osh->pdev)->bus->number;
277 /* return slot # for the pci device pointed by osh->pdev */
278 uint osl_pci_slot(osl_t *osh)
280 ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev);
282 return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn);
285 uint osl_dma_consistent_align(void)
290 void *osl_dma_alloc_consistent(osl_t *osh, uint size, u16 align_bits,
291 uint *alloced, unsigned long *pap)
293 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
296 u16 align = (1 << align_bits);
297 if (!IS_ALIGNED(DMA_CONSISTENT_ALIGN, align))
301 return pci_alloc_consistent(osh->pdev, size, (dma_addr_t *) pap);
304 void osl_dma_free_consistent(osl_t *osh, void *va, uint size, unsigned long pa)
306 ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC)));
308 pci_free_consistent(osh->pdev, size, va, (dma_addr_t) pa);
311 uint BCMFASTPATH osl_dma_map(osl_t *osh, void *va, uint size, int direction)
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);
320 void BCMFASTPATH osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction)
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);
329 #if defined(BCMDBG_ASSERT)
330 void osl_assert(char *exp, char *file, int line)
335 basename = strrchr(file, '/');
344 snprintf(tempbuf, 256,
345 "assertion \"%s\" failed: file \"%s\", line %d\n", exp,
348 /* Print assert message and give it time to be written to /var/log/messages */
349 if (!in_interrupt()) {
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);
357 switch (g_assert_type) {
359 panic(KERN_ERR "%s", tempbuf);
362 printk(KERN_ERR "%s", tempbuf);
366 printk(KERN_ERR "%s", tempbuf);
371 #endif /* BCMDBG_ASSERT */
374 #endif /* defined(BCMDBG_ASSERT) */
376 #if defined(BCMSDIO) && !defined(BRCM_FULLMAC)
377 u8 osl_readb(osl_t *osh, volatile u8 *r)
379 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
380 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
382 return (u8) ((rreg) (ctx, (void *)r, sizeof(u8)));
385 u16 osl_readw(osl_t *osh, volatile u16 *r)
387 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
388 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
390 return (u16) ((rreg) (ctx, (void *)r, sizeof(u16)));
393 u32 osl_readl(osl_t *osh, volatile u32 *r)
395 osl_rreg_fn_t rreg = ((osl_pubinfo_t *) osh)->rreg_fn;
396 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
398 return (u32) ((rreg) (ctx, (void *)r, sizeof(u32)));
401 void osl_writeb(osl_t *osh, volatile u8 *r, u8 v)
403 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
404 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
406 ((wreg) (ctx, (void *)r, v, sizeof(u8)));
409 void osl_writew(osl_t *osh, volatile u16 *r, u16 v)
411 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
412 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
414 ((wreg) (ctx, (void *)r, v, sizeof(u16)));
417 void osl_writel(osl_t *osh, volatile u32 *r, u32 v)
419 osl_wreg_fn_t wreg = ((osl_pubinfo_t *) osh)->wreg_fn;
420 void *ctx = ((osl_pubinfo_t *) osh)->reg_ctx;
422 ((wreg) (ctx, (void *)r, v, sizeof(u32)));