// SPDX-License-Identifier: GPL-2.0-only /* * nicstar.c * * Device driver supporting CBR for IDT 77201/77211 "NICStAR" based cards. * * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME. * It was taken from the frle-0.22 device driver. * As the file doesn't have a copyright notice, in the file * nicstarmac.copyright I put the copyright notice from the * frle-0.22 device driver. * Some code is based on the nicstar driver by M. Welsh. * * Author: Rui Prior (rprior@inescn.pt) * PowerPC support by Jay Talbott (jay_talbott@mcg.mot.com) April 1999 * * * (C) INESC 1999
*/
/* * IMPORTANT INFORMATION * * There are currently three types of spinlocks: * * 1 - Per card interrupt spinlock (to protect structures and such) * 2 - Per SCQ scq spinlock * 3 - Per card resource spinlock (to access registers, etc.) * * These must NEVER be grabbed in reverse order. *
*/
staticvoid ns_write_sram(ns_dev * card, u32 sram_address, u32 * value, int count)
{ unsignedlong flags; int i, c;
count--; /* count range now is 0..3 instead of 1..4 */
c = count;
c <<= 2; /* to use increments of 4 */
spin_lock_irqsave(&card->res_lock, flags); while (CMD_BUSY(card)) ; for (i = 0; i <= c; i += 4)
writel(*(value++), card->membase + i); /* Note: DR# registers are the first 4 dwords in nicstar's memspace,
so card->membase + DR0 == card->membase */
sram_address <<= 2;
sram_address &= 0x0007FFFC;
sram_address |= (0x40000000 | count);
writel(sram_address, card->membase + CMD);
spin_unlock_irqrestore(&card->res_lock, flags);
}
staticint ns_init_card(int i, struct pci_dev *pcidev)
{ int j; struct ns_dev *card = NULL; unsignedchar pci_latency; unsigned error;
u32 data;
u32 u32d[4];
u32 ns_cfg_rctsize; int bcount; unsignedlong membase;
/* Detect PHY type */ while (CMD_BUSY(card)) ;
writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD); while (CMD_BUSY(card)) ;
data = readl(card->membase + DR0); switch (data) { case 0x00000009:
printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
card->max_pcr = ATM_25_PCR; while (CMD_BUSY(card)) ;
writel(0x00000008, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD); /* Clear an eventual pending interrupt */
writel(NS_STAT_SFBQF, card->membase + STAT); #ifdef PHY_LOOPBACK while (CMD_BUSY(card)) ;
writel(0x00000022, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD); #endif/* PHY_LOOPBACK */ break; case 0x00000030: case 0x00000031:
printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
card->max_pcr = ATM_OC3_PCR; #ifdef PHY_LOOPBACK while (CMD_BUSY(card)) ;
writel(0x00000002, card->membase + DR0);
writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD); #endif/* PHY_LOOPBACK */ break; default:
printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data);
error = 8;
ns_init_card_error(card, error); return error;
}
writel(0x00000000, card->membase + GP);
/* Determine SRAM size */
data = 0x76543210;
ns_write_sram(card, 0x1C003, &data, 1);
data = 0x89ABCDEF;
ns_write_sram(card, 0x14003, &data, 1); if (ns_read_sram(card, 0x14003) == 0x89ABCDEF &&
ns_read_sram(card, 0x1C003) == 0x76543210)
card->sram_size = 128; else
card->sram_size = 32;
PRINTK("nicstar%d: %dK x 32bit SRAM size.\n", i, card->sram_size);
card->rct_size = NS_MAX_RCTSIZE;
#if (NS_MAX_RCTSIZE == 4096) if (card->sram_size == 128)
printk
("nicstar%d: limiting maximum VCI. See NS_MAX_RCTSIZE in nicstar.h\n",
i); #elif (NS_MAX_RCTSIZE == 16384) if (card->sram_size == 32) {
printk
("nicstar%d: wasting memory. See NS_MAX_RCTSIZE in nicstar.h\n",
i);
card->rct_size = 4096;
} #else #error NS_MAX_RCTSIZE must be either 4096 or 16384 in nicstar.c #endif
/* For variable rate SCQ vcc must be NULL */ staticvoid free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc)
{ int i;
if (scq->num_entries == VBR_SCQ_NUM_ENTRIES) for (i = 0; i < scq->num_entries; i++) { if (scq->skb[i] != NULL) {
vcc = ATM_SKB(scq->skb[i])->vcc; if (vcc->pop != NULL)
vcc->pop(vcc, scq->skb[i]); else
dev_kfree_skb_any(scq->skb[i]);
}
} else { /* vcc must be != NULL */
if (vcc == NULL) {
printk
("nicstar: free_scq() called with vcc == NULL for fixed rate scq."); for (i = 0; i < scq->num_entries; i++)
dev_kfree_skb_any(scq->skb[i]);
} else for (i = 0; i < scq->num_entries; i++) { if (scq->skb[i] != NULL) { if (vcc->pop != NULL)
vcc->pop(vcc, scq->skb[i]); else
dev_kfree_skb_any(scq->skb[i]);
}
}
}
kfree(scq->skb);
dma_free_coherent(&card->pcidev->dev,
2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ?
VBR_SCQSIZE : CBR_SCQSIZE),
scq->org, scq->dma);
kfree(scq);
}
/* The handles passed must be pointers to the sk_buff containing the small
or large buffer(s) cast to u32. */ staticvoid push_rxbufs(ns_dev * card, struct sk_buff *skb)
{ struct sk_buff *handle1, *handle2; int id1, id2;
u32 addr1, addr2;
u32 stat; unsignedlong flags;
/* *BARF* */
handle2 = NULL;
addr2 = 0;
handle1 = skb;
addr1 = dma_map_single(&card->pcidev->dev,
skb->data,
(NS_PRV_BUFTYPE(skb) == BUF_SM
? NS_SMSKBSIZE : NS_LGSKBSIZE),
DMA_TO_DEVICE);
NS_PRV_DMA(skb) = addr1; /* save so we can unmap later */
#ifdef GENERAL_DEBUG if (!addr1)
printk("nicstar%d: push_rxbufs called with addr1 = 0.\n",
card->index); #endif/* GENERAL_DEBUG */
stat = readl(card->membase + STAT);
card->sbfqc = ns_stat_sfbqc_get(stat);
card->lbfqc = ns_stat_lfbqc_get(stat); if (NS_PRV_BUFTYPE(skb) == BUF_SM) { if (!addr2) { if (card->sm_addr) {
addr2 = card->sm_addr;
handle2 = card->sm_handle;
card->sm_addr = 0x00000000;
card->sm_handle = NULL;
} else { /* (!sm_addr) */
card = (ns_dev *) dev_id;
dev = card->atmdev;
card->intcnt++;
PRINTK("nicstar%d: NICStAR generated an interrupt\n", card->index);
spin_lock_irqsave(&card->int_lock, flags);
stat_r = readl(card->membase + STAT);
/* Transmit Status Indicator has been written to T. S. Queue */ if (stat_r & NS_STAT_TSIF) {
TXPRINTK("nicstar%d: TSI interrupt\n", card->index);
process_tsq(card);
writel(NS_STAT_TSIF, card->membase + STAT);
}
/* Incomplete CS-PDU has been transmitted */ if (stat_r & NS_STAT_TXICP) {
writel(NS_STAT_TXICP, card->membase + STAT);
TXPRINTK("nicstar%d: Incomplete CS-PDU transmitted.\n",
card->index);
}
/* Transmit Status Queue 7/8 full */ if (stat_r & NS_STAT_TSQF) {
writel(NS_STAT_TSQF, card->membase + STAT);
PRINTK("nicstar%d: TSQ full.\n", card->index);
process_tsq(card);
}
/* PHY device interrupt signal active */ if (stat_r & NS_STAT_PHYI) {
writel(NS_STAT_PHYI, card->membase + STAT);
PRINTK("nicstar%d: PHY interrupt.\n", card->index); if (dev->phy && dev->phy->interrupt) {
dev->phy->interrupt(dev);
}
}
/* Small Buffer Queue is full */ if (stat_r & NS_STAT_SFBQF) {
writel(NS_STAT_SFBQF, card->membase + STAT);
printk("nicstar%d: Small free buffer queue is full.\n",
card->index);
}
/* Large Buffer Queue is full */ if (stat_r & NS_STAT_LFBQF) {
writel(NS_STAT_LFBQF, card->membase + STAT);
printk("nicstar%d: Large free buffer queue is full.\n",
card->index);
}
/* Receive Status Queue is full */ if (stat_r & NS_STAT_RSQF) {
writel(NS_STAT_RSQF, card->membase + STAT);
printk("nicstar%d: RSQ full.\n", card->index);
process_rsq(card);
}
/* Complete CS-PDU received */ if (stat_r & NS_STAT_EOPDU) {
RXPRINTK("nicstar%d: End of CS-PDU received.\n", card->index);
process_rsq(card);
writel(NS_STAT_EOPDU, card->membase + STAT);
}
/* Raw cell received */ if (stat_r & NS_STAT_RAWCF) {
writel(NS_STAT_RAWCF, card->membase + STAT); #ifndef RCQ_SUPPORT
printk("nicstar%d: Raw cell received and no support yet...\n",
card->index); #endif/* RCQ_SUPPORT */ /* NOTE: the following procedure may keep a raw cell pending until the next interrupt. As this preliminary support is only meant to
avoid buffer leakage, this is not an issue. */ while (readl(card->membase + RAWCT) != card->rawch) {
if (ns_rcqe_islast(card->rawcell)) { struct sk_buff *oldbuf;
/* Receive Status Queue is 7/8 full */ if (stat_r & NS_STAT_RSQAF) {
writel(NS_STAT_RSQAF, card->membase + STAT);
RXPRINTK("nicstar%d: RSQ almost full.\n", card->index);
process_rsq(card);
}
spin_unlock_irqrestore(&card->int_lock, flags);
PRINTK("nicstar%d: end of interrupt service\n", card->index); return IRQ_HANDLED;
}
staticint ns_open(struct atm_vcc *vcc)
{
ns_dev *card;
vc_map *vc; unsignedlong tmpl, modl; int tcr, tcra; /* target cell rate, and absolute value */ int n = 0; /* Number of entries in the TST. Initialized to remove
the compiler warning. */
u32 u32d[4]; int frscdi = 0; /* Index of the SCD. Initialized to remove the compiler warning. How I wish compilers were clever enough to tell which variables can truly be used
uninitialized... */ int inuse; /* tx or rx vc already in use by another vcc */ short vpi = vcc->vpi; int vci = vcc->vci;
inuse = 0; if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx)
inuse = 1; if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx)
inuse += 2; if (inuse) {
printk("nicstar%d: %s vci already in use.\n", card->index,
inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx"); return -EINVAL;
}
set_bit(ATM_VF_ADDR, &vcc->flags);
/* NOTE: You are not allowed to modify an open connection's QOS. To change that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
needed to do that. */ if (!test_bit(ATM_VF_PARTIAL, &vcc->flags)) {
scq_info *scq;
set_bit(ATM_VF_PARTIAL, &vcc->flags); if (vcc->qos.txtp.traffic_class == ATM_CBR) { /* Check requested cell rate and availability of SCD */ if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0
&& vcc->qos.txtp.min_pcr == 0) {
PRINTK
("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
card->index);
clear_bit(ATM_VF_PARTIAL, &vcc->flags);
clear_bit(ATM_VF_ADDR, &vcc->flags); return -EINVAL;
}
for (;;) {
spin_lock_irqsave(&scq->lock, flags);
scqep = scq->next; if (scqep == scq->base)
scqep = scq->last; else
scqep--; if (scqep == scq->tail) {
spin_unlock_irqrestore(&scq->lock, flags); break;
} /* If the last entry is not a TSR, place one in the SCQ in order to
be able to completely drain it and then close. */ if (!ns_scqe_is_tsr(scqep) && scq->tail != scq->next) {
ns_scqe tsr;
u32 scdi, scqi;
u32 data; int index;
/* Free all TST entries */
data = NS_TST_OPCODE_VARIABLE; for (i = 0; i < NS_TST_NUM_ENTRIES; i++) { if (card->tste2vc[i] == vc) {
ns_write_sram(card, card->tst_addr + i, &data,
1);
card->tste2vc[i] = NULL;
card->tst_free_entries++;
}
}
staticvoid fill_tst(ns_dev * card, int n, vc_map * vc)
{
u32 new_tst; unsignedlong cl; int e, r;
u32 data;
/* It would be very complicated to keep the two TSTs synchronized while assuring that writes are only made to the inactive TST. So, for now I
will use only one TST. If problems occur, I will change this again */
new_tst = card->tst_addr;
/* Fill procedure */
for (e = 0; e < NS_TST_NUM_ENTRIES; e++) { if (card->tste2vc[e] == NULL) break;
} if (e == NS_TST_NUM_ENTRIES) {
printk("nicstar%d: No free TST entries found. \n", card->index); return;
}
r = n;
cl = NS_TST_NUM_ENTRIES;
data = ns_tste_make(NS_TST_OPCODE_FIXED, vc->cbr_scd);
while (r > 0) { if (cl >= NS_TST_NUM_ENTRIES && card->tste2vc[e] == NULL) {
card->tste2vc[e] = vc;
ns_write_sram(card, new_tst + e, &data, 1);
cl -= NS_TST_NUM_ENTRIES;
r--;
}
if (++e == NS_TST_NUM_ENTRIES) {
e = 0;
}
cl += n;
}
if (!vc->tx) {
printk("nicstar%d: Trying to transmit on a non-tx VC.\n",
card->index);
atomic_inc(&vcc->stats->tx_err);
dev_kfree_skb_any(skb); return -EINVAL;
}
if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
printk("nicstar%d: Only AAL0 and AAL5 are supported.\n",
card->index);
atomic_inc(&vcc->stats->tx_err);
dev_kfree_skb_any(skb); return -EINVAL;
}
if (skb_shinfo(skb)->nr_frags != 0) {
printk("nicstar%d: No scatter-gather yet.\n", card->index);
atomic_inc(&vcc->stats->tx_err);
dev_kfree_skb_any(skb); return -EINVAL;
}
staticvoid process_tsq(ns_dev * card)
{
u32 scdi;
scq_info *scq;
ns_tsi *previous = NULL, *one_ahead, *two_ahead; int serviced_entries; /* flag indicating at least on entry was serviced */
while (!ns_tsi_isempty(card->tsq.next) || !ns_tsi_isempty(one_ahead) ||
!ns_tsi_isempty(two_ahead)) /* At most two empty, as stated in the 77201 errata */
{
serviced_entries = 1;
/* Skip the one or two possible empty entries */ while (ns_tsi_isempty(card->tsq.next)) { if (card->tsq.next == card->tsq.last)
card->tsq.next = card->tsq.base; else
card->tsq.next++;
}
if (!ns_tsi_tmrof(card->tsq.next)) {
scdi = ns_tsi_getscdindex(card->tsq.next); if (scdi == NS_TSI_SCDISVBR)
scq = card->scq0; else { if (card->scd2vc[scdi] == NULL) {
printk
("nicstar%d: could not find VC from SCD index.\n",
card->index);
ns_tsi_init(card->tsq.next); return;
}
scq = card->scd2vc[scdi]->scq;
}
drain_scq(card, scq, ns_tsi_getscqpos(card->tsq.next));
scq->full = 0;
wake_up_interruptible(&(scq->scqfull_waitq));
}
if (serviced_entries)
writel(PTR_DIFF(previous, card->tsq.base),
card->membase + TSQH);
}
staticvoid drain_scq(ns_dev * card, scq_info * scq, int pos)
{ struct atm_vcc *vcc; struct sk_buff *skb; int i; unsignedlong flags;
XPRINTK("nicstar%d: drain_scq() called, scq at 0x%p, pos %d.\n",
card->index, scq, pos); if (pos >= scq->num_entries) {
printk("nicstar%d: Bad index on drain_scq().\n", card->index); return;
}
spin_lock_irqsave(&scq->lock, flags);
i = (int)(scq->tail - scq->base); if (++i == scq->num_entries)
i = 0; while (i != pos) {
skb = scq->skb[i];
XPRINTK("nicstar%d: freeing skb at 0x%p (index %d).\n",
card->index, skb, i); if (skb != NULL) {
dma_unmap_single(&card->pcidev->dev,
NS_PRV_DMA(skb),
skb->len,
DMA_TO_DEVICE);
vcc = ATM_SKB(skb)->vcc; if (vcc && vcc->pop != NULL) {
vcc->pop(vcc, skb);
} else {
dev_kfree_skb_irq(skb);
}
scq->skb[i] = NULL;
} if (++i == scq->num_entries)
i = 0;
}
scq->tail = scq->base + pos;
spin_unlock_irqrestore(&scq->lock, flags);
}
/* To reach this point, the AAL layer can only be AAL5 */
if ((iovb = vc->rx_iov) == NULL) {
iovb = skb_dequeue(&(card->iovpool.queue)); if (iovb == NULL) { /* No buffers in the queue */
iovb = alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC); if (iovb == NULL) {
printk("nicstar%d: Out of iovec buffers.\n",
card->index);
atomic_inc(&vcc->stats->rx_drop);
recycle_rx_buf(card, skb); return;
}
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
} elseif (--card->iovpool.count < card->iovnr.min) { struct sk_buff *new_iovb; if ((new_iovb =
alloc_skb(NS_IOVBUFSIZE, GFP_ATOMIC)) != NULL) {
NS_PRV_BUFTYPE(iovb) = BUF_NONE;
skb_queue_tail(&card->iovpool.queue, new_iovb);
card->iovpool.count++;
}
}
vc->rx_iov = iovb;
NS_PRV_IOVCNT(iovb) = 0;
iovb->len = 0;
iovb->data = iovb->head;
skb_reset_tail_pointer(iovb); /* IMPORTANT: a pointer to the sk_buff containing the small or large buffer is stored as iovec base, NOT a pointer to the
small or large buffer itself. */
} elseif (NS_PRV_IOVCNT(iovb) >= NS_MAX_IOVECS) {
printk("nicstar%d: received too big AAL5 SDU.\n", card->index);
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_MAX_IOVECS);
NS_PRV_IOVCNT(iovb) = 0;
iovb->len = 0;
iovb->data = iovb->head;
skb_reset_tail_pointer(iovb);
}
iov = &((struct iovec *)iovb->data)[NS_PRV_IOVCNT(iovb)++];
iov->iov_base = (void *)skb;
iov->iov_len = ns_rsqe_cellcount(rsqe) * 48;
iovb->len += iov->iov_len;
#ifdef EXTRA_DEBUG if (NS_PRV_IOVCNT(iovb) == 1) { if (NS_PRV_BUFTYPE(skb) != BUF_SM) {
printk
("nicstar%d: Expected a small buffer, and this is not one.\n",
card->index);
which_list(card, skb);
atomic_inc(&vcc->stats->rx_err);
recycle_rx_buf(card, skb);
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb); return;
}
} else { /* NS_PRV_IOVCNT(iovb) >= 2 */
if (NS_PRV_BUFTYPE(skb) != BUF_LG) {
printk
("nicstar%d: Expected a large buffer, and this is not one.\n",
card->index);
which_list(card, skb);
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_PRV_IOVCNT(iovb));
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb); return;
}
} #endif/* EXTRA_DEBUG */
if (ns_rsqe_eopdu(rsqe)) { /* This works correctly regardless of the endianness of the host */ unsignedchar *L1L2 = (unsignedchar *)
(skb->data + iov->iov_len - 6);
aal5_len = L1L2[0] << 8 | L1L2[1];
len = (aal5_len == 0x0000) ? 0x10000 : aal5_len; if (ns_rsqe_crcerr(rsqe) ||
len + 8 > iovb->len || len + (47 + 8) < iovb->len) {
printk("nicstar%d: AAL5 CRC error", card->index); if (len + 8 > iovb->len || len + (47 + 8) < iovb->len)
printk(" - PDU size mismatch.\n"); else
printk(".\n");
atomic_inc(&vcc->stats->rx_err);
recycle_iovec_rx_bufs(card, (struct iovec *)iovb->data,
NS_PRV_IOVCNT(iovb));
vc->rx_iov = NULL;
recycle_iov_buf(card, iovb); return;
}
/* By this point we (hopefully) have a complete SDU without errors. */
if (NS_PRV_IOVCNT(iovb) == 1) { /* Just a small buffer */ /* skb points to a small buffer */ if (!atm_charge(vcc, skb->truesize)) {
push_rxbufs(card, skb);
atomic_inc(&vcc->stats->rx_drop);
} else {
skb_put(skb, len);
dequeue_sm_buf(card, skb);
ATM_SKB(skb)->vcc = vcc;
__net_timestamp(skb);
vcc->push(vcc, skb);
atomic_inc(&vcc->stats->rx);
}
} elseif (NS_PRV_IOVCNT(iovb) == 2) { /* One small plus one large buffer */ struct sk_buff *sb;
sb = (struct sk_buff *)(iov - 1)->iov_base; /* skb points to a large buffer */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.