// SPDX-License-Identifier: GPL-2.0-or-later /* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com> * * This driver supports ATM cards based on the Efficient "Lanai" * chipset such as the Speedstream 3010 and the ENI-25p. The * Speedstream 3060 is currently not supported since we don't * have the code to drive the on-board Alcatel DSL chipset (yet). * * Thanks to Efficient for supporting this project with hardware, * documentation, and by answering my questions. * * Things not working yet: * * o We don't support the Speedstream 3060 yet - this card has * an on-board DSL modem chip by Alcatel and the driver will * need some extra code added to handle it * * o Note that due to limitations of the Lanai only one VCC can be * in CBR at once * * o We don't currently parse the EEPROM at all. The code is all * there as per the spec, but it doesn't actually work. I think * there may be some issues with the docs. Anyway, do NOT * enable it yet - bugs in that code may actually damage your * hardware! Because of this you should hardware an ESI before * trying to use this in a LANE or MPOA environment. * * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: * vcc_tx_aal0() needs to send or queue a SKB * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs * vcc_rx_aal0() needs to handle AAL0 interrupts * This isn't too much work - I just wanted to get other things * done first. * * o lanai_change_qos() isn't written yet * * o There aren't any ioctl's yet -- I'd like to eventually support * setting loopback and LED modes that way. * * o If the segmentation engine or DMA gets shut down we should restart * card as per section 17.0i. (see lanai_reset) * * o setsockopt(SO_CIRANGE) isn't done (although despite what the * API says it isn't exactly commonly implemented)
*/
/* * Maximum number of VCIs per card. Setting it lower could theoretically * save some memory, but since we allocate our vcc list with get_free_pages, * it's not really likely for most architectures
*/ #define NUM_VCI (1024)
/* * Enable extra debugging
*/ #define DEBUG /* * Debug _all_ register operations with card, except the memory test. * Also disables the timed poll to prevent extra chattiness. This * isn't for normal use
*/ #undef DEBUG_RW
/* * The programming guide specifies a full test of the on-board SRAM * at initialization time. Undefine to remove this
*/ #define FULL_MEMORY_TEST
/* * This is the number of (4 byte) service entries that we will * try to allocate at startup. Note that we will end up with * one PAGE_SIZE's worth regardless of what this is set to
*/ #define SERVICE_ENTRIES (1024) /* TODO: make above a module load-time option */
/* * We normally read the onboard EEPROM in order to discover our MAC * address. Undefine to _not_ do this
*/ /* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ /* TODO: make above a module load-time option (also) */
/* * Depth of TX fifo (in 128 byte units; range 2-31) * Smaller numbers are better for network latency * Larger numbers are better for PCI latency * I'm really sure where the best tradeoff is, but the BSD driver uses * 7 and it seems to work ok.
*/ #define TX_FIFO_DEPTH (7) /* TODO: make above a module load-time option */
/* * How often (in jiffies) we will try to unstick stuck connections - * shouldn't need to happen much
*/ #define LANAI_POLL_PERIOD (10*HZ) /* TODO: make above a module load-time option */
/* * When allocating an AAL5 receiving buffer, try to make it at least * large enough to hold this many max_sdu sized PDUs
*/ #define AAL5_RX_MULTIPLIER (3) /* TODO: make above a module load-time option */
/* * Same for transmitting buffer
*/ #define AAL5_TX_MULTIPLIER (3) /* TODO: make above a module load-time option */
/* * When allocating an AAL0 transmiting buffer, how many cells should fit. * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't * really critical
*/ #define AAL0_TX_MULTIPLIER (40) /* TODO: make above a module load-time option */
/* * How large should we make the AAL0 receiving buffer. Remember that this * is shared between all AAL0 VC's
*/ #define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) /* TODO: make above a module load-time option */
/* * Should we use Lanai's "powerdown" feature when no vcc's are bound?
*/ /* #define USE_POWERDOWN */ /* TODO: make above a module load-time option (also) */
/* -------------------- DEBUGGING AIDS: */
#define DEV_LABEL "lanai"
#ifdef DEBUG
#define DPRINTK(format, args...) \
printk(KERN_DEBUG DEV_LABEL ": " format, ##args) #define APRINTK(truth, format, args...) \ do { \ if (unlikely(!(truth))) \
printk(KERN_ERR DEV_LABEL ": " format, ##args); \
} while (0)
#else/* !DEBUG */
#define DPRINTK(format, args...) #define APRINTK(truth, format, args...)
/* DMA buffer in host memory for TX, RX, or service list. */ struct lanai_buffer {
u32 *start; /* From get_free_pages */
u32 *end; /* One past last byte */
u32 *ptr; /* Pointer to current host location */
dma_addr_t dmaaddr;
};
/* * This is the card-specific per-vcc data. Note that unlike some other * drivers there is NOT a 1-to-1 correspondance between these and * atm_vcc's - each one of these represents an actual 2-way vcc, but * an atm_vcc can be 1-way and share with a 1-way vcc in the other * direction. To make it weirder, there can even be 0-way vccs * bound to us, waiting to do a change_qos
*/ struct lanai_vcc {
bus_addr_t vbase; /* Base of VCC's registers */ struct lanai_vcc_stats stats; int nref; /* # of atm_vcc's who reference us */
vci_t vci; struct { struct lanai_buffer buf; struct atm_vcc *atmvcc; /* atm_vcc who is receiver */
} rx; struct { struct lanai_buffer buf; struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ int endptr; /* last endptr from service entry */ struct sk_buff_head backlog; void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int);
} tx;
};
struct lanai_dev {
bus_addr_t base; struct lanai_dev_stats stats; struct lanai_buffer service; struct lanai_vcc **vccs; #ifdef USE_POWERDOWN int nbound; /* number of bound vccs */ #endif enum lanai_type type;
vci_t num_vci; /* Currently just NUM_VCI */
u8 eeprom[LANAI_EEPROM_SIZE];
u32 serialno, magicno; struct pci_dev *pci;
DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */
DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */ struct timer_list timer; int naal0; struct lanai_buffer aal0buf; /* AAL0 RX buffers */
u32 conf1, conf2; /* CONFIG[12] registers */
u32 status; /* STATUS register */
spinlock_t endtxlock;
spinlock_t servicelock; struct atm_vcc *cbrvcc; int number; int board_rev; /* TODO - look at race conditions with maintence of conf1/conf2 */ /* TODO - transmit locking: should we use _irq not _irqsave? */ /* TODO - organize above in some rational fashion (see <asm/cache.h>) */
};
/* * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready) * This function iterates one of these, calling a given function for each * vci with their bit set
*/ staticvoid vci_bitfield_iterate(struct lanai_dev *lanai, constunsignedlong *lp, void (*func)(struct lanai_dev *,vci_t vci))
{
vci_t vci;
/* * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - * usually any page allocation will do. Just to be safe in case * PAGE_SIZE is insanely tiny, though...
*/ #define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024)
/* * Allocate a buffer in host RAM for service list, RX, or TX * Returns buf->start==NULL if no memory * Note that the size will be rounded up 2^n bytes, and * if we can't allocate that we'll settle for something smaller * until minbytes
*/ staticvoid lanai_buf_allocate(struct lanai_buffer *buf,
size_t bytes, size_t minbytes, struct pci_dev *pci)
{ int size;
if (bytes > (128 * 1024)) /* max lanai buffer size */
bytes = 128 * 1024; for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2)
; if (minbytes < LANAI_PAGE_SIZE)
minbytes = LANAI_PAGE_SIZE; do { /* * Technically we could use non-consistent mappings for * everything, but the way the lanai uses DMA memory would * make that a terrific pain. This is much simpler.
*/
buf->start = dma_alloc_coherent(&pci->dev,
size, &buf->dmaaddr, GFP_KERNEL); if (buf->start != NULL) { /* Success */ /* Lanai requires 256-byte alignment of DMA bufs */
APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, "bad dmaaddr: 0x%lx\n",
(unsignedlong) buf->dmaaddr);
buf->ptr = buf->start;
buf->end = (u32 *)
(&((unsignedchar *) buf->start)[size]);
memset(buf->start, 0, size); break;
}
size /= 2;
} while (size >= minbytes);
}
/* size of buffer in bytes */ staticinline size_t lanai_buf_size(conststruct lanai_buffer *buf)
{ return ((unsignedlong) buf->end) - ((unsignedlong) buf->start);
}
/* Same as conf2_write(), but defers I/O if we're powered down */ staticinlinevoid conf2_write_if_powerup(conststruct lanai_dev *lanai)
{ #ifdef USE_POWERDOWN if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0)) return; #endif/* USE_POWERDOWN */
conf2_write(lanai);
}
staticinlinevoid reset_board(conststruct lanai_dev *lanai)
{
DPRINTK("about to reset board\n");
reg_write(lanai, 0, Reset_Reg); /* * If we don't delay a little while here then we can end up * leaving the card in a VERY weird state and lock up the * PCI bus. This isn't documented anywhere but I've convinced * myself after a lot of painful experimentation
*/
udelay(5);
}
/* -------------------- CARD SRAM UTILITIES: */
/* The SRAM is mapped into normal PCI memory space - the only catch is * that it is only 16-bits wide but must be accessed as 32-bit. The * 16 high bits will be zero. We don't hide this, since they get * programmed mostly like discrete registers anyway
*/ #define SRAM_START (0x20000) #define SRAM_BYTES (0x20000) /* Again, half don't really exist */
/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */
/* How many bytes will an AAL5 PDU take to transmit - remember that: * o we need to add 8 bytes for length, CPI, UU, and CRC * o we need to round up to 48 bytes for cells
*/ staticinlineint aal5_size(int size)
{ int cells = (size + 8 + 47) / 48; return cells * 48;
}
/* Shutdown receiving on card */ staticvoid lanai_shutdown_rx_vci(conststruct lanai_vcc *lvcc)
{ if (lvcc->vbase == NULL) /* We were never bound to a VCI */ return; /* 15.1.1 - set to trashing, wait one cell time (15us) */
cardvcc_write(lvcc,
RXADDR1_SET_RMMODE(RMMODE_TRASH) |
RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1);
udelay(15); /* 15.1.2 - clear rest of entries */
cardvcc_write(lvcc, 0, vcc_rxaddr2);
cardvcc_write(lvcc, 0, vcc_rxcrc1);
cardvcc_write(lvcc, 0, vcc_rxcrc2);
cardvcc_write(lvcc, 0, vcc_rxwriteptr);
cardvcc_write(lvcc, 0, vcc_rxbufstart);
cardvcc_write(lvcc, 0, vcc_rxreadptr);
}
/* Shutdown transmitting on card. * Unfortunately the lanai needs us to wait until all the data * drains out of the buffer before we can dealloc it, so this * can take a while -- up to 370ms for a full 128KB buffer * assuming everone else is quiet. In theory the time is * boundless if there's a CBR VCC holding things up.
*/ staticvoid lanai_shutdown_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc)
{ struct sk_buff *skb; unsignedlong flags, timeout; int read, write, lastread = -1;
if (lvcc->vbase == NULL) /* We were never bound to a VCI */ return; /* 15.2.1 - wait for queue to drain */ while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL)
lanai_free_skb(lvcc->tx.atmvcc, skb);
read_lock_irqsave(&vcc_sklist_lock, flags);
__clear_bit(lvcc->vci, lanai->backlog_vccs);
read_unlock_irqrestore(&vcc_sklist_lock, flags); /* * We need to wait for the VCC to drain but don't wait forever. We * give each 1K of buffer size 1/128th of a second to clear out. * TODO: maybe disable CBR if we're about to timeout?
*/
timeout = jiffies +
(((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7);
write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); for (;;) {
read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); if (read == write && /* Is TX buffer empty? */
(lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR ||
(cardvcc_read(lvcc, vcc_txcbr_next) &
TXCBR_NEXT_BOZO) == 0)) break; if (read != lastread) { /* Has there been any progress? */
lastread = read;
timeout += HZ / 10;
} if (unlikely(time_after(jiffies, timeout))) {
printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on " "backlog closing vci %d\n",
lvcc->tx.atmvcc->dev->number, lvcc->vci);
DPRINTK("read, write = %d, %d\n", read, write); break;
}
msleep(40);
} /* 15.2.2 - clear out all tx registers */
cardvcc_write(lvcc, 0, vcc_txreadptr);
cardvcc_write(lvcc, 0, vcc_txwriteptr);
cardvcc_write(lvcc, 0, vcc_txendptr);
cardvcc_write(lvcc, 0, vcc_txcrc1);
cardvcc_write(lvcc, 0, vcc_txcrc2);
cardvcc_write(lvcc, 0, vcc_txaddr2);
cardvcc_write(lvcc, 0, vcc_txaddr1);
}
staticint eeprom_read(struct lanai_dev *lanai)
{ int i, address;
u8 data;
u32 tmp; #define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \
} while (0) #define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) #define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) #define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) #define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) #define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) #define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) #define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \
data_h(); udelay(5); } while (0) /* start with both clock and data high */
data_h(); clock_h(); udelay(5); for (address = 0; address < LANAI_EEPROM_SIZE; address++) {
data = (address << 1) | 1; /* Command=read + address */ /* send start bit */
data_l(); udelay(5);
clock_l(); udelay(5); for (i = 128; i != 0; i >>= 1) { /* write command out */
tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) |
((data & i) ? CONFIG1_PROMDATA : 0); if (lanai->conf1 != tmp) {
set_config1(tmp);
udelay(5); /* Let new data settle */
}
clock_h(); udelay(5); clock_l(); udelay(5);
} /* look for ack */
data_h(); clock_h(); udelay(5); if (read_pin() != 0) goto error; /* No ack seen */
clock_l(); udelay(5); /* read back result */ for (data = 0, i = 7; i >= 0; i--) {
data_h(); clock_h(); udelay(5);
data = (data << 1) | !!read_pin();
clock_l(); udelay(5);
} /* look again for ack */
data_h(); clock_h(); udelay(5); if (read_pin() == 0) goto error; /* Spurious ack */
clock_l(); udelay(5);
send_stop();
lanai->eeprom[address] = data;
DPRINTK("EEPROM 0x%04X %02X\n",
(unsignedint) address, (unsignedint) data);
} return 0;
error:
clock_l(); udelay(5); /* finish read */
send_stop();
printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n",
lanai->number, address); return -EIO; #undef set_config1 #undef clock_h #undef clock_l #undef data_h #undef data_l #undef pre_read #undef read_pin #undef send_stop
}
/* read a big-endian 4-byte value out of eeprom */ staticinline u32 eeprom_be4(conststruct lanai_dev *lanai, int address)
{ return be32_to_cpup((const u32 *) &lanai->eeprom[address]);
}
/* Checksum/validate EEPROM contents */ staticint eeprom_validate(struct lanai_dev *lanai)
{ int i, s;
u32 v; const u8 *e = lanai->eeprom; #ifdef DEBUG /* First, see if we can get an ASCIIZ string out of the copyright */ for (i = EEPROM_COPYRIGHT;
i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) if (e[i] < 0x20 || e[i] > 0x7E) break; if ( i != EEPROM_COPYRIGHT &&
i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0')
DPRINTK("eeprom: copyright = \"%s\"\n",
(char *) &e[EEPROM_COPYRIGHT]); else
DPRINTK("eeprom: copyright not found\n"); #endif /* Validate checksum */ for (i = s = 0; i < EEPROM_CHECKSUM; i++)
s += e[i];
s &= 0xFF; if (s != e[EEPROM_CHECKSUM]) {
printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " "(wanted 0x%02X, got 0x%02X)\n", lanai->number,
(unsignedint) s, (unsignedint) e[EEPROM_CHECKSUM]); return -EIO;
}
s ^= 0xFF; if (s != e[EEPROM_CHECKSUM_REV]) {
printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number,
(unsignedint) s, (unsignedint) e[EEPROM_CHECKSUM_REV]); return -EIO;
} /* Verify MAC address */ for (i = 0; i < 6; i++) if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) {
printk(KERN_ERR DEV_LABEL "(itf %d) : EEPROM MAC addresses don't match " "(0x%02X, inverse 0x%02X)\n", lanai->number,
(unsignedint) e[EEPROM_MAC + i],
(unsignedint) e[EEPROM_MAC_REV + i]); return -EIO;
}
DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]); /* Verify serial number */
lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL);
v = eeprom_be4(lanai, EEPROM_SERIAL_REV); if ((lanai->serialno ^ v) != 0xFFFFFFFF) {
printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
(unsignedint) lanai->serialno, (unsignedint) v); return -EIO;
}
DPRINTK("eeprom: Serial number = %d\n", (unsignedint) lanai->serialno); /* Verify magic number */
lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC);
v = eeprom_be4(lanai, EEPROM_MAGIC_REV); if ((lanai->magicno ^ v) != 0xFFFFFFFF) {
printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " "don't match (0x%08X, inverse 0x%08X)\n", lanai->number,
lanai->magicno, v); return -EIO;
}
DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); if (lanai->magicno != EEPROM_MAGIC_VALUE)
printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " "magic not what expected (got 0x%08X, not 0x%08X)\n",
lanai->number, (unsignedint) lanai->magicno,
(unsignedint) EEPROM_MAGIC_VALUE); return 0;
}
/* space left in tx buffer in bytes */ staticinlineint vcc_tx_space(conststruct lanai_vcc *lvcc, int endptr)
{ int r;
r = endptr * 16;
r -= ((unsignedlong) lvcc->tx.buf.ptr) -
((unsignedlong) lvcc->tx.buf.start);
r -= 16; /* Leave "bubble" - if start==end it looks empty */ if (r < 0)
r += lanai_buf_size(&lvcc->tx.buf); return r;
}
/* test if VCC is currently backlogged */ staticinlineint vcc_is_backlogged(conststruct lanai_vcc *lvcc)
{ return !skb_queue_empty(&lvcc->tx.backlog);
}
/* Bit fields in the segmentation buffer descriptor */ #define DESCRIPTOR_MAGIC (0xD0000000) #define DESCRIPTOR_AAL5 (0x00008000) #define DESCRIPTOR_AAL5_STREAM (0x00004000) #define DESCRIPTOR_CLP (0x00002000)
/* Add 32-bit AAL5 trailer and leave room for its CRC */ staticinlinevoid vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc, int len, int cpi, int uu)
{
APRINTK((((unsignedlong) lvcc->tx.buf.ptr) & 15) == 8, "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr);
lvcc->tx.buf.ptr += 2;
lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end)
lvcc->tx.buf.ptr = lvcc->tx.buf.start;
}
staticinlinevoid vcc_tx_memcpy(struct lanai_vcc *lvcc, constunsignedchar *src, int n)
{ unsignedchar *e; int m;
e = ((unsignedchar *) lvcc->tx.buf.ptr) + n;
m = e - (unsignedchar *) lvcc->tx.buf.end; if (m < 0)
m = 0;
memcpy(lvcc->tx.buf.ptr, src, n - m); if (m != 0) {
memcpy(lvcc->tx.buf.start, src + n - m, m);
e = ((unsignedchar *) lvcc->tx.buf.start) + m;
}
lvcc->tx.buf.ptr = (u32 *) e;
}
staticinlinevoid vcc_tx_memzero(struct lanai_vcc *lvcc, int n)
{ unsignedchar *e; int m; if (n == 0) return;
e = ((unsignedchar *) lvcc->tx.buf.ptr) + n;
m = e - (unsignedchar *) lvcc->tx.buf.end; if (m < 0)
m = 0;
memset(lvcc->tx.buf.ptr, 0, n - m); if (m != 0) {
memset(lvcc->tx.buf.start, 0, m);
e = ((unsignedchar *) lvcc->tx.buf.start) + m;
}
lvcc->tx.buf.ptr = (u32 *) e;
}
/* Update "butt" register to specify new WritePtr */ staticinlinevoid lanai_endtx(struct lanai_dev *lanai, conststruct lanai_vcc *lvcc)
{ int i, ptr = ((unsignedchar *) lvcc->tx.buf.ptr) -
(unsignedchar *) lvcc->tx.buf.start;
APRINTK((ptr & ~0x0001FFF0) == 0, "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n",
ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr,
lvcc->tx.buf.end);
/* * Since the "butt register" is a shared resounce on the card we * serialize all accesses to it through this spinlock. This is * mostly just paranoia since the register is rarely "busy" anyway * but is needed for correctness.
*/
spin_lock(&lanai->endtxlock); /* * We need to check if the "butt busy" bit is set before * updating the butt register. In theory this should * never happen because the ATM card is plenty fast at * updating the register. Still, we should make sure
*/ for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { if (unlikely(i > 50)) {
printk(KERN_ERR DEV_LABEL "(itf %d): butt register " "always busy!\n", lanai->number); break;
}
udelay(5);
} /* * Before we tall the card to start work we need to be sure 100% of * the info in the service buffer has been written before we tell * the card about it
*/
wmb();
reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg);
spin_unlock(&lanai->endtxlock);
}
/* * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's * space available. "pdusize" is the number of bytes the PDU will take
*/ staticvoid lanai_send_one_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize)
{ int pad;
APRINTK(pdusize == aal5_size(skb->len), "lanai_send_one_aal5: wrong size packet (%d != %d)\n",
pdusize, aal5_size(skb->len));
vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize);
pad = pdusize - skb->len - 8;
APRINTK(pad >= 0, "pad is negative (%d)\n", pad);
APRINTK(pad < 48, "pad is too big (%d)\n", pad);
vcc_tx_memcpy(lvcc, skb->data, skb->len);
vcc_tx_memzero(lvcc, pad);
vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0);
lanai_endtx(lanai, lvcc);
lanai_free_skb(lvcc->tx.atmvcc, skb);
atomic_inc(&lvcc->tx.atmvcc->stats->tx);
}
/* Try to fill the buffer - don't call unless there is backlog */ staticvoid vcc_tx_unqueue_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, int endptr)
{ int n; struct sk_buff *skb; int space = vcc_tx_space(lvcc, endptr);
APRINTK(vcc_is_backlogged(lvcc), "vcc_tx_unqueue() called with empty backlog (vci=%d)\n",
lvcc->vci); while (space >= 64) {
skb = skb_dequeue(&lvcc->tx.backlog); if (skb == NULL) goto no_backlog;
n = aal5_size(skb->len); if (n + 16 > space) { /* No room for this packet - put it back on queue */
skb_queue_head(&lvcc->tx.backlog, skb); return;
}
lanai_send_one_aal5(lanai, lvcc, skb, n);
space -= n + 16;
} if (!vcc_is_backlogged(lvcc)) {
no_backlog:
__clear_bit(lvcc->vci, lanai->backlog_vccs);
}
}
/* Given an skb that we want to transmit either send it now or queue */ staticvoid vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, struct sk_buff *skb)
{ int space, n; if (vcc_is_backlogged(lvcc)) /* Already backlogged */ goto queue_it;
space = vcc_tx_space(lvcc,
TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)));
n = aal5_size(skb->len);
APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n); if (space < n + 16) { /* No space for this PDU */
__set_bit(lvcc->vci, lanai->backlog_vccs);
queue_it:
skb_queue_tail(&lvcc->tx.backlog, skb); return;
}
lanai_send_one_aal5(lanai, lvcc, skb, n);
}
staticvoid vcc_tx_unqueue_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, int endptr)
{
printk(KERN_INFO DEV_LABEL ": vcc_tx_unqueue_aal0: not implemented\n");
}
/* unlike the _tx_ cousins, this doesn't update ptr */ staticinlinevoid vcc_rx_memcpy(unsignedchar *dest, conststruct lanai_vcc *lvcc, int n)
{ int m = ((constunsignedchar *) lvcc->rx.buf.ptr) + n -
((constunsignedchar *) (lvcc->rx.buf.end)); if (m < 0)
m = 0;
memcpy(dest, lvcc->rx.buf.ptr, n - m);
memcpy(dest + n - m, lvcc->rx.buf.start, m); /* Make sure that these copies don't get reordered */
barrier();
}
/* Receive AAL5 data on a VCC with a particular endptr */ staticvoid vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr)
{ int size; struct sk_buff *skb; const u32 *x;
u32 *end = &lvcc->rx.buf.start[endptr * 4]; int n = ((unsignedlong) end) - ((unsignedlong) lvcc->rx.buf.ptr); if (n < 0)
n += lanai_buf_size(&lvcc->rx.buf);
APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), "vcc_rx_aal5: n out of range (%d/%zu)\n",
n, lanai_buf_size(&lvcc->rx.buf)); /* Recover the second-to-last word to get true pdu length */ if ((x = &end[-2]) < lvcc->rx.buf.start)
x = &lvcc->rx.buf.end[-2]; /* * Before we actually read from the buffer, make sure the memory * changes have arrived
*/
rmb();
size = be32_to_cpup(x) & 0xffff; if (unlikely(n != aal5_size(size))) { /* Make sure size matches padding */
printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " "on vci=%d - size=%d n=%d\n",
lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n);
lvcc->stats.x.aal5.rx_badlen++; goto out;
}
skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); if (unlikely(skb == NULL)) {
lvcc->stats.rx_nomem++; goto out;
}
skb_put(skb, size);
vcc_rx_memcpy(skb->data, lvcc, size);
ATM_SKB(skb)->vcc = lvcc->rx.atmvcc;
__net_timestamp(skb);
lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb);
atomic_inc(&lvcc->rx.atmvcc->stats->rx);
out:
lvcc->rx.buf.ptr = end;
cardvcc_write(lvcc, endptr, vcc_rxreadptr);
}
staticvoid vcc_rx_aal0(struct lanai_dev *lanai)
{
printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */ /* Remember to increment lvcc->rx.atmvcc->stats->rx */
}
staticinlinevoid host_vcc_bind(struct lanai_dev *lanai, struct lanai_vcc *lvcc, vci_t vci)
{ if (lvcc->vbase != NULL) return; /* We already were bound in the other direction */
DPRINTK("Binding vci %d\n", vci); #ifdef USE_POWERDOWN if (lanai->nbound++ == 0) {
DPRINTK("Coming out of powerdown\n");
lanai->conf1 &= ~CONFIG1_POWERDOWN;
conf1_write(lanai);
conf2_write(lanai);
} #endif
lvcc->vbase = cardvcc_addr(lanai, vci);
lanai->vccs[lvcc->vci = vci] = lvcc;
}
staticinlinevoid host_vcc_unbind(struct lanai_dev *lanai, struct lanai_vcc *lvcc)
{ if (lvcc->vbase == NULL) return; /* This vcc was never bound */
DPRINTK("Unbinding vci %d\n", lvcc->vci);
lvcc->vbase = NULL;
lanai->vccs[lvcc->vci] = NULL; #ifdef USE_POWERDOWN if (--lanai->nbound == 0) {
DPRINTK("Going into powerdown\n");
lanai->conf1 |= CONFIG1_POWERDOWN;
conf1_write(lanai);
} #endif
}
/* -------------------- RESET CARD: */
staticvoid lanai_reset(struct lanai_dev *lanai)
{
printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not " "implemented\n", lanai->number); /* TODO */ /* The following is just a hack until we write the real * resetter - at least ack whatever interrupt sent us * here
*/
reg_write(lanai, INT_ALL, IntAck_Reg);
lanai->stats.card_reset++;
}
/* -------------------- SERVICE LIST UTILITIES: */
/* * Allocate service buffer and tell card about it
*/ staticint service_buffer_allocate(struct lanai_dev *lanai)
{
lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8,
lanai->pci); if (unlikely(lanai->service.start == NULL)) return -ENOMEM;
DPRINTK("allocated service buffer at %p, size %zu(%d)\n",
lanai->service.start,
lanai_buf_size(&lanai->service),
lanai_buf_size_cardorder(&lanai->service)); /* Clear ServWrite register to be safe */
reg_write(lanai, 0, ServWrite_Reg); /* ServiceStuff register contains size and address of buffer */
reg_write(lanai,
SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) |
SSTUFF_SET_ADDR(lanai->service.dmaaddr),
ServiceStuff_Reg); return 0;
}
staticvoid lanai_timed_poll(struct timer_list *t)
{ struct lanai_dev *lanai = timer_container_of(lanai, t, timer); #ifndef DEBUG_RW unsignedlong flags; #ifdef USE_POWERDOWN if (lanai->conf1 & CONFIG1_POWERDOWN) return; #endif/* USE_POWERDOWN */
local_irq_save(flags); /* If we can grab the spinlock, check if any services need to be run */ if (spin_trylock(&lanai->servicelock)) {
run_service(lanai);
spin_unlock(&lanai->servicelock);
} /* ...and see if any backlogged VCs can make progress */ /* unfortunately linux has no read_trylock() currently */
read_lock(&vcc_sklist_lock);
vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue);
read_unlock(&vcc_sklist_lock);
local_irq_restore(flags);
#ifdef USE_POWERDOWN /* * If we're powered down we shouldn't be generating any interrupts - * so assume that this is a shared interrupt line and it's for someone * else
*/ if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN)) return IRQ_NONE; #endif
reason = intr_pending(lanai); if (reason == 0) return IRQ_NONE; /* Must be for someone else */
do { if (unlikely(reason == 0xFFFFFFFF)) break; /* Maybe we've been unplugged? */
lanai_int_1(lanai, reason);
reason = intr_pending(lanai);
} while (reason != 0);
return IRQ_HANDLED;
}
/* TODO - it would be nice if we could use the "delayed interrupt" system * to some advantage
*/
/* -------------------- CHECK BOARD ID/REV: */
/* * The board id and revision are stored both in the reset register and * in the PCI configuration space - the documentation says to check * each of them. If revp!=NULL we store the revision there
*/ staticint check_board_id_and_rev(constchar *name, u32 val, int *revp)
{
DPRINTK("%s says board_id=%d, board_rev=%d\n", name,
(int) RESET_GET_BOARD_ID(val),
(int) RESET_GET_BOARD_REV(val)); if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) {
printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val)); return -ENODEV;
} if (revp != NULL)
*revp = RESET_GET_BOARD_REV(val); return 0;
}
if (pci_enable_device(pci) != 0) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable " "PCI device", lanai->number); return -ENXIO;
}
pci_set_master(pci); if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) {
printk(KERN_WARNING DEV_LABEL "(itf %d): No suitable DMA available.\n", lanai->number); return -EBUSY;
}
result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL); if (result != 0) return result; /* Set latency timer to zero as per lanai docs */
result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); if (result != PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't write " "PCI_LATENCY_TIMER: %d\n", lanai->number, result); return -EINVAL;
}
pcistatus_check(lanai, 1);
pcistatus_check(lanai, 0); return 0;
}
/* -------------------- VPI/VCI ALLOCATION: */
/* * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll * get a CBRZERO interrupt), and we can use it only if no one is receiving * AAL0 traffic (since they will use the same queue) - according to the * docs we shouldn't even use it for AAL0 traffic
*/ staticinlineint vci0_is_ok(struct lanai_dev *lanai, conststruct atm_qos *qos)
{ if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) return 0; if (qos->rxtp.traffic_class != ATM_NONE) { if (lanai->naal0 != 0) return 0;
lanai->conf2 |= CONFIG2_VCI0_NORMAL;
conf2_write_if_powerup(lanai);
} return 1;
}
/* return true if vci is currently unused, or if requested qos is * compatible
*/ staticint vci_is_ok(struct lanai_dev *lanai, vci_t vci, conststruct atm_vcc *atmvcc)
{ conststruct atm_qos *qos = &atmvcc->qos; conststruct lanai_vcc *lvcc = lanai->vccs[vci]; if (vci == 0 && !vci0_is_ok(lanai, qos)) return 0; if (unlikely(lvcc != NULL)) { if (qos->rxtp.traffic_class != ATM_NONE &&
lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) return 0; if (qos->txtp.traffic_class != ATM_NONE &&
lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) return 0; if (qos->txtp.traffic_class == ATM_CBR &&
lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) return 0;
} if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 &&
qos->rxtp.traffic_class != ATM_NONE) { conststruct lanai_vcc *vci0 = lanai->vccs[0]; if (vci0 != NULL && vci0->rx.atmvcc != NULL) return 0;
lanai->conf2 &= ~CONFIG2_VCI0_NORMAL;
conf2_write_if_powerup(lanai);
} return 1;
}
staticint lanai_normalize_ci(struct lanai_dev *lanai, conststruct atm_vcc *atmvcc, short *vpip, vci_t *vcip)
{ switch (*vpip) { case ATM_VPI_ANY:
*vpip = 0;
fallthrough; case 0: break; default: return -EADDRINUSE;
} switch (*vcip) { case ATM_VCI_ANY: for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci;
(*vcip)++) if (vci_is_ok(lanai, *vcip, atmvcc)) return 0; return -EADDRINUSE; default: if (*vcip >= lanai->num_vci || *vcip < 0 ||
!vci_is_ok(lanai, *vcip, atmvcc)) return -EADDRINUSE;
} return 0;
}
/* -------------------- MANAGE CBR: */
/* * CBR ICG is stored as a fixed-point number with 4 fractional bits. * Note that storing a number greater than 2046.0 will result in * incorrect shaping
*/ #define CBRICG_FRAC_BITS (4) #define CBRICG_MAX (2046 << CBRICG_FRAC_BITS)
/* * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) * where MAXPCR is (according to the docs) 25600000/(54*8), * which is equal to (3125<<9)/27. * * Solving for ICG, we get: * ICG = MAXPCR/PCR - 1 * ICG = (3125<<9)/(27*PCR) - 1
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.32 Sekunden
(vorverarbeitet)
¤
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.