Release 4.11 drivers/atm/lanai.c
/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* 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)
*/
/* Version history:
* v.1.00 -- 26-JUL-2003 -- PCI/DMA updates
* v.0.02 -- 11-JAN-2000 -- Endian fixes
* v.0.01 -- 30-NOV-1999 -- Initial release
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/atmdev.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
/* -------------------- TUNABLE PARAMATERS: */
/*
* 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...)
#endif /* DEBUG */
#ifdef DEBUG_RW
#define RWDEBUG(format, args...) \
printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
#else /* !DEBUG_RW */
#define RWDEBUG(format, args...)
#endif
/* -------------------- DATA DEFINITIONS: */
#define LANAI_MAPPING_SIZE (0x40000)
#define LANAI_EEPROM_SIZE (128)
typedef int vci_t;
typedef void __iomem *bus_addr_t;
/* 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;
};
struct lanai_vcc_stats {
unsigned rx_nomem;
union {
struct {
unsigned rx_badlen;
unsigned service_trash;
unsigned service_stream;
unsigned service_rxcrc;
}
aal5;
struct {
}
aal0;
}
x;
};
struct lanai_dev; /* Forward declaration */
/*
* 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;
};
enum lanai_type {
lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2,
lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB
};
struct lanai_dev_stats {
unsigned ovfl_trash; /* # of cells dropped - buffer overflow */
unsigned vci_trash; /* # of cells dropped - closed vci */
unsigned hec_err; /* # of cells dropped - bad HEC */
unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */
unsigned pcierr_parity_detect;
unsigned pcierr_serr_set;
unsigned pcierr_master_abort;
unsigned pcierr_m_target_abort;
unsigned pcierr_s_target_abort;
unsigned pcierr_master_parity;
unsigned service_notx;
unsigned service_norx;
unsigned service_rxnotaal5;
unsigned dma_reenable;
unsigned card_reset;
};
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
*/
static void vci_bitfield_iterate(struct lanai_dev *lanai,
const unsigned long *lp,
void (*func)(struct lanai_dev *,vci_t vci))
{
vci_t vci;
for_each_set_bit(vci, lp, NUM_VCI)
func(lanai, vci);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 30 | 61.22% | 1 | 25.00% |
Chas Williams | 12 | 24.49% | 1 | 25.00% |
Akinobu Mita | 6 | 12.24% | 1 | 25.00% |
Mitchell Blank Jr. | 1 | 2.04% | 1 | 25.00% |
Total | 49 | 100.00% | 4 | 100.00% |
/* -------------------- BUFFER UTILITIES: */
/*
* 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
*/
static void 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",
(unsigned long) buf->dmaaddr);
buf->ptr = buf->start;
buf->end = (u32 *)
(&((unsigned char *) buf->start)[size]);
memset(buf->start, 0, size);
break;
}
size /= 2;
} while (size >= minbytes);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 109 | 59.24% | 2 | 66.67% |
Linus Torvalds | 75 | 40.76% | 1 | 33.33% |
Total | 184 | 100.00% | 3 | 100.00% |
/* size of buffer in bytes */
static inline size_t lanai_buf_size(const struct lanai_buffer *buf)
{
return ((unsigned long) buf->end) - ((unsigned long) buf->start);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 33 | 97.06% | 1 | 50.00% |
Chas Williams | 1 | 2.94% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
static void lanai_buf_deallocate(struct lanai_buffer *buf,
struct pci_dev *pci)
{
if (buf->start != NULL) {
dma_free_coherent(&pci->dev, lanai_buf_size(buf),
buf->start, buf->dmaaddr);
buf->start = buf->end = buf->ptr = NULL;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 50 | 81.97% | 2 | 66.67% |
Linus Torvalds | 11 | 18.03% | 1 | 33.33% |
Total | 61 | 100.00% | 3 | 100.00% |
/* size of buffer as "card order" (0=1k .. 7=128k) */
static int lanai_buf_size_cardorder(const struct lanai_buffer *buf)
{
int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10);
/* This can only happen if PAGE_SIZE is gigantic, but just in case */
if (order > 7)
order = 7;
return order;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 24 | 55.81% | 1 | 50.00% |
Linus Torvalds | 19 | 44.19% | 1 | 50.00% |
Total | 43 | 100.00% | 2 | 100.00% |
/* -------------------- PORT I/O UTILITIES: */
/* Registers (and their bit-fields) */
enum lanai_register {
Reset_Reg = 0x00, /* Reset; read for chip type; bits: */
#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */
#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */
#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */
Endian_Reg = 0x04, /* Endian setting */
IntStatus_Reg = 0x08, /* Interrupt status */
IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */
IntAck_Reg = 0x10, /* Interrupt acknowledge */
IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */
IntStatusSet_Reg = 0x18, /* Get status + enable/disable */
IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */
IntControlEna_Reg = 0x20, /* Interrupt control enable */
IntControlDis_Reg = 0x24, /* Interrupt control disable */
Status_Reg = 0x28, /* Status */
#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */
#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */
#define STATUS_SOOL (0x00000004) /* SOOL alarm */
#define STATUS_LOCD (0x00000008) /* LOCD alarm */
#define STATUS_LED (0x00000010) /* LED (HAPPI) output */
#define STATUS_GPIN (0x00000020) /* GPIN pin */
#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */
Config1_Reg = 0x2C, /* Config word 1; bits: */
#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */
#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */
#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */
#define READMODE_PLAIN (0) /* Plain memory read */
#define READMODE_LINE (2) /* Memory read line */
#define READMODE_MULTIPLE (3) /* Memory read multiple */
#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */
#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */
#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */
#define LOOPMODE_NORMAL (0) /* Normal - no loop */
#define LOOPMODE_TIME (1)
#define LOOPMODE_DIAG (2)
#define LOOPMODE_LINE (3)
#define CONFIG1_MASK_LOOPMODE (0x00000180)
#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */
#define LEDMODE_NOT_SOOL (0) /* !SOOL */
#define LEDMODE_OFF (1) /* 0 */
#define LEDMODE_ON (2) /* 1 */
#define LEDMODE_NOT_LOCD (3) /* !LOCD */
#define LEDMORE_GPIN (4) /* GPIN */
#define LEDMODE_NOT_GPIN (7) /* !GPIN */
#define CONFIG1_MASK_LEDMODE (0x00000E00)
#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */
#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */
#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */
Config2_Reg = 0x30, /* Config word 2; bits: */
#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */
#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */
#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */
#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */
#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */
#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */
#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */
#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */
#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */
Statistics_Reg = 0x34, /* Statistics; bits: */
#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */
#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */
#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */
#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */
ServiceStuff_Reg = 0x38, /* Service stuff; bits: */
#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */
#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */
ServWrite_Reg = 0x3C, /* ServWrite Pointer */
ServRead_Reg = 0x40, /* ServRead Pointer */
TxDepth_Reg = 0x44, /* FIFO Transmit Depth */
Butt_Reg = 0x48, /* Butt register */
CBR_ICG_Reg = 0x50,
CBR_PTR_Reg = 0x54,
PingCount_Reg = 0x58, /* Ping count */
DMA_Addr_Reg = 0x5C /* DMA address */
};
static inline bus_addr_t reg_addr(const struct lanai_dev *lanai,
enum lanai_register reg)
{
return lanai->base + reg;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static inline u32 reg_read(const struct lanai_dev *lanai,
enum lanai_register reg)
{
u32 t;
t = readl(reg_addr(lanai, reg));
RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base,
(int) reg, t);
return t;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 55 | 100.00% | 1 | 100.00% |
Total | 55 | 100.00% | 1 | 100.00% |
static inline void reg_write(const struct lanai_dev *lanai, u32 val,
enum lanai_register reg)
{
RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base,
(int) reg, val);
writel(val, reg_addr(lanai, reg));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 52 | 100.00% | 1 | 100.00% |
Total | 52 | 100.00% | 1 | 100.00% |
static inline void conf1_write(const struct lanai_dev *lanai)
{
reg_write(lanai, lanai->conf1, Config1_Reg);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static inline void conf2_write(const struct lanai_dev *lanai)
{
reg_write(lanai, lanai->conf2, Config2_Reg);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
/* Same as conf2_write(), but defers I/O if we're powered down */
static inline void conf2_write_if_powerup(const struct lanai_dev *lanai)
{
#ifdef USE_POWERDOWN
if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0))
return;
#endif /* USE_POWERDOWN */
conf2_write(lanai);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Chas Williams | 40 | 100.00% | 1 | 100.00% |
Total | 40 | 100.00% | 1 | 100.00% |
static inline void reset_board(const struct 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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
/* -------------------- 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 */
static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset)
{
return lanai->base + SRAM_START + offset;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 25 | 100.00% | 1 | 100.00% |
Total | 25 | 100.00% | 1 | 100.00% |
static inline u32 sram_read(const struct lanai_dev *lanai, int offset)
{
return readl(sram_addr(lanai, offset));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 27 | 100.00% | 1 | 100.00% |
Total | 27 | 100.00% | 1 | 100.00% |
static inline void sram_write(const struct lanai_dev *lanai,
u32 val, int offset)
{
writel(val, sram_addr(lanai, offset));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 31 | 100.00% | 1 | 100.00% |
Total | 31 | 100.00% | 1 | 100.00% |
static int sram_test_word(const struct lanai_dev *lanai, int offset,
u32 pattern)
{
u32 readback;
sram_write(lanai, pattern, offset);
readback = sram_read(lanai, offset);
if (likely(readback == pattern))
return 0;
printk(KERN_ERR DEV_LABEL
"(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n",
lanai->number, offset,
(unsigned int) pattern, (unsigned int) readback);
return -EIO;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 64 | 92.75% | 1 | 33.33% |
Chas Williams | 5 | 7.25% | 2 | 66.67% |
Total | 69 | 100.00% | 3 | 100.00% |
static int sram_test_pass(const struct lanai_dev *lanai, u32 pattern)
{
int offset, result = 0;
for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4)
result = sram_test_word(lanai, offset, pattern);
return result;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 54 | 100.00% | 1 | 100.00% |
Total | 54 | 100.00% | 1 | 100.00% |
static int sram_test_and_clear(const struct lanai_dev *lanai)
{
#ifdef FULL_MEMORY_TEST
int result;
DPRINTK("testing SRAM\n");
if ((result = sram_test_pass(lanai, 0x5555)) != 0)
return result;
if ((result = sram_test_pass(lanai, 0xAAAA)) != 0)
return result;
#endif
DPRINTK("clearing SRAM\n");
return sram_test_pass(lanai, 0x0000);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 74 | 100.00% | 1 | 100.00% |
Total | 74 | 100.00% | 1 | 100.00% |
/* -------------------- CARD-BASED VCC TABLE UTILITIES: */
/* vcc table */
enum lanai_vcc_offset {
vcc_rxaddr1 = 0x00, /* Location1, plus bits: */
#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */
#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */
#define RMMODE_TRASH (0) /* discard */
#define RMMODE_PRESERVE (1) /* input as AAL0 */
#define RMMODE_PIPE (2) /* pipe to coscheduler */
#define RMMODE_PIPEALL (3) /* pipe non-RM too */
#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */
#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */
#define RXMODE_TRASH (0) /* discard */
#define RXMODE_AAL0 (1) /* non-AAL5 mode */
#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */
#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */
vcc_rxaddr2 = 0x04, /* Location2 */
vcc_rxcrc1 = 0x08, /* RX CRC claculation space */
vcc_rxcrc2 = 0x0C,
vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */
#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */
#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */
#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */
vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */
#define RXBUFSTART_CLP (0x00004000)
#define RXBUFSTART_CI (0x00008000)
vcc_rxreadptr = 0x18, /* RX readptr */
vcc_txicg = 0x1C, /* TX ICG */
vcc_txaddr1 = 0x20, /* Location1, plus bits: */
#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */
#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */
vcc_txaddr2 = 0x24, /* Location2 */
vcc_txcrc1 = 0x28, /* TX CRC claculation space */
vcc_txcrc2 = 0x2C,
vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */
#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF)
#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */
vcc_txendptr = 0x34, /* TX Endptr, plus bits: */
#define TXENDPTR_CLP (0x00002000)
#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */
#define PDUMODE_AAL0 (0*0x04000)
#define PDUMODE_AAL5 (2*0x04000)
#define PDUMODE_AAL5STREAM (3*0x04000)
vcc_txwriteptr = 0x38, /* TX Writeptr */
#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF)
vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */
#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */
};
#define CARDVCC_SIZE (0x40)
static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai,
vci_t vci)
{
return sram_addr(lanai, vci * CARDVCC_SIZE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 26 | 100.00% | 1 | 100.00% |
Total | 26 | 100.00% | 1 | 100.00% |
static inline u32 cardvcc_read(const struct lanai_vcc *lvcc,
enum lanai_vcc_offset offset)
{
u32 val;
APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n");
val= readl(lvcc->vbase + offset);
RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n",
lvcc->vci, (int) offset, val);
return val;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 60 | 98.36% | 1 | 50.00% |
Chas Williams | 1 | 1.64% | 1 | 50.00% |
Total | 61 | 100.00% | 2 | 100.00% |
static inline void cardvcc_write(const struct lanai_vcc *lvcc,
u32 val, enum lanai_vcc_offset offset)
{
APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n");
APRINTK((val & ~0xFFFF) == 0,
"cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n",
(unsigned int) val, lvcc->vci, (unsigned int) offset);
RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n",
lvcc->vci, (unsigned int) offset, (unsigned int) val);
writel(val, lvcc->vbase + offset);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 82 | 88.17% | 1 | 33.33% |
Chas Williams | 11 | 11.83% | 2 | 66.67% |
Total | 93 | 100.00% | 3 | 100.00% |
/* -------------------- 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
*/
static inline int aal5_size(int size)
{
int cells = (size + 8 + 47) / 48;
return cells * 48;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
/* -------------------- FREE AN ATM SKB: */
static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb)
{
if (atmvcc->pop != NULL)
atmvcc->pop(atmvcc, skb);
else
dev_kfree_skb_any(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 40 | 100.00% | 1 | 100.00% |
Total | 40 | 100.00% | 1 | 100.00% |
/* -------------------- TURN VCCS ON AND OFF: */
static void host_vcc_start_rx(const struct lanai_vcc *lvcc)
{
u32 addr1;
if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) {
dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr;
cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1);
cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2);
cardvcc_write(lvcc, 0, vcc_rxwriteptr);
cardvcc_write(lvcc, 0, vcc_rxbufstart);
cardvcc_write(lvcc, 0, vcc_rxreadptr);
cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2);
addr1 = ((dmaaddr >> 8) & 0xFF) |
RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))|
RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */
/* RXADDR1_OAM_PRESERVE | --- no OAM support yet */
RXADDR1_SET_MODE(RXMODE_AAL5);
} else
addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */
RXADDR1_OAM_PRESERVE | /* ??? */
RXADDR1_SET_MODE(RXMODE_AAL0);
/* This one must be last! */
cardvcc_write(lvcc, addr1, vcc_rxaddr1);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 163 | 98.19% | 1 | 50.00% |
Chas Williams | 3 | 1.81% | 1 | 50.00% |
Total | 166 | 100.00% | 2 | 100.00% |
static void host_vcc_start_tx(const struct lanai_vcc *lvcc)
{
dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr;
cardvcc_write(lvcc, 0, vcc_txicg);
cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1);
cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2);
cardvcc_write(lvcc, 0, vcc_txreadptr);
cardvcc_write(lvcc, 0, vcc_txendptr);
cardvcc_write(lvcc, 0, vcc_txwriteptr);
cardvcc_write(lvcc,
(lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ?
TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next);
cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2);
cardvcc_write(lvcc,
((dmaaddr >> 8) & 0xFF) |
TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)),
vcc_txaddr1);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 150 | 98.04% | 1 | 50.00% |
Chas Williams | 3 | 1.96% | 1 | 50.00% |
Total | 153 | 100.00% | 2 | 100.00% |
/* Shutdown receiving on card */
static void lanai_shutdown_rx_vci(const struct 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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds | 99 | 99.00% | 1 | 50.00% |
Chas Williams | 1 | 1.00% | 1 | 50.00% |
Total | 100 | 100.00% | 2 | 100.00% |
/* 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 awhile -- 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.
*/
static void lanai_shutdown_tx_vci(struct lanai_dev *lanai,
struct lanai_vcc *lvcc)
{
struct sk_buff *skb;
unsigned long flags, timeout;
int read, write, lastread = -1;
APRINTK(!in_interrupt(),
"lanai_shutdown_tx_vci called w/o process context!\n");
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 !=