2 * Generic Broadcom Home Networking Division (HND) DMA module.
3 * This supports the following chips: BCM42xx, 44xx, 47xx .
5 * Copyright 2004, Broadcom Corporation
8 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
9 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
10 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
11 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
18 #include <bcmendian.h>
21 struct dma_info; /* forward declaration */
22 #define di_t struct dma_info
26 #define DMA_ERROR(args)
27 #define DMA_TRACE(args)
29 /* default dma message level(if input msg_level pointer is null in dma_attach()) */
30 static uint dma_msg_level = 0;
33 #define MAXDD (DMAMAXRINGSZ / sizeof (dmadd_t))
35 /* dma engine software state */
36 typedef struct dma_info {
37 hnddma_t hnddma; /* exported structure */
38 uint *msg_level; /* message level pointer */
40 char name[MAXNAMEL]; /* callers name for diag msgs */
41 void *drv; /* driver handle */
42 void *dev; /* device handle */
43 dmaregs_t *regs; /* dma engine registers */
45 dmadd_t *txd; /* pointer to chip-specific tx descriptor ring */
46 uint txin; /* index of next descriptor to reclaim */
47 uint txout; /* index of next descriptor to post */
48 uint txavail; /* # free tx descriptors */
49 void *txp[MAXDD]; /* parallel array of pointers to packets */
50 ulong txdpa; /* physical address of descriptor ring */
51 uint txdalign; /* #bytes added to alloc'd mem to align txd */
53 dmadd_t *rxd; /* pointer to chip-specific rx descriptor ring */
54 uint rxin; /* index of next descriptor to reclaim */
55 uint rxout; /* index of next descriptor to post */
56 void *rxp[MAXDD]; /* parallel array of pointers to packets */
57 ulong rxdpa; /* physical address of descriptor ring */
58 uint rxdalign; /* #bytes added to alloc'd mem to align rxd */
61 uint ntxd; /* # tx descriptors */
62 uint nrxd; /* # rx descriptors */
63 uint rxbufsize; /* rx buffer size in bytes */
64 uint nrxpost; /* # rx buffers to keep posted */
65 uint rxoffset; /* rxcontrol offset */
66 uint ddoffset; /* add to get dma address of descriptor ring */
67 uint dataoffset; /* add to get dma address of data buffer */
70 /* descriptor bumping macros */
71 #define TXD(x) ((x) & (di->ntxd - 1))
72 #define RXD(x) ((x) & (di->nrxd - 1))
73 #define NEXTTXD(i) TXD(i + 1)
74 #define PREVTXD(i) TXD(i - 1)
75 #define NEXTRXD(i) RXD(i + 1)
76 #define NTXDACTIVE(h, t) TXD(t - h)
77 #define NRXDACTIVE(h, t) RXD(t - h)
79 /* macros to convert between byte offsets and indexes */
80 #define B2I(bytes) ((bytes) / sizeof (dmadd_t))
81 #define I2B(index) ((index) * sizeof (dmadd_t))
84 dma_attach(void *drv, void *dev, char *name, dmaregs_t *regs, uint ntxd, uint nrxd,
85 uint rxbufsize, uint nrxpost, uint rxoffset, uint ddoffset, uint dataoffset, uint *msg_level)
90 ASSERT(ntxd <= MAXDD);
91 ASSERT(nrxd <= MAXDD);
93 /* allocate private info structure */
94 if ((di = MALLOC(sizeof (dma_info_t))) == NULL)
96 bzero((char*)di, sizeof (dma_info_t));
98 /* set message level */
99 di->msg_level = msg_level ? msg_level : &dma_msg_level;
101 DMA_TRACE(("%s: dma_attach: drv 0x%x dev 0x%x regs 0x%x ntxd %d nrxd %d rxbufsize %d nrxpost %d rxoffset %d ddoffset 0x%x dataoffset 0x%x\n", name, (uint)drv, (uint)dev, (uint)regs, ntxd, nrxd, rxbufsize, nrxpost, rxoffset, ddoffset, dataoffset));
103 /* make a private copy of our callers name */
104 strncpy(di->name, name, MAXNAMEL);
105 di->name[MAXNAMEL-1] = '\0';
111 /* allocate transmit descriptor ring */
113 if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->txdpa)) == NULL)
115 di->txd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
116 di->txdalign = ((uint)di->txd - (uint)va);
117 di->txdpa = di->txdpa + di->txdalign;
118 ASSERT(ISALIGNED(di->txd, DMARINGALIGN));
121 /* allocate receive descriptor ring */
123 if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->rxdpa)) == NULL)
125 di->rxd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
126 di->rxdalign = ((uint)di->rxd - (uint)va);
127 di->rxdpa = di->rxdpa + di->rxdalign;
128 ASSERT(ISALIGNED(di->rxd, DMARINGALIGN));
134 di->rxbufsize = rxbufsize;
135 di->nrxpost = nrxpost;
136 di->rxoffset = rxoffset;
137 di->ddoffset = ddoffset;
138 di->dataoffset = dataoffset;
143 dma_detach((void*)di);
147 /* may be called with core in reset */
149 dma_detach(dma_info_t *di)
154 DMA_TRACE(("%s: dma_detach\n", di->name));
156 /* shouldn't be here if descriptors are unreclaimed */
157 ASSERT(di->txin == di->txout);
158 ASSERT(di->rxin == di->rxout);
160 /* free dma descriptor rings */
162 DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->txd - di->txdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->txdpa);
164 DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->rxd - di->rxdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->rxdpa);
166 /* free our private info structure */
167 MFREE((void*)di, sizeof (dma_info_t));
172 dma_txreset(dma_info_t *di)
176 DMA_TRACE(("%s: dma_txreset\n", di->name));
178 /* suspend tx DMA first */
179 W_REG(&di->regs->xmtcontrol, XC_SE);
180 SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED &&
181 status != XS_XS_IDLE &&
182 status != XS_XS_STOPPED,
185 W_REG(&di->regs->xmtcontrol, 0);
186 SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED,
189 if (status != XS_XS_DISABLED) {
190 DMA_ERROR(("%s: dma_txreset: dma cannot be stopped\n", di->name));
193 /* wait for the last transaction to complete */
198 dma_rxreset(dma_info_t *di)
202 DMA_TRACE(("%s: dma_rxreset\n", di->name));
204 W_REG(&di->regs->rcvcontrol, 0);
205 SPINWAIT((status = (R_REG(&di->regs->rcvstatus) & RS_RS_MASK)) != RS_RS_DISABLED,
208 if (status != RS_RS_DISABLED) {
209 DMA_ERROR(("%s: dma_rxreset: dma cannot be stopped\n", di->name));
214 dma_txinit(dma_info_t *di)
216 DMA_TRACE(("%s: dma_txinit\n", di->name));
218 di->txin = di->txout = 0;
219 di->txavail = di->ntxd - 1;
221 /* clear tx descriptor ring */
222 BZERO_SM((void*)di->txd, (di->ntxd * sizeof (dmadd_t)));
224 W_REG(&di->regs->xmtcontrol, XC_XE);
225 W_REG(&di->regs->xmtaddr, (di->txdpa + di->ddoffset));
229 dma_txenabled(dma_info_t *di)
233 /* If the chip is dead, it is not enabled :-) */
234 xc = R_REG(&di->regs->xmtcontrol);
235 return ((xc != 0xffffffff) && (xc & XC_XE));
239 dma_txsuspend(dma_info_t *di)
241 DMA_TRACE(("%s: dma_txsuspend\n", di->name));
242 OR_REG(&di->regs->xmtcontrol, XC_SE);
246 dma_txresume(dma_info_t *di)
248 DMA_TRACE(("%s: dma_txresume\n", di->name));
249 AND_REG(&di->regs->xmtcontrol, ~XC_SE);
253 dma_txsuspended(dma_info_t *di)
255 if (!(R_REG(&di->regs->xmtcontrol) & XC_SE))
258 if ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) != XS_XS_IDLE)
262 return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_IDLE);
266 dma_txstopped(dma_info_t *di)
268 return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_STOPPED);
272 dma_rxstopped(dma_info_t *di)
274 return ((R_REG(&di->regs->rcvstatus) & RS_RS_MASK) == RS_RS_STOPPED);
278 dma_fifoloopbackenable(dma_info_t *di)
280 DMA_TRACE(("%s: dma_fifoloopbackenable\n", di->name));
281 OR_REG(&di->regs->xmtcontrol, XC_LE);
285 dma_rxinit(dma_info_t *di)
287 DMA_TRACE(("%s: dma_rxinit\n", di->name));
289 di->rxin = di->rxout = 0;
291 /* clear rx descriptor ring */
292 BZERO_SM((void*)di->rxd, (di->nrxd * sizeof (dmadd_t)));
295 W_REG(&di->regs->rcvaddr, (di->rxdpa + di->ddoffset));
299 dma_rxenable(dma_info_t *di)
301 DMA_TRACE(("%s: dma_rxenable\n", di->name));
302 W_REG(&di->regs->rcvcontrol, ((di->rxoffset << RC_RO_SHIFT) | RC_RE));
306 dma_rxenabled(dma_info_t *di)
310 rc = R_REG(&di->regs->rcvcontrol);
311 return ((rc != 0xffffffff) && (rc & RC_RE));
315 * The BCM47XX family supports full 32bit dma engine buffer addressing so
316 * dma buffers can cross 4 Kbyte page boundaries.
319 dma_txfast(dma_info_t *di, void *p0, uint32 coreflags)
328 DMA_TRACE(("%s: dma_txfast\n", di->name));
334 * Walk the chain of packet buffers
335 * allocating and initializing transmit descriptor entries.
337 for (p = p0; p; p = next) {
338 data = PKTDATA(di->drv, p);
339 len = PKTLEN(di->drv, p);
340 next = PKTNEXT(di->drv, p);
342 /* return nonzero if out of tx descriptors */
343 if (NEXTTXD(txout) == di->txin)
349 /* get physical address of buffer start */
350 pa = (uint32) DMA_MAP(di->dev, data, len, DMA_TX, p);
352 /* build the descriptor control value */
353 ctrl = len & CTRL_BC_MASK;
360 ctrl |= (CTRL_IOC | CTRL_EOF);
361 if (txout == (di->ntxd - 1))
364 /* init the tx descriptor */
365 W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
366 W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
368 ASSERT(di->txp[txout] == NULL);
370 txout = NEXTTXD(txout);
373 /* if last txd eof not set, fix it */
374 if (!(ctrl & CTRL_EOF))
375 W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
377 /* save the packet */
378 di->txp[PREVTXD(txout)] = p0;
380 /* bump the tx descriptor index */
384 W_REG(&di->regs->xmtptr, I2B(txout));
386 /* tx flow control */
387 di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
392 DMA_ERROR(("%s: dma_txfast: out of txds\n", di->name));
393 PKTFREE(di->drv, p0, TRUE);
395 di->hnddma.txnobuf++;
400 #define PAGEBASE(x) ((uint)(x) & ~4095)
403 * Just like above except go through the extra effort of splitting
404 * buffers that cross 4Kbyte boundaries into multiple tx descriptors.
407 dma_tx(dma_info_t *di, void *p0, uint32 coreflags)
412 uchar *page, *start, *end;
417 DMA_TRACE(("%s: dma_tx\n", di->name));
423 * Walk the chain of packet buffers
424 * splitting those that cross 4 Kbyte boundaries
425 * allocating and initializing transmit descriptor entries.
427 for (p = p0; p; p = next) {
428 data = PKTDATA(di->drv, p);
429 plen = PKTLEN(di->drv, p);
430 next = PKTNEXT(di->drv, p);
435 for (page = (uchar*)PAGEBASE(data);
436 page <= (uchar*)PAGEBASE(data + plen - 1);
439 /* return nonzero if out of tx descriptors */
440 if (NEXTTXD(txout) == di->txin)
443 start = (page == (uchar*)PAGEBASE(data))? data: page;
444 end = (page == (uchar*)PAGEBASE(data + plen))?
445 (data + plen): (page + PAGESZ);
448 /* build the descriptor control value */
449 ctrl = len & CTRL_BC_MASK;
453 if ((p == p0) && (start == data))
455 if ((next == NULL) && (end == (data + plen)))
456 ctrl |= (CTRL_IOC | CTRL_EOF);
457 if (txout == (di->ntxd - 1))
460 /* get physical address of buffer start */
461 pa = (uint32) DMA_MAP(di->dev, start, len, DMA_TX, p);
463 /* init the tx descriptor */
464 W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
465 W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
467 ASSERT(di->txp[txout] == NULL);
469 txout = NEXTTXD(txout);
473 /* if last txd eof not set, fix it */
474 if (!(ctrl & CTRL_EOF))
475 W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
477 /* save the packet */
478 di->txp[PREVTXD(txout)] = p0;
480 /* bump the tx descriptor index */
484 W_REG(&di->regs->xmtptr, I2B(txout));
486 /* tx flow control */
487 di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
492 DMA_ERROR(("%s: dma_tx: out of txds\n", di->name));
493 PKTFREE(di->drv, p0, TRUE);
495 di->hnddma.txnobuf++;
499 /* returns a pointer to the next frame received, or NULL if there are no more */
501 dma_rx(dma_info_t *di)
507 while ((p = dma_getnextrxp(di, FALSE))) {
508 /* skip giant packets which span multiple rx descriptors */
510 skiplen -= di->rxbufsize;
513 PKTFREE(di->drv, p, FALSE);
517 len = ltoh16(*(uint16*)(PKTDATA(di->drv, p)));
518 DMA_TRACE(("%s: dma_rx len %d\n", di->name, len));
520 /* bad frame length check */
521 if (len > (di->rxbufsize - di->rxoffset)) {
522 DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n", di->name, len));
524 skiplen = len - (di->rxbufsize - di->rxoffset);
525 PKTFREE(di->drv, p, FALSE);
526 di->hnddma.rxgiants++;
530 /* set actual length */
531 PKTSETLEN(di->drv, p, (di->rxoffset + len));
539 /* post receive buffers */
541 dma_rxfill(dma_info_t *di)
552 * Determine how many receive buffers we're lacking
553 * from the full complement, allocate, initialize,
554 * and post them, then update the chip rx lastdscr.
559 rxbufsize = di->rxbufsize;
561 n = di->nrxpost - NRXDACTIVE(rxin, rxout);
563 DMA_TRACE(("%s: dma_rxfill: post %d\n", di->name, n));
565 for (i = 0; i < n; i++) {
566 if ((p = PKTGET(di->drv, rxbufsize, FALSE)) == NULL) {
567 DMA_ERROR(("%s: dma_rxfill: out of rxbufs\n", di->name));
568 di->hnddma.rxnobuf++;
572 *(uint32*)(OSL_UNCACHED(PKTDATA(di->drv, p))) = 0;
574 pa = (uint32) DMA_MAP(di->dev, PKTDATA(di->drv, p), rxbufsize, DMA_RX, p);
575 ASSERT(ISALIGNED(pa, 4));
577 /* save the free packet pointer */
578 ASSERT(di->rxp[rxout] == NULL);
581 /* prep the descriptor control value */
583 if (rxout == (di->nrxd - 1))
586 /* init the rx descriptor */
587 W_SM(&di->rxd[rxout].ctrl, BUS_SWAP32(ctrl));
588 W_SM(&di->rxd[rxout].addr, BUS_SWAP32(pa + di->dataoffset));
590 rxout = NEXTRXD(rxout);
595 /* update the chip lastdscr pointer */
596 W_REG(&di->regs->rcvptr, I2B(rxout));
600 dma_txreclaim(dma_info_t *di, bool forceall)
604 DMA_TRACE(("%s: dma_txreclaim %s\n", di->name, forceall ? "all" : ""));
606 while ((p = dma_getnexttxp(di, forceall)))
607 PKTFREE(di->drv, p, TRUE);
611 * Reclaim next completed txd (txds if using chained buffers) and
612 * return associated packet.
613 * If 'force' is true, reclaim txd(s) and return associated packet
614 * regardless of the value of the hardware "curr" pointer.
617 dma_getnexttxp(dma_info_t *di, bool forceall)
622 DMA_TRACE(("%s: dma_getnexttxp %s\n", di->name, forceall ? "all" : ""));
630 end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
632 if ((start == 0) && (end > di->txout))
635 for (i = start; i != end && !txp; i = NEXTTXD(i)) {
636 DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->txd[i].addr)) - di->dataoffset),
637 (BUS_SWAP32(R_SM(&di->txd[i].ctrl)) & CTRL_BC_MASK), DMA_TX, di->txp[i]);
638 W_SM(&di->txd[i].addr, 0xdeadbeef);
645 /* tx flow control */
646 di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
652 DMA_ERROR(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n",
653 start, end, di->txout, forceall));
658 /* like getnexttxp but no reclaim */
660 dma_peeknexttxp(dma_info_t *di)
664 end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
666 for (i = di->txin; i != end; i = NEXTTXD(i))
674 dma_rxreclaim(dma_info_t *di)
678 DMA_TRACE(("%s: dma_rxreclaim\n", di->name));
680 while ((p = dma_getnextrxp(di, TRUE)))
681 PKTFREE(di->drv, p, FALSE);
685 dma_getnextrxp(dma_info_t *di, bool forceall)
690 /* if forcing, dma engine must be disabled */
691 ASSERT(!forceall || !dma_rxenabled(di));
695 /* return if no packets posted */
699 /* ignore curr if forceall */
700 if (!forceall && (i == B2I(R_REG(&di->regs->rcvstatus) & RS_CD_MASK)))
703 /* get the packet pointer that corresponds to the rx descriptor */
708 /* clear this packet from the descriptor ring */
709 DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->rxd[i].addr)) - di->dataoffset),
710 di->rxbufsize, DMA_RX, rxp);
711 W_SM(&di->rxd[i].addr, 0xdeadbeef);
713 di->rxin = NEXTRXD(i);
719 dma_dumptx(dma_info_t *di, char *buf)
721 buf += sprintf(buf, "txd 0x%lx txdpa 0x%lx txp 0x%lx txin %d txout %d txavail %d\n",
722 (ulong)di->txd, di->txdpa, (ulong)di->txp, di->txin, di->txout, di->txavail);
723 buf += sprintf(buf, "xmtcontrol 0x%x xmtaddr 0x%x xmtptr 0x%x xmtstatus 0x%x\n",
724 R_REG(&di->regs->xmtcontrol),
725 R_REG(&di->regs->xmtaddr),
726 R_REG(&di->regs->xmtptr),
727 R_REG(&di->regs->xmtstatus));
732 dma_dumprx(dma_info_t *di, char *buf)
734 buf += sprintf(buf, "rxd 0x%lx rxdpa 0x%lx rxp 0x%lx rxin %d rxout %d\n",
735 (ulong)di->rxd, di->rxdpa, (ulong)di->rxp, di->rxin, di->rxout);
736 buf += sprintf(buf, "rcvcontrol 0x%x rcvaddr 0x%x rcvptr 0x%x rcvstatus 0x%x\n",
737 R_REG(&di->regs->rcvcontrol),
738 R_REG(&di->regs->rcvaddr),
739 R_REG(&di->regs->rcvptr),
740 R_REG(&di->regs->rcvstatus));
745 dma_dump(dma_info_t *di, char *buf)
747 buf = dma_dumptx(di, buf);
748 buf = dma_dumprx(di, buf);
753 dma_getvar(dma_info_t *di, char *name)
755 if (!strcmp(name, "&txavail"))
756 return ((uint) &di->txavail);
764 dma_txblock(dma_info_t *di)
770 dma_txunblock(dma_info_t *di)
772 di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
776 dma_txactive(dma_info_t *di)
778 return (NTXDACTIVE(di->txin, di->txout));
782 * Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
785 dma_txrotate(di_t *di)
794 ASSERT(dma_txsuspended(di));
796 nactive = dma_txactive(di);
797 ad = B2I((R_REG(&di->regs->xmtstatus) & XS_AD_MASK) >> XS_AD_SHIFT);
798 rot = TXD(ad - di->txin);
800 ASSERT(rot < di->ntxd);
802 /* full-ring case is a lot harder - don't worry about this */
803 if (rot >= (di->ntxd - nactive)) {
804 DMA_ERROR(("%s: dma_txrotate: ring full - punt\n", di->name));
809 last = PREVTXD(di->txout);
811 /* move entries starting at last and moving backwards to first */
812 for (old = last; old != PREVTXD(first); old = PREVTXD(old)) {
813 new = TXD(old + rot);
816 * Move the tx dma descriptor.
817 * EOT is set only in the last entry in the ring.
819 w = R_SM(&di->txd[old].ctrl) & ~CTRL_EOT;
820 if (new == (di->ntxd - 1))
822 W_SM(&di->txd[new].ctrl, w);
823 W_SM(&di->txd[new].addr, R_SM(&di->txd[old].addr));
825 /* zap the old tx dma descriptor address field */
826 W_SM(&di->txd[old].addr, 0xdeadbeef);
828 /* move the corresponding txp[] entry */
829 ASSERT(di->txp[new] == NULL);
830 di->txp[new] = di->txp[old];
834 /* update txin and txout */
836 di->txout = TXD(di->txout + rot);
837 di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
840 W_REG(&di->regs->xmtptr, I2B(di->txout));