162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com> 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This driver supports ATM cards based on the Efficient "Lanai" 562306a36Sopenharmony_ci * chipset such as the Speedstream 3010 and the ENI-25p. The 662306a36Sopenharmony_ci * Speedstream 3060 is currently not supported since we don't 762306a36Sopenharmony_ci * have the code to drive the on-board Alcatel DSL chipset (yet). 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Thanks to Efficient for supporting this project with hardware, 1062306a36Sopenharmony_ci * documentation, and by answering my questions. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Things not working yet: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * o We don't support the Speedstream 3060 yet - this card has 1562306a36Sopenharmony_ci * an on-board DSL modem chip by Alcatel and the driver will 1662306a36Sopenharmony_ci * need some extra code added to handle it 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * o Note that due to limitations of the Lanai only one VCC can be 1962306a36Sopenharmony_ci * in CBR at once 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * o We don't currently parse the EEPROM at all. The code is all 2262306a36Sopenharmony_ci * there as per the spec, but it doesn't actually work. I think 2362306a36Sopenharmony_ci * there may be some issues with the docs. Anyway, do NOT 2462306a36Sopenharmony_ci * enable it yet - bugs in that code may actually damage your 2562306a36Sopenharmony_ci * hardware! Because of this you should hardware an ESI before 2662306a36Sopenharmony_ci * trying to use this in a LANE or MPOA environment. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: 2962306a36Sopenharmony_ci * vcc_tx_aal0() needs to send or queue a SKB 3062306a36Sopenharmony_ci * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs 3162306a36Sopenharmony_ci * vcc_rx_aal0() needs to handle AAL0 interrupts 3262306a36Sopenharmony_ci * This isn't too much work - I just wanted to get other things 3362306a36Sopenharmony_ci * done first. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * o lanai_change_qos() isn't written yet 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * o There aren't any ioctl's yet -- I'd like to eventually support 3862306a36Sopenharmony_ci * setting loopback and LED modes that way. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * o If the segmentation engine or DMA gets shut down we should restart 4162306a36Sopenharmony_ci * card as per section 17.0i. (see lanai_reset) 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * o setsockopt(SO_CIRANGE) isn't done (although despite what the 4462306a36Sopenharmony_ci * API says it isn't exactly commonly implemented) 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Version history: 4862306a36Sopenharmony_ci * v.1.00 -- 26-JUL-2003 -- PCI/DMA updates 4962306a36Sopenharmony_ci * v.0.02 -- 11-JAN-2000 -- Endian fixes 5062306a36Sopenharmony_ci * v.0.01 -- 30-NOV-1999 -- Initial release 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#include <linux/module.h> 5462306a36Sopenharmony_ci#include <linux/slab.h> 5562306a36Sopenharmony_ci#include <linux/mm.h> 5662306a36Sopenharmony_ci#include <linux/atmdev.h> 5762306a36Sopenharmony_ci#include <asm/io.h> 5862306a36Sopenharmony_ci#include <asm/byteorder.h> 5962306a36Sopenharmony_ci#include <linux/spinlock.h> 6062306a36Sopenharmony_ci#include <linux/pci.h> 6162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 6262306a36Sopenharmony_ci#include <linux/init.h> 6362306a36Sopenharmony_ci#include <linux/delay.h> 6462306a36Sopenharmony_ci#include <linux/interrupt.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* -------------------- TUNABLE PARAMATERS: */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Maximum number of VCIs per card. Setting it lower could theoretically 7062306a36Sopenharmony_ci * save some memory, but since we allocate our vcc list with get_free_pages, 7162306a36Sopenharmony_ci * it's not really likely for most architectures 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci#define NUM_VCI (1024) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Enable extra debugging 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci#define DEBUG 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * Debug _all_ register operations with card, except the memory test. 8162306a36Sopenharmony_ci * Also disables the timed poll to prevent extra chattiness. This 8262306a36Sopenharmony_ci * isn't for normal use 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci#undef DEBUG_RW 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * The programming guide specifies a full test of the on-board SRAM 8862306a36Sopenharmony_ci * at initialization time. Undefine to remove this 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci#define FULL_MEMORY_TEST 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * This is the number of (4 byte) service entries that we will 9462306a36Sopenharmony_ci * try to allocate at startup. Note that we will end up with 9562306a36Sopenharmony_ci * one PAGE_SIZE's worth regardless of what this is set to 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci#define SERVICE_ENTRIES (1024) 9862306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * We normally read the onboard EEPROM in order to discover our MAC 10262306a36Sopenharmony_ci * address. Undefine to _not_ do this 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ 10562306a36Sopenharmony_ci/* TODO: make above a module load-time option (also) */ 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Depth of TX fifo (in 128 byte units; range 2-31) 10962306a36Sopenharmony_ci * Smaller numbers are better for network latency 11062306a36Sopenharmony_ci * Larger numbers are better for PCI latency 11162306a36Sopenharmony_ci * I'm really sure where the best tradeoff is, but the BSD driver uses 11262306a36Sopenharmony_ci * 7 and it seems to work ok. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci#define TX_FIFO_DEPTH (7) 11562306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * How often (in jiffies) we will try to unstick stuck connections - 11962306a36Sopenharmony_ci * shouldn't need to happen much 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define LANAI_POLL_PERIOD (10*HZ) 12262306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * When allocating an AAL5 receiving buffer, try to make it at least 12662306a36Sopenharmony_ci * large enough to hold this many max_sdu sized PDUs 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#define AAL5_RX_MULTIPLIER (3) 12962306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * Same for transmitting buffer 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci#define AAL5_TX_MULTIPLIER (3) 13562306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * When allocating an AAL0 transmiting buffer, how many cells should fit. 13962306a36Sopenharmony_ci * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't 14062306a36Sopenharmony_ci * really critical 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci#define AAL0_TX_MULTIPLIER (40) 14362306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * How large should we make the AAL0 receiving buffer. Remember that this 14762306a36Sopenharmony_ci * is shared between all AAL0 VC's 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci#define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) 15062306a36Sopenharmony_ci/* TODO: make above a module load-time option */ 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* 15362306a36Sopenharmony_ci * Should we use Lanai's "powerdown" feature when no vcc's are bound? 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci/* #define USE_POWERDOWN */ 15662306a36Sopenharmony_ci/* TODO: make above a module load-time option (also) */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* -------------------- DEBUGGING AIDS: */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define DEV_LABEL "lanai" 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#ifdef DEBUG 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define DPRINTK(format, args...) \ 16562306a36Sopenharmony_ci printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 16662306a36Sopenharmony_ci#define APRINTK(truth, format, args...) \ 16762306a36Sopenharmony_ci do { \ 16862306a36Sopenharmony_ci if (unlikely(!(truth))) \ 16962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": " format, ##args); \ 17062306a36Sopenharmony_ci } while (0) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#else /* !DEBUG */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define DPRINTK(format, args...) 17562306a36Sopenharmony_ci#define APRINTK(truth, format, args...) 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci#endif /* DEBUG */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#ifdef DEBUG_RW 18062306a36Sopenharmony_ci#define RWDEBUG(format, args...) \ 18162306a36Sopenharmony_ci printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 18262306a36Sopenharmony_ci#else /* !DEBUG_RW */ 18362306a36Sopenharmony_ci#define RWDEBUG(format, args...) 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* -------------------- DATA DEFINITIONS: */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#define LANAI_MAPPING_SIZE (0x40000) 18962306a36Sopenharmony_ci#define LANAI_EEPROM_SIZE (128) 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_citypedef int vci_t; 19262306a36Sopenharmony_citypedef void __iomem *bus_addr_t; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* DMA buffer in host memory for TX, RX, or service list. */ 19562306a36Sopenharmony_cistruct lanai_buffer { 19662306a36Sopenharmony_ci u32 *start; /* From get_free_pages */ 19762306a36Sopenharmony_ci u32 *end; /* One past last byte */ 19862306a36Sopenharmony_ci u32 *ptr; /* Pointer to current host location */ 19962306a36Sopenharmony_ci dma_addr_t dmaaddr; 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistruct lanai_vcc_stats { 20362306a36Sopenharmony_ci unsigned rx_nomem; 20462306a36Sopenharmony_ci union { 20562306a36Sopenharmony_ci struct { 20662306a36Sopenharmony_ci unsigned rx_badlen; 20762306a36Sopenharmony_ci unsigned service_trash; 20862306a36Sopenharmony_ci unsigned service_stream; 20962306a36Sopenharmony_ci unsigned service_rxcrc; 21062306a36Sopenharmony_ci } aal5; 21162306a36Sopenharmony_ci struct { 21262306a36Sopenharmony_ci } aal0; 21362306a36Sopenharmony_ci } x; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct lanai_dev; /* Forward declaration */ 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * This is the card-specific per-vcc data. Note that unlike some other 22062306a36Sopenharmony_ci * drivers there is NOT a 1-to-1 correspondance between these and 22162306a36Sopenharmony_ci * atm_vcc's - each one of these represents an actual 2-way vcc, but 22262306a36Sopenharmony_ci * an atm_vcc can be 1-way and share with a 1-way vcc in the other 22362306a36Sopenharmony_ci * direction. To make it weirder, there can even be 0-way vccs 22462306a36Sopenharmony_ci * bound to us, waiting to do a change_qos 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistruct lanai_vcc { 22762306a36Sopenharmony_ci bus_addr_t vbase; /* Base of VCC's registers */ 22862306a36Sopenharmony_ci struct lanai_vcc_stats stats; 22962306a36Sopenharmony_ci int nref; /* # of atm_vcc's who reference us */ 23062306a36Sopenharmony_ci vci_t vci; 23162306a36Sopenharmony_ci struct { 23262306a36Sopenharmony_ci struct lanai_buffer buf; 23362306a36Sopenharmony_ci struct atm_vcc *atmvcc; /* atm_vcc who is receiver */ 23462306a36Sopenharmony_ci } rx; 23562306a36Sopenharmony_ci struct { 23662306a36Sopenharmony_ci struct lanai_buffer buf; 23762306a36Sopenharmony_ci struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ 23862306a36Sopenharmony_ci int endptr; /* last endptr from service entry */ 23962306a36Sopenharmony_ci struct sk_buff_head backlog; 24062306a36Sopenharmony_ci void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int); 24162306a36Sopenharmony_ci } tx; 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cienum lanai_type { 24562306a36Sopenharmony_ci lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2, 24662306a36Sopenharmony_ci lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistruct lanai_dev_stats { 25062306a36Sopenharmony_ci unsigned ovfl_trash; /* # of cells dropped - buffer overflow */ 25162306a36Sopenharmony_ci unsigned vci_trash; /* # of cells dropped - closed vci */ 25262306a36Sopenharmony_ci unsigned hec_err; /* # of cells dropped - bad HEC */ 25362306a36Sopenharmony_ci unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */ 25462306a36Sopenharmony_ci unsigned pcierr_parity_detect; 25562306a36Sopenharmony_ci unsigned pcierr_serr_set; 25662306a36Sopenharmony_ci unsigned pcierr_master_abort; 25762306a36Sopenharmony_ci unsigned pcierr_m_target_abort; 25862306a36Sopenharmony_ci unsigned pcierr_s_target_abort; 25962306a36Sopenharmony_ci unsigned pcierr_master_parity; 26062306a36Sopenharmony_ci unsigned service_notx; 26162306a36Sopenharmony_ci unsigned service_norx; 26262306a36Sopenharmony_ci unsigned service_rxnotaal5; 26362306a36Sopenharmony_ci unsigned dma_reenable; 26462306a36Sopenharmony_ci unsigned card_reset; 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct lanai_dev { 26862306a36Sopenharmony_ci bus_addr_t base; 26962306a36Sopenharmony_ci struct lanai_dev_stats stats; 27062306a36Sopenharmony_ci struct lanai_buffer service; 27162306a36Sopenharmony_ci struct lanai_vcc **vccs; 27262306a36Sopenharmony_ci#ifdef USE_POWERDOWN 27362306a36Sopenharmony_ci int nbound; /* number of bound vccs */ 27462306a36Sopenharmony_ci#endif 27562306a36Sopenharmony_ci enum lanai_type type; 27662306a36Sopenharmony_ci vci_t num_vci; /* Currently just NUM_VCI */ 27762306a36Sopenharmony_ci u8 eeprom[LANAI_EEPROM_SIZE]; 27862306a36Sopenharmony_ci u32 serialno, magicno; 27962306a36Sopenharmony_ci struct pci_dev *pci; 28062306a36Sopenharmony_ci DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */ 28162306a36Sopenharmony_ci DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */ 28262306a36Sopenharmony_ci struct timer_list timer; 28362306a36Sopenharmony_ci int naal0; 28462306a36Sopenharmony_ci struct lanai_buffer aal0buf; /* AAL0 RX buffers */ 28562306a36Sopenharmony_ci u32 conf1, conf2; /* CONFIG[12] registers */ 28662306a36Sopenharmony_ci u32 status; /* STATUS register */ 28762306a36Sopenharmony_ci spinlock_t endtxlock; 28862306a36Sopenharmony_ci spinlock_t servicelock; 28962306a36Sopenharmony_ci struct atm_vcc *cbrvcc; 29062306a36Sopenharmony_ci int number; 29162306a36Sopenharmony_ci int board_rev; 29262306a36Sopenharmony_ci/* TODO - look at race conditions with maintence of conf1/conf2 */ 29362306a36Sopenharmony_ci/* TODO - transmit locking: should we use _irq not _irqsave? */ 29462306a36Sopenharmony_ci/* TODO - organize above in some rational fashion (see <asm/cache.h>) */ 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* 29862306a36Sopenharmony_ci * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready) 29962306a36Sopenharmony_ci * This function iterates one of these, calling a given function for each 30062306a36Sopenharmony_ci * vci with their bit set 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic void vci_bitfield_iterate(struct lanai_dev *lanai, 30362306a36Sopenharmony_ci const unsigned long *lp, 30462306a36Sopenharmony_ci void (*func)(struct lanai_dev *,vci_t vci)) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci vci_t vci; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for_each_set_bit(vci, lp, NUM_VCI) 30962306a36Sopenharmony_ci func(lanai, vci); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* -------------------- BUFFER UTILITIES: */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - 31662306a36Sopenharmony_ci * usually any page allocation will do. Just to be safe in case 31762306a36Sopenharmony_ci * PAGE_SIZE is insanely tiny, though... 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci#define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024) 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* 32262306a36Sopenharmony_ci * Allocate a buffer in host RAM for service list, RX, or TX 32362306a36Sopenharmony_ci * Returns buf->start==NULL if no memory 32462306a36Sopenharmony_ci * Note that the size will be rounded up 2^n bytes, and 32562306a36Sopenharmony_ci * if we can't allocate that we'll settle for something smaller 32662306a36Sopenharmony_ci * until minbytes 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic void lanai_buf_allocate(struct lanai_buffer *buf, 32962306a36Sopenharmony_ci size_t bytes, size_t minbytes, struct pci_dev *pci) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci int size; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (bytes > (128 * 1024)) /* max lanai buffer size */ 33462306a36Sopenharmony_ci bytes = 128 * 1024; 33562306a36Sopenharmony_ci for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2) 33662306a36Sopenharmony_ci ; 33762306a36Sopenharmony_ci if (minbytes < LANAI_PAGE_SIZE) 33862306a36Sopenharmony_ci minbytes = LANAI_PAGE_SIZE; 33962306a36Sopenharmony_ci do { 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * Technically we could use non-consistent mappings for 34262306a36Sopenharmony_ci * everything, but the way the lanai uses DMA memory would 34362306a36Sopenharmony_ci * make that a terrific pain. This is much simpler. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci buf->start = dma_alloc_coherent(&pci->dev, 34662306a36Sopenharmony_ci size, &buf->dmaaddr, GFP_KERNEL); 34762306a36Sopenharmony_ci if (buf->start != NULL) { /* Success */ 34862306a36Sopenharmony_ci /* Lanai requires 256-byte alignment of DMA bufs */ 34962306a36Sopenharmony_ci APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, 35062306a36Sopenharmony_ci "bad dmaaddr: 0x%lx\n", 35162306a36Sopenharmony_ci (unsigned long) buf->dmaaddr); 35262306a36Sopenharmony_ci buf->ptr = buf->start; 35362306a36Sopenharmony_ci buf->end = (u32 *) 35462306a36Sopenharmony_ci (&((unsigned char *) buf->start)[size]); 35562306a36Sopenharmony_ci memset(buf->start, 0, size); 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci size /= 2; 35962306a36Sopenharmony_ci } while (size >= minbytes); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* size of buffer in bytes */ 36362306a36Sopenharmony_cistatic inline size_t lanai_buf_size(const struct lanai_buffer *buf) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci return ((unsigned long) buf->end) - ((unsigned long) buf->start); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void lanai_buf_deallocate(struct lanai_buffer *buf, 36962306a36Sopenharmony_ci struct pci_dev *pci) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci if (buf->start != NULL) { 37262306a36Sopenharmony_ci dma_free_coherent(&pci->dev, lanai_buf_size(buf), 37362306a36Sopenharmony_ci buf->start, buf->dmaaddr); 37462306a36Sopenharmony_ci buf->start = buf->end = buf->ptr = NULL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* size of buffer as "card order" (0=1k .. 7=128k) */ 37962306a36Sopenharmony_cistatic int lanai_buf_size_cardorder(const struct lanai_buffer *buf) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* This can only happen if PAGE_SIZE is gigantic, but just in case */ 38462306a36Sopenharmony_ci if (order > 7) 38562306a36Sopenharmony_ci order = 7; 38662306a36Sopenharmony_ci return order; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* -------------------- PORT I/O UTILITIES: */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* Registers (and their bit-fields) */ 39262306a36Sopenharmony_cienum lanai_register { 39362306a36Sopenharmony_ci Reset_Reg = 0x00, /* Reset; read for chip type; bits: */ 39462306a36Sopenharmony_ci#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */ 39562306a36Sopenharmony_ci#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */ 39662306a36Sopenharmony_ci#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */ 39762306a36Sopenharmony_ci Endian_Reg = 0x04, /* Endian setting */ 39862306a36Sopenharmony_ci IntStatus_Reg = 0x08, /* Interrupt status */ 39962306a36Sopenharmony_ci IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ 40062306a36Sopenharmony_ci IntAck_Reg = 0x10, /* Interrupt acknowledge */ 40162306a36Sopenharmony_ci IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ 40262306a36Sopenharmony_ci IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ 40362306a36Sopenharmony_ci IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ 40462306a36Sopenharmony_ci IntControlEna_Reg = 0x20, /* Interrupt control enable */ 40562306a36Sopenharmony_ci IntControlDis_Reg = 0x24, /* Interrupt control disable */ 40662306a36Sopenharmony_ci Status_Reg = 0x28, /* Status */ 40762306a36Sopenharmony_ci#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */ 40862306a36Sopenharmony_ci#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */ 40962306a36Sopenharmony_ci#define STATUS_SOOL (0x00000004) /* SOOL alarm */ 41062306a36Sopenharmony_ci#define STATUS_LOCD (0x00000008) /* LOCD alarm */ 41162306a36Sopenharmony_ci#define STATUS_LED (0x00000010) /* LED (HAPPI) output */ 41262306a36Sopenharmony_ci#define STATUS_GPIN (0x00000020) /* GPIN pin */ 41362306a36Sopenharmony_ci#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ 41462306a36Sopenharmony_ci Config1_Reg = 0x2C, /* Config word 1; bits: */ 41562306a36Sopenharmony_ci#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */ 41662306a36Sopenharmony_ci#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */ 41762306a36Sopenharmony_ci#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */ 41862306a36Sopenharmony_ci#define READMODE_PLAIN (0) /* Plain memory read */ 41962306a36Sopenharmony_ci#define READMODE_LINE (2) /* Memory read line */ 42062306a36Sopenharmony_ci#define READMODE_MULTIPLE (3) /* Memory read multiple */ 42162306a36Sopenharmony_ci#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */ 42262306a36Sopenharmony_ci#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */ 42362306a36Sopenharmony_ci#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */ 42462306a36Sopenharmony_ci#define LOOPMODE_NORMAL (0) /* Normal - no loop */ 42562306a36Sopenharmony_ci#define LOOPMODE_TIME (1) 42662306a36Sopenharmony_ci#define LOOPMODE_DIAG (2) 42762306a36Sopenharmony_ci#define LOOPMODE_LINE (3) 42862306a36Sopenharmony_ci#define CONFIG1_MASK_LOOPMODE (0x00000180) 42962306a36Sopenharmony_ci#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */ 43062306a36Sopenharmony_ci#define LEDMODE_NOT_SOOL (0) /* !SOOL */ 43162306a36Sopenharmony_ci#define LEDMODE_OFF (1) /* 0 */ 43262306a36Sopenharmony_ci#define LEDMODE_ON (2) /* 1 */ 43362306a36Sopenharmony_ci#define LEDMODE_NOT_LOCD (3) /* !LOCD */ 43462306a36Sopenharmony_ci#define LEDMORE_GPIN (4) /* GPIN */ 43562306a36Sopenharmony_ci#define LEDMODE_NOT_GPIN (7) /* !GPIN */ 43662306a36Sopenharmony_ci#define CONFIG1_MASK_LEDMODE (0x00000E00) 43762306a36Sopenharmony_ci#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */ 43862306a36Sopenharmony_ci#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */ 43962306a36Sopenharmony_ci#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ 44062306a36Sopenharmony_ci Config2_Reg = 0x30, /* Config word 2; bits: */ 44162306a36Sopenharmony_ci#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */ 44262306a36Sopenharmony_ci#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */ 44362306a36Sopenharmony_ci#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */ 44462306a36Sopenharmony_ci#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */ 44562306a36Sopenharmony_ci#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */ 44662306a36Sopenharmony_ci#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */ 44762306a36Sopenharmony_ci#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */ 44862306a36Sopenharmony_ci#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */ 44962306a36Sopenharmony_ci#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ 45062306a36Sopenharmony_ci Statistics_Reg = 0x34, /* Statistics; bits: */ 45162306a36Sopenharmony_ci#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */ 45262306a36Sopenharmony_ci#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */ 45362306a36Sopenharmony_ci#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */ 45462306a36Sopenharmony_ci#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ 45562306a36Sopenharmony_ci ServiceStuff_Reg = 0x38, /* Service stuff; bits: */ 45662306a36Sopenharmony_ci#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */ 45762306a36Sopenharmony_ci#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ 45862306a36Sopenharmony_ci ServWrite_Reg = 0x3C, /* ServWrite Pointer */ 45962306a36Sopenharmony_ci ServRead_Reg = 0x40, /* ServRead Pointer */ 46062306a36Sopenharmony_ci TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ 46162306a36Sopenharmony_ci Butt_Reg = 0x48, /* Butt register */ 46262306a36Sopenharmony_ci CBR_ICG_Reg = 0x50, 46362306a36Sopenharmony_ci CBR_PTR_Reg = 0x54, 46462306a36Sopenharmony_ci PingCount_Reg = 0x58, /* Ping count */ 46562306a36Sopenharmony_ci DMA_Addr_Reg = 0x5C /* DMA address */ 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic inline bus_addr_t reg_addr(const struct lanai_dev *lanai, 46962306a36Sopenharmony_ci enum lanai_register reg) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return lanai->base + reg; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic inline u32 reg_read(const struct lanai_dev *lanai, 47562306a36Sopenharmony_ci enum lanai_register reg) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci u32 t; 47862306a36Sopenharmony_ci t = readl(reg_addr(lanai, reg)); 47962306a36Sopenharmony_ci RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, 48062306a36Sopenharmony_ci (int) reg, t); 48162306a36Sopenharmony_ci return t; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic inline void reg_write(const struct lanai_dev *lanai, u32 val, 48562306a36Sopenharmony_ci enum lanai_register reg) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, 48862306a36Sopenharmony_ci (int) reg, val); 48962306a36Sopenharmony_ci writel(val, reg_addr(lanai, reg)); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic inline void conf1_write(const struct lanai_dev *lanai) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci reg_write(lanai, lanai->conf1, Config1_Reg); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic inline void conf2_write(const struct lanai_dev *lanai) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci reg_write(lanai, lanai->conf2, Config2_Reg); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* Same as conf2_write(), but defers I/O if we're powered down */ 50362306a36Sopenharmony_cistatic inline void conf2_write_if_powerup(const struct lanai_dev *lanai) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci#ifdef USE_POWERDOWN 50662306a36Sopenharmony_ci if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0)) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci#endif /* USE_POWERDOWN */ 50962306a36Sopenharmony_ci conf2_write(lanai); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic inline void reset_board(const struct lanai_dev *lanai) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci DPRINTK("about to reset board\n"); 51562306a36Sopenharmony_ci reg_write(lanai, 0, Reset_Reg); 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * If we don't delay a little while here then we can end up 51862306a36Sopenharmony_ci * leaving the card in a VERY weird state and lock up the 51962306a36Sopenharmony_ci * PCI bus. This isn't documented anywhere but I've convinced 52062306a36Sopenharmony_ci * myself after a lot of painful experimentation 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci udelay(5); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* -------------------- CARD SRAM UTILITIES: */ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* The SRAM is mapped into normal PCI memory space - the only catch is 52862306a36Sopenharmony_ci * that it is only 16-bits wide but must be accessed as 32-bit. The 52962306a36Sopenharmony_ci * 16 high bits will be zero. We don't hide this, since they get 53062306a36Sopenharmony_ci * programmed mostly like discrete registers anyway 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci#define SRAM_START (0x20000) 53362306a36Sopenharmony_ci#define SRAM_BYTES (0x20000) /* Again, half don't really exist */ 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci return lanai->base + SRAM_START + offset; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic inline u32 sram_read(const struct lanai_dev *lanai, int offset) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci return readl(sram_addr(lanai, offset)); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic inline void sram_write(const struct lanai_dev *lanai, 54662306a36Sopenharmony_ci u32 val, int offset) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci writel(val, sram_addr(lanai, offset)); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int sram_test_word(const struct lanai_dev *lanai, int offset, 55262306a36Sopenharmony_ci u32 pattern) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci u32 readback; 55562306a36Sopenharmony_ci sram_write(lanai, pattern, offset); 55662306a36Sopenharmony_ci readback = sram_read(lanai, offset); 55762306a36Sopenharmony_ci if (likely(readback == pattern)) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL 56062306a36Sopenharmony_ci "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", 56162306a36Sopenharmony_ci lanai->number, offset, 56262306a36Sopenharmony_ci (unsigned int) pattern, (unsigned int) readback); 56362306a36Sopenharmony_ci return -EIO; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int sram_test_pass(const struct lanai_dev *lanai, u32 pattern) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci int offset, result = 0; 56962306a36Sopenharmony_ci for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) 57062306a36Sopenharmony_ci result = sram_test_word(lanai, offset, pattern); 57162306a36Sopenharmony_ci return result; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int sram_test_and_clear(const struct lanai_dev *lanai) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci#ifdef FULL_MEMORY_TEST 57762306a36Sopenharmony_ci int result; 57862306a36Sopenharmony_ci DPRINTK("testing SRAM\n"); 57962306a36Sopenharmony_ci if ((result = sram_test_pass(lanai, 0x5555)) != 0) 58062306a36Sopenharmony_ci return result; 58162306a36Sopenharmony_ci if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) 58262306a36Sopenharmony_ci return result; 58362306a36Sopenharmony_ci#endif 58462306a36Sopenharmony_ci DPRINTK("clearing SRAM\n"); 58562306a36Sopenharmony_ci return sram_test_pass(lanai, 0x0000); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* -------------------- CARD-BASED VCC TABLE UTILITIES: */ 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* vcc table */ 59162306a36Sopenharmony_cienum lanai_vcc_offset { 59262306a36Sopenharmony_ci vcc_rxaddr1 = 0x00, /* Location1, plus bits: */ 59362306a36Sopenharmony_ci#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */ 59462306a36Sopenharmony_ci#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */ 59562306a36Sopenharmony_ci#define RMMODE_TRASH (0) /* discard */ 59662306a36Sopenharmony_ci#define RMMODE_PRESERVE (1) /* input as AAL0 */ 59762306a36Sopenharmony_ci#define RMMODE_PIPE (2) /* pipe to coscheduler */ 59862306a36Sopenharmony_ci#define RMMODE_PIPEALL (3) /* pipe non-RM too */ 59962306a36Sopenharmony_ci#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */ 60062306a36Sopenharmony_ci#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */ 60162306a36Sopenharmony_ci#define RXMODE_TRASH (0) /* discard */ 60262306a36Sopenharmony_ci#define RXMODE_AAL0 (1) /* non-AAL5 mode */ 60362306a36Sopenharmony_ci#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */ 60462306a36Sopenharmony_ci#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ 60562306a36Sopenharmony_ci vcc_rxaddr2 = 0x04, /* Location2 */ 60662306a36Sopenharmony_ci vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ 60762306a36Sopenharmony_ci vcc_rxcrc2 = 0x0C, 60862306a36Sopenharmony_ci vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */ 60962306a36Sopenharmony_ci#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */ 61062306a36Sopenharmony_ci#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */ 61162306a36Sopenharmony_ci#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ 61262306a36Sopenharmony_ci vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */ 61362306a36Sopenharmony_ci#define RXBUFSTART_CLP (0x00004000) 61462306a36Sopenharmony_ci#define RXBUFSTART_CI (0x00008000) 61562306a36Sopenharmony_ci vcc_rxreadptr = 0x18, /* RX readptr */ 61662306a36Sopenharmony_ci vcc_txicg = 0x1C, /* TX ICG */ 61762306a36Sopenharmony_ci vcc_txaddr1 = 0x20, /* Location1, plus bits: */ 61862306a36Sopenharmony_ci#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */ 61962306a36Sopenharmony_ci#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ 62062306a36Sopenharmony_ci vcc_txaddr2 = 0x24, /* Location2 */ 62162306a36Sopenharmony_ci vcc_txcrc1 = 0x28, /* TX CRC claculation space */ 62262306a36Sopenharmony_ci vcc_txcrc2 = 0x2C, 62362306a36Sopenharmony_ci vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */ 62462306a36Sopenharmony_ci#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF) 62562306a36Sopenharmony_ci#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ 62662306a36Sopenharmony_ci vcc_txendptr = 0x34, /* TX Endptr, plus bits: */ 62762306a36Sopenharmony_ci#define TXENDPTR_CLP (0x00002000) 62862306a36Sopenharmony_ci#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */ 62962306a36Sopenharmony_ci#define PDUMODE_AAL0 (0*0x04000) 63062306a36Sopenharmony_ci#define PDUMODE_AAL5 (2*0x04000) 63162306a36Sopenharmony_ci#define PDUMODE_AAL5STREAM (3*0x04000) 63262306a36Sopenharmony_ci vcc_txwriteptr = 0x38, /* TX Writeptr */ 63362306a36Sopenharmony_ci#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) 63462306a36Sopenharmony_ci vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */ 63562306a36Sopenharmony_ci#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */ 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci#define CARDVCC_SIZE (0x40) 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, 64162306a36Sopenharmony_ci vci_t vci) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci return sram_addr(lanai, vci * CARDVCC_SIZE); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic inline u32 cardvcc_read(const struct lanai_vcc *lvcc, 64762306a36Sopenharmony_ci enum lanai_vcc_offset offset) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 val; 65062306a36Sopenharmony_ci APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n"); 65162306a36Sopenharmony_ci val= readl(lvcc->vbase + offset); 65262306a36Sopenharmony_ci RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", 65362306a36Sopenharmony_ci lvcc->vci, (int) offset, val); 65462306a36Sopenharmony_ci return val; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic inline void cardvcc_write(const struct lanai_vcc *lvcc, 65862306a36Sopenharmony_ci u32 val, enum lanai_vcc_offset offset) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n"); 66162306a36Sopenharmony_ci APRINTK((val & ~0xFFFF) == 0, 66262306a36Sopenharmony_ci "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n", 66362306a36Sopenharmony_ci (unsigned int) val, lvcc->vci, (unsigned int) offset); 66462306a36Sopenharmony_ci RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n", 66562306a36Sopenharmony_ci lvcc->vci, (unsigned int) offset, (unsigned int) val); 66662306a36Sopenharmony_ci writel(val, lvcc->vbase + offset); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */ 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci/* How many bytes will an AAL5 PDU take to transmit - remember that: 67262306a36Sopenharmony_ci * o we need to add 8 bytes for length, CPI, UU, and CRC 67362306a36Sopenharmony_ci * o we need to round up to 48 bytes for cells 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_cistatic inline int aal5_size(int size) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci int cells = (size + 8 + 47) / 48; 67862306a36Sopenharmony_ci return cells * 48; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/* -------------------- FREE AN ATM SKB: */ 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci if (atmvcc->pop != NULL) 68662306a36Sopenharmony_ci atmvcc->pop(atmvcc, skb); 68762306a36Sopenharmony_ci else 68862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/* -------------------- TURN VCCS ON AND OFF: */ 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void host_vcc_start_rx(const struct lanai_vcc *lvcc) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci u32 addr1; 69662306a36Sopenharmony_ci if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) { 69762306a36Sopenharmony_ci dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr; 69862306a36Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1); 69962306a36Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2); 70062306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxwriteptr); 70162306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxbufstart); 70262306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxreadptr); 70362306a36Sopenharmony_ci cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2); 70462306a36Sopenharmony_ci addr1 = ((dmaaddr >> 8) & 0xFF) | 70562306a36Sopenharmony_ci RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))| 70662306a36Sopenharmony_ci RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */ 70762306a36Sopenharmony_ci /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */ 70862306a36Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_AAL5); 70962306a36Sopenharmony_ci } else 71062306a36Sopenharmony_ci addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */ 71162306a36Sopenharmony_ci RXADDR1_OAM_PRESERVE | /* ??? */ 71262306a36Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_AAL0); 71362306a36Sopenharmony_ci /* This one must be last! */ 71462306a36Sopenharmony_ci cardvcc_write(lvcc, addr1, vcc_rxaddr1); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic void host_vcc_start_tx(const struct lanai_vcc *lvcc) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr; 72062306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txicg); 72162306a36Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1); 72262306a36Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2); 72362306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txreadptr); 72462306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txendptr); 72562306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txwriteptr); 72662306a36Sopenharmony_ci cardvcc_write(lvcc, 72762306a36Sopenharmony_ci (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ? 72862306a36Sopenharmony_ci TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next); 72962306a36Sopenharmony_ci cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2); 73062306a36Sopenharmony_ci cardvcc_write(lvcc, 73162306a36Sopenharmony_ci ((dmaaddr >> 8) & 0xFF) | 73262306a36Sopenharmony_ci TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)), 73362306a36Sopenharmony_ci vcc_txaddr1); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/* Shutdown receiving on card */ 73762306a36Sopenharmony_cistatic void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 74062306a36Sopenharmony_ci return; 74162306a36Sopenharmony_ci /* 15.1.1 - set to trashing, wait one cell time (15us) */ 74262306a36Sopenharmony_ci cardvcc_write(lvcc, 74362306a36Sopenharmony_ci RXADDR1_SET_RMMODE(RMMODE_TRASH) | 74462306a36Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1); 74562306a36Sopenharmony_ci udelay(15); 74662306a36Sopenharmony_ci /* 15.1.2 - clear rest of entries */ 74762306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxaddr2); 74862306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxcrc1); 74962306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxcrc2); 75062306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxwriteptr); 75162306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxbufstart); 75262306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxreadptr); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* Shutdown transmitting on card. 75662306a36Sopenharmony_ci * Unfortunately the lanai needs us to wait until all the data 75762306a36Sopenharmony_ci * drains out of the buffer before we can dealloc it, so this 75862306a36Sopenharmony_ci * can take awhile -- up to 370ms for a full 128KB buffer 75962306a36Sopenharmony_ci * assuming everone else is quiet. In theory the time is 76062306a36Sopenharmony_ci * boundless if there's a CBR VCC holding things up. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_cistatic void lanai_shutdown_tx_vci(struct lanai_dev *lanai, 76362306a36Sopenharmony_ci struct lanai_vcc *lvcc) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct sk_buff *skb; 76662306a36Sopenharmony_ci unsigned long flags, timeout; 76762306a36Sopenharmony_ci int read, write, lastread = -1; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 77062306a36Sopenharmony_ci return; 77162306a36Sopenharmony_ci /* 15.2.1 - wait for queue to drain */ 77262306a36Sopenharmony_ci while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL) 77362306a36Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 77462306a36Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 77562306a36Sopenharmony_ci __clear_bit(lvcc->vci, lanai->backlog_vccs); 77662306a36Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * We need to wait for the VCC to drain but don't wait forever. We 77962306a36Sopenharmony_ci * give each 1K of buffer size 1/128th of a second to clear out. 78062306a36Sopenharmony_ci * TODO: maybe disable CBR if we're about to timeout? 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci timeout = jiffies + 78362306a36Sopenharmony_ci (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7); 78462306a36Sopenharmony_ci write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); 78562306a36Sopenharmony_ci for (;;) { 78662306a36Sopenharmony_ci read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 78762306a36Sopenharmony_ci if (read == write && /* Is TX buffer empty? */ 78862306a36Sopenharmony_ci (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR || 78962306a36Sopenharmony_ci (cardvcc_read(lvcc, vcc_txcbr_next) & 79062306a36Sopenharmony_ci TXCBR_NEXT_BOZO) == 0)) 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci if (read != lastread) { /* Has there been any progress? */ 79362306a36Sopenharmony_ci lastread = read; 79462306a36Sopenharmony_ci timeout += HZ / 10; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci if (unlikely(time_after(jiffies, timeout))) { 79762306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on " 79862306a36Sopenharmony_ci "backlog closing vci %d\n", 79962306a36Sopenharmony_ci lvcc->tx.atmvcc->dev->number, lvcc->vci); 80062306a36Sopenharmony_ci DPRINTK("read, write = %d, %d\n", read, write); 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci msleep(40); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci /* 15.2.2 - clear out all tx registers */ 80662306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txreadptr); 80762306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txwriteptr); 80862306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txendptr); 80962306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txcrc1); 81062306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txcrc2); 81162306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txaddr2); 81262306a36Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txaddr1); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci/* -------------------- MANAGING AAL0 RX BUFFER: */ 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic inline int aal0_buffer_allocate(struct lanai_dev *lanai) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n"); 82062306a36Sopenharmony_ci lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80, 82162306a36Sopenharmony_ci lanai->pci); 82262306a36Sopenharmony_ci return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic inline void aal0_buffer_free(struct lanai_dev *lanai) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n"); 82862306a36Sopenharmony_ci lanai_buf_deallocate(&lanai->aal0buf, lanai->pci); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci/* -------------------- EEPROM UTILITIES: */ 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* Offsets of data in the EEPROM */ 83462306a36Sopenharmony_ci#define EEPROM_COPYRIGHT (0) 83562306a36Sopenharmony_ci#define EEPROM_COPYRIGHT_LEN (44) 83662306a36Sopenharmony_ci#define EEPROM_CHECKSUM (62) 83762306a36Sopenharmony_ci#define EEPROM_CHECKSUM_REV (63) 83862306a36Sopenharmony_ci#define EEPROM_MAC (64) 83962306a36Sopenharmony_ci#define EEPROM_MAC_REV (70) 84062306a36Sopenharmony_ci#define EEPROM_SERIAL (112) 84162306a36Sopenharmony_ci#define EEPROM_SERIAL_REV (116) 84262306a36Sopenharmony_ci#define EEPROM_MAGIC (120) 84362306a36Sopenharmony_ci#define EEPROM_MAGIC_REV (124) 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci#define EEPROM_MAGIC_VALUE (0x5AB478D2) 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci#ifndef READ_EEPROM 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* Stub functions to use if EEPROM reading is disabled */ 85062306a36Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n", 85362306a36Sopenharmony_ci lanai->number); 85462306a36Sopenharmony_ci memset(&lanai->eeprom[EEPROM_MAC], 0, 6); 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci lanai->serialno = 0; 86162306a36Sopenharmony_ci lanai->magicno = EEPROM_MAGIC_VALUE; 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci#else /* READ_EEPROM */ 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int i, address; 87062306a36Sopenharmony_ci u8 data; 87162306a36Sopenharmony_ci u32 tmp; 87262306a36Sopenharmony_ci#define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \ 87362306a36Sopenharmony_ci } while (0) 87462306a36Sopenharmony_ci#define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) 87562306a36Sopenharmony_ci#define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) 87662306a36Sopenharmony_ci#define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) 87762306a36Sopenharmony_ci#define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) 87862306a36Sopenharmony_ci#define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) 87962306a36Sopenharmony_ci#define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) 88062306a36Sopenharmony_ci#define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \ 88162306a36Sopenharmony_ci data_h(); udelay(5); } while (0) 88262306a36Sopenharmony_ci /* start with both clock and data high */ 88362306a36Sopenharmony_ci data_h(); clock_h(); udelay(5); 88462306a36Sopenharmony_ci for (address = 0; address < LANAI_EEPROM_SIZE; address++) { 88562306a36Sopenharmony_ci data = (address << 1) | 1; /* Command=read + address */ 88662306a36Sopenharmony_ci /* send start bit */ 88762306a36Sopenharmony_ci data_l(); udelay(5); 88862306a36Sopenharmony_ci clock_l(); udelay(5); 88962306a36Sopenharmony_ci for (i = 128; i != 0; i >>= 1) { /* write command out */ 89062306a36Sopenharmony_ci tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | 89162306a36Sopenharmony_ci ((data & i) ? CONFIG1_PROMDATA : 0); 89262306a36Sopenharmony_ci if (lanai->conf1 != tmp) { 89362306a36Sopenharmony_ci set_config1(tmp); 89462306a36Sopenharmony_ci udelay(5); /* Let new data settle */ 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci clock_h(); udelay(5); clock_l(); udelay(5); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci /* look for ack */ 89962306a36Sopenharmony_ci data_h(); clock_h(); udelay(5); 90062306a36Sopenharmony_ci if (read_pin() != 0) 90162306a36Sopenharmony_ci goto error; /* No ack seen */ 90262306a36Sopenharmony_ci clock_l(); udelay(5); 90362306a36Sopenharmony_ci /* read back result */ 90462306a36Sopenharmony_ci for (data = 0, i = 7; i >= 0; i--) { 90562306a36Sopenharmony_ci data_h(); clock_h(); udelay(5); 90662306a36Sopenharmony_ci data = (data << 1) | !!read_pin(); 90762306a36Sopenharmony_ci clock_l(); udelay(5); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci /* look again for ack */ 91062306a36Sopenharmony_ci data_h(); clock_h(); udelay(5); 91162306a36Sopenharmony_ci if (read_pin() == 0) 91262306a36Sopenharmony_ci goto error; /* Spurious ack */ 91362306a36Sopenharmony_ci clock_l(); udelay(5); 91462306a36Sopenharmony_ci send_stop(); 91562306a36Sopenharmony_ci lanai->eeprom[address] = data; 91662306a36Sopenharmony_ci DPRINTK("EEPROM 0x%04X %02X\n", 91762306a36Sopenharmony_ci (unsigned int) address, (unsigned int) data); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci error: 92162306a36Sopenharmony_ci clock_l(); udelay(5); /* finish read */ 92262306a36Sopenharmony_ci send_stop(); 92362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n", 92462306a36Sopenharmony_ci lanai->number, address); 92562306a36Sopenharmony_ci return -EIO; 92662306a36Sopenharmony_ci#undef set_config1 92762306a36Sopenharmony_ci#undef clock_h 92862306a36Sopenharmony_ci#undef clock_l 92962306a36Sopenharmony_ci#undef data_h 93062306a36Sopenharmony_ci#undef data_l 93162306a36Sopenharmony_ci#undef pre_read 93262306a36Sopenharmony_ci#undef read_pin 93362306a36Sopenharmony_ci#undef send_stop 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci/* read a big-endian 4-byte value out of eeprom */ 93762306a36Sopenharmony_cistatic inline u32 eeprom_be4(const struct lanai_dev *lanai, int address) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci return be32_to_cpup((const u32 *) &lanai->eeprom[address]); 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* Checksum/validate EEPROM contents */ 94362306a36Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci int i, s; 94662306a36Sopenharmony_ci u32 v; 94762306a36Sopenharmony_ci const u8 *e = lanai->eeprom; 94862306a36Sopenharmony_ci#ifdef DEBUG 94962306a36Sopenharmony_ci /* First, see if we can get an ASCIIZ string out of the copyright */ 95062306a36Sopenharmony_ci for (i = EEPROM_COPYRIGHT; 95162306a36Sopenharmony_ci i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) 95262306a36Sopenharmony_ci if (e[i] < 0x20 || e[i] > 0x7E) 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci if ( i != EEPROM_COPYRIGHT && 95562306a36Sopenharmony_ci i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0') 95662306a36Sopenharmony_ci DPRINTK("eeprom: copyright = \"%s\"\n", 95762306a36Sopenharmony_ci (char *) &e[EEPROM_COPYRIGHT]); 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci DPRINTK("eeprom: copyright not found\n"); 96062306a36Sopenharmony_ci#endif 96162306a36Sopenharmony_ci /* Validate checksum */ 96262306a36Sopenharmony_ci for (i = s = 0; i < EEPROM_CHECKSUM; i++) 96362306a36Sopenharmony_ci s += e[i]; 96462306a36Sopenharmony_ci s &= 0xFF; 96562306a36Sopenharmony_ci if (s != e[EEPROM_CHECKSUM]) { 96662306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " 96762306a36Sopenharmony_ci "(wanted 0x%02X, got 0x%02X)\n", lanai->number, 96862306a36Sopenharmony_ci (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]); 96962306a36Sopenharmony_ci return -EIO; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci s ^= 0xFF; 97262306a36Sopenharmony_ci if (s != e[EEPROM_CHECKSUM_REV]) { 97362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " 97462306a36Sopenharmony_ci "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number, 97562306a36Sopenharmony_ci (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]); 97662306a36Sopenharmony_ci return -EIO; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci /* Verify MAC address */ 97962306a36Sopenharmony_ci for (i = 0; i < 6; i++) 98062306a36Sopenharmony_ci if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) { 98162306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL 98262306a36Sopenharmony_ci "(itf %d) : EEPROM MAC addresses don't match " 98362306a36Sopenharmony_ci "(0x%02X, inverse 0x%02X)\n", lanai->number, 98462306a36Sopenharmony_ci (unsigned int) e[EEPROM_MAC + i], 98562306a36Sopenharmony_ci (unsigned int) e[EEPROM_MAC_REV + i]); 98662306a36Sopenharmony_ci return -EIO; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]); 98962306a36Sopenharmony_ci /* Verify serial number */ 99062306a36Sopenharmony_ci lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL); 99162306a36Sopenharmony_ci v = eeprom_be4(lanai, EEPROM_SERIAL_REV); 99262306a36Sopenharmony_ci if ((lanai->serialno ^ v) != 0xFFFFFFFF) { 99362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " 99462306a36Sopenharmony_ci "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 99562306a36Sopenharmony_ci (unsigned int) lanai->serialno, (unsigned int) v); 99662306a36Sopenharmony_ci return -EIO; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno); 99962306a36Sopenharmony_ci /* Verify magic number */ 100062306a36Sopenharmony_ci lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC); 100162306a36Sopenharmony_ci v = eeprom_be4(lanai, EEPROM_MAGIC_REV); 100262306a36Sopenharmony_ci if ((lanai->magicno ^ v) != 0xFFFFFFFF) { 100362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " 100462306a36Sopenharmony_ci "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 100562306a36Sopenharmony_ci lanai->magicno, v); 100662306a36Sopenharmony_ci return -EIO; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); 100962306a36Sopenharmony_ci if (lanai->magicno != EEPROM_MAGIC_VALUE) 101062306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " 101162306a36Sopenharmony_ci "magic not what expected (got 0x%08X, not 0x%08X)\n", 101262306a36Sopenharmony_ci lanai->number, (unsigned int) lanai->magicno, 101362306a36Sopenharmony_ci (unsigned int) EEPROM_MAGIC_VALUE); 101462306a36Sopenharmony_ci return 0; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci#endif /* READ_EEPROM */ 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic inline const u8 *eeprom_mac(const struct lanai_dev *lanai) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci return &lanai->eeprom[EEPROM_MAC]; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci/* -------------------- INTERRUPT HANDLING UTILITIES: */ 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci/* Interrupt types */ 102762306a36Sopenharmony_ci#define INT_STATS (0x00000002) /* Statistics counter overflow */ 102862306a36Sopenharmony_ci#define INT_SOOL (0x00000004) /* SOOL changed state */ 102962306a36Sopenharmony_ci#define INT_LOCD (0x00000008) /* LOCD changed state */ 103062306a36Sopenharmony_ci#define INT_LED (0x00000010) /* LED (HAPPI) changed state */ 103162306a36Sopenharmony_ci#define INT_GPIN (0x00000020) /* GPIN changed state */ 103262306a36Sopenharmony_ci#define INT_PING (0x00000040) /* PING_COUNT fulfilled */ 103362306a36Sopenharmony_ci#define INT_WAKE (0x00000080) /* Lanai wants bus */ 103462306a36Sopenharmony_ci#define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */ 103562306a36Sopenharmony_ci#define INT_LOCK (0x00000200) /* Service list overflow */ 103662306a36Sopenharmony_ci#define INT_MISMATCH (0x00000400) /* TX magic list mismatch */ 103762306a36Sopenharmony_ci#define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */ 103862306a36Sopenharmony_ci#define INT_AAL0 (0x00001000) /* Non-AAL5 data available */ 103962306a36Sopenharmony_ci#define INT_SERVICE (0x00002000) /* Service list entries available */ 104062306a36Sopenharmony_ci#define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */ 104162306a36Sopenharmony_ci#define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */ 104262306a36Sopenharmony_ci#define INT_TIMEOUTBM (0x00010000) /* No response to bus master */ 104362306a36Sopenharmony_ci#define INT_PCIPARITY (0x00020000) /* Parity error on PCI */ 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci/* Sets of the above */ 104662306a36Sopenharmony_ci#define INT_ALL (0x0003FFFE) /* All interrupts */ 104762306a36Sopenharmony_ci#define INT_STATUS (0x0000003C) /* Some status pin changed */ 104862306a36Sopenharmony_ci#define INT_DMASHUT (0x00038000) /* DMA engine got shut down */ 104962306a36Sopenharmony_ci#define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */ 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic inline u32 intr_pending(const struct lanai_dev *lanai) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci return reg_read(lanai, IntStatusMasked_Reg); 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic inline void intr_enable(const struct lanai_dev *lanai, u32 i) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci reg_write(lanai, i, IntControlEna_Reg); 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic inline void intr_disable(const struct lanai_dev *lanai, u32 i) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci reg_write(lanai, i, IntControlDis_Reg); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/* -------------------- CARD/PCI STATUS: */ 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void status_message(int itf, const char *name, int status) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci static const char *onoff[2] = { "off to on", "on to off" }; 107162306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", 107262306a36Sopenharmony_ci itf, name, onoff[!status]); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void lanai_check_status(struct lanai_dev *lanai) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci u32 new = reg_read(lanai, Status_Reg); 107862306a36Sopenharmony_ci u32 changes = new ^ lanai->status; 107962306a36Sopenharmony_ci lanai->status = new; 108062306a36Sopenharmony_ci#define e(flag, name) \ 108162306a36Sopenharmony_ci if (changes & flag) \ 108262306a36Sopenharmony_ci status_message(lanai->number, name, new & flag) 108362306a36Sopenharmony_ci e(STATUS_SOOL, "SOOL"); 108462306a36Sopenharmony_ci e(STATUS_LOCD, "LOCD"); 108562306a36Sopenharmony_ci e(STATUS_LED, "LED"); 108662306a36Sopenharmony_ci e(STATUS_GPIN, "GPIN"); 108762306a36Sopenharmony_ci#undef e 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic void pcistatus_got(int itf, const char *name) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic void pcistatus_check(struct lanai_dev *lanai, int clearonly) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci u16 s; 109862306a36Sopenharmony_ci int result; 109962306a36Sopenharmony_ci result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); 110062306a36Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) { 110162306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " 110262306a36Sopenharmony_ci "%d\n", lanai->number, result); 110362306a36Sopenharmony_ci return; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | 110662306a36Sopenharmony_ci PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | 110762306a36Sopenharmony_ci PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; 110862306a36Sopenharmony_ci if (s == 0) 110962306a36Sopenharmony_ci return; 111062306a36Sopenharmony_ci result = pci_write_config_word(lanai->pci, PCI_STATUS, s); 111162306a36Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) 111262306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " 111362306a36Sopenharmony_ci "%d\n", lanai->number, result); 111462306a36Sopenharmony_ci if (clearonly) 111562306a36Sopenharmony_ci return; 111662306a36Sopenharmony_ci#define e(flag, name, stat) \ 111762306a36Sopenharmony_ci if (s & flag) { \ 111862306a36Sopenharmony_ci pcistatus_got(lanai->number, name); \ 111962306a36Sopenharmony_ci ++lanai->stats.pcierr_##stat; \ 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); 112262306a36Sopenharmony_ci e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); 112362306a36Sopenharmony_ci e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); 112462306a36Sopenharmony_ci e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); 112562306a36Sopenharmony_ci e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); 112662306a36Sopenharmony_ci e(PCI_STATUS_PARITY, "master parity", master_parity); 112762306a36Sopenharmony_ci#undef e 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci/* -------------------- VCC TX BUFFER UTILITIES: */ 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci/* space left in tx buffer in bytes */ 113362306a36Sopenharmony_cistatic inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci int r; 113662306a36Sopenharmony_ci r = endptr * 16; 113762306a36Sopenharmony_ci r -= ((unsigned long) lvcc->tx.buf.ptr) - 113862306a36Sopenharmony_ci ((unsigned long) lvcc->tx.buf.start); 113962306a36Sopenharmony_ci r -= 16; /* Leave "bubble" - if start==end it looks empty */ 114062306a36Sopenharmony_ci if (r < 0) 114162306a36Sopenharmony_ci r += lanai_buf_size(&lvcc->tx.buf); 114262306a36Sopenharmony_ci return r; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci/* test if VCC is currently backlogged */ 114662306a36Sopenharmony_cistatic inline int vcc_is_backlogged(const struct lanai_vcc *lvcc) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci return !skb_queue_empty(&lvcc->tx.backlog); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* Bit fields in the segmentation buffer descriptor */ 115262306a36Sopenharmony_ci#define DESCRIPTOR_MAGIC (0xD0000000) 115362306a36Sopenharmony_ci#define DESCRIPTOR_AAL5 (0x00008000) 115462306a36Sopenharmony_ci#define DESCRIPTOR_AAL5_STREAM (0x00004000) 115562306a36Sopenharmony_ci#define DESCRIPTOR_CLP (0x00002000) 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci/* Add 32-bit descriptor with its padding */ 115862306a36Sopenharmony_cistatic inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, 115962306a36Sopenharmony_ci u32 flags, int len) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci int pos; 116262306a36Sopenharmony_ci APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, 116362306a36Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); 116462306a36Sopenharmony_ci lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ 116562306a36Sopenharmony_ci pos = ((unsigned char *) lvcc->tx.buf.ptr) - 116662306a36Sopenharmony_ci (unsigned char *) lvcc->tx.buf.start; 116762306a36Sopenharmony_ci APRINTK((pos & ~0x0001FFF0) == 0, 116862306a36Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " 116962306a36Sopenharmony_ci "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 117062306a36Sopenharmony_ci lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 117162306a36Sopenharmony_ci pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); 117262306a36Sopenharmony_ci APRINTK((pos & ~0x0001FFF0) == 0, 117362306a36Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " 117462306a36Sopenharmony_ci "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 117562306a36Sopenharmony_ci lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 117662306a36Sopenharmony_ci lvcc->tx.buf.ptr[-1] = 117762306a36Sopenharmony_ci cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | 117862306a36Sopenharmony_ci ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? 117962306a36Sopenharmony_ci DESCRIPTOR_CLP : 0) | flags | pos >> 4); 118062306a36Sopenharmony_ci if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 118162306a36Sopenharmony_ci lvcc->tx.buf.ptr = lvcc->tx.buf.start; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci/* Add 32-bit AAL5 trailer and leave room for its CRC */ 118562306a36Sopenharmony_cistatic inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc, 118662306a36Sopenharmony_ci int len, int cpi, int uu) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, 118962306a36Sopenharmony_ci "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr); 119062306a36Sopenharmony_ci lvcc->tx.buf.ptr += 2; 119162306a36Sopenharmony_ci lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); 119262306a36Sopenharmony_ci if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 119362306a36Sopenharmony_ci lvcc->tx.buf.ptr = lvcc->tx.buf.start; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, 119762306a36Sopenharmony_ci const unsigned char *src, int n) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci unsigned char *e; 120062306a36Sopenharmony_ci int m; 120162306a36Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 120262306a36Sopenharmony_ci m = e - (unsigned char *) lvcc->tx.buf.end; 120362306a36Sopenharmony_ci if (m < 0) 120462306a36Sopenharmony_ci m = 0; 120562306a36Sopenharmony_ci memcpy(lvcc->tx.buf.ptr, src, n - m); 120662306a36Sopenharmony_ci if (m != 0) { 120762306a36Sopenharmony_ci memcpy(lvcc->tx.buf.start, src + n - m, m); 120862306a36Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.start) + m; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci lvcc->tx.buf.ptr = (u32 *) e; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci unsigned char *e; 121662306a36Sopenharmony_ci int m; 121762306a36Sopenharmony_ci if (n == 0) 121862306a36Sopenharmony_ci return; 121962306a36Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 122062306a36Sopenharmony_ci m = e - (unsigned char *) lvcc->tx.buf.end; 122162306a36Sopenharmony_ci if (m < 0) 122262306a36Sopenharmony_ci m = 0; 122362306a36Sopenharmony_ci memset(lvcc->tx.buf.ptr, 0, n - m); 122462306a36Sopenharmony_ci if (m != 0) { 122562306a36Sopenharmony_ci memset(lvcc->tx.buf.start, 0, m); 122662306a36Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.start) + m; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci lvcc->tx.buf.ptr = (u32 *) e; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/* Update "butt" register to specify new WritePtr */ 123262306a36Sopenharmony_cistatic inline void lanai_endtx(struct lanai_dev *lanai, 123362306a36Sopenharmony_ci const struct lanai_vcc *lvcc) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - 123662306a36Sopenharmony_ci (unsigned char *) lvcc->tx.buf.start; 123762306a36Sopenharmony_ci APRINTK((ptr & ~0x0001FFF0) == 0, 123862306a36Sopenharmony_ci "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", 123962306a36Sopenharmony_ci ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, 124062306a36Sopenharmony_ci lvcc->tx.buf.end); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* 124362306a36Sopenharmony_ci * Since the "butt register" is a shared resounce on the card we 124462306a36Sopenharmony_ci * serialize all accesses to it through this spinlock. This is 124562306a36Sopenharmony_ci * mostly just paranoia since the register is rarely "busy" anyway 124662306a36Sopenharmony_ci * but is needed for correctness. 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_ci spin_lock(&lanai->endtxlock); 124962306a36Sopenharmony_ci /* 125062306a36Sopenharmony_ci * We need to check if the "butt busy" bit is set before 125162306a36Sopenharmony_ci * updating the butt register. In theory this should 125262306a36Sopenharmony_ci * never happen because the ATM card is plenty fast at 125362306a36Sopenharmony_ci * updating the register. Still, we should make sure 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_ci for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { 125662306a36Sopenharmony_ci if (unlikely(i > 50)) { 125762306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): butt register " 125862306a36Sopenharmony_ci "always busy!\n", lanai->number); 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci udelay(5); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci /* 126462306a36Sopenharmony_ci * Before we tall the card to start work we need to be sure 100% of 126562306a36Sopenharmony_ci * the info in the service buffer has been written before we tell 126662306a36Sopenharmony_ci * the card about it 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci wmb(); 126962306a36Sopenharmony_ci reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg); 127062306a36Sopenharmony_ci spin_unlock(&lanai->endtxlock); 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci/* 127462306a36Sopenharmony_ci * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's 127562306a36Sopenharmony_ci * space available. "pdusize" is the number of bytes the PDU will take 127662306a36Sopenharmony_ci */ 127762306a36Sopenharmony_cistatic void lanai_send_one_aal5(struct lanai_dev *lanai, 127862306a36Sopenharmony_ci struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci int pad; 128162306a36Sopenharmony_ci APRINTK(pdusize == aal5_size(skb->len), 128262306a36Sopenharmony_ci "lanai_send_one_aal5: wrong size packet (%d != %d)\n", 128362306a36Sopenharmony_ci pdusize, aal5_size(skb->len)); 128462306a36Sopenharmony_ci vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize); 128562306a36Sopenharmony_ci pad = pdusize - skb->len - 8; 128662306a36Sopenharmony_ci APRINTK(pad >= 0, "pad is negative (%d)\n", pad); 128762306a36Sopenharmony_ci APRINTK(pad < 48, "pad is too big (%d)\n", pad); 128862306a36Sopenharmony_ci vcc_tx_memcpy(lvcc, skb->data, skb->len); 128962306a36Sopenharmony_ci vcc_tx_memzero(lvcc, pad); 129062306a36Sopenharmony_ci vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0); 129162306a36Sopenharmony_ci lanai_endtx(lanai, lvcc); 129262306a36Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 129362306a36Sopenharmony_ci atomic_inc(&lvcc->tx.atmvcc->stats->tx); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci/* Try to fill the buffer - don't call unless there is backlog */ 129762306a36Sopenharmony_cistatic void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, 129862306a36Sopenharmony_ci struct lanai_vcc *lvcc, int endptr) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci int n; 130162306a36Sopenharmony_ci struct sk_buff *skb; 130262306a36Sopenharmony_ci int space = vcc_tx_space(lvcc, endptr); 130362306a36Sopenharmony_ci APRINTK(vcc_is_backlogged(lvcc), 130462306a36Sopenharmony_ci "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", 130562306a36Sopenharmony_ci lvcc->vci); 130662306a36Sopenharmony_ci while (space >= 64) { 130762306a36Sopenharmony_ci skb = skb_dequeue(&lvcc->tx.backlog); 130862306a36Sopenharmony_ci if (skb == NULL) 130962306a36Sopenharmony_ci goto no_backlog; 131062306a36Sopenharmony_ci n = aal5_size(skb->len); 131162306a36Sopenharmony_ci if (n + 16 > space) { 131262306a36Sopenharmony_ci /* No room for this packet - put it back on queue */ 131362306a36Sopenharmony_ci skb_queue_head(&lvcc->tx.backlog, skb); 131462306a36Sopenharmony_ci return; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci lanai_send_one_aal5(lanai, lvcc, skb, n); 131762306a36Sopenharmony_ci space -= n + 16; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci if (!vcc_is_backlogged(lvcc)) { 132062306a36Sopenharmony_ci no_backlog: 132162306a36Sopenharmony_ci __clear_bit(lvcc->vci, lanai->backlog_vccs); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci/* Given an skb that we want to transmit either send it now or queue */ 132662306a36Sopenharmony_cistatic void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 132762306a36Sopenharmony_ci struct sk_buff *skb) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci int space, n; 133062306a36Sopenharmony_ci if (vcc_is_backlogged(lvcc)) /* Already backlogged */ 133162306a36Sopenharmony_ci goto queue_it; 133262306a36Sopenharmony_ci space = vcc_tx_space(lvcc, 133362306a36Sopenharmony_ci TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr))); 133462306a36Sopenharmony_ci n = aal5_size(skb->len); 133562306a36Sopenharmony_ci APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n); 133662306a36Sopenharmony_ci if (space < n + 16) { /* No space for this PDU */ 133762306a36Sopenharmony_ci __set_bit(lvcc->vci, lanai->backlog_vccs); 133862306a36Sopenharmony_ci queue_it: 133962306a36Sopenharmony_ci skb_queue_tail(&lvcc->tx.backlog, skb); 134062306a36Sopenharmony_ci return; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci lanai_send_one_aal5(lanai, lvcc, skb, n); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, 134662306a36Sopenharmony_ci struct lanai_vcc *lvcc, int endptr) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL 134962306a36Sopenharmony_ci ": vcc_tx_unqueue_aal0: not implemented\n"); 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 135362306a36Sopenharmony_ci struct sk_buff *skb) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); 135662306a36Sopenharmony_ci /* Remember to increment lvcc->tx.atmvcc->stats->tx */ 135762306a36Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/* -------------------- VCC RX BUFFER UTILITIES: */ 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci/* unlike the _tx_ cousins, this doesn't update ptr */ 136362306a36Sopenharmony_cistatic inline void vcc_rx_memcpy(unsigned char *dest, 136462306a36Sopenharmony_ci const struct lanai_vcc *lvcc, int n) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - 136762306a36Sopenharmony_ci ((const unsigned char *) (lvcc->rx.buf.end)); 136862306a36Sopenharmony_ci if (m < 0) 136962306a36Sopenharmony_ci m = 0; 137062306a36Sopenharmony_ci memcpy(dest, lvcc->rx.buf.ptr, n - m); 137162306a36Sopenharmony_ci memcpy(dest + n - m, lvcc->rx.buf.start, m); 137262306a36Sopenharmony_ci /* Make sure that these copies don't get reordered */ 137362306a36Sopenharmony_ci barrier(); 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci/* Receive AAL5 data on a VCC with a particular endptr */ 137762306a36Sopenharmony_cistatic void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci int size; 138062306a36Sopenharmony_ci struct sk_buff *skb; 138162306a36Sopenharmony_ci const u32 *x; 138262306a36Sopenharmony_ci u32 *end = &lvcc->rx.buf.start[endptr * 4]; 138362306a36Sopenharmony_ci int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); 138462306a36Sopenharmony_ci if (n < 0) 138562306a36Sopenharmony_ci n += lanai_buf_size(&lvcc->rx.buf); 138662306a36Sopenharmony_ci APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), 138762306a36Sopenharmony_ci "vcc_rx_aal5: n out of range (%d/%zu)\n", 138862306a36Sopenharmony_ci n, lanai_buf_size(&lvcc->rx.buf)); 138962306a36Sopenharmony_ci /* Recover the second-to-last word to get true pdu length */ 139062306a36Sopenharmony_ci if ((x = &end[-2]) < lvcc->rx.buf.start) 139162306a36Sopenharmony_ci x = &lvcc->rx.buf.end[-2]; 139262306a36Sopenharmony_ci /* 139362306a36Sopenharmony_ci * Before we actually read from the buffer, make sure the memory 139462306a36Sopenharmony_ci * changes have arrived 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ci rmb(); 139762306a36Sopenharmony_ci size = be32_to_cpup(x) & 0xffff; 139862306a36Sopenharmony_ci if (unlikely(n != aal5_size(size))) { 139962306a36Sopenharmony_ci /* Make sure size matches padding */ 140062306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " 140162306a36Sopenharmony_ci "on vci=%d - size=%d n=%d\n", 140262306a36Sopenharmony_ci lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); 140362306a36Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen++; 140462306a36Sopenharmony_ci goto out; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); 140762306a36Sopenharmony_ci if (unlikely(skb == NULL)) { 140862306a36Sopenharmony_ci lvcc->stats.rx_nomem++; 140962306a36Sopenharmony_ci goto out; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci skb_put(skb, size); 141262306a36Sopenharmony_ci vcc_rx_memcpy(skb->data, lvcc, size); 141362306a36Sopenharmony_ci ATM_SKB(skb)->vcc = lvcc->rx.atmvcc; 141462306a36Sopenharmony_ci __net_timestamp(skb); 141562306a36Sopenharmony_ci lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); 141662306a36Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx); 141762306a36Sopenharmony_ci out: 141862306a36Sopenharmony_ci lvcc->rx.buf.ptr = end; 141962306a36Sopenharmony_ci cardvcc_write(lvcc, endptr, vcc_rxreadptr); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic void vcc_rx_aal0(struct lanai_dev *lanai) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); 142562306a36Sopenharmony_ci /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */ 142662306a36Sopenharmony_ci /* Remember to increment lvcc->rx.atmvcc->stats->rx */ 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci/* -------------------- MANAGING HOST-BASED VCC TABLE: */ 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci/* Decide whether to use vmalloc or get_zeroed_page for VCC table */ 143262306a36Sopenharmony_ci#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE 143362306a36Sopenharmony_ci#define VCCTABLE_GETFREEPAGE 143462306a36Sopenharmony_ci#else 143562306a36Sopenharmony_ci#include <linux/vmalloc.h> 143662306a36Sopenharmony_ci#endif 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic int vcc_table_allocate(struct lanai_dev *lanai) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE 144162306a36Sopenharmony_ci APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, 144262306a36Sopenharmony_ci "vcc table > PAGE_SIZE!"); 144362306a36Sopenharmony_ci lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL); 144462306a36Sopenharmony_ci return (lanai->vccs == NULL) ? -ENOMEM : 0; 144562306a36Sopenharmony_ci#else 144662306a36Sopenharmony_ci int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); 144762306a36Sopenharmony_ci lanai->vccs = vzalloc(bytes); 144862306a36Sopenharmony_ci if (unlikely(lanai->vccs == NULL)) 144962306a36Sopenharmony_ci return -ENOMEM; 145062306a36Sopenharmony_ci return 0; 145162306a36Sopenharmony_ci#endif 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic inline void vcc_table_deallocate(const struct lanai_dev *lanai) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE 145762306a36Sopenharmony_ci free_page((unsigned long) lanai->vccs); 145862306a36Sopenharmony_ci#else 145962306a36Sopenharmony_ci vfree(lanai->vccs); 146062306a36Sopenharmony_ci#endif 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci/* Allocate a fresh lanai_vcc, with the appropriate things cleared */ 146462306a36Sopenharmony_cistatic inline struct lanai_vcc *new_lanai_vcc(void) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci struct lanai_vcc *lvcc; 146762306a36Sopenharmony_ci lvcc = kzalloc(sizeof(*lvcc), GFP_KERNEL); 146862306a36Sopenharmony_ci if (likely(lvcc != NULL)) { 146962306a36Sopenharmony_ci skb_queue_head_init(&lvcc->tx.backlog); 147062306a36Sopenharmony_ci#ifdef DEBUG 147162306a36Sopenharmony_ci lvcc->vci = -1; 147262306a36Sopenharmony_ci#endif 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci return lvcc; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic int lanai_get_sized_buffer(struct lanai_dev *lanai, 147862306a36Sopenharmony_ci struct lanai_buffer *buf, int max_sdu, int multiplier, 147962306a36Sopenharmony_ci const char *name) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci int size; 148262306a36Sopenharmony_ci if (unlikely(max_sdu < 1)) 148362306a36Sopenharmony_ci max_sdu = 1; 148462306a36Sopenharmony_ci max_sdu = aal5_size(max_sdu); 148562306a36Sopenharmony_ci size = (max_sdu + 16) * multiplier + 16; 148662306a36Sopenharmony_ci lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci); 148762306a36Sopenharmony_ci if (unlikely(buf->start == NULL)) 148862306a36Sopenharmony_ci return -ENOMEM; 148962306a36Sopenharmony_ci if (unlikely(lanai_buf_size(buf) < size)) 149062306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " 149162306a36Sopenharmony_ci "for %s buffer, got only %zu\n", lanai->number, size, 149262306a36Sopenharmony_ci name, lanai_buf_size(buf)); 149362306a36Sopenharmony_ci DPRINTK("Allocated %zu byte %s buffer\n", lanai_buf_size(buf), name); 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci/* Setup a RX buffer for a currently unbound AAL5 vci */ 149862306a36Sopenharmony_cistatic inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai, 149962306a36Sopenharmony_ci struct lanai_vcc *lvcc, const struct atm_qos *qos) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci return lanai_get_sized_buffer(lanai, &lvcc->rx.buf, 150262306a36Sopenharmony_ci qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX"); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci/* Setup a TX buffer for a currently unbound AAL5 vci */ 150662306a36Sopenharmony_cistatic int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 150762306a36Sopenharmony_ci const struct atm_qos *qos) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci int max_sdu, multiplier; 151062306a36Sopenharmony_ci if (qos->aal == ATM_AAL0) { 151162306a36Sopenharmony_ci lvcc->tx.unqueue = vcc_tx_unqueue_aal0; 151262306a36Sopenharmony_ci max_sdu = ATM_CELL_SIZE - 1; 151362306a36Sopenharmony_ci multiplier = AAL0_TX_MULTIPLIER; 151462306a36Sopenharmony_ci } else { 151562306a36Sopenharmony_ci lvcc->tx.unqueue = vcc_tx_unqueue_aal5; 151662306a36Sopenharmony_ci max_sdu = qos->txtp.max_sdu; 151762306a36Sopenharmony_ci multiplier = AAL5_TX_MULTIPLIER; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu, 152062306a36Sopenharmony_ci multiplier, "TX"); 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic inline void host_vcc_bind(struct lanai_dev *lanai, 152462306a36Sopenharmony_ci struct lanai_vcc *lvcc, vci_t vci) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci if (lvcc->vbase != NULL) 152762306a36Sopenharmony_ci return; /* We already were bound in the other direction */ 152862306a36Sopenharmony_ci DPRINTK("Binding vci %d\n", vci); 152962306a36Sopenharmony_ci#ifdef USE_POWERDOWN 153062306a36Sopenharmony_ci if (lanai->nbound++ == 0) { 153162306a36Sopenharmony_ci DPRINTK("Coming out of powerdown\n"); 153262306a36Sopenharmony_ci lanai->conf1 &= ~CONFIG1_POWERDOWN; 153362306a36Sopenharmony_ci conf1_write(lanai); 153462306a36Sopenharmony_ci conf2_write(lanai); 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci#endif 153762306a36Sopenharmony_ci lvcc->vbase = cardvcc_addr(lanai, vci); 153862306a36Sopenharmony_ci lanai->vccs[lvcc->vci = vci] = lvcc; 153962306a36Sopenharmony_ci} 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic inline void host_vcc_unbind(struct lanai_dev *lanai, 154262306a36Sopenharmony_ci struct lanai_vcc *lvcc) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci if (lvcc->vbase == NULL) 154562306a36Sopenharmony_ci return; /* This vcc was never bound */ 154662306a36Sopenharmony_ci DPRINTK("Unbinding vci %d\n", lvcc->vci); 154762306a36Sopenharmony_ci lvcc->vbase = NULL; 154862306a36Sopenharmony_ci lanai->vccs[lvcc->vci] = NULL; 154962306a36Sopenharmony_ci#ifdef USE_POWERDOWN 155062306a36Sopenharmony_ci if (--lanai->nbound == 0) { 155162306a36Sopenharmony_ci DPRINTK("Going into powerdown\n"); 155262306a36Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 155362306a36Sopenharmony_ci conf1_write(lanai); 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci#endif 155662306a36Sopenharmony_ci} 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci/* -------------------- RESET CARD: */ 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic void lanai_reset(struct lanai_dev *lanai) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not " 156362306a36Sopenharmony_ci "implemented\n", lanai->number); 156462306a36Sopenharmony_ci /* TODO */ 156562306a36Sopenharmony_ci /* The following is just a hack until we write the real 156662306a36Sopenharmony_ci * resetter - at least ack whatever interrupt sent us 156762306a36Sopenharmony_ci * here 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_ci reg_write(lanai, INT_ALL, IntAck_Reg); 157062306a36Sopenharmony_ci lanai->stats.card_reset++; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci/* -------------------- SERVICE LIST UTILITIES: */ 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci/* 157662306a36Sopenharmony_ci * Allocate service buffer and tell card about it 157762306a36Sopenharmony_ci */ 157862306a36Sopenharmony_cistatic int service_buffer_allocate(struct lanai_dev *lanai) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8, 158162306a36Sopenharmony_ci lanai->pci); 158262306a36Sopenharmony_ci if (unlikely(lanai->service.start == NULL)) 158362306a36Sopenharmony_ci return -ENOMEM; 158462306a36Sopenharmony_ci DPRINTK("allocated service buffer at %p, size %zu(%d)\n", 158562306a36Sopenharmony_ci lanai->service.start, 158662306a36Sopenharmony_ci lanai_buf_size(&lanai->service), 158762306a36Sopenharmony_ci lanai_buf_size_cardorder(&lanai->service)); 158862306a36Sopenharmony_ci /* Clear ServWrite register to be safe */ 158962306a36Sopenharmony_ci reg_write(lanai, 0, ServWrite_Reg); 159062306a36Sopenharmony_ci /* ServiceStuff register contains size and address of buffer */ 159162306a36Sopenharmony_ci reg_write(lanai, 159262306a36Sopenharmony_ci SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | 159362306a36Sopenharmony_ci SSTUFF_SET_ADDR(lanai->service.dmaaddr), 159462306a36Sopenharmony_ci ServiceStuff_Reg); 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic inline void service_buffer_deallocate(struct lanai_dev *lanai) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci lanai_buf_deallocate(&lanai->service, lanai->pci); 160162306a36Sopenharmony_ci} 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci/* Bitfields in service list */ 160462306a36Sopenharmony_ci#define SERVICE_TX (0x80000000) /* Was from transmission */ 160562306a36Sopenharmony_ci#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */ 160662306a36Sopenharmony_ci#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */ 160762306a36Sopenharmony_ci#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */ 160862306a36Sopenharmony_ci#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */ 160962306a36Sopenharmony_ci#define SERVICE_STREAM (0x04000000) /* RX Stream mode */ 161062306a36Sopenharmony_ci#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF) 161162306a36Sopenharmony_ci#define SERVICE_GET_END(x) ((x)&0x1FFF) 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci/* Handle one thing from the service list - returns true if it marked a 161462306a36Sopenharmony_ci * VCC ready for xmit 161562306a36Sopenharmony_ci */ 161662306a36Sopenharmony_cistatic int handle_service(struct lanai_dev *lanai, u32 s) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci vci_t vci = SERVICE_GET_VCI(s); 161962306a36Sopenharmony_ci struct lanai_vcc *lvcc; 162062306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 162162306a36Sopenharmony_ci lvcc = lanai->vccs[vci]; 162262306a36Sopenharmony_ci if (unlikely(lvcc == NULL)) { 162362306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 162462306a36Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for nonexistent " 162562306a36Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 162662306a36Sopenharmony_ci if (s & SERVICE_TX) 162762306a36Sopenharmony_ci lanai->stats.service_notx++; 162862306a36Sopenharmony_ci else 162962306a36Sopenharmony_ci lanai->stats.service_norx++; 163062306a36Sopenharmony_ci return 0; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci if (s & SERVICE_TX) { /* segmentation interrupt */ 163362306a36Sopenharmony_ci if (unlikely(lvcc->tx.atmvcc == NULL)) { 163462306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 163562306a36Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for non-TX " 163662306a36Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 163762306a36Sopenharmony_ci lanai->stats.service_notx++; 163862306a36Sopenharmony_ci return 0; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci __set_bit(vci, lanai->transmit_ready); 164162306a36Sopenharmony_ci lvcc->tx.endptr = SERVICE_GET_END(s); 164262306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 164362306a36Sopenharmony_ci return 1; 164462306a36Sopenharmony_ci } 164562306a36Sopenharmony_ci if (unlikely(lvcc->rx.atmvcc == NULL)) { 164662306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 164762306a36Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for non-RX " 164862306a36Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 164962306a36Sopenharmony_ci lanai->stats.service_norx++; 165062306a36Sopenharmony_ci return 0; 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) { 165362306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 165462306a36Sopenharmony_ci DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " 165562306a36Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 165662306a36Sopenharmony_ci lanai->stats.service_rxnotaal5++; 165762306a36Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 165862306a36Sopenharmony_ci return 0; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) { 166162306a36Sopenharmony_ci vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); 166262306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 166362306a36Sopenharmony_ci return 0; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci if (s & SERVICE_TRASH) { 166662306a36Sopenharmony_ci int bytes; 166762306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 166862306a36Sopenharmony_ci DPRINTK("got trashed rx pdu on vci %d\n", vci); 166962306a36Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 167062306a36Sopenharmony_ci lvcc->stats.x.aal5.service_trash++; 167162306a36Sopenharmony_ci bytes = (SERVICE_GET_END(s) * 16) - 167262306a36Sopenharmony_ci (((unsigned long) lvcc->rx.buf.ptr) - 167362306a36Sopenharmony_ci ((unsigned long) lvcc->rx.buf.start)) + 47; 167462306a36Sopenharmony_ci if (bytes < 0) 167562306a36Sopenharmony_ci bytes += lanai_buf_size(&lvcc->rx.buf); 167662306a36Sopenharmony_ci lanai->stats.ovfl_trash += (bytes / 48); 167762306a36Sopenharmony_ci return 0; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci if (s & SERVICE_STREAM) { 168062306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 168162306a36Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 168262306a36Sopenharmony_ci lvcc->stats.x.aal5.service_stream++; 168362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " 168462306a36Sopenharmony_ci "PDU on VCI %d!\n", lanai->number, vci); 168562306a36Sopenharmony_ci lanai_reset(lanai); 168662306a36Sopenharmony_ci return 0; 168762306a36Sopenharmony_ci } 168862306a36Sopenharmony_ci DPRINTK("got rx crc error on vci %d\n", vci); 168962306a36Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 169062306a36Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc++; 169162306a36Sopenharmony_ci lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; 169262306a36Sopenharmony_ci cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); 169362306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 169462306a36Sopenharmony_ci return 0; 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci/* Try transmitting on all VCIs that we marked ready to serve */ 169862306a36Sopenharmony_cistatic void iter_transmit(struct lanai_dev *lanai, vci_t vci) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci struct lanai_vcc *lvcc = lanai->vccs[vci]; 170162306a36Sopenharmony_ci if (vcc_is_backlogged(lvcc)) 170262306a36Sopenharmony_ci lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr); 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci/* Run service queue -- called from interrupt context or with 170662306a36Sopenharmony_ci * interrupts otherwise disabled and with the lanai->servicelock 170762306a36Sopenharmony_ci * lock held 170862306a36Sopenharmony_ci */ 170962306a36Sopenharmony_cistatic void run_service(struct lanai_dev *lanai) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci int ntx = 0; 171262306a36Sopenharmony_ci u32 wreg = reg_read(lanai, ServWrite_Reg); 171362306a36Sopenharmony_ci const u32 *end = lanai->service.start + wreg; 171462306a36Sopenharmony_ci while (lanai->service.ptr != end) { 171562306a36Sopenharmony_ci ntx += handle_service(lanai, 171662306a36Sopenharmony_ci le32_to_cpup(lanai->service.ptr++)); 171762306a36Sopenharmony_ci if (lanai->service.ptr >= lanai->service.end) 171862306a36Sopenharmony_ci lanai->service.ptr = lanai->service.start; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci reg_write(lanai, wreg, ServRead_Reg); 172162306a36Sopenharmony_ci if (ntx != 0) { 172262306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 172362306a36Sopenharmony_ci vci_bitfield_iterate(lanai, lanai->transmit_ready, 172462306a36Sopenharmony_ci iter_transmit); 172562306a36Sopenharmony_ci bitmap_zero(lanai->transmit_ready, NUM_VCI); 172662306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci/* -------------------- GATHER STATISTICS: */ 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void get_statistics(struct lanai_dev *lanai) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci u32 statreg = reg_read(lanai, Statistics_Reg); 173562306a36Sopenharmony_ci lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); 173662306a36Sopenharmony_ci lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); 173762306a36Sopenharmony_ci lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); 173862306a36Sopenharmony_ci lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg); 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/* -------------------- POLLING TIMER: */ 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci#ifndef DEBUG_RW 174462306a36Sopenharmony_ci/* Try to undequeue 1 backlogged vcc */ 174562306a36Sopenharmony_cistatic void iter_dequeue(struct lanai_dev *lanai, vci_t vci) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct lanai_vcc *lvcc = lanai->vccs[vci]; 174862306a36Sopenharmony_ci int endptr; 174962306a36Sopenharmony_ci if (lvcc == NULL || lvcc->tx.atmvcc == NULL || 175062306a36Sopenharmony_ci !vcc_is_backlogged(lvcc)) { 175162306a36Sopenharmony_ci __clear_bit(vci, lanai->backlog_vccs); 175262306a36Sopenharmony_ci return; 175362306a36Sopenharmony_ci } 175462306a36Sopenharmony_ci endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 175562306a36Sopenharmony_ci lvcc->tx.unqueue(lanai, lvcc, endptr); 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_ci#endif /* !DEBUG_RW */ 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic void lanai_timed_poll(struct timer_list *t) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci struct lanai_dev *lanai = from_timer(lanai, t, timer); 176262306a36Sopenharmony_ci#ifndef DEBUG_RW 176362306a36Sopenharmony_ci unsigned long flags; 176462306a36Sopenharmony_ci#ifdef USE_POWERDOWN 176562306a36Sopenharmony_ci if (lanai->conf1 & CONFIG1_POWERDOWN) 176662306a36Sopenharmony_ci return; 176762306a36Sopenharmony_ci#endif /* USE_POWERDOWN */ 176862306a36Sopenharmony_ci local_irq_save(flags); 176962306a36Sopenharmony_ci /* If we can grab the spinlock, check if any services need to be run */ 177062306a36Sopenharmony_ci if (spin_trylock(&lanai->servicelock)) { 177162306a36Sopenharmony_ci run_service(lanai); 177262306a36Sopenharmony_ci spin_unlock(&lanai->servicelock); 177362306a36Sopenharmony_ci } 177462306a36Sopenharmony_ci /* ...and see if any backlogged VCs can make progress */ 177562306a36Sopenharmony_ci /* unfortunately linux has no read_trylock() currently */ 177662306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 177762306a36Sopenharmony_ci vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue); 177862306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 177962306a36Sopenharmony_ci local_irq_restore(flags); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci get_statistics(lanai); 178262306a36Sopenharmony_ci#endif /* !DEBUG_RW */ 178362306a36Sopenharmony_ci mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD); 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cistatic inline void lanai_timed_poll_start(struct lanai_dev *lanai) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci timer_setup(&lanai->timer, lanai_timed_poll, 0); 178962306a36Sopenharmony_ci lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; 179062306a36Sopenharmony_ci add_timer(&lanai->timer); 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_cistatic inline void lanai_timed_poll_stop(struct lanai_dev *lanai) 179462306a36Sopenharmony_ci{ 179562306a36Sopenharmony_ci del_timer_sync(&lanai->timer); 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci/* -------------------- INTERRUPT SERVICE: */ 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic inline void lanai_int_1(struct lanai_dev *lanai, u32 reason) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci u32 ack = 0; 180362306a36Sopenharmony_ci if (reason & INT_SERVICE) { 180462306a36Sopenharmony_ci ack = INT_SERVICE; 180562306a36Sopenharmony_ci spin_lock(&lanai->servicelock); 180662306a36Sopenharmony_ci run_service(lanai); 180762306a36Sopenharmony_ci spin_unlock(&lanai->servicelock); 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci if (reason & (INT_AAL0_STR | INT_AAL0)) { 181062306a36Sopenharmony_ci ack |= reason & (INT_AAL0_STR | INT_AAL0); 181162306a36Sopenharmony_ci vcc_rx_aal0(lanai); 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci /* The rest of the interrupts are pretty rare */ 181462306a36Sopenharmony_ci if (ack == reason) 181562306a36Sopenharmony_ci goto done; 181662306a36Sopenharmony_ci if (reason & INT_STATS) { 181762306a36Sopenharmony_ci reason &= ~INT_STATS; /* No need to ack */ 181862306a36Sopenharmony_ci get_statistics(lanai); 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci if (reason & INT_STATUS) { 182162306a36Sopenharmony_ci ack |= reason & INT_STATUS; 182262306a36Sopenharmony_ci lanai_check_status(lanai); 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci if (unlikely(reason & INT_DMASHUT)) { 182562306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA " 182662306a36Sopenharmony_ci "shutdown, reason=0x%08X, address=0x%08X\n", 182762306a36Sopenharmony_ci lanai->number, (unsigned int) (reason & INT_DMASHUT), 182862306a36Sopenharmony_ci (unsigned int) reg_read(lanai, DMA_Addr_Reg)); 182962306a36Sopenharmony_ci if (reason & INT_TABORTBM) { 183062306a36Sopenharmony_ci lanai_reset(lanai); 183162306a36Sopenharmony_ci return; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci ack |= (reason & INT_DMASHUT); 183462306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n", 183562306a36Sopenharmony_ci lanai->number); 183662306a36Sopenharmony_ci conf1_write(lanai); 183762306a36Sopenharmony_ci lanai->stats.dma_reenable++; 183862306a36Sopenharmony_ci pcistatus_check(lanai, 0); 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci if (unlikely(reason & INT_TABORTSENT)) { 184162306a36Sopenharmony_ci ack |= (reason & INT_TABORTSENT); 184262306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n", 184362306a36Sopenharmony_ci lanai->number); 184462306a36Sopenharmony_ci pcistatus_check(lanai, 0); 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci if (unlikely(reason & INT_SEGSHUT)) { 184762306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 184862306a36Sopenharmony_ci "segmentation shutdown, reason=0x%08X\n", lanai->number, 184962306a36Sopenharmony_ci (unsigned int) (reason & INT_SEGSHUT)); 185062306a36Sopenharmony_ci lanai_reset(lanai); 185162306a36Sopenharmony_ci return; 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci if (unlikely(reason & (INT_PING | INT_WAKE))) { 185462306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 185562306a36Sopenharmony_ci "unexpected interrupt 0x%08X, resetting\n", 185662306a36Sopenharmony_ci lanai->number, 185762306a36Sopenharmony_ci (unsigned int) (reason & (INT_PING | INT_WAKE))); 185862306a36Sopenharmony_ci lanai_reset(lanai); 185962306a36Sopenharmony_ci return; 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci#ifdef DEBUG 186262306a36Sopenharmony_ci if (unlikely(ack != reason)) { 186362306a36Sopenharmony_ci DPRINTK("unacked ints: 0x%08X\n", 186462306a36Sopenharmony_ci (unsigned int) (reason & ~ack)); 186562306a36Sopenharmony_ci ack = reason; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci#endif 186862306a36Sopenharmony_ci done: 186962306a36Sopenharmony_ci if (ack != 0) 187062306a36Sopenharmony_ci reg_write(lanai, ack, IntAck_Reg); 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cistatic irqreturn_t lanai_int(int irq, void *devid) 187462306a36Sopenharmony_ci{ 187562306a36Sopenharmony_ci struct lanai_dev *lanai = devid; 187662306a36Sopenharmony_ci u32 reason; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci#ifdef USE_POWERDOWN 187962306a36Sopenharmony_ci /* 188062306a36Sopenharmony_ci * If we're powered down we shouldn't be generating any interrupts - 188162306a36Sopenharmony_ci * so assume that this is a shared interrupt line and it's for someone 188262306a36Sopenharmony_ci * else 188362306a36Sopenharmony_ci */ 188462306a36Sopenharmony_ci if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN)) 188562306a36Sopenharmony_ci return IRQ_NONE; 188662306a36Sopenharmony_ci#endif 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci reason = intr_pending(lanai); 188962306a36Sopenharmony_ci if (reason == 0) 189062306a36Sopenharmony_ci return IRQ_NONE; /* Must be for someone else */ 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci do { 189362306a36Sopenharmony_ci if (unlikely(reason == 0xFFFFFFFF)) 189462306a36Sopenharmony_ci break; /* Maybe we've been unplugged? */ 189562306a36Sopenharmony_ci lanai_int_1(lanai, reason); 189662306a36Sopenharmony_ci reason = intr_pending(lanai); 189762306a36Sopenharmony_ci } while (reason != 0); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci return IRQ_HANDLED; 190062306a36Sopenharmony_ci} 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci/* TODO - it would be nice if we could use the "delayed interrupt" system 190362306a36Sopenharmony_ci * to some advantage 190462306a36Sopenharmony_ci */ 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci/* -------------------- CHECK BOARD ID/REV: */ 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci/* 190962306a36Sopenharmony_ci * The board id and revision are stored both in the reset register and 191062306a36Sopenharmony_ci * in the PCI configuration space - the documentation says to check 191162306a36Sopenharmony_ci * each of them. If revp!=NULL we store the revision there 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_cistatic int check_board_id_and_rev(const char *name, u32 val, int *revp) 191462306a36Sopenharmony_ci{ 191562306a36Sopenharmony_ci DPRINTK("%s says board_id=%d, board_rev=%d\n", name, 191662306a36Sopenharmony_ci (int) RESET_GET_BOARD_ID(val), 191762306a36Sopenharmony_ci (int) RESET_GET_BOARD_REV(val)); 191862306a36Sopenharmony_ci if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) { 191962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " 192062306a36Sopenharmony_ci "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val)); 192162306a36Sopenharmony_ci return -ENODEV; 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci if (revp != NULL) 192462306a36Sopenharmony_ci *revp = RESET_GET_BOARD_REV(val); 192562306a36Sopenharmony_ci return 0; 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci/* -------------------- PCI INITIALIZATION/SHUTDOWN: */ 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_cistatic int lanai_pci_start(struct lanai_dev *lanai) 193162306a36Sopenharmony_ci{ 193262306a36Sopenharmony_ci struct pci_dev *pci = lanai->pci; 193362306a36Sopenharmony_ci int result; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci if (pci_enable_device(pci) != 0) { 193662306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable " 193762306a36Sopenharmony_ci "PCI device", lanai->number); 193862306a36Sopenharmony_ci return -ENXIO; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci pci_set_master(pci); 194162306a36Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) { 194262306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL 194362306a36Sopenharmony_ci "(itf %d): No suitable DMA available.\n", lanai->number); 194462306a36Sopenharmony_ci return -EBUSY; 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL); 194762306a36Sopenharmony_ci if (result != 0) 194862306a36Sopenharmony_ci return result; 194962306a36Sopenharmony_ci /* Set latency timer to zero as per lanai docs */ 195062306a36Sopenharmony_ci result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); 195162306a36Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) { 195262306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't write " 195362306a36Sopenharmony_ci "PCI_LATENCY_TIMER: %d\n", lanai->number, result); 195462306a36Sopenharmony_ci return -EINVAL; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci pcistatus_check(lanai, 1); 195762306a36Sopenharmony_ci pcistatus_check(lanai, 0); 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci/* -------------------- VPI/VCI ALLOCATION: */ 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci/* 196462306a36Sopenharmony_ci * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll 196562306a36Sopenharmony_ci * get a CBRZERO interrupt), and we can use it only if no one is receiving 196662306a36Sopenharmony_ci * AAL0 traffic (since they will use the same queue) - according to the 196762306a36Sopenharmony_ci * docs we shouldn't even use it for AAL0 traffic 196862306a36Sopenharmony_ci */ 196962306a36Sopenharmony_cistatic inline int vci0_is_ok(struct lanai_dev *lanai, 197062306a36Sopenharmony_ci const struct atm_qos *qos) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) 197362306a36Sopenharmony_ci return 0; 197462306a36Sopenharmony_ci if (qos->rxtp.traffic_class != ATM_NONE) { 197562306a36Sopenharmony_ci if (lanai->naal0 != 0) 197662306a36Sopenharmony_ci return 0; 197762306a36Sopenharmony_ci lanai->conf2 |= CONFIG2_VCI0_NORMAL; 197862306a36Sopenharmony_ci conf2_write_if_powerup(lanai); 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci return 1; 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci/* return true if vci is currently unused, or if requested qos is 198462306a36Sopenharmony_ci * compatible 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_cistatic int vci_is_ok(struct lanai_dev *lanai, vci_t vci, 198762306a36Sopenharmony_ci const struct atm_vcc *atmvcc) 198862306a36Sopenharmony_ci{ 198962306a36Sopenharmony_ci const struct atm_qos *qos = &atmvcc->qos; 199062306a36Sopenharmony_ci const struct lanai_vcc *lvcc = lanai->vccs[vci]; 199162306a36Sopenharmony_ci if (vci == 0 && !vci0_is_ok(lanai, qos)) 199262306a36Sopenharmony_ci return 0; 199362306a36Sopenharmony_ci if (unlikely(lvcc != NULL)) { 199462306a36Sopenharmony_ci if (qos->rxtp.traffic_class != ATM_NONE && 199562306a36Sopenharmony_ci lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci if (qos->txtp.traffic_class != ATM_NONE && 199862306a36Sopenharmony_ci lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) 199962306a36Sopenharmony_ci return 0; 200062306a36Sopenharmony_ci if (qos->txtp.traffic_class == ATM_CBR && 200162306a36Sopenharmony_ci lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) 200262306a36Sopenharmony_ci return 0; 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 && 200562306a36Sopenharmony_ci qos->rxtp.traffic_class != ATM_NONE) { 200662306a36Sopenharmony_ci const struct lanai_vcc *vci0 = lanai->vccs[0]; 200762306a36Sopenharmony_ci if (vci0 != NULL && vci0->rx.atmvcc != NULL) 200862306a36Sopenharmony_ci return 0; 200962306a36Sopenharmony_ci lanai->conf2 &= ~CONFIG2_VCI0_NORMAL; 201062306a36Sopenharmony_ci conf2_write_if_powerup(lanai); 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci return 1; 201362306a36Sopenharmony_ci} 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_cistatic int lanai_normalize_ci(struct lanai_dev *lanai, 201662306a36Sopenharmony_ci const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip) 201762306a36Sopenharmony_ci{ 201862306a36Sopenharmony_ci switch (*vpip) { 201962306a36Sopenharmony_ci case ATM_VPI_ANY: 202062306a36Sopenharmony_ci *vpip = 0; 202162306a36Sopenharmony_ci fallthrough; 202262306a36Sopenharmony_ci case 0: 202362306a36Sopenharmony_ci break; 202462306a36Sopenharmony_ci default: 202562306a36Sopenharmony_ci return -EADDRINUSE; 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci switch (*vcip) { 202862306a36Sopenharmony_ci case ATM_VCI_ANY: 202962306a36Sopenharmony_ci for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci; 203062306a36Sopenharmony_ci (*vcip)++) 203162306a36Sopenharmony_ci if (vci_is_ok(lanai, *vcip, atmvcc)) 203262306a36Sopenharmony_ci return 0; 203362306a36Sopenharmony_ci return -EADDRINUSE; 203462306a36Sopenharmony_ci default: 203562306a36Sopenharmony_ci if (*vcip >= lanai->num_vci || *vcip < 0 || 203662306a36Sopenharmony_ci !vci_is_ok(lanai, *vcip, atmvcc)) 203762306a36Sopenharmony_ci return -EADDRINUSE; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci return 0; 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci/* -------------------- MANAGE CBR: */ 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci/* 204562306a36Sopenharmony_ci * CBR ICG is stored as a fixed-point number with 4 fractional bits. 204662306a36Sopenharmony_ci * Note that storing a number greater than 2046.0 will result in 204762306a36Sopenharmony_ci * incorrect shaping 204862306a36Sopenharmony_ci */ 204962306a36Sopenharmony_ci#define CBRICG_FRAC_BITS (4) 205062306a36Sopenharmony_ci#define CBRICG_MAX (2046 << CBRICG_FRAC_BITS) 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci/* 205362306a36Sopenharmony_ci * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) 205462306a36Sopenharmony_ci * where MAXPCR is (according to the docs) 25600000/(54*8), 205562306a36Sopenharmony_ci * which is equal to (3125<<9)/27. 205662306a36Sopenharmony_ci * 205762306a36Sopenharmony_ci * Solving for ICG, we get: 205862306a36Sopenharmony_ci * ICG = MAXPCR/PCR - 1 205962306a36Sopenharmony_ci * ICG = (3125<<9)/(27*PCR) - 1 206062306a36Sopenharmony_ci * ICG = ((3125<<9) - (27*PCR)) / (27*PCR) 206162306a36Sopenharmony_ci * 206262306a36Sopenharmony_ci * The end result is supposed to be a fixed-point number with FRAC_BITS 206362306a36Sopenharmony_ci * bits of a fractional part, so we keep everything in the numerator 206462306a36Sopenharmony_ci * shifted by that much as we compute 206562306a36Sopenharmony_ci * 206662306a36Sopenharmony_ci */ 206762306a36Sopenharmony_cistatic int pcr_to_cbricg(const struct atm_qos *qos) 206862306a36Sopenharmony_ci{ 206962306a36Sopenharmony_ci int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */ 207062306a36Sopenharmony_ci int x, icg, pcr = atm_pcr_goal(&qos->txtp); 207162306a36Sopenharmony_ci if (pcr == 0) /* Use maximum bandwidth */ 207262306a36Sopenharmony_ci return 0; 207362306a36Sopenharmony_ci if (pcr < 0) { 207462306a36Sopenharmony_ci rounddown = 1; 207562306a36Sopenharmony_ci pcr = -pcr; 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci x = pcr * 27; 207862306a36Sopenharmony_ci icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS); 207962306a36Sopenharmony_ci if (rounddown) 208062306a36Sopenharmony_ci icg += x - 1; 208162306a36Sopenharmony_ci icg /= x; 208262306a36Sopenharmony_ci if (icg > CBRICG_MAX) 208362306a36Sopenharmony_ci icg = CBRICG_MAX; 208462306a36Sopenharmony_ci DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n", 208562306a36Sopenharmony_ci pcr, rounddown ? 'Y' : 'N', icg); 208662306a36Sopenharmony_ci return icg; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistatic inline void lanai_cbr_setup(struct lanai_dev *lanai) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg); 209262306a36Sopenharmony_ci reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg); 209362306a36Sopenharmony_ci lanai->conf2 |= CONFIG2_CBR_ENABLE; 209462306a36Sopenharmony_ci conf2_write(lanai); 209562306a36Sopenharmony_ci} 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_cistatic inline void lanai_cbr_shutdown(struct lanai_dev *lanai) 209862306a36Sopenharmony_ci{ 209962306a36Sopenharmony_ci lanai->conf2 &= ~CONFIG2_CBR_ENABLE; 210062306a36Sopenharmony_ci conf2_write(lanai); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci/* -------------------- OPERATIONS: */ 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci/* setup a newly detected device */ 210662306a36Sopenharmony_cistatic int lanai_dev_open(struct atm_dev *atmdev) 210762306a36Sopenharmony_ci{ 210862306a36Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 210962306a36Sopenharmony_ci unsigned long raw_base; 211062306a36Sopenharmony_ci int result; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci DPRINTK("In lanai_dev_open()\n"); 211362306a36Sopenharmony_ci /* Basic device fields */ 211462306a36Sopenharmony_ci lanai->number = atmdev->number; 211562306a36Sopenharmony_ci lanai->num_vci = NUM_VCI; 211662306a36Sopenharmony_ci bitmap_zero(lanai->backlog_vccs, NUM_VCI); 211762306a36Sopenharmony_ci bitmap_zero(lanai->transmit_ready, NUM_VCI); 211862306a36Sopenharmony_ci lanai->naal0 = 0; 211962306a36Sopenharmony_ci#ifdef USE_POWERDOWN 212062306a36Sopenharmony_ci lanai->nbound = 0; 212162306a36Sopenharmony_ci#endif 212262306a36Sopenharmony_ci lanai->cbrvcc = NULL; 212362306a36Sopenharmony_ci memset(&lanai->stats, 0, sizeof lanai->stats); 212462306a36Sopenharmony_ci spin_lock_init(&lanai->endtxlock); 212562306a36Sopenharmony_ci spin_lock_init(&lanai->servicelock); 212662306a36Sopenharmony_ci atmdev->ci_range.vpi_bits = 0; 212762306a36Sopenharmony_ci atmdev->ci_range.vci_bits = 0; 212862306a36Sopenharmony_ci while (1 << atmdev->ci_range.vci_bits < lanai->num_vci) 212962306a36Sopenharmony_ci atmdev->ci_range.vci_bits++; 213062306a36Sopenharmony_ci atmdev->link_rate = ATM_25_PCR; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci /* 3.2: PCI initialization */ 213362306a36Sopenharmony_ci if ((result = lanai_pci_start(lanai)) != 0) 213462306a36Sopenharmony_ci goto error; 213562306a36Sopenharmony_ci raw_base = lanai->pci->resource[0].start; 213662306a36Sopenharmony_ci lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE); 213762306a36Sopenharmony_ci if (lanai->base == NULL) { 213862306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n"); 213962306a36Sopenharmony_ci result = -ENOMEM; 214062306a36Sopenharmony_ci goto error_pci; 214162306a36Sopenharmony_ci } 214262306a36Sopenharmony_ci /* 3.3: Reset lanai and PHY */ 214362306a36Sopenharmony_ci reset_board(lanai); 214462306a36Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg); 214562306a36Sopenharmony_ci lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN | 214662306a36Sopenharmony_ci CONFIG1_MASK_LEDMODE); 214762306a36Sopenharmony_ci lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL); 214862306a36Sopenharmony_ci reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 214962306a36Sopenharmony_ci udelay(1000); 215062306a36Sopenharmony_ci conf1_write(lanai); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci /* 215362306a36Sopenharmony_ci * 3.4: Turn on endian mode for big-endian hardware 215462306a36Sopenharmony_ci * We don't actually want to do this - the actual bit fields 215562306a36Sopenharmony_ci * in the endian register are not documented anywhere. 215662306a36Sopenharmony_ci * Instead we do the bit-flipping ourselves on big-endian 215762306a36Sopenharmony_ci * hardware. 215862306a36Sopenharmony_ci * 215962306a36Sopenharmony_ci * 3.5: get the board ID/rev by reading the reset register 216062306a36Sopenharmony_ci */ 216162306a36Sopenharmony_ci result = check_board_id_and_rev("register", 216262306a36Sopenharmony_ci reg_read(lanai, Reset_Reg), &lanai->board_rev); 216362306a36Sopenharmony_ci if (result != 0) 216462306a36Sopenharmony_ci goto error_unmap; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci /* 3.6: read EEPROM */ 216762306a36Sopenharmony_ci if ((result = eeprom_read(lanai)) != 0) 216862306a36Sopenharmony_ci goto error_unmap; 216962306a36Sopenharmony_ci if ((result = eeprom_validate(lanai)) != 0) 217062306a36Sopenharmony_ci goto error_unmap; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci /* 3.7: re-reset PHY, do loopback tests, setup PHY */ 217362306a36Sopenharmony_ci reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 217462306a36Sopenharmony_ci udelay(1000); 217562306a36Sopenharmony_ci conf1_write(lanai); 217662306a36Sopenharmony_ci /* TODO - loopback tests */ 217762306a36Sopenharmony_ci lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE); 217862306a36Sopenharmony_ci conf1_write(lanai); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* 3.8/3.9: test and initialize card SRAM */ 218162306a36Sopenharmony_ci if ((result = sram_test_and_clear(lanai)) != 0) 218262306a36Sopenharmony_ci goto error_unmap; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci /* 3.10: initialize lanai registers */ 218562306a36Sopenharmony_ci lanai->conf1 |= CONFIG1_DMA_ENABLE; 218662306a36Sopenharmony_ci conf1_write(lanai); 218762306a36Sopenharmony_ci if ((result = service_buffer_allocate(lanai)) != 0) 218862306a36Sopenharmony_ci goto error_unmap; 218962306a36Sopenharmony_ci if ((result = vcc_table_allocate(lanai)) != 0) 219062306a36Sopenharmony_ci goto error_service; 219162306a36Sopenharmony_ci lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) | 219262306a36Sopenharmony_ci CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE; 219362306a36Sopenharmony_ci conf2_write(lanai); 219462306a36Sopenharmony_ci reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg); 219562306a36Sopenharmony_ci reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */ 219662306a36Sopenharmony_ci if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED, 219762306a36Sopenharmony_ci DEV_LABEL, lanai)) != 0) { 219862306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n"); 219962306a36Sopenharmony_ci goto error_vcctable; 220062306a36Sopenharmony_ci } 220162306a36Sopenharmony_ci mb(); /* Make sure that all that made it */ 220262306a36Sopenharmony_ci intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE)); 220362306a36Sopenharmony_ci /* 3.11: initialize loop mode (i.e. turn looping off) */ 220462306a36Sopenharmony_ci lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) | 220562306a36Sopenharmony_ci CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) | 220662306a36Sopenharmony_ci CONFIG1_GPOUT2 | CONFIG1_GPOUT3; 220762306a36Sopenharmony_ci conf1_write(lanai); 220862306a36Sopenharmony_ci lanai->status = reg_read(lanai, Status_Reg); 220962306a36Sopenharmony_ci /* We're now done initializing this card */ 221062306a36Sopenharmony_ci#ifdef USE_POWERDOWN 221162306a36Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 221262306a36Sopenharmony_ci conf1_write(lanai); 221362306a36Sopenharmony_ci#endif 221462306a36Sopenharmony_ci memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN); 221562306a36Sopenharmony_ci lanai_timed_poll_start(lanai); 221662306a36Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=%p, irq=%u " 221762306a36Sopenharmony_ci "(%pMF)\n", lanai->number, (int) lanai->pci->revision, 221862306a36Sopenharmony_ci lanai->base, lanai->pci->irq, atmdev->esi); 221962306a36Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), " 222062306a36Sopenharmony_ci "board_rev=%d\n", lanai->number, 222162306a36Sopenharmony_ci lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno, 222262306a36Sopenharmony_ci (unsigned int) lanai->serialno, lanai->board_rev); 222362306a36Sopenharmony_ci return 0; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci error_vcctable: 222662306a36Sopenharmony_ci vcc_table_deallocate(lanai); 222762306a36Sopenharmony_ci error_service: 222862306a36Sopenharmony_ci service_buffer_deallocate(lanai); 222962306a36Sopenharmony_ci error_unmap: 223062306a36Sopenharmony_ci reset_board(lanai); 223162306a36Sopenharmony_ci#ifdef USE_POWERDOWN 223262306a36Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN; 223362306a36Sopenharmony_ci conf1_write(lanai); 223462306a36Sopenharmony_ci#endif 223562306a36Sopenharmony_ci iounmap(lanai->base); 223662306a36Sopenharmony_ci lanai->base = NULL; 223762306a36Sopenharmony_ci error_pci: 223862306a36Sopenharmony_ci pci_disable_device(lanai->pci); 223962306a36Sopenharmony_ci error: 224062306a36Sopenharmony_ci return result; 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci/* called when device is being shutdown, and all vcc's are gone - higher 224462306a36Sopenharmony_ci * levels will deallocate the atm device for us 224562306a36Sopenharmony_ci */ 224662306a36Sopenharmony_cistatic void lanai_dev_close(struct atm_dev *atmdev) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 224962306a36Sopenharmony_ci if (lanai->base==NULL) 225062306a36Sopenharmony_ci return; 225162306a36Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n", 225262306a36Sopenharmony_ci lanai->number); 225362306a36Sopenharmony_ci lanai_timed_poll_stop(lanai); 225462306a36Sopenharmony_ci#ifdef USE_POWERDOWN 225562306a36Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN; 225662306a36Sopenharmony_ci conf1_write(lanai); 225762306a36Sopenharmony_ci#endif 225862306a36Sopenharmony_ci intr_disable(lanai, INT_ALL); 225962306a36Sopenharmony_ci free_irq(lanai->pci->irq, lanai); 226062306a36Sopenharmony_ci reset_board(lanai); 226162306a36Sopenharmony_ci#ifdef USE_POWERDOWN 226262306a36Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 226362306a36Sopenharmony_ci conf1_write(lanai); 226462306a36Sopenharmony_ci#endif 226562306a36Sopenharmony_ci pci_disable_device(lanai->pci); 226662306a36Sopenharmony_ci vcc_table_deallocate(lanai); 226762306a36Sopenharmony_ci service_buffer_deallocate(lanai); 226862306a36Sopenharmony_ci iounmap(lanai->base); 226962306a36Sopenharmony_ci kfree(lanai); 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci/* close a vcc */ 227362306a36Sopenharmony_cistatic void lanai_close(struct atm_vcc *atmvcc) 227462306a36Sopenharmony_ci{ 227562306a36Sopenharmony_ci struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 227662306a36Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 227762306a36Sopenharmony_ci if (lvcc == NULL) 227862306a36Sopenharmony_ci return; 227962306a36Sopenharmony_ci clear_bit(ATM_VF_READY, &atmvcc->flags); 228062306a36Sopenharmony_ci clear_bit(ATM_VF_PARTIAL, &atmvcc->flags); 228162306a36Sopenharmony_ci if (lvcc->rx.atmvcc == atmvcc) { 228262306a36Sopenharmony_ci lanai_shutdown_rx_vci(lvcc); 228362306a36Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) { 228462306a36Sopenharmony_ci if (--lanai->naal0 <= 0) 228562306a36Sopenharmony_ci aal0_buffer_free(lanai); 228662306a36Sopenharmony_ci } else 228762306a36Sopenharmony_ci lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci); 228862306a36Sopenharmony_ci lvcc->rx.atmvcc = NULL; 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci if (lvcc->tx.atmvcc == atmvcc) { 229162306a36Sopenharmony_ci if (atmvcc == lanai->cbrvcc) { 229262306a36Sopenharmony_ci if (lvcc->vbase != NULL) 229362306a36Sopenharmony_ci lanai_cbr_shutdown(lanai); 229462306a36Sopenharmony_ci lanai->cbrvcc = NULL; 229562306a36Sopenharmony_ci } 229662306a36Sopenharmony_ci lanai_shutdown_tx_vci(lanai, lvcc); 229762306a36Sopenharmony_ci lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci); 229862306a36Sopenharmony_ci lvcc->tx.atmvcc = NULL; 229962306a36Sopenharmony_ci } 230062306a36Sopenharmony_ci if (--lvcc->nref == 0) { 230162306a36Sopenharmony_ci host_vcc_unbind(lanai, lvcc); 230262306a36Sopenharmony_ci kfree(lvcc); 230362306a36Sopenharmony_ci } 230462306a36Sopenharmony_ci atmvcc->dev_data = NULL; 230562306a36Sopenharmony_ci clear_bit(ATM_VF_ADDR, &atmvcc->flags); 230662306a36Sopenharmony_ci} 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci/* open a vcc on the card to vpi/vci */ 230962306a36Sopenharmony_cistatic int lanai_open(struct atm_vcc *atmvcc) 231062306a36Sopenharmony_ci{ 231162306a36Sopenharmony_ci struct lanai_dev *lanai; 231262306a36Sopenharmony_ci struct lanai_vcc *lvcc; 231362306a36Sopenharmony_ci int result = 0; 231462306a36Sopenharmony_ci int vci = atmvcc->vci; 231562306a36Sopenharmony_ci short vpi = atmvcc->vpi; 231662306a36Sopenharmony_ci /* we don't support partial open - it's not really useful anyway */ 231762306a36Sopenharmony_ci if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) || 231862306a36Sopenharmony_ci (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC)) 231962306a36Sopenharmony_ci return -EINVAL; 232062306a36Sopenharmony_ci lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 232162306a36Sopenharmony_ci result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci); 232262306a36Sopenharmony_ci if (unlikely(result != 0)) 232362306a36Sopenharmony_ci goto out; 232462306a36Sopenharmony_ci set_bit(ATM_VF_ADDR, &atmvcc->flags); 232562306a36Sopenharmony_ci if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5) 232662306a36Sopenharmony_ci return -EINVAL; 232762306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number, 232862306a36Sopenharmony_ci (int) vpi, vci); 232962306a36Sopenharmony_ci lvcc = lanai->vccs[vci]; 233062306a36Sopenharmony_ci if (lvcc == NULL) { 233162306a36Sopenharmony_ci lvcc = new_lanai_vcc(); 233262306a36Sopenharmony_ci if (unlikely(lvcc == NULL)) 233362306a36Sopenharmony_ci return -ENOMEM; 233462306a36Sopenharmony_ci atmvcc->dev_data = lvcc; 233562306a36Sopenharmony_ci } 233662306a36Sopenharmony_ci lvcc->nref++; 233762306a36Sopenharmony_ci if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) { 233862306a36Sopenharmony_ci APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n", 233962306a36Sopenharmony_ci vci); 234062306a36Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) { 234162306a36Sopenharmony_ci if (lanai->naal0 == 0) 234262306a36Sopenharmony_ci result = aal0_buffer_allocate(lanai); 234362306a36Sopenharmony_ci } else 234462306a36Sopenharmony_ci result = lanai_setup_rx_vci_aal5( 234562306a36Sopenharmony_ci lanai, lvcc, &atmvcc->qos); 234662306a36Sopenharmony_ci if (unlikely(result != 0)) 234762306a36Sopenharmony_ci goto out_free; 234862306a36Sopenharmony_ci lvcc->rx.atmvcc = atmvcc; 234962306a36Sopenharmony_ci lvcc->stats.rx_nomem = 0; 235062306a36Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen = 0; 235162306a36Sopenharmony_ci lvcc->stats.x.aal5.service_trash = 0; 235262306a36Sopenharmony_ci lvcc->stats.x.aal5.service_stream = 0; 235362306a36Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc = 0; 235462306a36Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) 235562306a36Sopenharmony_ci lanai->naal0++; 235662306a36Sopenharmony_ci } 235762306a36Sopenharmony_ci if (atmvcc->qos.txtp.traffic_class != ATM_NONE) { 235862306a36Sopenharmony_ci APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n", 235962306a36Sopenharmony_ci vci); 236062306a36Sopenharmony_ci result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos); 236162306a36Sopenharmony_ci if (unlikely(result != 0)) 236262306a36Sopenharmony_ci goto out_free; 236362306a36Sopenharmony_ci lvcc->tx.atmvcc = atmvcc; 236462306a36Sopenharmony_ci if (atmvcc->qos.txtp.traffic_class == ATM_CBR) { 236562306a36Sopenharmony_ci APRINTK(lanai->cbrvcc == NULL, 236662306a36Sopenharmony_ci "cbrvcc!=NULL, vci=%d\n", vci); 236762306a36Sopenharmony_ci lanai->cbrvcc = atmvcc; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci host_vcc_bind(lanai, lvcc, vci); 237162306a36Sopenharmony_ci /* 237262306a36Sopenharmony_ci * Make sure everything made it to RAM before we tell the card about 237362306a36Sopenharmony_ci * the VCC 237462306a36Sopenharmony_ci */ 237562306a36Sopenharmony_ci wmb(); 237662306a36Sopenharmony_ci if (atmvcc == lvcc->rx.atmvcc) 237762306a36Sopenharmony_ci host_vcc_start_rx(lvcc); 237862306a36Sopenharmony_ci if (atmvcc == lvcc->tx.atmvcc) { 237962306a36Sopenharmony_ci host_vcc_start_tx(lvcc); 238062306a36Sopenharmony_ci if (lanai->cbrvcc == atmvcc) 238162306a36Sopenharmony_ci lanai_cbr_setup(lanai); 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci set_bit(ATM_VF_READY, &atmvcc->flags); 238462306a36Sopenharmony_ci return 0; 238562306a36Sopenharmony_ci out_free: 238662306a36Sopenharmony_ci lanai_close(atmvcc); 238762306a36Sopenharmony_ci out: 238862306a36Sopenharmony_ci return result; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cistatic int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 239462306a36Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 239562306a36Sopenharmony_ci unsigned long flags; 239662306a36Sopenharmony_ci if (unlikely(lvcc == NULL || lvcc->vbase == NULL || 239762306a36Sopenharmony_ci lvcc->tx.atmvcc != atmvcc)) 239862306a36Sopenharmony_ci goto einval; 239962306a36Sopenharmony_ci#ifdef DEBUG 240062306a36Sopenharmony_ci if (unlikely(skb == NULL)) { 240162306a36Sopenharmony_ci DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci); 240262306a36Sopenharmony_ci goto einval; 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci if (unlikely(lanai == NULL)) { 240562306a36Sopenharmony_ci DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci); 240662306a36Sopenharmony_ci goto einval; 240762306a36Sopenharmony_ci } 240862306a36Sopenharmony_ci#endif 240962306a36Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc; 241062306a36Sopenharmony_ci switch (atmvcc->qos.aal) { 241162306a36Sopenharmony_ci case ATM_AAL5: 241262306a36Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 241362306a36Sopenharmony_ci vcc_tx_aal5(lanai, lvcc, skb); 241462306a36Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 241562306a36Sopenharmony_ci return 0; 241662306a36Sopenharmony_ci case ATM_AAL0: 241762306a36Sopenharmony_ci if (unlikely(skb->len != ATM_CELL_SIZE-1)) 241862306a36Sopenharmony_ci goto einval; 241962306a36Sopenharmony_ci /* NOTE - this next line is technically invalid - we haven't unshared skb */ 242062306a36Sopenharmony_ci cpu_to_be32s((u32 *) skb->data); 242162306a36Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 242262306a36Sopenharmony_ci vcc_tx_aal0(lanai, lvcc, skb); 242362306a36Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 242462306a36Sopenharmony_ci return 0; 242562306a36Sopenharmony_ci } 242662306a36Sopenharmony_ci DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal, 242762306a36Sopenharmony_ci atmvcc->vci); 242862306a36Sopenharmony_ci einval: 242962306a36Sopenharmony_ci lanai_free_skb(atmvcc, skb); 243062306a36Sopenharmony_ci return -EINVAL; 243162306a36Sopenharmony_ci} 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_cistatic int lanai_change_qos(struct atm_vcc *atmvcc, 243462306a36Sopenharmony_ci /*const*/ struct atm_qos *qos, int flags) 243562306a36Sopenharmony_ci{ 243662306a36Sopenharmony_ci return -EBUSY; /* TODO: need to write this */ 243762306a36Sopenharmony_ci} 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci#ifndef CONFIG_PROC_FS 244062306a36Sopenharmony_ci#define lanai_proc_read NULL 244162306a36Sopenharmony_ci#else 244262306a36Sopenharmony_cistatic int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) 244362306a36Sopenharmony_ci{ 244462306a36Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 244562306a36Sopenharmony_ci loff_t left = *pos; 244662306a36Sopenharmony_ci struct lanai_vcc *lvcc; 244762306a36Sopenharmony_ci if (left-- == 0) 244862306a36Sopenharmony_ci return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, " 244962306a36Sopenharmony_ci "serial=%u, magic=0x%08X, num_vci=%d\n", 245062306a36Sopenharmony_ci atmdev->number, lanai->type==lanai2 ? "2" : "HB", 245162306a36Sopenharmony_ci (unsigned int) lanai->serialno, 245262306a36Sopenharmony_ci (unsigned int) lanai->magicno, lanai->num_vci); 245362306a36Sopenharmony_ci if (left-- == 0) 245462306a36Sopenharmony_ci return sprintf(page, "revision: board=%d, pci_if=%d\n", 245562306a36Sopenharmony_ci lanai->board_rev, (int) lanai->pci->revision); 245662306a36Sopenharmony_ci if (left-- == 0) 245762306a36Sopenharmony_ci return sprintf(page, "EEPROM ESI: %pM\n", 245862306a36Sopenharmony_ci &lanai->eeprom[EEPROM_MAC]); 245962306a36Sopenharmony_ci if (left-- == 0) 246062306a36Sopenharmony_ci return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, " 246162306a36Sopenharmony_ci "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0, 246262306a36Sopenharmony_ci (lanai->status & STATUS_LOCD) ? 1 : 0, 246362306a36Sopenharmony_ci (lanai->status & STATUS_LED) ? 1 : 0, 246462306a36Sopenharmony_ci (lanai->status & STATUS_GPIN) ? 1 : 0); 246562306a36Sopenharmony_ci if (left-- == 0) 246662306a36Sopenharmony_ci return sprintf(page, "global buffer sizes: service=%zu, " 246762306a36Sopenharmony_ci "aal0_rx=%zu\n", lanai_buf_size(&lanai->service), 246862306a36Sopenharmony_ci lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); 246962306a36Sopenharmony_ci if (left-- == 0) { 247062306a36Sopenharmony_ci get_statistics(lanai); 247162306a36Sopenharmony_ci return sprintf(page, "cells in error: overflow=%u, " 247262306a36Sopenharmony_ci "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n", 247362306a36Sopenharmony_ci lanai->stats.ovfl_trash, lanai->stats.vci_trash, 247462306a36Sopenharmony_ci lanai->stats.hec_err, lanai->stats.atm_ovfl); 247562306a36Sopenharmony_ci } 247662306a36Sopenharmony_ci if (left-- == 0) 247762306a36Sopenharmony_ci return sprintf(page, "PCI errors: parity_detect=%u, " 247862306a36Sopenharmony_ci "master_abort=%u, master_target_abort=%u,\n", 247962306a36Sopenharmony_ci lanai->stats.pcierr_parity_detect, 248062306a36Sopenharmony_ci lanai->stats.pcierr_serr_set, 248162306a36Sopenharmony_ci lanai->stats.pcierr_m_target_abort); 248262306a36Sopenharmony_ci if (left-- == 0) 248362306a36Sopenharmony_ci return sprintf(page, " slave_target_abort=%u, " 248462306a36Sopenharmony_ci "master_parity=%u\n", lanai->stats.pcierr_s_target_abort, 248562306a36Sopenharmony_ci lanai->stats.pcierr_master_parity); 248662306a36Sopenharmony_ci if (left-- == 0) 248762306a36Sopenharmony_ci return sprintf(page, " no_tx=%u, " 248862306a36Sopenharmony_ci "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx, 248962306a36Sopenharmony_ci lanai->stats.service_notx, 249062306a36Sopenharmony_ci lanai->stats.service_rxnotaal5); 249162306a36Sopenharmony_ci if (left-- == 0) 249262306a36Sopenharmony_ci return sprintf(page, "resets: dma=%u, card=%u\n", 249362306a36Sopenharmony_ci lanai->stats.dma_reenable, lanai->stats.card_reset); 249462306a36Sopenharmony_ci /* At this point, "left" should be the VCI we're looking for */ 249562306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 249662306a36Sopenharmony_ci for (; ; left++) { 249762306a36Sopenharmony_ci if (left >= NUM_VCI) { 249862306a36Sopenharmony_ci left = 0; 249962306a36Sopenharmony_ci goto out; 250062306a36Sopenharmony_ci } 250162306a36Sopenharmony_ci if ((lvcc = lanai->vccs[left]) != NULL) 250262306a36Sopenharmony_ci break; 250362306a36Sopenharmony_ci (*pos)++; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci /* Note that we re-use "left" here since we're done with it */ 250662306a36Sopenharmony_ci left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u", (vci_t) left, 250762306a36Sopenharmony_ci lvcc->nref, lvcc->stats.rx_nomem); 250862306a36Sopenharmony_ci if (lvcc->rx.atmvcc != NULL) { 250962306a36Sopenharmony_ci left += sprintf(&page[left], ",\n rx_AAL=%d", 251062306a36Sopenharmony_ci lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); 251162306a36Sopenharmony_ci if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) 251262306a36Sopenharmony_ci left += sprintf(&page[left], ", rx_buf_size=%zu, " 251362306a36Sopenharmony_ci "rx_bad_len=%u,\n rx_service_trash=%u, " 251462306a36Sopenharmony_ci "rx_service_stream=%u, rx_bad_crc=%u", 251562306a36Sopenharmony_ci lanai_buf_size(&lvcc->rx.buf), 251662306a36Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen, 251762306a36Sopenharmony_ci lvcc->stats.x.aal5.service_trash, 251862306a36Sopenharmony_ci lvcc->stats.x.aal5.service_stream, 251962306a36Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc); 252062306a36Sopenharmony_ci } 252162306a36Sopenharmony_ci if (lvcc->tx.atmvcc != NULL) 252262306a36Sopenharmony_ci left += sprintf(&page[left], ",\n tx_AAL=%d, " 252362306a36Sopenharmony_ci "tx_buf_size=%zu, tx_qos=%cBR, tx_backlogged=%c", 252462306a36Sopenharmony_ci lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, 252562306a36Sopenharmony_ci lanai_buf_size(&lvcc->tx.buf), 252662306a36Sopenharmony_ci lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', 252762306a36Sopenharmony_ci vcc_is_backlogged(lvcc) ? 'Y' : 'N'); 252862306a36Sopenharmony_ci page[left++] = '\n'; 252962306a36Sopenharmony_ci page[left] = '\0'; 253062306a36Sopenharmony_ci out: 253162306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 253262306a36Sopenharmony_ci return left; 253362306a36Sopenharmony_ci} 253462306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci/* -------------------- HOOKS: */ 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic const struct atmdev_ops ops = { 253962306a36Sopenharmony_ci .dev_close = lanai_dev_close, 254062306a36Sopenharmony_ci .open = lanai_open, 254162306a36Sopenharmony_ci .close = lanai_close, 254262306a36Sopenharmony_ci .send = lanai_send, 254362306a36Sopenharmony_ci .phy_put = NULL, 254462306a36Sopenharmony_ci .phy_get = NULL, 254562306a36Sopenharmony_ci .change_qos = lanai_change_qos, 254662306a36Sopenharmony_ci .proc_read = lanai_proc_read, 254762306a36Sopenharmony_ci .owner = THIS_MODULE 254862306a36Sopenharmony_ci}; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci/* initialize one probed card */ 255162306a36Sopenharmony_cistatic int lanai_init_one(struct pci_dev *pci, 255262306a36Sopenharmony_ci const struct pci_device_id *ident) 255362306a36Sopenharmony_ci{ 255462306a36Sopenharmony_ci struct lanai_dev *lanai; 255562306a36Sopenharmony_ci struct atm_dev *atmdev; 255662306a36Sopenharmony_ci int result; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci lanai = kzalloc(sizeof(*lanai), GFP_KERNEL); 255962306a36Sopenharmony_ci if (lanai == NULL) { 256062306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL 256162306a36Sopenharmony_ci ": couldn't allocate dev_data structure!\n"); 256262306a36Sopenharmony_ci return -ENOMEM; 256362306a36Sopenharmony_ci } 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL); 256662306a36Sopenharmony_ci if (atmdev == NULL) { 256762306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL 256862306a36Sopenharmony_ci ": couldn't register atm device!\n"); 256962306a36Sopenharmony_ci kfree(lanai); 257062306a36Sopenharmony_ci return -EBUSY; 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci atmdev->dev_data = lanai; 257462306a36Sopenharmony_ci lanai->pci = pci; 257562306a36Sopenharmony_ci lanai->type = (enum lanai_type) ident->device; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci result = lanai_dev_open(atmdev); 257862306a36Sopenharmony_ci if (result != 0) { 257962306a36Sopenharmony_ci DPRINTK("lanai_start() failed, err=%d\n", -result); 258062306a36Sopenharmony_ci atm_dev_deregister(atmdev); 258162306a36Sopenharmony_ci kfree(lanai); 258262306a36Sopenharmony_ci } 258362306a36Sopenharmony_ci return result; 258462306a36Sopenharmony_ci} 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_cistatic const struct pci_device_id lanai_pci_tbl[] = { 258762306a36Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) }, 258862306a36Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) }, 258962306a36Sopenharmony_ci { 0, } /* terminal entry */ 259062306a36Sopenharmony_ci}; 259162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, lanai_pci_tbl); 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_cistatic struct pci_driver lanai_driver = { 259462306a36Sopenharmony_ci .name = DEV_LABEL, 259562306a36Sopenharmony_ci .id_table = lanai_pci_tbl, 259662306a36Sopenharmony_ci .probe = lanai_init_one, 259762306a36Sopenharmony_ci}; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_cimodule_pci_driver(lanai_driver); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ciMODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>"); 260262306a36Sopenharmony_ciMODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); 260362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2604