11da177e4SLinus Torvalds /* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com> 21da177e4SLinus Torvalds * 31da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 41da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 51da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 61da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This driver supports ATM cards based on the Efficient "Lanai" 91da177e4SLinus Torvalds * chipset such as the Speedstream 3010 and the ENI-25p. The 101da177e4SLinus Torvalds * Speedstream 3060 is currently not supported since we don't 111da177e4SLinus Torvalds * have the code to drive the on-board Alcatel DSL chipset (yet). 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Thanks to Efficient for supporting this project with hardware, 141da177e4SLinus Torvalds * documentation, and by answering my questions. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Things not working yet: 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * o We don't support the Speedstream 3060 yet - this card has 191da177e4SLinus Torvalds * an on-board DSL modem chip by Alcatel and the driver will 201da177e4SLinus Torvalds * need some extra code added to handle it 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * o Note that due to limitations of the Lanai only one VCC can be 231da177e4SLinus Torvalds * in CBR at once 241da177e4SLinus Torvalds * 251da177e4SLinus Torvalds * o We don't currently parse the EEPROM at all. The code is all 261da177e4SLinus Torvalds * there as per the spec, but it doesn't actually work. I think 271da177e4SLinus Torvalds * there may be some issues with the docs. Anyway, do NOT 281da177e4SLinus Torvalds * enable it yet - bugs in that code may actually damage your 291da177e4SLinus Torvalds * hardware! Because of this you should hardware an ESI before 301da177e4SLinus Torvalds * trying to use this in a LANE or MPOA environment. 311da177e4SLinus Torvalds * 321da177e4SLinus Torvalds * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: 331da177e4SLinus Torvalds * vcc_tx_aal0() needs to send or queue a SKB 341da177e4SLinus Torvalds * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs 351da177e4SLinus Torvalds * vcc_rx_aal0() needs to handle AAL0 interrupts 361da177e4SLinus Torvalds * This isn't too much work - I just wanted to get other things 371da177e4SLinus Torvalds * done first. 381da177e4SLinus Torvalds * 391da177e4SLinus Torvalds * o lanai_change_qos() isn't written yet 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * o There aren't any ioctl's yet -- I'd like to eventually support 4249693280SMitchell Blank Jr * setting loopback and LED modes that way. 431da177e4SLinus Torvalds * 441da177e4SLinus Torvalds * o If the segmentation engine or DMA gets shut down we should restart 451da177e4SLinus Torvalds * card as per section 17.0i. (see lanai_reset) 461da177e4SLinus Torvalds * 471da177e4SLinus Torvalds * o setsockopt(SO_CIRANGE) isn't done (although despite what the 481da177e4SLinus Torvalds * API says it isn't exactly commonly implemented) 491da177e4SLinus Torvalds */ 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* Version history: 521da177e4SLinus Torvalds * v.1.00 -- 26-JUL-2003 -- PCI/DMA updates 531da177e4SLinus Torvalds * v.0.02 -- 11-JAN-2000 -- Endian fixes 541da177e4SLinus Torvalds * v.0.01 -- 30-NOV-1999 -- Initial release 551da177e4SLinus Torvalds */ 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds #include <linux/module.h> 585a0e3ad6STejun Heo #include <linux/slab.h> 591da177e4SLinus Torvalds #include <linux/mm.h> 601da177e4SLinus Torvalds #include <linux/atmdev.h> 611da177e4SLinus Torvalds #include <asm/io.h> 621da177e4SLinus Torvalds #include <asm/byteorder.h> 631da177e4SLinus Torvalds #include <linux/spinlock.h> 641da177e4SLinus Torvalds #include <linux/pci.h> 651da177e4SLinus Torvalds #include <linux/dma-mapping.h> 661da177e4SLinus Torvalds #include <linux/init.h> 671da177e4SLinus Torvalds #include <linux/delay.h> 681da177e4SLinus Torvalds #include <linux/interrupt.h> 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds /* -------------------- TUNABLE PARAMATERS: */ 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds /* 731da177e4SLinus Torvalds * Maximum number of VCIs per card. Setting it lower could theoretically 741da177e4SLinus Torvalds * save some memory, but since we allocate our vcc list with get_free_pages, 751da177e4SLinus Torvalds * it's not really likely for most architectures 761da177e4SLinus Torvalds */ 771da177e4SLinus Torvalds #define NUM_VCI (1024) 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds /* 801da177e4SLinus Torvalds * Enable extra debugging 811da177e4SLinus Torvalds */ 821da177e4SLinus Torvalds #define DEBUG 831da177e4SLinus Torvalds /* 841da177e4SLinus Torvalds * Debug _all_ register operations with card, except the memory test. 851da177e4SLinus Torvalds * Also disables the timed poll to prevent extra chattiness. This 861da177e4SLinus Torvalds * isn't for normal use 871da177e4SLinus Torvalds */ 881da177e4SLinus Torvalds #undef DEBUG_RW 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* 911da177e4SLinus Torvalds * The programming guide specifies a full test of the on-board SRAM 921da177e4SLinus Torvalds * at initialization time. Undefine to remove this 931da177e4SLinus Torvalds */ 941da177e4SLinus Torvalds #define FULL_MEMORY_TEST 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds /* 971da177e4SLinus Torvalds * This is the number of (4 byte) service entries that we will 981da177e4SLinus Torvalds * try to allocate at startup. Note that we will end up with 991da177e4SLinus Torvalds * one PAGE_SIZE's worth regardless of what this is set to 1001da177e4SLinus Torvalds */ 1011da177e4SLinus Torvalds #define SERVICE_ENTRIES (1024) 1021da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds /* 1051da177e4SLinus Torvalds * We normally read the onboard EEPROM in order to discover our MAC 1061da177e4SLinus Torvalds * address. Undefine to _not_ do this 1071da177e4SLinus Torvalds */ 1081da177e4SLinus Torvalds /* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ 1091da177e4SLinus Torvalds /* TODO: make above a module load-time option (also) */ 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /* 1121da177e4SLinus Torvalds * Depth of TX fifo (in 128 byte units; range 2-31) 1131da177e4SLinus Torvalds * Smaller numbers are better for network latency 1141da177e4SLinus Torvalds * Larger numbers are better for PCI latency 1151da177e4SLinus Torvalds * I'm really sure where the best tradeoff is, but the BSD driver uses 1161da177e4SLinus Torvalds * 7 and it seems to work ok. 1171da177e4SLinus Torvalds */ 1181da177e4SLinus Torvalds #define TX_FIFO_DEPTH (7) 1191da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds /* 1221da177e4SLinus Torvalds * How often (in jiffies) we will try to unstick stuck connections - 1231da177e4SLinus Torvalds * shouldn't need to happen much 1241da177e4SLinus Torvalds */ 1251da177e4SLinus Torvalds #define LANAI_POLL_PERIOD (10*HZ) 1261da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds /* 1291da177e4SLinus Torvalds * When allocating an AAL5 receiving buffer, try to make it at least 1301da177e4SLinus Torvalds * large enough to hold this many max_sdu sized PDUs 1311da177e4SLinus Torvalds */ 1321da177e4SLinus Torvalds #define AAL5_RX_MULTIPLIER (3) 1331da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds /* 1361da177e4SLinus Torvalds * Same for transmitting buffer 1371da177e4SLinus Torvalds */ 1381da177e4SLinus Torvalds #define AAL5_TX_MULTIPLIER (3) 1391da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds /* 1421da177e4SLinus Torvalds * When allocating an AAL0 transmiting buffer, how many cells should fit. 1431da177e4SLinus Torvalds * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't 1441da177e4SLinus Torvalds * really critical 1451da177e4SLinus Torvalds */ 1461da177e4SLinus Torvalds #define AAL0_TX_MULTIPLIER (40) 1471da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds /* 1501da177e4SLinus Torvalds * How large should we make the AAL0 receiving buffer. Remember that this 1511da177e4SLinus Torvalds * is shared between all AAL0 VC's 1521da177e4SLinus Torvalds */ 1531da177e4SLinus Torvalds #define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) 1541da177e4SLinus Torvalds /* TODO: make above a module load-time option */ 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds /* 1571da177e4SLinus Torvalds * Should we use Lanai's "powerdown" feature when no vcc's are bound? 1581da177e4SLinus Torvalds */ 1591da177e4SLinus Torvalds /* #define USE_POWERDOWN */ 1601da177e4SLinus Torvalds /* TODO: make above a module load-time option (also) */ 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds /* -------------------- DEBUGGING AIDS: */ 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds #define DEV_LABEL "lanai" 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds #ifdef DEBUG 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds #define DPRINTK(format, args...) \ 1691da177e4SLinus Torvalds printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 1701da177e4SLinus Torvalds #define APRINTK(truth, format, args...) \ 1711da177e4SLinus Torvalds do { \ 1721da177e4SLinus Torvalds if (unlikely(!(truth))) \ 1731da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL ": " format, ##args); \ 1741da177e4SLinus Torvalds } while (0) 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds #else /* !DEBUG */ 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds #define DPRINTK(format, args...) 1791da177e4SLinus Torvalds #define APRINTK(truth, format, args...) 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds #endif /* DEBUG */ 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds #ifdef DEBUG_RW 1841da177e4SLinus Torvalds #define RWDEBUG(format, args...) \ 1851da177e4SLinus Torvalds printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 1861da177e4SLinus Torvalds #else /* !DEBUG_RW */ 1871da177e4SLinus Torvalds #define RWDEBUG(format, args...) 1881da177e4SLinus Torvalds #endif 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds /* -------------------- DATA DEFINITIONS: */ 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds #define LANAI_MAPPING_SIZE (0x40000) 1931da177e4SLinus Torvalds #define LANAI_EEPROM_SIZE (128) 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds typedef int vci_t; 1961da177e4SLinus Torvalds typedef void __iomem *bus_addr_t; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds /* DMA buffer in host memory for TX, RX, or service list. */ 1991da177e4SLinus Torvalds struct lanai_buffer { 2001da177e4SLinus Torvalds u32 *start; /* From get_free_pages */ 2011da177e4SLinus Torvalds u32 *end; /* One past last byte */ 2021da177e4SLinus Torvalds u32 *ptr; /* Pointer to current host location */ 2031da177e4SLinus Torvalds dma_addr_t dmaaddr; 2041da177e4SLinus Torvalds }; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds struct lanai_vcc_stats { 2071da177e4SLinus Torvalds unsigned rx_nomem; 2081da177e4SLinus Torvalds union { 2091da177e4SLinus Torvalds struct { 2101da177e4SLinus Torvalds unsigned rx_badlen; 2111da177e4SLinus Torvalds unsigned service_trash; 2121da177e4SLinus Torvalds unsigned service_stream; 2131da177e4SLinus Torvalds unsigned service_rxcrc; 2141da177e4SLinus Torvalds } aal5; 2151da177e4SLinus Torvalds struct { 2161da177e4SLinus Torvalds } aal0; 2171da177e4SLinus Torvalds } x; 2181da177e4SLinus Torvalds }; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds struct lanai_dev; /* Forward declaration */ 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds /* 2231da177e4SLinus Torvalds * This is the card-specific per-vcc data. Note that unlike some other 2241da177e4SLinus Torvalds * drivers there is NOT a 1-to-1 correspondance between these and 2251da177e4SLinus Torvalds * atm_vcc's - each one of these represents an actual 2-way vcc, but 2261da177e4SLinus Torvalds * an atm_vcc can be 1-way and share with a 1-way vcc in the other 2271da177e4SLinus Torvalds * direction. To make it weirder, there can even be 0-way vccs 2281da177e4SLinus Torvalds * bound to us, waiting to do a change_qos 2291da177e4SLinus Torvalds */ 2301da177e4SLinus Torvalds struct lanai_vcc { 2311da177e4SLinus Torvalds bus_addr_t vbase; /* Base of VCC's registers */ 2321da177e4SLinus Torvalds struct lanai_vcc_stats stats; 2331da177e4SLinus Torvalds int nref; /* # of atm_vcc's who reference us */ 2341da177e4SLinus Torvalds vci_t vci; 2351da177e4SLinus Torvalds struct { 2361da177e4SLinus Torvalds struct lanai_buffer buf; 2371da177e4SLinus Torvalds struct atm_vcc *atmvcc; /* atm_vcc who is receiver */ 2381da177e4SLinus Torvalds } rx; 2391da177e4SLinus Torvalds struct { 2401da177e4SLinus Torvalds struct lanai_buffer buf; 2411da177e4SLinus Torvalds struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ 2421da177e4SLinus Torvalds int endptr; /* last endptr from service entry */ 2431da177e4SLinus Torvalds struct sk_buff_head backlog; 2441da177e4SLinus Torvalds void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int); 2451da177e4SLinus Torvalds } tx; 2461da177e4SLinus Torvalds }; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds enum lanai_type { 2491d0ed384SJiri Slaby lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2, 2501d0ed384SJiri Slaby lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB 2511da177e4SLinus Torvalds }; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds struct lanai_dev_stats { 2541da177e4SLinus Torvalds unsigned ovfl_trash; /* # of cells dropped - buffer overflow */ 2551da177e4SLinus Torvalds unsigned vci_trash; /* # of cells dropped - closed vci */ 2561da177e4SLinus Torvalds unsigned hec_err; /* # of cells dropped - bad HEC */ 2571da177e4SLinus Torvalds unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */ 2581da177e4SLinus Torvalds unsigned pcierr_parity_detect; 2591da177e4SLinus Torvalds unsigned pcierr_serr_set; 2601da177e4SLinus Torvalds unsigned pcierr_master_abort; 2611da177e4SLinus Torvalds unsigned pcierr_m_target_abort; 2621da177e4SLinus Torvalds unsigned pcierr_s_target_abort; 2631da177e4SLinus Torvalds unsigned pcierr_master_parity; 2641da177e4SLinus Torvalds unsigned service_notx; 2651da177e4SLinus Torvalds unsigned service_norx; 2661da177e4SLinus Torvalds unsigned service_rxnotaal5; 2671da177e4SLinus Torvalds unsigned dma_reenable; 2681da177e4SLinus Torvalds unsigned card_reset; 2691da177e4SLinus Torvalds }; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds struct lanai_dev { 2721da177e4SLinus Torvalds bus_addr_t base; 2731da177e4SLinus Torvalds struct lanai_dev_stats stats; 2741da177e4SLinus Torvalds struct lanai_buffer service; 2751da177e4SLinus Torvalds struct lanai_vcc **vccs; 2761da177e4SLinus Torvalds #ifdef USE_POWERDOWN 2771da177e4SLinus Torvalds int nbound; /* number of bound vccs */ 2781da177e4SLinus Torvalds #endif 2791da177e4SLinus Torvalds enum lanai_type type; 2801da177e4SLinus Torvalds vci_t num_vci; /* Currently just NUM_VCI */ 2811da177e4SLinus Torvalds u8 eeprom[LANAI_EEPROM_SIZE]; 2821da177e4SLinus Torvalds u32 serialno, magicno; 2831da177e4SLinus Torvalds struct pci_dev *pci; 2841da177e4SLinus Torvalds DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */ 2851da177e4SLinus Torvalds DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */ 2861da177e4SLinus Torvalds struct timer_list timer; 2871da177e4SLinus Torvalds int naal0; 2881da177e4SLinus Torvalds struct lanai_buffer aal0buf; /* AAL0 RX buffers */ 2891da177e4SLinus Torvalds u32 conf1, conf2; /* CONFIG[12] registers */ 2901da177e4SLinus Torvalds u32 status; /* STATUS register */ 2911da177e4SLinus Torvalds spinlock_t endtxlock; 2921da177e4SLinus Torvalds spinlock_t servicelock; 2931da177e4SLinus Torvalds struct atm_vcc *cbrvcc; 2941da177e4SLinus Torvalds int number; 2951da177e4SLinus Torvalds int board_rev; 2961da177e4SLinus Torvalds /* TODO - look at race conditions with maintence of conf1/conf2 */ 2971da177e4SLinus Torvalds /* TODO - transmit locking: should we use _irq not _irqsave? */ 2981da177e4SLinus Torvalds /* TODO - organize above in some rational fashion (see <asm/cache.h>) */ 2991da177e4SLinus Torvalds }; 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds /* 3021da177e4SLinus Torvalds * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready) 3031da177e4SLinus Torvalds * This function iterates one of these, calling a given function for each 3041da177e4SLinus Torvalds * vci with their bit set 3051da177e4SLinus Torvalds */ 3061da177e4SLinus Torvalds static void vci_bitfield_iterate(struct lanai_dev *lanai, 307c22c28f6SMitchell Blank Jr const unsigned long *lp, 3081da177e4SLinus Torvalds void (*func)(struct lanai_dev *,vci_t vci)) 3091da177e4SLinus Torvalds { 310d287d66eSAkinobu Mita vci_t vci; 311d287d66eSAkinobu Mita 312d287d66eSAkinobu Mita for_each_set_bit(vci, lp, NUM_VCI) 3131da177e4SLinus Torvalds func(lanai, vci); 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds /* -------------------- BUFFER UTILITIES: */ 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds /* 3191da177e4SLinus Torvalds * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - 3201da177e4SLinus Torvalds * usually any page allocation will do. Just to be safe in case 3211da177e4SLinus Torvalds * PAGE_SIZE is insanely tiny, though... 3221da177e4SLinus Torvalds */ 3231da177e4SLinus Torvalds #define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024) 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds /* 3261da177e4SLinus Torvalds * Allocate a buffer in host RAM for service list, RX, or TX 3271da177e4SLinus Torvalds * Returns buf->start==NULL if no memory 3281da177e4SLinus Torvalds * Note that the size will be rounded up 2^n bytes, and 3291da177e4SLinus Torvalds * if we can't allocate that we'll settle for something smaller 3301da177e4SLinus Torvalds * until minbytes 3311da177e4SLinus Torvalds */ 3321da177e4SLinus Torvalds static void lanai_buf_allocate(struct lanai_buffer *buf, 3331da177e4SLinus Torvalds size_t bytes, size_t minbytes, struct pci_dev *pci) 3341da177e4SLinus Torvalds { 3351da177e4SLinus Torvalds int size; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds if (bytes > (128 * 1024)) /* max lanai buffer size */ 3381da177e4SLinus Torvalds bytes = 128 * 1024; 3391da177e4SLinus Torvalds for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2) 3401da177e4SLinus Torvalds ; 3411da177e4SLinus Torvalds if (minbytes < LANAI_PAGE_SIZE) 3421da177e4SLinus Torvalds minbytes = LANAI_PAGE_SIZE; 3431da177e4SLinus Torvalds do { 3441da177e4SLinus Torvalds /* 3451da177e4SLinus Torvalds * Technically we could use non-consistent mappings for 3461da177e4SLinus Torvalds * everything, but the way the lanai uses DMA memory would 3471da177e4SLinus Torvalds * make that a terrific pain. This is much simpler. 3481da177e4SLinus Torvalds */ 3491da177e4SLinus Torvalds buf->start = pci_alloc_consistent(pci, size, &buf->dmaaddr); 3501da177e4SLinus Torvalds if (buf->start != NULL) { /* Success */ 3511da177e4SLinus Torvalds /* Lanai requires 256-byte alignment of DMA bufs */ 3521da177e4SLinus Torvalds APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, 3531da177e4SLinus Torvalds "bad dmaaddr: 0x%lx\n", 3541da177e4SLinus Torvalds (unsigned long) buf->dmaaddr); 3551da177e4SLinus Torvalds buf->ptr = buf->start; 3561da177e4SLinus Torvalds buf->end = (u32 *) 3571da177e4SLinus Torvalds (&((unsigned char *) buf->start)[size]); 3581da177e4SLinus Torvalds memset(buf->start, 0, size); 3591da177e4SLinus Torvalds break; 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds size /= 2; 3621da177e4SLinus Torvalds } while (size >= minbytes); 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds /* size of buffer in bytes */ 3661da177e4SLinus Torvalds static inline size_t lanai_buf_size(const struct lanai_buffer *buf) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds return ((unsigned long) buf->end) - ((unsigned long) buf->start); 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds static void lanai_buf_deallocate(struct lanai_buffer *buf, 3721da177e4SLinus Torvalds struct pci_dev *pci) 3731da177e4SLinus Torvalds { 3741da177e4SLinus Torvalds if (buf->start != NULL) { 3751da177e4SLinus Torvalds pci_free_consistent(pci, lanai_buf_size(buf), 3761da177e4SLinus Torvalds buf->start, buf->dmaaddr); 3771da177e4SLinus Torvalds buf->start = buf->end = buf->ptr = NULL; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds /* size of buffer as "card order" (0=1k .. 7=128k) */ 3821da177e4SLinus Torvalds static int lanai_buf_size_cardorder(const struct lanai_buffer *buf) 3831da177e4SLinus Torvalds { 3841da177e4SLinus Torvalds int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10); 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds /* This can only happen if PAGE_SIZE is gigantic, but just in case */ 3871da177e4SLinus Torvalds if (order > 7) 3881da177e4SLinus Torvalds order = 7; 3891da177e4SLinus Torvalds return order; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds /* -------------------- PORT I/O UTILITIES: */ 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds /* Registers (and their bit-fields) */ 3951da177e4SLinus Torvalds enum lanai_register { 3961da177e4SLinus Torvalds Reset_Reg = 0x00, /* Reset; read for chip type; bits: */ 3971da177e4SLinus Torvalds #define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */ 3981da177e4SLinus Torvalds #define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */ 3991da177e4SLinus Torvalds #define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */ 4001da177e4SLinus Torvalds Endian_Reg = 0x04, /* Endian setting */ 4011da177e4SLinus Torvalds IntStatus_Reg = 0x08, /* Interrupt status */ 4021da177e4SLinus Torvalds IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ 4031da177e4SLinus Torvalds IntAck_Reg = 0x10, /* Interrupt acknowledge */ 4041da177e4SLinus Torvalds IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ 4051da177e4SLinus Torvalds IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ 4061da177e4SLinus Torvalds IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ 4071da177e4SLinus Torvalds IntControlEna_Reg = 0x20, /* Interrupt control enable */ 4081da177e4SLinus Torvalds IntControlDis_Reg = 0x24, /* Interrupt control disable */ 4091da177e4SLinus Torvalds Status_Reg = 0x28, /* Status */ 4101da177e4SLinus Torvalds #define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */ 4111da177e4SLinus Torvalds #define STATUS_WAITING (0x00000002) /* Interrupt being delayed */ 4121da177e4SLinus Torvalds #define STATUS_SOOL (0x00000004) /* SOOL alarm */ 4131da177e4SLinus Torvalds #define STATUS_LOCD (0x00000008) /* LOCD alarm */ 4141da177e4SLinus Torvalds #define STATUS_LED (0x00000010) /* LED (HAPPI) output */ 4151da177e4SLinus Torvalds #define STATUS_GPIN (0x00000020) /* GPIN pin */ 4161da177e4SLinus Torvalds #define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ 4171da177e4SLinus Torvalds Config1_Reg = 0x2C, /* Config word 1; bits: */ 4181da177e4SLinus Torvalds #define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */ 4191da177e4SLinus Torvalds #define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */ 4201da177e4SLinus Torvalds #define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */ 4211da177e4SLinus Torvalds #define READMODE_PLAIN (0) /* Plain memory read */ 4221da177e4SLinus Torvalds #define READMODE_LINE (2) /* Memory read line */ 4231da177e4SLinus Torvalds #define READMODE_MULTIPLE (3) /* Memory read multiple */ 4241da177e4SLinus Torvalds #define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */ 4251da177e4SLinus Torvalds #define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */ 4261da177e4SLinus Torvalds #define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */ 4271da177e4SLinus Torvalds #define LOOPMODE_NORMAL (0) /* Normal - no loop */ 4281da177e4SLinus Torvalds #define LOOPMODE_TIME (1) 4291da177e4SLinus Torvalds #define LOOPMODE_DIAG (2) 4301da177e4SLinus Torvalds #define LOOPMODE_LINE (3) 4311da177e4SLinus Torvalds #define CONFIG1_MASK_LOOPMODE (0x00000180) 4321da177e4SLinus Torvalds #define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */ 4331da177e4SLinus Torvalds #define LEDMODE_NOT_SOOL (0) /* !SOOL */ 4341da177e4SLinus Torvalds #define LEDMODE_OFF (1) /* 0 */ 4351da177e4SLinus Torvalds #define LEDMODE_ON (2) /* 1 */ 4361da177e4SLinus Torvalds #define LEDMODE_NOT_LOCD (3) /* !LOCD */ 4371da177e4SLinus Torvalds #define LEDMORE_GPIN (4) /* GPIN */ 4381da177e4SLinus Torvalds #define LEDMODE_NOT_GPIN (7) /* !GPIN */ 4391da177e4SLinus Torvalds #define CONFIG1_MASK_LEDMODE (0x00000E00) 4401da177e4SLinus Torvalds #define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */ 4411da177e4SLinus Torvalds #define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */ 4421da177e4SLinus Torvalds #define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ 4431da177e4SLinus Torvalds Config2_Reg = 0x30, /* Config word 2; bits: */ 4441da177e4SLinus Torvalds #define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */ 4451da177e4SLinus Torvalds #define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */ 4461da177e4SLinus Torvalds #define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */ 4471da177e4SLinus Torvalds #define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */ 4481da177e4SLinus Torvalds #define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */ 4491da177e4SLinus Torvalds #define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */ 4501da177e4SLinus Torvalds #define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */ 4511da177e4SLinus Torvalds #define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */ 4521da177e4SLinus Torvalds #define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ 4531da177e4SLinus Torvalds Statistics_Reg = 0x34, /* Statistics; bits: */ 4541da177e4SLinus Torvalds #define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */ 4551da177e4SLinus Torvalds #define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */ 4561da177e4SLinus Torvalds #define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */ 4571da177e4SLinus Torvalds #define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ 4581da177e4SLinus Torvalds ServiceStuff_Reg = 0x38, /* Service stuff; bits: */ 4591da177e4SLinus Torvalds #define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */ 4601da177e4SLinus Torvalds #define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ 4611da177e4SLinus Torvalds ServWrite_Reg = 0x3C, /* ServWrite Pointer */ 4621da177e4SLinus Torvalds ServRead_Reg = 0x40, /* ServRead Pointer */ 4631da177e4SLinus Torvalds TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ 4641da177e4SLinus Torvalds Butt_Reg = 0x48, /* Butt register */ 4651da177e4SLinus Torvalds CBR_ICG_Reg = 0x50, 4661da177e4SLinus Torvalds CBR_PTR_Reg = 0x54, 4671da177e4SLinus Torvalds PingCount_Reg = 0x58, /* Ping count */ 4681da177e4SLinus Torvalds DMA_Addr_Reg = 0x5C /* DMA address */ 4691da177e4SLinus Torvalds }; 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds static inline bus_addr_t reg_addr(const struct lanai_dev *lanai, 4721da177e4SLinus Torvalds enum lanai_register reg) 4731da177e4SLinus Torvalds { 4741da177e4SLinus Torvalds return lanai->base + reg; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds static inline u32 reg_read(const struct lanai_dev *lanai, 4781da177e4SLinus Torvalds enum lanai_register reg) 4791da177e4SLinus Torvalds { 4801da177e4SLinus Torvalds u32 t; 4811da177e4SLinus Torvalds t = readl(reg_addr(lanai, reg)); 4821da177e4SLinus Torvalds RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, 4831da177e4SLinus Torvalds (int) reg, t); 4841da177e4SLinus Torvalds return t; 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds static inline void reg_write(const struct lanai_dev *lanai, u32 val, 4881da177e4SLinus Torvalds enum lanai_register reg) 4891da177e4SLinus Torvalds { 4901da177e4SLinus Torvalds RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, 4911da177e4SLinus Torvalds (int) reg, val); 4921da177e4SLinus Torvalds writel(val, reg_addr(lanai, reg)); 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds 4951da177e4SLinus Torvalds static inline void conf1_write(const struct lanai_dev *lanai) 4961da177e4SLinus Torvalds { 4971da177e4SLinus Torvalds reg_write(lanai, lanai->conf1, Config1_Reg); 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds static inline void conf2_write(const struct lanai_dev *lanai) 5011da177e4SLinus Torvalds { 5021da177e4SLinus Torvalds reg_write(lanai, lanai->conf2, Config2_Reg); 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds /* Same as conf2_write(), but defers I/O if we're powered down */ 5061da177e4SLinus Torvalds static inline void conf2_write_if_powerup(const struct lanai_dev *lanai) 5071da177e4SLinus Torvalds { 5081da177e4SLinus Torvalds #ifdef USE_POWERDOWN 5091da177e4SLinus Torvalds if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0)) 5101da177e4SLinus Torvalds return; 5111da177e4SLinus Torvalds #endif /* USE_POWERDOWN */ 5121da177e4SLinus Torvalds conf2_write(lanai); 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds static inline void reset_board(const struct lanai_dev *lanai) 5161da177e4SLinus Torvalds { 5171da177e4SLinus Torvalds DPRINTK("about to reset board\n"); 5181da177e4SLinus Torvalds reg_write(lanai, 0, Reset_Reg); 5191da177e4SLinus Torvalds /* 5201da177e4SLinus Torvalds * If we don't delay a little while here then we can end up 5211da177e4SLinus Torvalds * leaving the card in a VERY weird state and lock up the 5221da177e4SLinus Torvalds * PCI bus. This isn't documented anywhere but I've convinced 5231da177e4SLinus Torvalds * myself after a lot of painful experimentation 5241da177e4SLinus Torvalds */ 5251da177e4SLinus Torvalds udelay(5); 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds /* -------------------- CARD SRAM UTILITIES: */ 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds /* The SRAM is mapped into normal PCI memory space - the only catch is 5311da177e4SLinus Torvalds * that it is only 16-bits wide but must be accessed as 32-bit. The 5321da177e4SLinus Torvalds * 16 high bits will be zero. We don't hide this, since they get 5331da177e4SLinus Torvalds * programmed mostly like discrete registers anyway 5341da177e4SLinus Torvalds */ 5351da177e4SLinus Torvalds #define SRAM_START (0x20000) 5361da177e4SLinus Torvalds #define SRAM_BYTES (0x20000) /* Again, half don't really exist */ 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds static inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset) 5391da177e4SLinus Torvalds { 5401da177e4SLinus Torvalds return lanai->base + SRAM_START + offset; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds static inline u32 sram_read(const struct lanai_dev *lanai, int offset) 5441da177e4SLinus Torvalds { 5451da177e4SLinus Torvalds return readl(sram_addr(lanai, offset)); 5461da177e4SLinus Torvalds } 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds static inline void sram_write(const struct lanai_dev *lanai, 5491da177e4SLinus Torvalds u32 val, int offset) 5501da177e4SLinus Torvalds { 5511da177e4SLinus Torvalds writel(val, sram_addr(lanai, offset)); 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 554de24a193SAdrian Bunk static int __devinit sram_test_word(const struct lanai_dev *lanai, 555de24a193SAdrian Bunk int offset, u32 pattern) 5561da177e4SLinus Torvalds { 5571da177e4SLinus Torvalds u32 readback; 5581da177e4SLinus Torvalds sram_write(lanai, pattern, offset); 5591da177e4SLinus Torvalds readback = sram_read(lanai, offset); 5601da177e4SLinus Torvalds if (likely(readback == pattern)) 5611da177e4SLinus Torvalds return 0; 5621da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL 5631da177e4SLinus Torvalds "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", 5641da177e4SLinus Torvalds lanai->number, offset, 5651da177e4SLinus Torvalds (unsigned int) pattern, (unsigned int) readback); 5661da177e4SLinus Torvalds return -EIO; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds static int __devinit sram_test_pass(const struct lanai_dev *lanai, u32 pattern) 5701da177e4SLinus Torvalds { 5711da177e4SLinus Torvalds int offset, result = 0; 5721da177e4SLinus Torvalds for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) 5731da177e4SLinus Torvalds result = sram_test_word(lanai, offset, pattern); 5741da177e4SLinus Torvalds return result; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds static int __devinit sram_test_and_clear(const struct lanai_dev *lanai) 5781da177e4SLinus Torvalds { 5791da177e4SLinus Torvalds #ifdef FULL_MEMORY_TEST 5801da177e4SLinus Torvalds int result; 5811da177e4SLinus Torvalds DPRINTK("testing SRAM\n"); 5821da177e4SLinus Torvalds if ((result = sram_test_pass(lanai, 0x5555)) != 0) 5831da177e4SLinus Torvalds return result; 5841da177e4SLinus Torvalds if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) 5851da177e4SLinus Torvalds return result; 5861da177e4SLinus Torvalds #endif 5871da177e4SLinus Torvalds DPRINTK("clearing SRAM\n"); 5881da177e4SLinus Torvalds return sram_test_pass(lanai, 0x0000); 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds /* -------------------- CARD-BASED VCC TABLE UTILITIES: */ 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds /* vcc table */ 5941da177e4SLinus Torvalds enum lanai_vcc_offset { 5951da177e4SLinus Torvalds vcc_rxaddr1 = 0x00, /* Location1, plus bits: */ 5961da177e4SLinus Torvalds #define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */ 5971da177e4SLinus Torvalds #define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */ 5981da177e4SLinus Torvalds #define RMMODE_TRASH (0) /* discard */ 5991da177e4SLinus Torvalds #define RMMODE_PRESERVE (1) /* input as AAL0 */ 6001da177e4SLinus Torvalds #define RMMODE_PIPE (2) /* pipe to coscheduler */ 6011da177e4SLinus Torvalds #define RMMODE_PIPEALL (3) /* pipe non-RM too */ 6021da177e4SLinus Torvalds #define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */ 6031da177e4SLinus Torvalds #define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */ 6041da177e4SLinus Torvalds #define RXMODE_TRASH (0) /* discard */ 6051da177e4SLinus Torvalds #define RXMODE_AAL0 (1) /* non-AAL5 mode */ 6061da177e4SLinus Torvalds #define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */ 6071da177e4SLinus Torvalds #define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ 6081da177e4SLinus Torvalds vcc_rxaddr2 = 0x04, /* Location2 */ 6091da177e4SLinus Torvalds vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ 6101da177e4SLinus Torvalds vcc_rxcrc2 = 0x0C, 6111da177e4SLinus Torvalds vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */ 6121da177e4SLinus Torvalds #define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */ 6131da177e4SLinus Torvalds #define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */ 6141da177e4SLinus Torvalds #define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ 6151da177e4SLinus Torvalds vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */ 6161da177e4SLinus Torvalds #define RXBUFSTART_CLP (0x00004000) 6171da177e4SLinus Torvalds #define RXBUFSTART_CI (0x00008000) 6181da177e4SLinus Torvalds vcc_rxreadptr = 0x18, /* RX readptr */ 6191da177e4SLinus Torvalds vcc_txicg = 0x1C, /* TX ICG */ 6201da177e4SLinus Torvalds vcc_txaddr1 = 0x20, /* Location1, plus bits: */ 6211da177e4SLinus Torvalds #define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */ 6221da177e4SLinus Torvalds #define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ 6231da177e4SLinus Torvalds vcc_txaddr2 = 0x24, /* Location2 */ 6241da177e4SLinus Torvalds vcc_txcrc1 = 0x28, /* TX CRC claculation space */ 6251da177e4SLinus Torvalds vcc_txcrc2 = 0x2C, 6261da177e4SLinus Torvalds vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */ 6271da177e4SLinus Torvalds #define TXREADPTR_GET_PTR(x) ((x)&0x01FFF) 6281da177e4SLinus Torvalds #define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ 6291da177e4SLinus Torvalds vcc_txendptr = 0x34, /* TX Endptr, plus bits: */ 6301da177e4SLinus Torvalds #define TXENDPTR_CLP (0x00002000) 6311da177e4SLinus Torvalds #define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */ 6321da177e4SLinus Torvalds #define PDUMODE_AAL0 (0*0x04000) 6331da177e4SLinus Torvalds #define PDUMODE_AAL5 (2*0x04000) 6341da177e4SLinus Torvalds #define PDUMODE_AAL5STREAM (3*0x04000) 6351da177e4SLinus Torvalds vcc_txwriteptr = 0x38, /* TX Writeptr */ 6361da177e4SLinus Torvalds #define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) 6371da177e4SLinus Torvalds vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */ 6381da177e4SLinus Torvalds #define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */ 6391da177e4SLinus Torvalds }; 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds #define CARDVCC_SIZE (0x40) 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds static inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, 6441da177e4SLinus Torvalds vci_t vci) 6451da177e4SLinus Torvalds { 6461da177e4SLinus Torvalds return sram_addr(lanai, vci * CARDVCC_SIZE); 6471da177e4SLinus Torvalds } 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds static inline u32 cardvcc_read(const struct lanai_vcc *lvcc, 6501da177e4SLinus Torvalds enum lanai_vcc_offset offset) 6511da177e4SLinus Torvalds { 6521da177e4SLinus Torvalds u32 val; 6531da177e4SLinus Torvalds APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n"); 6541da177e4SLinus Torvalds val= readl(lvcc->vbase + offset); 6551da177e4SLinus Torvalds RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", 6561da177e4SLinus Torvalds lvcc->vci, (int) offset, val); 6571da177e4SLinus Torvalds return val; 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds static inline void cardvcc_write(const struct lanai_vcc *lvcc, 6611da177e4SLinus Torvalds u32 val, enum lanai_vcc_offset offset) 6621da177e4SLinus Torvalds { 6631da177e4SLinus Torvalds APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n"); 6641da177e4SLinus Torvalds APRINTK((val & ~0xFFFF) == 0, 6651da177e4SLinus Torvalds "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n", 6661da177e4SLinus Torvalds (unsigned int) val, lvcc->vci, (unsigned int) offset); 6671da177e4SLinus Torvalds RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n", 6681da177e4SLinus Torvalds lvcc->vci, (unsigned int) offset, (unsigned int) val); 6691da177e4SLinus Torvalds writel(val, lvcc->vbase + offset); 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds /* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */ 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds /* How many bytes will an AAL5 PDU take to transmit - remember that: 6751da177e4SLinus Torvalds * o we need to add 8 bytes for length, CPI, UU, and CRC 6761da177e4SLinus Torvalds * o we need to round up to 48 bytes for cells 6771da177e4SLinus Torvalds */ 6781da177e4SLinus Torvalds static inline int aal5_size(int size) 6791da177e4SLinus Torvalds { 6801da177e4SLinus Torvalds int cells = (size + 8 + 47) / 48; 6811da177e4SLinus Torvalds return cells * 48; 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds /* How many bytes can we send if we have "space" space, assuming we have 6851da177e4SLinus Torvalds * to send full cells 6861da177e4SLinus Torvalds */ 6871da177e4SLinus Torvalds static inline int aal5_spacefor(int space) 6881da177e4SLinus Torvalds { 6891da177e4SLinus Torvalds int cells = space / 48; 6901da177e4SLinus Torvalds return cells * 48; 6911da177e4SLinus Torvalds } 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds /* -------------------- FREE AN ATM SKB: */ 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) 6961da177e4SLinus Torvalds { 6971da177e4SLinus Torvalds if (atmvcc->pop != NULL) 6981da177e4SLinus Torvalds atmvcc->pop(atmvcc, skb); 6991da177e4SLinus Torvalds else 7001da177e4SLinus Torvalds dev_kfree_skb_any(skb); 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds /* -------------------- TURN VCCS ON AND OFF: */ 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds static void host_vcc_start_rx(const struct lanai_vcc *lvcc) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds u32 addr1; 7081da177e4SLinus Torvalds if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) { 7091da177e4SLinus Torvalds dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr; 7101da177e4SLinus Torvalds cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1); 7111da177e4SLinus Torvalds cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2); 7121da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxwriteptr); 7131da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxbufstart); 7141da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxreadptr); 7151da177e4SLinus Torvalds cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2); 7161da177e4SLinus Torvalds addr1 = ((dmaaddr >> 8) & 0xFF) | 7171da177e4SLinus Torvalds RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))| 7181da177e4SLinus Torvalds RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */ 7191da177e4SLinus Torvalds /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */ 7201da177e4SLinus Torvalds RXADDR1_SET_MODE(RXMODE_AAL5); 7211da177e4SLinus Torvalds } else 7221da177e4SLinus Torvalds addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */ 7231da177e4SLinus Torvalds RXADDR1_OAM_PRESERVE | /* ??? */ 7241da177e4SLinus Torvalds RXADDR1_SET_MODE(RXMODE_AAL0); 7251da177e4SLinus Torvalds /* This one must be last! */ 7261da177e4SLinus Torvalds cardvcc_write(lvcc, addr1, vcc_rxaddr1); 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds static void host_vcc_start_tx(const struct lanai_vcc *lvcc) 7301da177e4SLinus Torvalds { 7311da177e4SLinus Torvalds dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr; 7321da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txicg); 7331da177e4SLinus Torvalds cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1); 7341da177e4SLinus Torvalds cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2); 7351da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txreadptr); 7361da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txendptr); 7371da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txwriteptr); 7381da177e4SLinus Torvalds cardvcc_write(lvcc, 7391da177e4SLinus Torvalds (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ? 7401da177e4SLinus Torvalds TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next); 7411da177e4SLinus Torvalds cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2); 7421da177e4SLinus Torvalds cardvcc_write(lvcc, 7431da177e4SLinus Torvalds ((dmaaddr >> 8) & 0xFF) | 7441da177e4SLinus Torvalds TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)), 7451da177e4SLinus Torvalds vcc_txaddr1); 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds /* Shutdown receiving on card */ 7491da177e4SLinus Torvalds static void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) 7501da177e4SLinus Torvalds { 7511da177e4SLinus Torvalds if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 7521da177e4SLinus Torvalds return; 7531da177e4SLinus Torvalds /* 15.1.1 - set to trashing, wait one cell time (15us) */ 7541da177e4SLinus Torvalds cardvcc_write(lvcc, 7551da177e4SLinus Torvalds RXADDR1_SET_RMMODE(RMMODE_TRASH) | 7561da177e4SLinus Torvalds RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1); 7571da177e4SLinus Torvalds udelay(15); 7581da177e4SLinus Torvalds /* 15.1.2 - clear rest of entries */ 7591da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxaddr2); 7601da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxcrc1); 7611da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxcrc2); 7621da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxwriteptr); 7631da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxbufstart); 7641da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_rxreadptr); 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds /* Shutdown transmitting on card. 7681da177e4SLinus Torvalds * Unfortunately the lanai needs us to wait until all the data 7691da177e4SLinus Torvalds * drains out of the buffer before we can dealloc it, so this 7701da177e4SLinus Torvalds * can take awhile -- up to 370ms for a full 128KB buffer 7711da177e4SLinus Torvalds * assuming everone else is quiet. In theory the time is 7721da177e4SLinus Torvalds * boundless if there's a CBR VCC holding things up. 7731da177e4SLinus Torvalds */ 7741da177e4SLinus Torvalds static void lanai_shutdown_tx_vci(struct lanai_dev *lanai, 7751da177e4SLinus Torvalds struct lanai_vcc *lvcc) 7761da177e4SLinus Torvalds { 7771da177e4SLinus Torvalds struct sk_buff *skb; 7781da177e4SLinus Torvalds unsigned long flags, timeout; 7791da177e4SLinus Torvalds int read, write, lastread = -1; 7801da177e4SLinus Torvalds APRINTK(!in_interrupt(), 7811da177e4SLinus Torvalds "lanai_shutdown_tx_vci called w/o process context!\n"); 7821da177e4SLinus Torvalds if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 7831da177e4SLinus Torvalds return; 7841da177e4SLinus Torvalds /* 15.2.1 - wait for queue to drain */ 7851da177e4SLinus Torvalds while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL) 7861da177e4SLinus Torvalds lanai_free_skb(lvcc->tx.atmvcc, skb); 7871da177e4SLinus Torvalds read_lock_irqsave(&vcc_sklist_lock, flags); 7881da177e4SLinus Torvalds __clear_bit(lvcc->vci, lanai->backlog_vccs); 7891da177e4SLinus Torvalds read_unlock_irqrestore(&vcc_sklist_lock, flags); 7901da177e4SLinus Torvalds /* 7911da177e4SLinus Torvalds * We need to wait for the VCC to drain but don't wait forever. We 7921da177e4SLinus Torvalds * give each 1K of buffer size 1/128th of a second to clear out. 7931da177e4SLinus Torvalds * TODO: maybe disable CBR if we're about to timeout? 7941da177e4SLinus Torvalds */ 7951da177e4SLinus Torvalds timeout = jiffies + 7961da177e4SLinus Torvalds (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7); 7971da177e4SLinus Torvalds write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); 7981da177e4SLinus Torvalds for (;;) { 7991da177e4SLinus Torvalds read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 8001da177e4SLinus Torvalds if (read == write && /* Is TX buffer empty? */ 8011da177e4SLinus Torvalds (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR || 8021da177e4SLinus Torvalds (cardvcc_read(lvcc, vcc_txcbr_next) & 8031da177e4SLinus Torvalds TXCBR_NEXT_BOZO) == 0)) 8041da177e4SLinus Torvalds break; 8051da177e4SLinus Torvalds if (read != lastread) { /* Has there been any progress? */ 8061da177e4SLinus Torvalds lastread = read; 8071da177e4SLinus Torvalds timeout += HZ / 10; 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds if (unlikely(time_after(jiffies, timeout))) { 8101da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on " 8111da177e4SLinus Torvalds "backlog closing vci %d\n", 8121da177e4SLinus Torvalds lvcc->tx.atmvcc->dev->number, lvcc->vci); 8131da177e4SLinus Torvalds DPRINTK("read, write = %d, %d\n", read, write); 8141da177e4SLinus Torvalds break; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds msleep(40); 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds /* 15.2.2 - clear out all tx registers */ 8191da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txreadptr); 8201da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txwriteptr); 8211da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txendptr); 8221da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txcrc1); 8231da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txcrc2); 8241da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txaddr2); 8251da177e4SLinus Torvalds cardvcc_write(lvcc, 0, vcc_txaddr1); 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds 8281da177e4SLinus Torvalds /* -------------------- MANAGING AAL0 RX BUFFER: */ 8291da177e4SLinus Torvalds 8301da177e4SLinus Torvalds static inline int aal0_buffer_allocate(struct lanai_dev *lanai) 8311da177e4SLinus Torvalds { 8321da177e4SLinus Torvalds DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n"); 8331da177e4SLinus Torvalds lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80, 8341da177e4SLinus Torvalds lanai->pci); 8351da177e4SLinus Torvalds return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0; 8361da177e4SLinus Torvalds } 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds static inline void aal0_buffer_free(struct lanai_dev *lanai) 8391da177e4SLinus Torvalds { 8401da177e4SLinus Torvalds DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n"); 8411da177e4SLinus Torvalds lanai_buf_deallocate(&lanai->aal0buf, lanai->pci); 8421da177e4SLinus Torvalds } 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds /* -------------------- EEPROM UTILITIES: */ 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds /* Offsets of data in the EEPROM */ 8471da177e4SLinus Torvalds #define EEPROM_COPYRIGHT (0) 8481da177e4SLinus Torvalds #define EEPROM_COPYRIGHT_LEN (44) 8491da177e4SLinus Torvalds #define EEPROM_CHECKSUM (62) 8501da177e4SLinus Torvalds #define EEPROM_CHECKSUM_REV (63) 8511da177e4SLinus Torvalds #define EEPROM_MAC (64) 8521da177e4SLinus Torvalds #define EEPROM_MAC_REV (70) 8531da177e4SLinus Torvalds #define EEPROM_SERIAL (112) 8541da177e4SLinus Torvalds #define EEPROM_SERIAL_REV (116) 8551da177e4SLinus Torvalds #define EEPROM_MAGIC (120) 8561da177e4SLinus Torvalds #define EEPROM_MAGIC_REV (124) 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds #define EEPROM_MAGIC_VALUE (0x5AB478D2) 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds #ifndef READ_EEPROM 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds /* Stub functions to use if EEPROM reading is disabled */ 8631da177e4SLinus Torvalds static int __devinit eeprom_read(struct lanai_dev *lanai) 8641da177e4SLinus Torvalds { 8651da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n", 8661da177e4SLinus Torvalds lanai->number); 8671da177e4SLinus Torvalds memset(&lanai->eeprom[EEPROM_MAC], 0, 6); 8681da177e4SLinus Torvalds return 0; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds static int __devinit eeprom_validate(struct lanai_dev *lanai) 8721da177e4SLinus Torvalds { 8731da177e4SLinus Torvalds lanai->serialno = 0; 8741da177e4SLinus Torvalds lanai->magicno = EEPROM_MAGIC_VALUE; 8751da177e4SLinus Torvalds return 0; 8761da177e4SLinus Torvalds } 8771da177e4SLinus Torvalds 8781da177e4SLinus Torvalds #else /* READ_EEPROM */ 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds static int __devinit eeprom_read(struct lanai_dev *lanai) 8811da177e4SLinus Torvalds { 8821da177e4SLinus Torvalds int i, address; 8831da177e4SLinus Torvalds u8 data; 8841da177e4SLinus Torvalds u32 tmp; 8851da177e4SLinus Torvalds #define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \ 8861da177e4SLinus Torvalds } while (0) 8871da177e4SLinus Torvalds #define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) 8881da177e4SLinus Torvalds #define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) 8891da177e4SLinus Torvalds #define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) 8901da177e4SLinus Torvalds #define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) 8911da177e4SLinus Torvalds #define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) 8921da177e4SLinus Torvalds #define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) 8931da177e4SLinus Torvalds #define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \ 8941da177e4SLinus Torvalds data_h(); udelay(5); } while (0) 8951da177e4SLinus Torvalds /* start with both clock and data high */ 8961da177e4SLinus Torvalds data_h(); clock_h(); udelay(5); 8971da177e4SLinus Torvalds for (address = 0; address < LANAI_EEPROM_SIZE; address++) { 8981da177e4SLinus Torvalds data = (address << 1) | 1; /* Command=read + address */ 8991da177e4SLinus Torvalds /* send start bit */ 9001da177e4SLinus Torvalds data_l(); udelay(5); 9011da177e4SLinus Torvalds clock_l(); udelay(5); 9021da177e4SLinus Torvalds for (i = 128; i != 0; i >>= 1) { /* write command out */ 9031da177e4SLinus Torvalds tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | 904858671f8SRoel Kluin ((data & i) ? CONFIG1_PROMDATA : 0); 9051da177e4SLinus Torvalds if (lanai->conf1 != tmp) { 9061da177e4SLinus Torvalds set_config1(tmp); 9071da177e4SLinus Torvalds udelay(5); /* Let new data settle */ 9081da177e4SLinus Torvalds } 9091da177e4SLinus Torvalds clock_h(); udelay(5); clock_l(); udelay(5); 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds /* look for ack */ 9121da177e4SLinus Torvalds data_h(); clock_h(); udelay(5); 9131da177e4SLinus Torvalds if (read_pin() != 0) 9141da177e4SLinus Torvalds goto error; /* No ack seen */ 9151da177e4SLinus Torvalds clock_l(); udelay(5); 9161da177e4SLinus Torvalds /* read back result */ 9171da177e4SLinus Torvalds for (data = 0, i = 7; i >= 0; i--) { 9181da177e4SLinus Torvalds data_h(); clock_h(); udelay(5); 9191da177e4SLinus Torvalds data = (data << 1) | !!read_pin(); 9201da177e4SLinus Torvalds clock_l(); udelay(5); 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds /* look again for ack */ 9231da177e4SLinus Torvalds data_h(); clock_h(); udelay(5); 9241da177e4SLinus Torvalds if (read_pin() == 0) 9251da177e4SLinus Torvalds goto error; /* Spurious ack */ 9261da177e4SLinus Torvalds clock_l(); udelay(5); 9271da177e4SLinus Torvalds send_stop(); 9281da177e4SLinus Torvalds lanai->eeprom[address] = data; 9291da177e4SLinus Torvalds DPRINTK("EEPROM 0x%04X %02X\n", 9301da177e4SLinus Torvalds (unsigned int) address, (unsigned int) data); 9311da177e4SLinus Torvalds } 9321da177e4SLinus Torvalds return 0; 9331da177e4SLinus Torvalds error: 9341da177e4SLinus Torvalds clock_l(); udelay(5); /* finish read */ 9351da177e4SLinus Torvalds send_stop(); 9361da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n", 9371da177e4SLinus Torvalds lanai->number, address); 9381da177e4SLinus Torvalds return -EIO; 9391da177e4SLinus Torvalds #undef set_config1 9401da177e4SLinus Torvalds #undef clock_h 9411da177e4SLinus Torvalds #undef clock_l 9421da177e4SLinus Torvalds #undef data_h 9431da177e4SLinus Torvalds #undef data_l 9441da177e4SLinus Torvalds #undef pre_read 9451da177e4SLinus Torvalds #undef read_pin 9461da177e4SLinus Torvalds #undef send_stop 9471da177e4SLinus Torvalds } 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds /* read a big-endian 4-byte value out of eeprom */ 9501da177e4SLinus Torvalds static inline u32 eeprom_be4(const struct lanai_dev *lanai, int address) 9511da177e4SLinus Torvalds { 952c22c28f6SMitchell Blank Jr return be32_to_cpup((const u32 *) &lanai->eeprom[address]); 9531da177e4SLinus Torvalds } 9541da177e4SLinus Torvalds 9551da177e4SLinus Torvalds /* Checksum/validate EEPROM contents */ 9561da177e4SLinus Torvalds static int __devinit eeprom_validate(struct lanai_dev *lanai) 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds int i, s; 9591da177e4SLinus Torvalds u32 v; 9601da177e4SLinus Torvalds const u8 *e = lanai->eeprom; 9611da177e4SLinus Torvalds #ifdef DEBUG 9621da177e4SLinus Torvalds /* First, see if we can get an ASCIIZ string out of the copyright */ 9631da177e4SLinus Torvalds for (i = EEPROM_COPYRIGHT; 9641da177e4SLinus Torvalds i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) 9651da177e4SLinus Torvalds if (e[i] < 0x20 || e[i] > 0x7E) 9661da177e4SLinus Torvalds break; 9671da177e4SLinus Torvalds if ( i != EEPROM_COPYRIGHT && 9681da177e4SLinus Torvalds i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0') 9691da177e4SLinus Torvalds DPRINTK("eeprom: copyright = \"%s\"\n", 9701da177e4SLinus Torvalds (char *) &e[EEPROM_COPYRIGHT]); 9711da177e4SLinus Torvalds else 9721da177e4SLinus Torvalds DPRINTK("eeprom: copyright not found\n"); 9731da177e4SLinus Torvalds #endif 9741da177e4SLinus Torvalds /* Validate checksum */ 9751da177e4SLinus Torvalds for (i = s = 0; i < EEPROM_CHECKSUM; i++) 9761da177e4SLinus Torvalds s += e[i]; 9771da177e4SLinus Torvalds s &= 0xFF; 9781da177e4SLinus Torvalds if (s != e[EEPROM_CHECKSUM]) { 9791da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " 9801da177e4SLinus Torvalds "(wanted 0x%02X, got 0x%02X)\n", lanai->number, 9811da177e4SLinus Torvalds (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]); 9821da177e4SLinus Torvalds return -EIO; 9831da177e4SLinus Torvalds } 9841da177e4SLinus Torvalds s ^= 0xFF; 9851da177e4SLinus Torvalds if (s != e[EEPROM_CHECKSUM_REV]) { 9861da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " 9871da177e4SLinus Torvalds "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number, 9881da177e4SLinus Torvalds (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]); 9891da177e4SLinus Torvalds return -EIO; 9901da177e4SLinus Torvalds } 9911da177e4SLinus Torvalds /* Verify MAC address */ 9921da177e4SLinus Torvalds for (i = 0; i < 6; i++) 9931da177e4SLinus Torvalds if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) { 9941da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL 9951da177e4SLinus Torvalds "(itf %d) : EEPROM MAC addresses don't match " 9961da177e4SLinus Torvalds "(0x%02X, inverse 0x%02X)\n", lanai->number, 9971da177e4SLinus Torvalds (unsigned int) e[EEPROM_MAC + i], 9981da177e4SLinus Torvalds (unsigned int) e[EEPROM_MAC_REV + i]); 9991da177e4SLinus Torvalds return -EIO; 10001da177e4SLinus Torvalds } 10018d9ded23Shartleys DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]); 10021da177e4SLinus Torvalds /* Verify serial number */ 10031da177e4SLinus Torvalds lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL); 10041da177e4SLinus Torvalds v = eeprom_be4(lanai, EEPROM_SERIAL_REV); 10051da177e4SLinus Torvalds if ((lanai->serialno ^ v) != 0xFFFFFFFF) { 10061da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " 10071da177e4SLinus Torvalds "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 10081da177e4SLinus Torvalds (unsigned int) lanai->serialno, (unsigned int) v); 10091da177e4SLinus Torvalds return -EIO; 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno); 10121da177e4SLinus Torvalds /* Verify magic number */ 10131da177e4SLinus Torvalds lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC); 10141da177e4SLinus Torvalds v = eeprom_be4(lanai, EEPROM_MAGIC_REV); 10151da177e4SLinus Torvalds if ((lanai->magicno ^ v) != 0xFFFFFFFF) { 10161da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " 10171da177e4SLinus Torvalds "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 10181da177e4SLinus Torvalds lanai->magicno, v); 10191da177e4SLinus Torvalds return -EIO; 10201da177e4SLinus Torvalds } 10211da177e4SLinus Torvalds DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); 10221da177e4SLinus Torvalds if (lanai->magicno != EEPROM_MAGIC_VALUE) 10231da177e4SLinus Torvalds printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " 10241da177e4SLinus Torvalds "magic not what expected (got 0x%08X, not 0x%08X)\n", 10251da177e4SLinus Torvalds lanai->number, (unsigned int) lanai->magicno, 10261da177e4SLinus Torvalds (unsigned int) EEPROM_MAGIC_VALUE); 10271da177e4SLinus Torvalds return 0; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds #endif /* READ_EEPROM */ 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds static inline const u8 *eeprom_mac(const struct lanai_dev *lanai) 10331da177e4SLinus Torvalds { 10341da177e4SLinus Torvalds return &lanai->eeprom[EEPROM_MAC]; 10351da177e4SLinus Torvalds } 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds /* -------------------- INTERRUPT HANDLING UTILITIES: */ 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds /* Interrupt types */ 10401da177e4SLinus Torvalds #define INT_STATS (0x00000002) /* Statistics counter overflow */ 10411da177e4SLinus Torvalds #define INT_SOOL (0x00000004) /* SOOL changed state */ 10421da177e4SLinus Torvalds #define INT_LOCD (0x00000008) /* LOCD changed state */ 10431da177e4SLinus Torvalds #define INT_LED (0x00000010) /* LED (HAPPI) changed state */ 10441da177e4SLinus Torvalds #define INT_GPIN (0x00000020) /* GPIN changed state */ 10451da177e4SLinus Torvalds #define INT_PING (0x00000040) /* PING_COUNT fulfilled */ 10461da177e4SLinus Torvalds #define INT_WAKE (0x00000080) /* Lanai wants bus */ 10471da177e4SLinus Torvalds #define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */ 10481da177e4SLinus Torvalds #define INT_LOCK (0x00000200) /* Service list overflow */ 10491da177e4SLinus Torvalds #define INT_MISMATCH (0x00000400) /* TX magic list mismatch */ 10501da177e4SLinus Torvalds #define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */ 10511da177e4SLinus Torvalds #define INT_AAL0 (0x00001000) /* Non-AAL5 data available */ 10521da177e4SLinus Torvalds #define INT_SERVICE (0x00002000) /* Service list entries available */ 10531da177e4SLinus Torvalds #define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */ 10541da177e4SLinus Torvalds #define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */ 10551da177e4SLinus Torvalds #define INT_TIMEOUTBM (0x00010000) /* No response to bus master */ 10561da177e4SLinus Torvalds #define INT_PCIPARITY (0x00020000) /* Parity error on PCI */ 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds /* Sets of the above */ 10591da177e4SLinus Torvalds #define INT_ALL (0x0003FFFE) /* All interrupts */ 10601da177e4SLinus Torvalds #define INT_STATUS (0x0000003C) /* Some status pin changed */ 10611da177e4SLinus Torvalds #define INT_DMASHUT (0x00038000) /* DMA engine got shut down */ 10621da177e4SLinus Torvalds #define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */ 10631da177e4SLinus Torvalds 10641da177e4SLinus Torvalds static inline u32 intr_pending(const struct lanai_dev *lanai) 10651da177e4SLinus Torvalds { 10661da177e4SLinus Torvalds return reg_read(lanai, IntStatusMasked_Reg); 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds 10691da177e4SLinus Torvalds static inline void intr_enable(const struct lanai_dev *lanai, u32 i) 10701da177e4SLinus Torvalds { 10711da177e4SLinus Torvalds reg_write(lanai, i, IntControlEna_Reg); 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds static inline void intr_disable(const struct lanai_dev *lanai, u32 i) 10751da177e4SLinus Torvalds { 10761da177e4SLinus Torvalds reg_write(lanai, i, IntControlDis_Reg); 10771da177e4SLinus Torvalds } 10781da177e4SLinus Torvalds 10791da177e4SLinus Torvalds /* -------------------- CARD/PCI STATUS: */ 10801da177e4SLinus Torvalds 10811da177e4SLinus Torvalds static void status_message(int itf, const char *name, int status) 10821da177e4SLinus Torvalds { 10831da177e4SLinus Torvalds static const char *onoff[2] = { "off to on", "on to off" }; 10841da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", 10851da177e4SLinus Torvalds itf, name, onoff[!status]); 10861da177e4SLinus Torvalds } 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds static void lanai_check_status(struct lanai_dev *lanai) 10891da177e4SLinus Torvalds { 10901da177e4SLinus Torvalds u32 new = reg_read(lanai, Status_Reg); 10911da177e4SLinus Torvalds u32 changes = new ^ lanai->status; 10921da177e4SLinus Torvalds lanai->status = new; 10931da177e4SLinus Torvalds #define e(flag, name) \ 10941da177e4SLinus Torvalds if (changes & flag) \ 10951da177e4SLinus Torvalds status_message(lanai->number, name, new & flag) 10961da177e4SLinus Torvalds e(STATUS_SOOL, "SOOL"); 10971da177e4SLinus Torvalds e(STATUS_LOCD, "LOCD"); 10981da177e4SLinus Torvalds e(STATUS_LED, "LED"); 10991da177e4SLinus Torvalds e(STATUS_GPIN, "GPIN"); 11001da177e4SLinus Torvalds #undef e 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds static void pcistatus_got(int itf, const char *name) 11041da177e4SLinus Torvalds { 11051da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name); 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds 11081da177e4SLinus Torvalds static void pcistatus_check(struct lanai_dev *lanai, int clearonly) 11091da177e4SLinus Torvalds { 11101da177e4SLinus Torvalds u16 s; 11111da177e4SLinus Torvalds int result; 11121da177e4SLinus Torvalds result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); 11131da177e4SLinus Torvalds if (result != PCIBIOS_SUCCESSFUL) { 11141da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " 11151da177e4SLinus Torvalds "%d\n", lanai->number, result); 11161da177e4SLinus Torvalds return; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | 11191da177e4SLinus Torvalds PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | 11201da177e4SLinus Torvalds PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; 11211da177e4SLinus Torvalds if (s == 0) 11221da177e4SLinus Torvalds return; 11231da177e4SLinus Torvalds result = pci_write_config_word(lanai->pci, PCI_STATUS, s); 11241da177e4SLinus Torvalds if (result != PCIBIOS_SUCCESSFUL) 11251da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " 11261da177e4SLinus Torvalds "%d\n", lanai->number, result); 11271da177e4SLinus Torvalds if (clearonly) 11281da177e4SLinus Torvalds return; 11291da177e4SLinus Torvalds #define e(flag, name, stat) \ 11301da177e4SLinus Torvalds if (s & flag) { \ 11311da177e4SLinus Torvalds pcistatus_got(lanai->number, name); \ 11321da177e4SLinus Torvalds ++lanai->stats.pcierr_##stat; \ 11331da177e4SLinus Torvalds } 11341da177e4SLinus Torvalds e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); 11351da177e4SLinus Torvalds e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); 11361da177e4SLinus Torvalds e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); 11371da177e4SLinus Torvalds e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); 11381da177e4SLinus Torvalds e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); 11391da177e4SLinus Torvalds e(PCI_STATUS_PARITY, "master parity", master_parity); 11401da177e4SLinus Torvalds #undef e 11411da177e4SLinus Torvalds } 11421da177e4SLinus Torvalds 11431da177e4SLinus Torvalds /* -------------------- VCC TX BUFFER UTILITIES: */ 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds /* space left in tx buffer in bytes */ 11461da177e4SLinus Torvalds static inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr) 11471da177e4SLinus Torvalds { 11481da177e4SLinus Torvalds int r; 11491da177e4SLinus Torvalds r = endptr * 16; 11501da177e4SLinus Torvalds r -= ((unsigned long) lvcc->tx.buf.ptr) - 11511da177e4SLinus Torvalds ((unsigned long) lvcc->tx.buf.start); 11521da177e4SLinus Torvalds r -= 16; /* Leave "bubble" - if start==end it looks empty */ 11531da177e4SLinus Torvalds if (r < 0) 11541da177e4SLinus Torvalds r += lanai_buf_size(&lvcc->tx.buf); 11551da177e4SLinus Torvalds return r; 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds /* test if VCC is currently backlogged */ 1159c22c28f6SMitchell Blank Jr static inline int vcc_is_backlogged(const struct lanai_vcc *lvcc) 11601da177e4SLinus Torvalds { 11611da177e4SLinus Torvalds return !skb_queue_empty(&lvcc->tx.backlog); 11621da177e4SLinus Torvalds } 11631da177e4SLinus Torvalds 11641da177e4SLinus Torvalds /* Bit fields in the segmentation buffer descriptor */ 11651da177e4SLinus Torvalds #define DESCRIPTOR_MAGIC (0xD0000000) 11661da177e4SLinus Torvalds #define DESCRIPTOR_AAL5 (0x00008000) 11671da177e4SLinus Torvalds #define DESCRIPTOR_AAL5_STREAM (0x00004000) 11681da177e4SLinus Torvalds #define DESCRIPTOR_CLP (0x00002000) 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds /* Add 32-bit descriptor with its padding */ 11711da177e4SLinus Torvalds static inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, 11721da177e4SLinus Torvalds u32 flags, int len) 11731da177e4SLinus Torvalds { 11741da177e4SLinus Torvalds int pos; 11751da177e4SLinus Torvalds APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, 11761da177e4SLinus Torvalds "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); 11771da177e4SLinus Torvalds lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ 11781da177e4SLinus Torvalds pos = ((unsigned char *) lvcc->tx.buf.ptr) - 11791da177e4SLinus Torvalds (unsigned char *) lvcc->tx.buf.start; 11801da177e4SLinus Torvalds APRINTK((pos & ~0x0001FFF0) == 0, 11811da177e4SLinus Torvalds "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " 11821da177e4SLinus Torvalds "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 11831da177e4SLinus Torvalds lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 11841da177e4SLinus Torvalds pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); 11851da177e4SLinus Torvalds APRINTK((pos & ~0x0001FFF0) == 0, 11861da177e4SLinus Torvalds "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " 11871da177e4SLinus Torvalds "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 11881da177e4SLinus Torvalds lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 11891da177e4SLinus Torvalds lvcc->tx.buf.ptr[-1] = 11901da177e4SLinus Torvalds cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | 11911da177e4SLinus Torvalds ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? 11921da177e4SLinus Torvalds DESCRIPTOR_CLP : 0) | flags | pos >> 4); 11931da177e4SLinus Torvalds if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 11941da177e4SLinus Torvalds lvcc->tx.buf.ptr = lvcc->tx.buf.start; 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds 11971da177e4SLinus Torvalds /* Add 32-bit AAL5 trailer and leave room for its CRC */ 11981da177e4SLinus Torvalds static inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc, 11991da177e4SLinus Torvalds int len, int cpi, int uu) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, 12021da177e4SLinus Torvalds "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr); 12031da177e4SLinus Torvalds lvcc->tx.buf.ptr += 2; 12041da177e4SLinus Torvalds lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); 12051da177e4SLinus Torvalds if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 12061da177e4SLinus Torvalds lvcc->tx.buf.ptr = lvcc->tx.buf.start; 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds 12091da177e4SLinus Torvalds static inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, 12101da177e4SLinus Torvalds const unsigned char *src, int n) 12111da177e4SLinus Torvalds { 12121da177e4SLinus Torvalds unsigned char *e; 12131da177e4SLinus Torvalds int m; 12141da177e4SLinus Torvalds e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 12151da177e4SLinus Torvalds m = e - (unsigned char *) lvcc->tx.buf.end; 12161da177e4SLinus Torvalds if (m < 0) 12171da177e4SLinus Torvalds m = 0; 12181da177e4SLinus Torvalds memcpy(lvcc->tx.buf.ptr, src, n - m); 12191da177e4SLinus Torvalds if (m != 0) { 12201da177e4SLinus Torvalds memcpy(lvcc->tx.buf.start, src + n - m, m); 12211da177e4SLinus Torvalds e = ((unsigned char *) lvcc->tx.buf.start) + m; 12221da177e4SLinus Torvalds } 12231da177e4SLinus Torvalds lvcc->tx.buf.ptr = (u32 *) e; 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 12261da177e4SLinus Torvalds static inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n) 12271da177e4SLinus Torvalds { 12281da177e4SLinus Torvalds unsigned char *e; 12291da177e4SLinus Torvalds int m; 12301da177e4SLinus Torvalds if (n == 0) 12311da177e4SLinus Torvalds return; 12321da177e4SLinus Torvalds e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 12331da177e4SLinus Torvalds m = e - (unsigned char *) lvcc->tx.buf.end; 12341da177e4SLinus Torvalds if (m < 0) 12351da177e4SLinus Torvalds m = 0; 12361da177e4SLinus Torvalds memset(lvcc->tx.buf.ptr, 0, n - m); 12371da177e4SLinus Torvalds if (m != 0) { 12381da177e4SLinus Torvalds memset(lvcc->tx.buf.start, 0, m); 12391da177e4SLinus Torvalds e = ((unsigned char *) lvcc->tx.buf.start) + m; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds lvcc->tx.buf.ptr = (u32 *) e; 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds /* Update "butt" register to specify new WritePtr */ 12451da177e4SLinus Torvalds static inline void lanai_endtx(struct lanai_dev *lanai, 12461da177e4SLinus Torvalds const struct lanai_vcc *lvcc) 12471da177e4SLinus Torvalds { 12481da177e4SLinus Torvalds int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - 12491da177e4SLinus Torvalds (unsigned char *) lvcc->tx.buf.start; 12501da177e4SLinus Torvalds APRINTK((ptr & ~0x0001FFF0) == 0, 12511da177e4SLinus Torvalds "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", 12521da177e4SLinus Torvalds ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, 12531da177e4SLinus Torvalds lvcc->tx.buf.end); 12541da177e4SLinus Torvalds 12551da177e4SLinus Torvalds /* 12561da177e4SLinus Torvalds * Since the "butt register" is a shared resounce on the card we 12571da177e4SLinus Torvalds * serialize all accesses to it through this spinlock. This is 125825985edcSLucas De Marchi * mostly just paranoia since the register is rarely "busy" anyway 12591da177e4SLinus Torvalds * but is needed for correctness. 12601da177e4SLinus Torvalds */ 12611da177e4SLinus Torvalds spin_lock(&lanai->endtxlock); 12621da177e4SLinus Torvalds /* 12631da177e4SLinus Torvalds * We need to check if the "butt busy" bit is set before 12641da177e4SLinus Torvalds * updating the butt register. In theory this should 12651da177e4SLinus Torvalds * never happen because the ATM card is plenty fast at 12661da177e4SLinus Torvalds * updating the register. Still, we should make sure 12671da177e4SLinus Torvalds */ 12681da177e4SLinus Torvalds for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { 12691da177e4SLinus Torvalds if (unlikely(i > 50)) { 12701da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): butt register " 12711da177e4SLinus Torvalds "always busy!\n", lanai->number); 12721da177e4SLinus Torvalds break; 12731da177e4SLinus Torvalds } 12741da177e4SLinus Torvalds udelay(5); 12751da177e4SLinus Torvalds } 12761da177e4SLinus Torvalds /* 12771da177e4SLinus Torvalds * Before we tall the card to start work we need to be sure 100% of 12781da177e4SLinus Torvalds * the info in the service buffer has been written before we tell 12791da177e4SLinus Torvalds * the card about it 12801da177e4SLinus Torvalds */ 12811da177e4SLinus Torvalds wmb(); 12821da177e4SLinus Torvalds reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg); 12831da177e4SLinus Torvalds spin_unlock(&lanai->endtxlock); 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds /* 12871da177e4SLinus Torvalds * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's 12881da177e4SLinus Torvalds * space available. "pdusize" is the number of bytes the PDU will take 12891da177e4SLinus Torvalds */ 12901da177e4SLinus Torvalds static void lanai_send_one_aal5(struct lanai_dev *lanai, 12911da177e4SLinus Torvalds struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize) 12921da177e4SLinus Torvalds { 12931da177e4SLinus Torvalds int pad; 12941da177e4SLinus Torvalds APRINTK(pdusize == aal5_size(skb->len), 12951da177e4SLinus Torvalds "lanai_send_one_aal5: wrong size packet (%d != %d)\n", 12961da177e4SLinus Torvalds pdusize, aal5_size(skb->len)); 12971da177e4SLinus Torvalds vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize); 12981da177e4SLinus Torvalds pad = pdusize - skb->len - 8; 12991da177e4SLinus Torvalds APRINTK(pad >= 0, "pad is negative (%d)\n", pad); 13001da177e4SLinus Torvalds APRINTK(pad < 48, "pad is too big (%d)\n", pad); 13011da177e4SLinus Torvalds vcc_tx_memcpy(lvcc, skb->data, skb->len); 13021da177e4SLinus Torvalds vcc_tx_memzero(lvcc, pad); 13031da177e4SLinus Torvalds vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0); 13041da177e4SLinus Torvalds lanai_endtx(lanai, lvcc); 13051da177e4SLinus Torvalds lanai_free_skb(lvcc->tx.atmvcc, skb); 13061da177e4SLinus Torvalds atomic_inc(&lvcc->tx.atmvcc->stats->tx); 13071da177e4SLinus Torvalds } 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds /* Try to fill the buffer - don't call unless there is backlog */ 13101da177e4SLinus Torvalds static void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, 13111da177e4SLinus Torvalds struct lanai_vcc *lvcc, int endptr) 13121da177e4SLinus Torvalds { 13131da177e4SLinus Torvalds int n; 13141da177e4SLinus Torvalds struct sk_buff *skb; 13151da177e4SLinus Torvalds int space = vcc_tx_space(lvcc, endptr); 13161da177e4SLinus Torvalds APRINTK(vcc_is_backlogged(lvcc), 13171da177e4SLinus Torvalds "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", 13181da177e4SLinus Torvalds lvcc->vci); 13191da177e4SLinus Torvalds while (space >= 64) { 13201da177e4SLinus Torvalds skb = skb_dequeue(&lvcc->tx.backlog); 13211da177e4SLinus Torvalds if (skb == NULL) 13221da177e4SLinus Torvalds goto no_backlog; 13231da177e4SLinus Torvalds n = aal5_size(skb->len); 13241da177e4SLinus Torvalds if (n + 16 > space) { 13251da177e4SLinus Torvalds /* No room for this packet - put it back on queue */ 13261da177e4SLinus Torvalds skb_queue_head(&lvcc->tx.backlog, skb); 13271da177e4SLinus Torvalds return; 13281da177e4SLinus Torvalds } 13291da177e4SLinus Torvalds lanai_send_one_aal5(lanai, lvcc, skb, n); 13301da177e4SLinus Torvalds space -= n + 16; 13311da177e4SLinus Torvalds } 13321da177e4SLinus Torvalds if (!vcc_is_backlogged(lvcc)) { 13331da177e4SLinus Torvalds no_backlog: 13341da177e4SLinus Torvalds __clear_bit(lvcc->vci, lanai->backlog_vccs); 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds 13381da177e4SLinus Torvalds /* Given an skb that we want to transmit either send it now or queue */ 13391da177e4SLinus Torvalds static void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 13401da177e4SLinus Torvalds struct sk_buff *skb) 13411da177e4SLinus Torvalds { 13421da177e4SLinus Torvalds int space, n; 13431da177e4SLinus Torvalds if (vcc_is_backlogged(lvcc)) /* Already backlogged */ 13441da177e4SLinus Torvalds goto queue_it; 13451da177e4SLinus Torvalds space = vcc_tx_space(lvcc, 13461da177e4SLinus Torvalds TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr))); 13471da177e4SLinus Torvalds n = aal5_size(skb->len); 13481da177e4SLinus Torvalds APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n); 13491da177e4SLinus Torvalds if (space < n + 16) { /* No space for this PDU */ 13501da177e4SLinus Torvalds __set_bit(lvcc->vci, lanai->backlog_vccs); 13511da177e4SLinus Torvalds queue_it: 13521da177e4SLinus Torvalds skb_queue_tail(&lvcc->tx.backlog, skb); 13531da177e4SLinus Torvalds return; 13541da177e4SLinus Torvalds } 13551da177e4SLinus Torvalds lanai_send_one_aal5(lanai, lvcc, skb, n); 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds 13581da177e4SLinus Torvalds static void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, 13591da177e4SLinus Torvalds struct lanai_vcc *lvcc, int endptr) 13601da177e4SLinus Torvalds { 13611da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL 13621da177e4SLinus Torvalds ": vcc_tx_unqueue_aal0: not implemented\n"); 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 13651da177e4SLinus Torvalds static void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 13661da177e4SLinus Torvalds struct sk_buff *skb) 13671da177e4SLinus Torvalds { 13681da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); 13691da177e4SLinus Torvalds /* Remember to increment lvcc->tx.atmvcc->stats->tx */ 13701da177e4SLinus Torvalds lanai_free_skb(lvcc->tx.atmvcc, skb); 13711da177e4SLinus Torvalds } 13721da177e4SLinus Torvalds 13731da177e4SLinus Torvalds /* -------------------- VCC RX BUFFER UTILITIES: */ 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds /* unlike the _tx_ cousins, this doesn't update ptr */ 13761da177e4SLinus Torvalds static inline void vcc_rx_memcpy(unsigned char *dest, 13771da177e4SLinus Torvalds const struct lanai_vcc *lvcc, int n) 13781da177e4SLinus Torvalds { 13791da177e4SLinus Torvalds int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - 13801da177e4SLinus Torvalds ((const unsigned char *) (lvcc->rx.buf.end)); 13811da177e4SLinus Torvalds if (m < 0) 13821da177e4SLinus Torvalds m = 0; 13831da177e4SLinus Torvalds memcpy(dest, lvcc->rx.buf.ptr, n - m); 13841da177e4SLinus Torvalds memcpy(dest + n - m, lvcc->rx.buf.start, m); 13851da177e4SLinus Torvalds /* Make sure that these copies don't get reordered */ 13861da177e4SLinus Torvalds barrier(); 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds /* Receive AAL5 data on a VCC with a particular endptr */ 13901da177e4SLinus Torvalds static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) 13911da177e4SLinus Torvalds { 13921da177e4SLinus Torvalds int size; 13931da177e4SLinus Torvalds struct sk_buff *skb; 1394c22c28f6SMitchell Blank Jr const u32 *x; 1395c22c28f6SMitchell Blank Jr u32 *end = &lvcc->rx.buf.start[endptr * 4]; 13961da177e4SLinus Torvalds int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); 13971da177e4SLinus Torvalds if (n < 0) 13981da177e4SLinus Torvalds n += lanai_buf_size(&lvcc->rx.buf); 13991da177e4SLinus Torvalds APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), 14001da177e4SLinus Torvalds "vcc_rx_aal5: n out of range (%d/%Zu)\n", 14011da177e4SLinus Torvalds n, lanai_buf_size(&lvcc->rx.buf)); 14021da177e4SLinus Torvalds /* Recover the second-to-last word to get true pdu length */ 14031da177e4SLinus Torvalds if ((x = &end[-2]) < lvcc->rx.buf.start) 14041da177e4SLinus Torvalds x = &lvcc->rx.buf.end[-2]; 14051da177e4SLinus Torvalds /* 14061da177e4SLinus Torvalds * Before we actually read from the buffer, make sure the memory 14071da177e4SLinus Torvalds * changes have arrived 14081da177e4SLinus Torvalds */ 14091da177e4SLinus Torvalds rmb(); 14101da177e4SLinus Torvalds size = be32_to_cpup(x) & 0xffff; 14111da177e4SLinus Torvalds if (unlikely(n != aal5_size(size))) { 14121da177e4SLinus Torvalds /* Make sure size matches padding */ 14131da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " 14141da177e4SLinus Torvalds "on vci=%d - size=%d n=%d\n", 14151da177e4SLinus Torvalds lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); 14161da177e4SLinus Torvalds lvcc->stats.x.aal5.rx_badlen++; 14171da177e4SLinus Torvalds goto out; 14181da177e4SLinus Torvalds } 14191da177e4SLinus Torvalds skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); 14201da177e4SLinus Torvalds if (unlikely(skb == NULL)) { 14211da177e4SLinus Torvalds lvcc->stats.rx_nomem++; 14221da177e4SLinus Torvalds goto out; 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds skb_put(skb, size); 14251da177e4SLinus Torvalds vcc_rx_memcpy(skb->data, lvcc, size); 14261da177e4SLinus Torvalds ATM_SKB(skb)->vcc = lvcc->rx.atmvcc; 1427a61bbcf2SPatrick McHardy __net_timestamp(skb); 14281da177e4SLinus Torvalds lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); 14291da177e4SLinus Torvalds atomic_inc(&lvcc->rx.atmvcc->stats->rx); 14301da177e4SLinus Torvalds out: 14311da177e4SLinus Torvalds lvcc->rx.buf.ptr = end; 14321da177e4SLinus Torvalds cardvcc_write(lvcc, endptr, vcc_rxreadptr); 14331da177e4SLinus Torvalds } 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds static void vcc_rx_aal0(struct lanai_dev *lanai) 14361da177e4SLinus Torvalds { 14371da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); 14381da177e4SLinus Torvalds /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */ 14391da177e4SLinus Torvalds /* Remember to increment lvcc->rx.atmvcc->stats->rx */ 14401da177e4SLinus Torvalds } 14411da177e4SLinus Torvalds 14421da177e4SLinus Torvalds /* -------------------- MANAGING HOST-BASED VCC TABLE: */ 14431da177e4SLinus Torvalds 14441da177e4SLinus Torvalds /* Decide whether to use vmalloc or get_zeroed_page for VCC table */ 14451da177e4SLinus Torvalds #if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE 14461da177e4SLinus Torvalds #define VCCTABLE_GETFREEPAGE 14471da177e4SLinus Torvalds #else 14481da177e4SLinus Torvalds #include <linux/vmalloc.h> 14491da177e4SLinus Torvalds #endif 14501da177e4SLinus Torvalds 14511da177e4SLinus Torvalds static int __devinit vcc_table_allocate(struct lanai_dev *lanai) 14521da177e4SLinus Torvalds { 14531da177e4SLinus Torvalds #ifdef VCCTABLE_GETFREEPAGE 14541da177e4SLinus Torvalds APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, 14551da177e4SLinus Torvalds "vcc table > PAGE_SIZE!"); 14561da177e4SLinus Torvalds lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL); 14571da177e4SLinus Torvalds return (lanai->vccs == NULL) ? -ENOMEM : 0; 14581da177e4SLinus Torvalds #else 14591da177e4SLinus Torvalds int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); 14603a816054SJoe Perches lanai->vccs = vzalloc(bytes); 14611da177e4SLinus Torvalds if (unlikely(lanai->vccs == NULL)) 14621da177e4SLinus Torvalds return -ENOMEM; 14631da177e4SLinus Torvalds return 0; 14641da177e4SLinus Torvalds #endif 14651da177e4SLinus Torvalds } 14661da177e4SLinus Torvalds 14671da177e4SLinus Torvalds static inline void vcc_table_deallocate(const struct lanai_dev *lanai) 14681da177e4SLinus Torvalds { 14691da177e4SLinus Torvalds #ifdef VCCTABLE_GETFREEPAGE 14701da177e4SLinus Torvalds free_page((unsigned long) lanai->vccs); 14711da177e4SLinus Torvalds #else 14721da177e4SLinus Torvalds vfree(lanai->vccs); 14731da177e4SLinus Torvalds #endif 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds /* Allocate a fresh lanai_vcc, with the appropriate things cleared */ 14771da177e4SLinus Torvalds static inline struct lanai_vcc *new_lanai_vcc(void) 14781da177e4SLinus Torvalds { 14791da177e4SLinus Torvalds struct lanai_vcc *lvcc; 14800c1cca1dSOm Narasimhan lvcc = kzalloc(sizeof(*lvcc), GFP_KERNEL); 14811da177e4SLinus Torvalds if (likely(lvcc != NULL)) { 14821da177e4SLinus Torvalds skb_queue_head_init(&lvcc->tx.backlog); 14831da177e4SLinus Torvalds #ifdef DEBUG 14841da177e4SLinus Torvalds lvcc->vci = -1; 14851da177e4SLinus Torvalds #endif 14861da177e4SLinus Torvalds } 14871da177e4SLinus Torvalds return lvcc; 14881da177e4SLinus Torvalds } 14891da177e4SLinus Torvalds 14901da177e4SLinus Torvalds static int lanai_get_sized_buffer(struct lanai_dev *lanai, 14911da177e4SLinus Torvalds struct lanai_buffer *buf, int max_sdu, int multiplier, 14921da177e4SLinus Torvalds const char *name) 14931da177e4SLinus Torvalds { 14941da177e4SLinus Torvalds int size; 14951da177e4SLinus Torvalds if (unlikely(max_sdu < 1)) 14961da177e4SLinus Torvalds max_sdu = 1; 14971da177e4SLinus Torvalds max_sdu = aal5_size(max_sdu); 14981da177e4SLinus Torvalds size = (max_sdu + 16) * multiplier + 16; 14991da177e4SLinus Torvalds lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci); 15001da177e4SLinus Torvalds if (unlikely(buf->start == NULL)) 15011da177e4SLinus Torvalds return -ENOMEM; 15021da177e4SLinus Torvalds if (unlikely(lanai_buf_size(buf) < size)) 15031da177e4SLinus Torvalds printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " 15041da177e4SLinus Torvalds "for %s buffer, got only %Zu\n", lanai->number, size, 15051da177e4SLinus Torvalds name, lanai_buf_size(buf)); 15061da177e4SLinus Torvalds DPRINTK("Allocated %Zu byte %s buffer\n", lanai_buf_size(buf), name); 15071da177e4SLinus Torvalds return 0; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds 15101da177e4SLinus Torvalds /* Setup a RX buffer for a currently unbound AAL5 vci */ 15111da177e4SLinus Torvalds static inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai, 15121da177e4SLinus Torvalds struct lanai_vcc *lvcc, const struct atm_qos *qos) 15131da177e4SLinus Torvalds { 15141da177e4SLinus Torvalds return lanai_get_sized_buffer(lanai, &lvcc->rx.buf, 15151da177e4SLinus Torvalds qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX"); 15161da177e4SLinus Torvalds } 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds /* Setup a TX buffer for a currently unbound AAL5 vci */ 15191da177e4SLinus Torvalds static int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 15201da177e4SLinus Torvalds const struct atm_qos *qos) 15211da177e4SLinus Torvalds { 15221da177e4SLinus Torvalds int max_sdu, multiplier; 15231da177e4SLinus Torvalds if (qos->aal == ATM_AAL0) { 15241da177e4SLinus Torvalds lvcc->tx.unqueue = vcc_tx_unqueue_aal0; 15251da177e4SLinus Torvalds max_sdu = ATM_CELL_SIZE - 1; 15261da177e4SLinus Torvalds multiplier = AAL0_TX_MULTIPLIER; 15271da177e4SLinus Torvalds } else { 15281da177e4SLinus Torvalds lvcc->tx.unqueue = vcc_tx_unqueue_aal5; 15291da177e4SLinus Torvalds max_sdu = qos->txtp.max_sdu; 15301da177e4SLinus Torvalds multiplier = AAL5_TX_MULTIPLIER; 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu, 15331da177e4SLinus Torvalds multiplier, "TX"); 15341da177e4SLinus Torvalds } 15351da177e4SLinus Torvalds 15361da177e4SLinus Torvalds static inline void host_vcc_bind(struct lanai_dev *lanai, 15371da177e4SLinus Torvalds struct lanai_vcc *lvcc, vci_t vci) 15381da177e4SLinus Torvalds { 15391da177e4SLinus Torvalds if (lvcc->vbase != NULL) 15401da177e4SLinus Torvalds return; /* We already were bound in the other direction */ 15411da177e4SLinus Torvalds DPRINTK("Binding vci %d\n", vci); 15421da177e4SLinus Torvalds #ifdef USE_POWERDOWN 15431da177e4SLinus Torvalds if (lanai->nbound++ == 0) { 15441da177e4SLinus Torvalds DPRINTK("Coming out of powerdown\n"); 15451da177e4SLinus Torvalds lanai->conf1 &= ~CONFIG1_POWERDOWN; 15461da177e4SLinus Torvalds conf1_write(lanai); 15471da177e4SLinus Torvalds conf2_write(lanai); 15481da177e4SLinus Torvalds } 15491da177e4SLinus Torvalds #endif 15501da177e4SLinus Torvalds lvcc->vbase = cardvcc_addr(lanai, vci); 15511da177e4SLinus Torvalds lanai->vccs[lvcc->vci = vci] = lvcc; 15521da177e4SLinus Torvalds } 15531da177e4SLinus Torvalds 15541da177e4SLinus Torvalds static inline void host_vcc_unbind(struct lanai_dev *lanai, 15551da177e4SLinus Torvalds struct lanai_vcc *lvcc) 15561da177e4SLinus Torvalds { 15571da177e4SLinus Torvalds if (lvcc->vbase == NULL) 15581da177e4SLinus Torvalds return; /* This vcc was never bound */ 15591da177e4SLinus Torvalds DPRINTK("Unbinding vci %d\n", lvcc->vci); 15601da177e4SLinus Torvalds lvcc->vbase = NULL; 15611da177e4SLinus Torvalds lanai->vccs[lvcc->vci] = NULL; 15621da177e4SLinus Torvalds #ifdef USE_POWERDOWN 15631da177e4SLinus Torvalds if (--lanai->nbound == 0) { 15641da177e4SLinus Torvalds DPRINTK("Going into powerdown\n"); 15651da177e4SLinus Torvalds lanai->conf1 |= CONFIG1_POWERDOWN; 15661da177e4SLinus Torvalds conf1_write(lanai); 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds #endif 15691da177e4SLinus Torvalds } 15701da177e4SLinus Torvalds 15711da177e4SLinus Torvalds /* -------------------- RESET CARD: */ 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds static void lanai_reset(struct lanai_dev *lanai) 15741da177e4SLinus Torvalds { 157512806a24SMasanari Iida printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not " 15761da177e4SLinus Torvalds "implemented\n", lanai->number); 15771da177e4SLinus Torvalds /* TODO */ 15781da177e4SLinus Torvalds /* The following is just a hack until we write the real 15791da177e4SLinus Torvalds * resetter - at least ack whatever interrupt sent us 15801da177e4SLinus Torvalds * here 15811da177e4SLinus Torvalds */ 15821da177e4SLinus Torvalds reg_write(lanai, INT_ALL, IntAck_Reg); 15831da177e4SLinus Torvalds lanai->stats.card_reset++; 15841da177e4SLinus Torvalds } 15851da177e4SLinus Torvalds 15861da177e4SLinus Torvalds /* -------------------- SERVICE LIST UTILITIES: */ 15871da177e4SLinus Torvalds 15881da177e4SLinus Torvalds /* 15891da177e4SLinus Torvalds * Allocate service buffer and tell card about it 15901da177e4SLinus Torvalds */ 15911da177e4SLinus Torvalds static int __devinit service_buffer_allocate(struct lanai_dev *lanai) 15921da177e4SLinus Torvalds { 15931da177e4SLinus Torvalds lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8, 15941da177e4SLinus Torvalds lanai->pci); 15951da177e4SLinus Torvalds if (unlikely(lanai->service.start == NULL)) 15961da177e4SLinus Torvalds return -ENOMEM; 15971da177e4SLinus Torvalds DPRINTK("allocated service buffer at 0x%08lX, size %Zu(%d)\n", 15981da177e4SLinus Torvalds (unsigned long) lanai->service.start, 15991da177e4SLinus Torvalds lanai_buf_size(&lanai->service), 16001da177e4SLinus Torvalds lanai_buf_size_cardorder(&lanai->service)); 16011da177e4SLinus Torvalds /* Clear ServWrite register to be safe */ 16021da177e4SLinus Torvalds reg_write(lanai, 0, ServWrite_Reg); 16031da177e4SLinus Torvalds /* ServiceStuff register contains size and address of buffer */ 16041da177e4SLinus Torvalds reg_write(lanai, 16051da177e4SLinus Torvalds SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | 16061da177e4SLinus Torvalds SSTUFF_SET_ADDR(lanai->service.dmaaddr), 16071da177e4SLinus Torvalds ServiceStuff_Reg); 16081da177e4SLinus Torvalds return 0; 16091da177e4SLinus Torvalds } 16101da177e4SLinus Torvalds 16111da177e4SLinus Torvalds static inline void service_buffer_deallocate(struct lanai_dev *lanai) 16121da177e4SLinus Torvalds { 16131da177e4SLinus Torvalds lanai_buf_deallocate(&lanai->service, lanai->pci); 16141da177e4SLinus Torvalds } 16151da177e4SLinus Torvalds 16161da177e4SLinus Torvalds /* Bitfields in service list */ 16171da177e4SLinus Torvalds #define SERVICE_TX (0x80000000) /* Was from transmission */ 16181da177e4SLinus Torvalds #define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */ 16191da177e4SLinus Torvalds #define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */ 16201da177e4SLinus Torvalds #define SERVICE_CI (0x10000000) /* RXed PDU had CI set */ 16211da177e4SLinus Torvalds #define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */ 16221da177e4SLinus Torvalds #define SERVICE_STREAM (0x04000000) /* RX Stream mode */ 16231da177e4SLinus Torvalds #define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF) 16241da177e4SLinus Torvalds #define SERVICE_GET_END(x) ((x)&0x1FFF) 16251da177e4SLinus Torvalds 16261da177e4SLinus Torvalds /* Handle one thing from the service list - returns true if it marked a 16271da177e4SLinus Torvalds * VCC ready for xmit 16281da177e4SLinus Torvalds */ 16291da177e4SLinus Torvalds static int handle_service(struct lanai_dev *lanai, u32 s) 16301da177e4SLinus Torvalds { 16311da177e4SLinus Torvalds vci_t vci = SERVICE_GET_VCI(s); 16321da177e4SLinus Torvalds struct lanai_vcc *lvcc; 16331da177e4SLinus Torvalds read_lock(&vcc_sklist_lock); 16341da177e4SLinus Torvalds lvcc = lanai->vccs[vci]; 16351da177e4SLinus Torvalds if (unlikely(lvcc == NULL)) { 16361da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16371da177e4SLinus Torvalds DPRINTK("(itf %d) got service entry 0x%X for nonexistent " 16381da177e4SLinus Torvalds "vcc %d\n", lanai->number, (unsigned int) s, vci); 16391da177e4SLinus Torvalds if (s & SERVICE_TX) 16401da177e4SLinus Torvalds lanai->stats.service_notx++; 16411da177e4SLinus Torvalds else 16421da177e4SLinus Torvalds lanai->stats.service_norx++; 16431da177e4SLinus Torvalds return 0; 16441da177e4SLinus Torvalds } 16451da177e4SLinus Torvalds if (s & SERVICE_TX) { /* segmentation interrupt */ 16461da177e4SLinus Torvalds if (unlikely(lvcc->tx.atmvcc == NULL)) { 16471da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16481da177e4SLinus Torvalds DPRINTK("(itf %d) got service entry 0x%X for non-TX " 16491da177e4SLinus Torvalds "vcc %d\n", lanai->number, (unsigned int) s, vci); 16501da177e4SLinus Torvalds lanai->stats.service_notx++; 16511da177e4SLinus Torvalds return 0; 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds __set_bit(vci, lanai->transmit_ready); 16541da177e4SLinus Torvalds lvcc->tx.endptr = SERVICE_GET_END(s); 16551da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16561da177e4SLinus Torvalds return 1; 16571da177e4SLinus Torvalds } 16581da177e4SLinus Torvalds if (unlikely(lvcc->rx.atmvcc == NULL)) { 16591da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16601da177e4SLinus Torvalds DPRINTK("(itf %d) got service entry 0x%X for non-RX " 16611da177e4SLinus Torvalds "vcc %d\n", lanai->number, (unsigned int) s, vci); 16621da177e4SLinus Torvalds lanai->stats.service_norx++; 16631da177e4SLinus Torvalds return 0; 16641da177e4SLinus Torvalds } 16651da177e4SLinus Torvalds if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) { 16661da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16671da177e4SLinus Torvalds DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " 16681da177e4SLinus Torvalds "vcc %d\n", lanai->number, (unsigned int) s, vci); 16691da177e4SLinus Torvalds lanai->stats.service_rxnotaal5++; 16701da177e4SLinus Torvalds atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16711da177e4SLinus Torvalds return 0; 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) { 16741da177e4SLinus Torvalds vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); 16751da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16761da177e4SLinus Torvalds return 0; 16771da177e4SLinus Torvalds } 16781da177e4SLinus Torvalds if (s & SERVICE_TRASH) { 16791da177e4SLinus Torvalds int bytes; 16801da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16811da177e4SLinus Torvalds DPRINTK("got trashed rx pdu on vci %d\n", vci); 16821da177e4SLinus Torvalds atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16831da177e4SLinus Torvalds lvcc->stats.x.aal5.service_trash++; 16841da177e4SLinus Torvalds bytes = (SERVICE_GET_END(s) * 16) - 16851da177e4SLinus Torvalds (((unsigned long) lvcc->rx.buf.ptr) - 16861da177e4SLinus Torvalds ((unsigned long) lvcc->rx.buf.start)) + 47; 16871da177e4SLinus Torvalds if (bytes < 0) 16881da177e4SLinus Torvalds bytes += lanai_buf_size(&lvcc->rx.buf); 16891da177e4SLinus Torvalds lanai->stats.ovfl_trash += (bytes / 48); 16901da177e4SLinus Torvalds return 0; 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds if (s & SERVICE_STREAM) { 16931da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 16941da177e4SLinus Torvalds atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16951da177e4SLinus Torvalds lvcc->stats.x.aal5.service_stream++; 16961da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " 16971da177e4SLinus Torvalds "PDU on VCI %d!\n", lanai->number, vci); 16981da177e4SLinus Torvalds lanai_reset(lanai); 16991da177e4SLinus Torvalds return 0; 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds DPRINTK("got rx crc error on vci %d\n", vci); 17021da177e4SLinus Torvalds atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 17031da177e4SLinus Torvalds lvcc->stats.x.aal5.service_rxcrc++; 17041da177e4SLinus Torvalds lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; 17051da177e4SLinus Torvalds cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); 17061da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 17071da177e4SLinus Torvalds return 0; 17081da177e4SLinus Torvalds } 17091da177e4SLinus Torvalds 17101da177e4SLinus Torvalds /* Try transmitting on all VCIs that we marked ready to serve */ 17111da177e4SLinus Torvalds static void iter_transmit(struct lanai_dev *lanai, vci_t vci) 17121da177e4SLinus Torvalds { 17131da177e4SLinus Torvalds struct lanai_vcc *lvcc = lanai->vccs[vci]; 17141da177e4SLinus Torvalds if (vcc_is_backlogged(lvcc)) 17151da177e4SLinus Torvalds lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr); 17161da177e4SLinus Torvalds } 17171da177e4SLinus Torvalds 17181da177e4SLinus Torvalds /* Run service queue -- called from interrupt context or with 17191da177e4SLinus Torvalds * interrupts otherwise disabled and with the lanai->servicelock 17201da177e4SLinus Torvalds * lock held 17211da177e4SLinus Torvalds */ 17221da177e4SLinus Torvalds static void run_service(struct lanai_dev *lanai) 17231da177e4SLinus Torvalds { 17241da177e4SLinus Torvalds int ntx = 0; 17251da177e4SLinus Torvalds u32 wreg = reg_read(lanai, ServWrite_Reg); 17261da177e4SLinus Torvalds const u32 *end = lanai->service.start + wreg; 17271da177e4SLinus Torvalds while (lanai->service.ptr != end) { 17281da177e4SLinus Torvalds ntx += handle_service(lanai, 17291da177e4SLinus Torvalds le32_to_cpup(lanai->service.ptr++)); 17301da177e4SLinus Torvalds if (lanai->service.ptr >= lanai->service.end) 17311da177e4SLinus Torvalds lanai->service.ptr = lanai->service.start; 17321da177e4SLinus Torvalds } 17331da177e4SLinus Torvalds reg_write(lanai, wreg, ServRead_Reg); 17341da177e4SLinus Torvalds if (ntx != 0) { 17351da177e4SLinus Torvalds read_lock(&vcc_sklist_lock); 17361da177e4SLinus Torvalds vci_bitfield_iterate(lanai, lanai->transmit_ready, 17371da177e4SLinus Torvalds iter_transmit); 17381da177e4SLinus Torvalds bitmap_zero(lanai->transmit_ready, NUM_VCI); 17391da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 17401da177e4SLinus Torvalds } 17411da177e4SLinus Torvalds } 17421da177e4SLinus Torvalds 17431da177e4SLinus Torvalds /* -------------------- GATHER STATISTICS: */ 17441da177e4SLinus Torvalds 17451da177e4SLinus Torvalds static void get_statistics(struct lanai_dev *lanai) 17461da177e4SLinus Torvalds { 17471da177e4SLinus Torvalds u32 statreg = reg_read(lanai, Statistics_Reg); 17481da177e4SLinus Torvalds lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); 17491da177e4SLinus Torvalds lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); 17501da177e4SLinus Torvalds lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); 17511da177e4SLinus Torvalds lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg); 17521da177e4SLinus Torvalds } 17531da177e4SLinus Torvalds 17541da177e4SLinus Torvalds /* -------------------- POLLING TIMER: */ 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds #ifndef DEBUG_RW 17571da177e4SLinus Torvalds /* Try to undequeue 1 backlogged vcc */ 17581da177e4SLinus Torvalds static void iter_dequeue(struct lanai_dev *lanai, vci_t vci) 17591da177e4SLinus Torvalds { 17601da177e4SLinus Torvalds struct lanai_vcc *lvcc = lanai->vccs[vci]; 17611da177e4SLinus Torvalds int endptr; 17621da177e4SLinus Torvalds if (lvcc == NULL || lvcc->tx.atmvcc == NULL || 17631da177e4SLinus Torvalds !vcc_is_backlogged(lvcc)) { 17641da177e4SLinus Torvalds __clear_bit(vci, lanai->backlog_vccs); 17651da177e4SLinus Torvalds return; 17661da177e4SLinus Torvalds } 17671da177e4SLinus Torvalds endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 17681da177e4SLinus Torvalds lvcc->tx.unqueue(lanai, lvcc, endptr); 17691da177e4SLinus Torvalds } 17701da177e4SLinus Torvalds #endif /* !DEBUG_RW */ 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds static void lanai_timed_poll(unsigned long arg) 17731da177e4SLinus Torvalds { 17741da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) arg; 17751da177e4SLinus Torvalds #ifndef DEBUG_RW 17761da177e4SLinus Torvalds unsigned long flags; 17771da177e4SLinus Torvalds #ifdef USE_POWERDOWN 17781da177e4SLinus Torvalds if (lanai->conf1 & CONFIG1_POWERDOWN) 17791da177e4SLinus Torvalds return; 17801da177e4SLinus Torvalds #endif /* USE_POWERDOWN */ 17811da177e4SLinus Torvalds local_irq_save(flags); 17821da177e4SLinus Torvalds /* If we can grab the spinlock, check if any services need to be run */ 17831da177e4SLinus Torvalds if (spin_trylock(&lanai->servicelock)) { 17841da177e4SLinus Torvalds run_service(lanai); 17851da177e4SLinus Torvalds spin_unlock(&lanai->servicelock); 17861da177e4SLinus Torvalds } 17871da177e4SLinus Torvalds /* ...and see if any backlogged VCs can make progress */ 17881da177e4SLinus Torvalds /* unfortunately linux has no read_trylock() currently */ 17891da177e4SLinus Torvalds read_lock(&vcc_sklist_lock); 17901da177e4SLinus Torvalds vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue); 17911da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 17921da177e4SLinus Torvalds local_irq_restore(flags); 17931da177e4SLinus Torvalds 17941da177e4SLinus Torvalds get_statistics(lanai); 17951da177e4SLinus Torvalds #endif /* !DEBUG_RW */ 17961da177e4SLinus Torvalds mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD); 17971da177e4SLinus Torvalds } 17981da177e4SLinus Torvalds 17991da177e4SLinus Torvalds static inline void lanai_timed_poll_start(struct lanai_dev *lanai) 18001da177e4SLinus Torvalds { 18011da177e4SLinus Torvalds init_timer(&lanai->timer); 18021da177e4SLinus Torvalds lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; 18031da177e4SLinus Torvalds lanai->timer.data = (unsigned long) lanai; 18041da177e4SLinus Torvalds lanai->timer.function = lanai_timed_poll; 18051da177e4SLinus Torvalds add_timer(&lanai->timer); 18061da177e4SLinus Torvalds } 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds static inline void lanai_timed_poll_stop(struct lanai_dev *lanai) 18091da177e4SLinus Torvalds { 18101da177e4SLinus Torvalds del_timer_sync(&lanai->timer); 18111da177e4SLinus Torvalds } 18121da177e4SLinus Torvalds 18131da177e4SLinus Torvalds /* -------------------- INTERRUPT SERVICE: */ 18141da177e4SLinus Torvalds 18151da177e4SLinus Torvalds static inline void lanai_int_1(struct lanai_dev *lanai, u32 reason) 18161da177e4SLinus Torvalds { 18171da177e4SLinus Torvalds u32 ack = 0; 18181da177e4SLinus Torvalds if (reason & INT_SERVICE) { 18191da177e4SLinus Torvalds ack = INT_SERVICE; 18201da177e4SLinus Torvalds spin_lock(&lanai->servicelock); 18211da177e4SLinus Torvalds run_service(lanai); 18221da177e4SLinus Torvalds spin_unlock(&lanai->servicelock); 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds if (reason & (INT_AAL0_STR | INT_AAL0)) { 18251da177e4SLinus Torvalds ack |= reason & (INT_AAL0_STR | INT_AAL0); 18261da177e4SLinus Torvalds vcc_rx_aal0(lanai); 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds /* The rest of the interrupts are pretty rare */ 18291da177e4SLinus Torvalds if (ack == reason) 18301da177e4SLinus Torvalds goto done; 18311da177e4SLinus Torvalds if (reason & INT_STATS) { 18321da177e4SLinus Torvalds reason &= ~INT_STATS; /* No need to ack */ 18331da177e4SLinus Torvalds get_statistics(lanai); 18341da177e4SLinus Torvalds } 18351da177e4SLinus Torvalds if (reason & INT_STATUS) { 18361da177e4SLinus Torvalds ack |= reason & INT_STATUS; 18371da177e4SLinus Torvalds lanai_check_status(lanai); 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds if (unlikely(reason & INT_DMASHUT)) { 18401da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA " 18411da177e4SLinus Torvalds "shutdown, reason=0x%08X, address=0x%08X\n", 18421da177e4SLinus Torvalds lanai->number, (unsigned int) (reason & INT_DMASHUT), 18431da177e4SLinus Torvalds (unsigned int) reg_read(lanai, DMA_Addr_Reg)); 18441da177e4SLinus Torvalds if (reason & INT_TABORTBM) { 18451da177e4SLinus Torvalds lanai_reset(lanai); 18461da177e4SLinus Torvalds return; 18471da177e4SLinus Torvalds } 18481da177e4SLinus Torvalds ack |= (reason & INT_DMASHUT); 18491da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n", 18501da177e4SLinus Torvalds lanai->number); 18511da177e4SLinus Torvalds conf1_write(lanai); 18521da177e4SLinus Torvalds lanai->stats.dma_reenable++; 18531da177e4SLinus Torvalds pcistatus_check(lanai, 0); 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds if (unlikely(reason & INT_TABORTSENT)) { 18561da177e4SLinus Torvalds ack |= (reason & INT_TABORTSENT); 18571da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n", 18581da177e4SLinus Torvalds lanai->number); 18591da177e4SLinus Torvalds pcistatus_check(lanai, 0); 18601da177e4SLinus Torvalds } 18611da177e4SLinus Torvalds if (unlikely(reason & INT_SEGSHUT)) { 18621da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 18631da177e4SLinus Torvalds "segmentation shutdown, reason=0x%08X\n", lanai->number, 18641da177e4SLinus Torvalds (unsigned int) (reason & INT_SEGSHUT)); 18651da177e4SLinus Torvalds lanai_reset(lanai); 18661da177e4SLinus Torvalds return; 18671da177e4SLinus Torvalds } 18681da177e4SLinus Torvalds if (unlikely(reason & (INT_PING | INT_WAKE))) { 18691da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 18701da177e4SLinus Torvalds "unexpected interrupt 0x%08X, resetting\n", 18711da177e4SLinus Torvalds lanai->number, 18721da177e4SLinus Torvalds (unsigned int) (reason & (INT_PING | INT_WAKE))); 18731da177e4SLinus Torvalds lanai_reset(lanai); 18741da177e4SLinus Torvalds return; 18751da177e4SLinus Torvalds } 18761da177e4SLinus Torvalds #ifdef DEBUG 18771da177e4SLinus Torvalds if (unlikely(ack != reason)) { 18781da177e4SLinus Torvalds DPRINTK("unacked ints: 0x%08X\n", 18791da177e4SLinus Torvalds (unsigned int) (reason & ~ack)); 18801da177e4SLinus Torvalds ack = reason; 18811da177e4SLinus Torvalds } 18821da177e4SLinus Torvalds #endif 18831da177e4SLinus Torvalds done: 18841da177e4SLinus Torvalds if (ack != 0) 18851da177e4SLinus Torvalds reg_write(lanai, ack, IntAck_Reg); 18861da177e4SLinus Torvalds } 18871da177e4SLinus Torvalds 18887d12e780SDavid Howells static irqreturn_t lanai_int(int irq, void *devid) 18891da177e4SLinus Torvalds { 1890c7bec5abSJeff Garzik struct lanai_dev *lanai = devid; 18911da177e4SLinus Torvalds u32 reason; 18921da177e4SLinus Torvalds 18931da177e4SLinus Torvalds #ifdef USE_POWERDOWN 18941da177e4SLinus Torvalds /* 18951da177e4SLinus Torvalds * If we're powered down we shouldn't be generating any interrupts - 18961da177e4SLinus Torvalds * so assume that this is a shared interrupt line and it's for someone 18971da177e4SLinus Torvalds * else 18981da177e4SLinus Torvalds */ 18991da177e4SLinus Torvalds if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN)) 19001da177e4SLinus Torvalds return IRQ_NONE; 19011da177e4SLinus Torvalds #endif 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds reason = intr_pending(lanai); 19041da177e4SLinus Torvalds if (reason == 0) 19051da177e4SLinus Torvalds return IRQ_NONE; /* Must be for someone else */ 19061da177e4SLinus Torvalds 19071da177e4SLinus Torvalds do { 19081da177e4SLinus Torvalds if (unlikely(reason == 0xFFFFFFFF)) 19091da177e4SLinus Torvalds break; /* Maybe we've been unplugged? */ 19101da177e4SLinus Torvalds lanai_int_1(lanai, reason); 19111da177e4SLinus Torvalds reason = intr_pending(lanai); 19121da177e4SLinus Torvalds } while (reason != 0); 19131da177e4SLinus Torvalds 19141da177e4SLinus Torvalds return IRQ_HANDLED; 19151da177e4SLinus Torvalds } 19161da177e4SLinus Torvalds 19171da177e4SLinus Torvalds /* TODO - it would be nice if we could use the "delayed interrupt" system 19181da177e4SLinus Torvalds * to some advantage 19191da177e4SLinus Torvalds */ 19201da177e4SLinus Torvalds 19211da177e4SLinus Torvalds /* -------------------- CHECK BOARD ID/REV: */ 19221da177e4SLinus Torvalds 19231da177e4SLinus Torvalds /* 19241da177e4SLinus Torvalds * The board id and revision are stored both in the reset register and 19251da177e4SLinus Torvalds * in the PCI configuration space - the documentation says to check 19261da177e4SLinus Torvalds * each of them. If revp!=NULL we store the revision there 19271da177e4SLinus Torvalds */ 19281da177e4SLinus Torvalds static int check_board_id_and_rev(const char *name, u32 val, int *revp) 19291da177e4SLinus Torvalds { 19301da177e4SLinus Torvalds DPRINTK("%s says board_id=%d, board_rev=%d\n", name, 19311da177e4SLinus Torvalds (int) RESET_GET_BOARD_ID(val), 19321da177e4SLinus Torvalds (int) RESET_GET_BOARD_REV(val)); 19331da177e4SLinus Torvalds if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) { 19341da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " 19351da177e4SLinus Torvalds "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val)); 19361da177e4SLinus Torvalds return -ENODEV; 19371da177e4SLinus Torvalds } 19381da177e4SLinus Torvalds if (revp != NULL) 19391da177e4SLinus Torvalds *revp = RESET_GET_BOARD_REV(val); 19401da177e4SLinus Torvalds return 0; 19411da177e4SLinus Torvalds } 19421da177e4SLinus Torvalds 19431da177e4SLinus Torvalds /* -------------------- PCI INITIALIZATION/SHUTDOWN: */ 19441da177e4SLinus Torvalds 19451da177e4SLinus Torvalds static int __devinit lanai_pci_start(struct lanai_dev *lanai) 19461da177e4SLinus Torvalds { 19471da177e4SLinus Torvalds struct pci_dev *pci = lanai->pci; 19481da177e4SLinus Torvalds int result; 19491da177e4SLinus Torvalds 19501da177e4SLinus Torvalds if (pci_enable_device(pci) != 0) { 19511da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): can't enable " 19521da177e4SLinus Torvalds "PCI device", lanai->number); 19531da177e4SLinus Torvalds return -ENXIO; 19541da177e4SLinus Torvalds } 19551da177e4SLinus Torvalds pci_set_master(pci); 1956284901a9SYang Hongyang if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) != 0) { 19571da177e4SLinus Torvalds printk(KERN_WARNING DEV_LABEL 19581da177e4SLinus Torvalds "(itf %d): No suitable DMA available.\n", lanai->number); 19591da177e4SLinus Torvalds return -EBUSY; 19601da177e4SLinus Torvalds } 1961284901a9SYang Hongyang if (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) != 0) { 19621da177e4SLinus Torvalds printk(KERN_WARNING DEV_LABEL 19631da177e4SLinus Torvalds "(itf %d): No suitable DMA available.\n", lanai->number); 19641da177e4SLinus Torvalds return -EBUSY; 19651da177e4SLinus Torvalds } 1966b691347aSSergei Shtylyov result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL); 19671da177e4SLinus Torvalds if (result != 0) 19681da177e4SLinus Torvalds return result; 19691da177e4SLinus Torvalds /* Set latency timer to zero as per lanai docs */ 19701da177e4SLinus Torvalds result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); 19711da177e4SLinus Torvalds if (result != PCIBIOS_SUCCESSFUL) { 19721da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL "(itf %d): can't write " 19731da177e4SLinus Torvalds "PCI_LATENCY_TIMER: %d\n", lanai->number, result); 19741da177e4SLinus Torvalds return -EINVAL; 19751da177e4SLinus Torvalds } 19761da177e4SLinus Torvalds pcistatus_check(lanai, 1); 19771da177e4SLinus Torvalds pcistatus_check(lanai, 0); 19781da177e4SLinus Torvalds return 0; 19791da177e4SLinus Torvalds } 19801da177e4SLinus Torvalds 19811da177e4SLinus Torvalds /* -------------------- VPI/VCI ALLOCATION: */ 19821da177e4SLinus Torvalds 19831da177e4SLinus Torvalds /* 19841da177e4SLinus Torvalds * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll 19851da177e4SLinus Torvalds * get a CBRZERO interrupt), and we can use it only if no one is receiving 19861da177e4SLinus Torvalds * AAL0 traffic (since they will use the same queue) - according to the 19871da177e4SLinus Torvalds * docs we shouldn't even use it for AAL0 traffic 19881da177e4SLinus Torvalds */ 19891da177e4SLinus Torvalds static inline int vci0_is_ok(struct lanai_dev *lanai, 19901da177e4SLinus Torvalds const struct atm_qos *qos) 19911da177e4SLinus Torvalds { 19921da177e4SLinus Torvalds if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) 19931da177e4SLinus Torvalds return 0; 19941da177e4SLinus Torvalds if (qos->rxtp.traffic_class != ATM_NONE) { 19951da177e4SLinus Torvalds if (lanai->naal0 != 0) 19961da177e4SLinus Torvalds return 0; 19971da177e4SLinus Torvalds lanai->conf2 |= CONFIG2_VCI0_NORMAL; 19981da177e4SLinus Torvalds conf2_write_if_powerup(lanai); 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds return 1; 20011da177e4SLinus Torvalds } 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds /* return true if vci is currently unused, or if requested qos is 20041da177e4SLinus Torvalds * compatible 20051da177e4SLinus Torvalds */ 20061da177e4SLinus Torvalds static int vci_is_ok(struct lanai_dev *lanai, vci_t vci, 20071da177e4SLinus Torvalds const struct atm_vcc *atmvcc) 20081da177e4SLinus Torvalds { 20091da177e4SLinus Torvalds const struct atm_qos *qos = &atmvcc->qos; 20101da177e4SLinus Torvalds const struct lanai_vcc *lvcc = lanai->vccs[vci]; 20111da177e4SLinus Torvalds if (vci == 0 && !vci0_is_ok(lanai, qos)) 20121da177e4SLinus Torvalds return 0; 20131da177e4SLinus Torvalds if (unlikely(lvcc != NULL)) { 20141da177e4SLinus Torvalds if (qos->rxtp.traffic_class != ATM_NONE && 20151da177e4SLinus Torvalds lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) 20161da177e4SLinus Torvalds return 0; 20171da177e4SLinus Torvalds if (qos->txtp.traffic_class != ATM_NONE && 20181da177e4SLinus Torvalds lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) 20191da177e4SLinus Torvalds return 0; 20201da177e4SLinus Torvalds if (qos->txtp.traffic_class == ATM_CBR && 20211da177e4SLinus Torvalds lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) 20221da177e4SLinus Torvalds return 0; 20231da177e4SLinus Torvalds } 20241da177e4SLinus Torvalds if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 && 20251da177e4SLinus Torvalds qos->rxtp.traffic_class != ATM_NONE) { 20261da177e4SLinus Torvalds const struct lanai_vcc *vci0 = lanai->vccs[0]; 20271da177e4SLinus Torvalds if (vci0 != NULL && vci0->rx.atmvcc != NULL) 20281da177e4SLinus Torvalds return 0; 20291da177e4SLinus Torvalds lanai->conf2 &= ~CONFIG2_VCI0_NORMAL; 20301da177e4SLinus Torvalds conf2_write_if_powerup(lanai); 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds return 1; 20331da177e4SLinus Torvalds } 20341da177e4SLinus Torvalds 20351da177e4SLinus Torvalds static int lanai_normalize_ci(struct lanai_dev *lanai, 20361da177e4SLinus Torvalds const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip) 20371da177e4SLinus Torvalds { 20381da177e4SLinus Torvalds switch (*vpip) { 20391da177e4SLinus Torvalds case ATM_VPI_ANY: 20401da177e4SLinus Torvalds *vpip = 0; 20411da177e4SLinus Torvalds /* FALLTHROUGH */ 20421da177e4SLinus Torvalds case 0: 20431da177e4SLinus Torvalds break; 20441da177e4SLinus Torvalds default: 20451da177e4SLinus Torvalds return -EADDRINUSE; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds switch (*vcip) { 20481da177e4SLinus Torvalds case ATM_VCI_ANY: 20491da177e4SLinus Torvalds for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci; 20501da177e4SLinus Torvalds (*vcip)++) 20511da177e4SLinus Torvalds if (vci_is_ok(lanai, *vcip, atmvcc)) 20521da177e4SLinus Torvalds return 0; 20531da177e4SLinus Torvalds return -EADDRINUSE; 20541da177e4SLinus Torvalds default: 20551da177e4SLinus Torvalds if (*vcip >= lanai->num_vci || *vcip < 0 || 20561da177e4SLinus Torvalds !vci_is_ok(lanai, *vcip, atmvcc)) 20571da177e4SLinus Torvalds return -EADDRINUSE; 20581da177e4SLinus Torvalds } 20591da177e4SLinus Torvalds return 0; 20601da177e4SLinus Torvalds } 20611da177e4SLinus Torvalds 20621da177e4SLinus Torvalds /* -------------------- MANAGE CBR: */ 20631da177e4SLinus Torvalds 20641da177e4SLinus Torvalds /* 20651da177e4SLinus Torvalds * CBR ICG is stored as a fixed-point number with 4 fractional bits. 20661da177e4SLinus Torvalds * Note that storing a number greater than 2046.0 will result in 20671da177e4SLinus Torvalds * incorrect shaping 20681da177e4SLinus Torvalds */ 20691da177e4SLinus Torvalds #define CBRICG_FRAC_BITS (4) 20701da177e4SLinus Torvalds #define CBRICG_MAX (2046 << CBRICG_FRAC_BITS) 20711da177e4SLinus Torvalds 20721da177e4SLinus Torvalds /* 20731da177e4SLinus Torvalds * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) 20741da177e4SLinus Torvalds * where MAXPCR is (according to the docs) 25600000/(54*8), 20751da177e4SLinus Torvalds * which is equal to (3125<<9)/27. 20761da177e4SLinus Torvalds * 20771da177e4SLinus Torvalds * Solving for ICG, we get: 20781da177e4SLinus Torvalds * ICG = MAXPCR/PCR - 1 20791da177e4SLinus Torvalds * ICG = (3125<<9)/(27*PCR) - 1 20801da177e4SLinus Torvalds * ICG = ((3125<<9) - (27*PCR)) / (27*PCR) 20811da177e4SLinus Torvalds * 20821da177e4SLinus Torvalds * The end result is supposed to be a fixed-point number with FRAC_BITS 20831da177e4SLinus Torvalds * bits of a fractional part, so we keep everything in the numerator 20841da177e4SLinus Torvalds * shifted by that much as we compute 20851da177e4SLinus Torvalds * 20861da177e4SLinus Torvalds */ 2087c22c28f6SMitchell Blank Jr static int pcr_to_cbricg(const struct atm_qos *qos) 20881da177e4SLinus Torvalds { 20891da177e4SLinus Torvalds int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */ 20901da177e4SLinus Torvalds int x, icg, pcr = atm_pcr_goal(&qos->txtp); 20911da177e4SLinus Torvalds if (pcr == 0) /* Use maximum bandwidth */ 20921da177e4SLinus Torvalds return 0; 20931da177e4SLinus Torvalds if (pcr < 0) { 20941da177e4SLinus Torvalds rounddown = 1; 20951da177e4SLinus Torvalds pcr = -pcr; 20961da177e4SLinus Torvalds } 20971da177e4SLinus Torvalds x = pcr * 27; 20981da177e4SLinus Torvalds icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS); 20991da177e4SLinus Torvalds if (rounddown) 21001da177e4SLinus Torvalds icg += x - 1; 21011da177e4SLinus Torvalds icg /= x; 21021da177e4SLinus Torvalds if (icg > CBRICG_MAX) 21031da177e4SLinus Torvalds icg = CBRICG_MAX; 21041da177e4SLinus Torvalds DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n", 21051da177e4SLinus Torvalds pcr, rounddown ? 'Y' : 'N', icg); 21061da177e4SLinus Torvalds return icg; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds 21091da177e4SLinus Torvalds static inline void lanai_cbr_setup(struct lanai_dev *lanai) 21101da177e4SLinus Torvalds { 21111da177e4SLinus Torvalds reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg); 21121da177e4SLinus Torvalds reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg); 21131da177e4SLinus Torvalds lanai->conf2 |= CONFIG2_CBR_ENABLE; 21141da177e4SLinus Torvalds conf2_write(lanai); 21151da177e4SLinus Torvalds } 21161da177e4SLinus Torvalds 21171da177e4SLinus Torvalds static inline void lanai_cbr_shutdown(struct lanai_dev *lanai) 21181da177e4SLinus Torvalds { 21191da177e4SLinus Torvalds lanai->conf2 &= ~CONFIG2_CBR_ENABLE; 21201da177e4SLinus Torvalds conf2_write(lanai); 21211da177e4SLinus Torvalds } 21221da177e4SLinus Torvalds 21231da177e4SLinus Torvalds /* -------------------- OPERATIONS: */ 21241da177e4SLinus Torvalds 21251da177e4SLinus Torvalds /* setup a newly detected device */ 21261da177e4SLinus Torvalds static int __devinit lanai_dev_open(struct atm_dev *atmdev) 21271da177e4SLinus Torvalds { 21281da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 21291da177e4SLinus Torvalds unsigned long raw_base; 21301da177e4SLinus Torvalds int result; 21311da177e4SLinus Torvalds 21321da177e4SLinus Torvalds DPRINTK("In lanai_dev_open()\n"); 21331da177e4SLinus Torvalds /* Basic device fields */ 21341da177e4SLinus Torvalds lanai->number = atmdev->number; 21351da177e4SLinus Torvalds lanai->num_vci = NUM_VCI; 21361da177e4SLinus Torvalds bitmap_zero(lanai->backlog_vccs, NUM_VCI); 21371da177e4SLinus Torvalds bitmap_zero(lanai->transmit_ready, NUM_VCI); 21381da177e4SLinus Torvalds lanai->naal0 = 0; 21391da177e4SLinus Torvalds #ifdef USE_POWERDOWN 21401da177e4SLinus Torvalds lanai->nbound = 0; 21411da177e4SLinus Torvalds #endif 21421da177e4SLinus Torvalds lanai->cbrvcc = NULL; 21431da177e4SLinus Torvalds memset(&lanai->stats, 0, sizeof lanai->stats); 21441da177e4SLinus Torvalds spin_lock_init(&lanai->endtxlock); 21451da177e4SLinus Torvalds spin_lock_init(&lanai->servicelock); 21461da177e4SLinus Torvalds atmdev->ci_range.vpi_bits = 0; 21471da177e4SLinus Torvalds atmdev->ci_range.vci_bits = 0; 21481da177e4SLinus Torvalds while (1 << atmdev->ci_range.vci_bits < lanai->num_vci) 21491da177e4SLinus Torvalds atmdev->ci_range.vci_bits++; 21501da177e4SLinus Torvalds atmdev->link_rate = ATM_25_PCR; 21511da177e4SLinus Torvalds 21521da177e4SLinus Torvalds /* 3.2: PCI initialization */ 21531da177e4SLinus Torvalds if ((result = lanai_pci_start(lanai)) != 0) 21541da177e4SLinus Torvalds goto error; 21551da177e4SLinus Torvalds raw_base = lanai->pci->resource[0].start; 21561da177e4SLinus Torvalds lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE); 21571da177e4SLinus Torvalds if (lanai->base == NULL) { 21581da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n"); 21591da177e4SLinus Torvalds goto error_pci; 21601da177e4SLinus Torvalds } 21611da177e4SLinus Torvalds /* 3.3: Reset lanai and PHY */ 21621da177e4SLinus Torvalds reset_board(lanai); 21631da177e4SLinus Torvalds lanai->conf1 = reg_read(lanai, Config1_Reg); 21641da177e4SLinus Torvalds lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN | 21651da177e4SLinus Torvalds CONFIG1_MASK_LEDMODE); 21661da177e4SLinus Torvalds lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL); 21671da177e4SLinus Torvalds reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 21681da177e4SLinus Torvalds udelay(1000); 21691da177e4SLinus Torvalds conf1_write(lanai); 21701da177e4SLinus Torvalds 21711da177e4SLinus Torvalds /* 21721da177e4SLinus Torvalds * 3.4: Turn on endian mode for big-endian hardware 21731da177e4SLinus Torvalds * We don't actually want to do this - the actual bit fields 21741da177e4SLinus Torvalds * in the endian register are not documented anywhere. 21751da177e4SLinus Torvalds * Instead we do the bit-flipping ourselves on big-endian 21761da177e4SLinus Torvalds * hardware. 21771da177e4SLinus Torvalds * 21781da177e4SLinus Torvalds * 3.5: get the board ID/rev by reading the reset register 21791da177e4SLinus Torvalds */ 21801da177e4SLinus Torvalds result = check_board_id_and_rev("register", 21811da177e4SLinus Torvalds reg_read(lanai, Reset_Reg), &lanai->board_rev); 21821da177e4SLinus Torvalds if (result != 0) 21831da177e4SLinus Torvalds goto error_unmap; 21841da177e4SLinus Torvalds 21851da177e4SLinus Torvalds /* 3.6: read EEPROM */ 21861da177e4SLinus Torvalds if ((result = eeprom_read(lanai)) != 0) 21871da177e4SLinus Torvalds goto error_unmap; 21881da177e4SLinus Torvalds if ((result = eeprom_validate(lanai)) != 0) 21891da177e4SLinus Torvalds goto error_unmap; 21901da177e4SLinus Torvalds 21911da177e4SLinus Torvalds /* 3.7: re-reset PHY, do loopback tests, setup PHY */ 21921da177e4SLinus Torvalds reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 21931da177e4SLinus Torvalds udelay(1000); 21941da177e4SLinus Torvalds conf1_write(lanai); 21951da177e4SLinus Torvalds /* TODO - loopback tests */ 21961da177e4SLinus Torvalds lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE); 21971da177e4SLinus Torvalds conf1_write(lanai); 21981da177e4SLinus Torvalds 21991da177e4SLinus Torvalds /* 3.8/3.9: test and initialize card SRAM */ 22001da177e4SLinus Torvalds if ((result = sram_test_and_clear(lanai)) != 0) 22011da177e4SLinus Torvalds goto error_unmap; 22021da177e4SLinus Torvalds 22031da177e4SLinus Torvalds /* 3.10: initialize lanai registers */ 22041da177e4SLinus Torvalds lanai->conf1 |= CONFIG1_DMA_ENABLE; 22051da177e4SLinus Torvalds conf1_write(lanai); 22061da177e4SLinus Torvalds if ((result = service_buffer_allocate(lanai)) != 0) 22071da177e4SLinus Torvalds goto error_unmap; 22081da177e4SLinus Torvalds if ((result = vcc_table_allocate(lanai)) != 0) 22091da177e4SLinus Torvalds goto error_service; 22101da177e4SLinus Torvalds lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) | 22111da177e4SLinus Torvalds CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE; 22121da177e4SLinus Torvalds conf2_write(lanai); 22131da177e4SLinus Torvalds reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg); 22141da177e4SLinus Torvalds reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */ 2215dace1453SThomas Gleixner if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED, 22161da177e4SLinus Torvalds DEV_LABEL, lanai)) != 0) { 22171da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n"); 22181da177e4SLinus Torvalds goto error_vcctable; 22191da177e4SLinus Torvalds } 22201da177e4SLinus Torvalds mb(); /* Make sure that all that made it */ 22211da177e4SLinus Torvalds intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE)); 22221da177e4SLinus Torvalds /* 3.11: initialize loop mode (i.e. turn looping off) */ 22231da177e4SLinus Torvalds lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) | 22241da177e4SLinus Torvalds CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) | 22251da177e4SLinus Torvalds CONFIG1_GPOUT2 | CONFIG1_GPOUT3; 22261da177e4SLinus Torvalds conf1_write(lanai); 22271da177e4SLinus Torvalds lanai->status = reg_read(lanai, Status_Reg); 22281da177e4SLinus Torvalds /* We're now done initializing this card */ 22291da177e4SLinus Torvalds #ifdef USE_POWERDOWN 22301da177e4SLinus Torvalds lanai->conf1 |= CONFIG1_POWERDOWN; 22311da177e4SLinus Torvalds conf1_write(lanai); 22321da177e4SLinus Torvalds #endif 22331da177e4SLinus Torvalds memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN); 22341da177e4SLinus Torvalds lanai_timed_poll_start(lanai); 22351da177e4SLinus Torvalds printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=0x%lx, irq=%u " 223658e481f6SAndy Shevchenko "(%pMF)\n", lanai->number, (int) lanai->pci->revision, 223758e481f6SAndy Shevchenko (unsigned long) lanai->base, lanai->pci->irq, atmdev->esi); 22381da177e4SLinus Torvalds printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), " 22391da177e4SLinus Torvalds "board_rev=%d\n", lanai->number, 22401da177e4SLinus Torvalds lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno, 22411da177e4SLinus Torvalds (unsigned int) lanai->serialno, lanai->board_rev); 22421da177e4SLinus Torvalds return 0; 22431da177e4SLinus Torvalds 22441da177e4SLinus Torvalds error_vcctable: 22451da177e4SLinus Torvalds vcc_table_deallocate(lanai); 22461da177e4SLinus Torvalds error_service: 22471da177e4SLinus Torvalds service_buffer_deallocate(lanai); 22481da177e4SLinus Torvalds error_unmap: 22491da177e4SLinus Torvalds reset_board(lanai); 22501da177e4SLinus Torvalds #ifdef USE_POWERDOWN 22511da177e4SLinus Torvalds lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN; 22521da177e4SLinus Torvalds conf1_write(lanai); 22531da177e4SLinus Torvalds #endif 22541da177e4SLinus Torvalds iounmap(lanai->base); 22551da177e4SLinus Torvalds error_pci: 22561da177e4SLinus Torvalds pci_disable_device(lanai->pci); 22571da177e4SLinus Torvalds error: 22581da177e4SLinus Torvalds return result; 22591da177e4SLinus Torvalds } 22601da177e4SLinus Torvalds 22611da177e4SLinus Torvalds /* called when device is being shutdown, and all vcc's are gone - higher 22621da177e4SLinus Torvalds * levels will deallocate the atm device for us 22631da177e4SLinus Torvalds */ 22641da177e4SLinus Torvalds static void lanai_dev_close(struct atm_dev *atmdev) 22651da177e4SLinus Torvalds { 22661da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 22671da177e4SLinus Torvalds printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n", 22681da177e4SLinus Torvalds lanai->number); 22691da177e4SLinus Torvalds lanai_timed_poll_stop(lanai); 22701da177e4SLinus Torvalds #ifdef USE_POWERDOWN 22711da177e4SLinus Torvalds lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN; 22721da177e4SLinus Torvalds conf1_write(lanai); 22731da177e4SLinus Torvalds #endif 22741da177e4SLinus Torvalds intr_disable(lanai, INT_ALL); 22751da177e4SLinus Torvalds free_irq(lanai->pci->irq, lanai); 22761da177e4SLinus Torvalds reset_board(lanai); 22771da177e4SLinus Torvalds #ifdef USE_POWERDOWN 22781da177e4SLinus Torvalds lanai->conf1 |= CONFIG1_POWERDOWN; 22791da177e4SLinus Torvalds conf1_write(lanai); 22801da177e4SLinus Torvalds #endif 22811da177e4SLinus Torvalds pci_disable_device(lanai->pci); 22821da177e4SLinus Torvalds vcc_table_deallocate(lanai); 22831da177e4SLinus Torvalds service_buffer_deallocate(lanai); 22841da177e4SLinus Torvalds iounmap(lanai->base); 22851da177e4SLinus Torvalds kfree(lanai); 22861da177e4SLinus Torvalds } 22871da177e4SLinus Torvalds 22881da177e4SLinus Torvalds /* close a vcc */ 22891da177e4SLinus Torvalds static void lanai_close(struct atm_vcc *atmvcc) 22901da177e4SLinus Torvalds { 22911da177e4SLinus Torvalds struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 22921da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 22931da177e4SLinus Torvalds if (lvcc == NULL) 22941da177e4SLinus Torvalds return; 22951da177e4SLinus Torvalds clear_bit(ATM_VF_READY, &atmvcc->flags); 22961da177e4SLinus Torvalds clear_bit(ATM_VF_PARTIAL, &atmvcc->flags); 22971da177e4SLinus Torvalds if (lvcc->rx.atmvcc == atmvcc) { 22981da177e4SLinus Torvalds lanai_shutdown_rx_vci(lvcc); 22991da177e4SLinus Torvalds if (atmvcc->qos.aal == ATM_AAL0) { 23001da177e4SLinus Torvalds if (--lanai->naal0 <= 0) 23011da177e4SLinus Torvalds aal0_buffer_free(lanai); 23021da177e4SLinus Torvalds } else 23031da177e4SLinus Torvalds lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci); 23041da177e4SLinus Torvalds lvcc->rx.atmvcc = NULL; 23051da177e4SLinus Torvalds } 23061da177e4SLinus Torvalds if (lvcc->tx.atmvcc == atmvcc) { 23071da177e4SLinus Torvalds if (atmvcc == lanai->cbrvcc) { 23081da177e4SLinus Torvalds if (lvcc->vbase != NULL) 23091da177e4SLinus Torvalds lanai_cbr_shutdown(lanai); 23101da177e4SLinus Torvalds lanai->cbrvcc = NULL; 23111da177e4SLinus Torvalds } 23121da177e4SLinus Torvalds lanai_shutdown_tx_vci(lanai, lvcc); 23131da177e4SLinus Torvalds lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci); 23141da177e4SLinus Torvalds lvcc->tx.atmvcc = NULL; 23151da177e4SLinus Torvalds } 23161da177e4SLinus Torvalds if (--lvcc->nref == 0) { 23171da177e4SLinus Torvalds host_vcc_unbind(lanai, lvcc); 23181da177e4SLinus Torvalds kfree(lvcc); 23191da177e4SLinus Torvalds } 23201da177e4SLinus Torvalds atmvcc->dev_data = NULL; 23211da177e4SLinus Torvalds clear_bit(ATM_VF_ADDR, &atmvcc->flags); 23221da177e4SLinus Torvalds } 23231da177e4SLinus Torvalds 23241da177e4SLinus Torvalds /* open a vcc on the card to vpi/vci */ 23251da177e4SLinus Torvalds static int lanai_open(struct atm_vcc *atmvcc) 23261da177e4SLinus Torvalds { 23271da177e4SLinus Torvalds struct lanai_dev *lanai; 23281da177e4SLinus Torvalds struct lanai_vcc *lvcc; 23291da177e4SLinus Torvalds int result = 0; 23301da177e4SLinus Torvalds int vci = atmvcc->vci; 23311da177e4SLinus Torvalds short vpi = atmvcc->vpi; 23321da177e4SLinus Torvalds /* we don't support partial open - it's not really useful anyway */ 23331da177e4SLinus Torvalds if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) || 23341da177e4SLinus Torvalds (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC)) 23351da177e4SLinus Torvalds return -EINVAL; 23361da177e4SLinus Torvalds lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 23371da177e4SLinus Torvalds result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci); 23381da177e4SLinus Torvalds if (unlikely(result != 0)) 23391da177e4SLinus Torvalds goto out; 23401da177e4SLinus Torvalds set_bit(ATM_VF_ADDR, &atmvcc->flags); 23411da177e4SLinus Torvalds if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5) 23421da177e4SLinus Torvalds return -EINVAL; 23431da177e4SLinus Torvalds DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number, 23441da177e4SLinus Torvalds (int) vpi, vci); 23451da177e4SLinus Torvalds lvcc = lanai->vccs[vci]; 23461da177e4SLinus Torvalds if (lvcc == NULL) { 23471da177e4SLinus Torvalds lvcc = new_lanai_vcc(); 23481da177e4SLinus Torvalds if (unlikely(lvcc == NULL)) 23491da177e4SLinus Torvalds return -ENOMEM; 23501da177e4SLinus Torvalds atmvcc->dev_data = lvcc; 23511da177e4SLinus Torvalds } 23521da177e4SLinus Torvalds lvcc->nref++; 23531da177e4SLinus Torvalds if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) { 23541da177e4SLinus Torvalds APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n", 23551da177e4SLinus Torvalds vci); 23561da177e4SLinus Torvalds if (atmvcc->qos.aal == ATM_AAL0) { 23571da177e4SLinus Torvalds if (lanai->naal0 == 0) 23581da177e4SLinus Torvalds result = aal0_buffer_allocate(lanai); 23591da177e4SLinus Torvalds } else 23601da177e4SLinus Torvalds result = lanai_setup_rx_vci_aal5( 23611da177e4SLinus Torvalds lanai, lvcc, &atmvcc->qos); 23621da177e4SLinus Torvalds if (unlikely(result != 0)) 23631da177e4SLinus Torvalds goto out_free; 23641da177e4SLinus Torvalds lvcc->rx.atmvcc = atmvcc; 23651da177e4SLinus Torvalds lvcc->stats.rx_nomem = 0; 23661da177e4SLinus Torvalds lvcc->stats.x.aal5.rx_badlen = 0; 23671da177e4SLinus Torvalds lvcc->stats.x.aal5.service_trash = 0; 23681da177e4SLinus Torvalds lvcc->stats.x.aal5.service_stream = 0; 23691da177e4SLinus Torvalds lvcc->stats.x.aal5.service_rxcrc = 0; 23701da177e4SLinus Torvalds if (atmvcc->qos.aal == ATM_AAL0) 23711da177e4SLinus Torvalds lanai->naal0++; 23721da177e4SLinus Torvalds } 23731da177e4SLinus Torvalds if (atmvcc->qos.txtp.traffic_class != ATM_NONE) { 23741da177e4SLinus Torvalds APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n", 23751da177e4SLinus Torvalds vci); 23761da177e4SLinus Torvalds result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos); 23771da177e4SLinus Torvalds if (unlikely(result != 0)) 23781da177e4SLinus Torvalds goto out_free; 23791da177e4SLinus Torvalds lvcc->tx.atmvcc = atmvcc; 23801da177e4SLinus Torvalds if (atmvcc->qos.txtp.traffic_class == ATM_CBR) { 23811da177e4SLinus Torvalds APRINTK(lanai->cbrvcc == NULL, 23821da177e4SLinus Torvalds "cbrvcc!=NULL, vci=%d\n", vci); 23831da177e4SLinus Torvalds lanai->cbrvcc = atmvcc; 23841da177e4SLinus Torvalds } 23851da177e4SLinus Torvalds } 23861da177e4SLinus Torvalds host_vcc_bind(lanai, lvcc, vci); 23871da177e4SLinus Torvalds /* 23881da177e4SLinus Torvalds * Make sure everything made it to RAM before we tell the card about 23891da177e4SLinus Torvalds * the VCC 23901da177e4SLinus Torvalds */ 23911da177e4SLinus Torvalds wmb(); 23921da177e4SLinus Torvalds if (atmvcc == lvcc->rx.atmvcc) 23931da177e4SLinus Torvalds host_vcc_start_rx(lvcc); 23941da177e4SLinus Torvalds if (atmvcc == lvcc->tx.atmvcc) { 23951da177e4SLinus Torvalds host_vcc_start_tx(lvcc); 23961da177e4SLinus Torvalds if (lanai->cbrvcc == atmvcc) 23971da177e4SLinus Torvalds lanai_cbr_setup(lanai); 23981da177e4SLinus Torvalds } 23991da177e4SLinus Torvalds set_bit(ATM_VF_READY, &atmvcc->flags); 24001da177e4SLinus Torvalds return 0; 24011da177e4SLinus Torvalds out_free: 24021da177e4SLinus Torvalds lanai_close(atmvcc); 24031da177e4SLinus Torvalds out: 24041da177e4SLinus Torvalds return result; 24051da177e4SLinus Torvalds } 24061da177e4SLinus Torvalds 24071da177e4SLinus Torvalds static int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb) 24081da177e4SLinus Torvalds { 24091da177e4SLinus Torvalds struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 24101da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 24111da177e4SLinus Torvalds unsigned long flags; 24121da177e4SLinus Torvalds if (unlikely(lvcc == NULL || lvcc->vbase == NULL || 24131da177e4SLinus Torvalds lvcc->tx.atmvcc != atmvcc)) 24141da177e4SLinus Torvalds goto einval; 24151da177e4SLinus Torvalds #ifdef DEBUG 24161da177e4SLinus Torvalds if (unlikely(skb == NULL)) { 24171da177e4SLinus Torvalds DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci); 24181da177e4SLinus Torvalds goto einval; 24191da177e4SLinus Torvalds } 24201da177e4SLinus Torvalds if (unlikely(lanai == NULL)) { 24211da177e4SLinus Torvalds DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci); 24221da177e4SLinus Torvalds goto einval; 24231da177e4SLinus Torvalds } 24241da177e4SLinus Torvalds #endif 24251da177e4SLinus Torvalds ATM_SKB(skb)->vcc = atmvcc; 24261da177e4SLinus Torvalds switch (atmvcc->qos.aal) { 24271da177e4SLinus Torvalds case ATM_AAL5: 24281da177e4SLinus Torvalds read_lock_irqsave(&vcc_sklist_lock, flags); 24291da177e4SLinus Torvalds vcc_tx_aal5(lanai, lvcc, skb); 24301da177e4SLinus Torvalds read_unlock_irqrestore(&vcc_sklist_lock, flags); 24311da177e4SLinus Torvalds return 0; 24321da177e4SLinus Torvalds case ATM_AAL0: 24331da177e4SLinus Torvalds if (unlikely(skb->len != ATM_CELL_SIZE-1)) 24341da177e4SLinus Torvalds goto einval; 24351da177e4SLinus Torvalds /* NOTE - this next line is technically invalid - we haven't unshared skb */ 24361da177e4SLinus Torvalds cpu_to_be32s((u32 *) skb->data); 24371da177e4SLinus Torvalds read_lock_irqsave(&vcc_sklist_lock, flags); 24381da177e4SLinus Torvalds vcc_tx_aal0(lanai, lvcc, skb); 24391da177e4SLinus Torvalds read_unlock_irqrestore(&vcc_sklist_lock, flags); 24401da177e4SLinus Torvalds return 0; 24411da177e4SLinus Torvalds } 24421da177e4SLinus Torvalds DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal, 24431da177e4SLinus Torvalds atmvcc->vci); 24441da177e4SLinus Torvalds einval: 24451da177e4SLinus Torvalds lanai_free_skb(atmvcc, skb); 24461da177e4SLinus Torvalds return -EINVAL; 24471da177e4SLinus Torvalds } 24481da177e4SLinus Torvalds 24491da177e4SLinus Torvalds static int lanai_change_qos(struct atm_vcc *atmvcc, 24501da177e4SLinus Torvalds /*const*/ struct atm_qos *qos, int flags) 24511da177e4SLinus Torvalds { 24521da177e4SLinus Torvalds return -EBUSY; /* TODO: need to write this */ 24531da177e4SLinus Torvalds } 24541da177e4SLinus Torvalds 24551da177e4SLinus Torvalds #ifndef CONFIG_PROC_FS 24561da177e4SLinus Torvalds #define lanai_proc_read NULL 24571da177e4SLinus Torvalds #else 24581da177e4SLinus Torvalds static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) 24591da177e4SLinus Torvalds { 24601da177e4SLinus Torvalds struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 24611da177e4SLinus Torvalds loff_t left = *pos; 24621da177e4SLinus Torvalds struct lanai_vcc *lvcc; 24631da177e4SLinus Torvalds if (left-- == 0) 24641da177e4SLinus Torvalds return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, " 24651da177e4SLinus Torvalds "serial=%u, magic=0x%08X, num_vci=%d\n", 24661da177e4SLinus Torvalds atmdev->number, lanai->type==lanai2 ? "2" : "HB", 24671da177e4SLinus Torvalds (unsigned int) lanai->serialno, 24681da177e4SLinus Torvalds (unsigned int) lanai->magicno, lanai->num_vci); 24691da177e4SLinus Torvalds if (left-- == 0) 24701da177e4SLinus Torvalds return sprintf(page, "revision: board=%d, pci_if=%d\n", 247144c10138SAuke Kok lanai->board_rev, (int) lanai->pci->revision); 24721da177e4SLinus Torvalds if (left-- == 0) 24738d9ded23Shartleys return sprintf(page, "EEPROM ESI: %pM\n", 24748d9ded23Shartleys &lanai->eeprom[EEPROM_MAC]); 24751da177e4SLinus Torvalds if (left-- == 0) 24761da177e4SLinus Torvalds return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, " 24771da177e4SLinus Torvalds "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0, 24781da177e4SLinus Torvalds (lanai->status & STATUS_LOCD) ? 1 : 0, 24791da177e4SLinus Torvalds (lanai->status & STATUS_LED) ? 1 : 0, 24801da177e4SLinus Torvalds (lanai->status & STATUS_GPIN) ? 1 : 0); 24811da177e4SLinus Torvalds if (left-- == 0) 24821da177e4SLinus Torvalds return sprintf(page, "global buffer sizes: service=%Zu, " 24831da177e4SLinus Torvalds "aal0_rx=%Zu\n", lanai_buf_size(&lanai->service), 24841da177e4SLinus Torvalds lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); 24851da177e4SLinus Torvalds if (left-- == 0) { 24861da177e4SLinus Torvalds get_statistics(lanai); 24871da177e4SLinus Torvalds return sprintf(page, "cells in error: overflow=%u, " 24881da177e4SLinus Torvalds "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n", 24891da177e4SLinus Torvalds lanai->stats.ovfl_trash, lanai->stats.vci_trash, 24901da177e4SLinus Torvalds lanai->stats.hec_err, lanai->stats.atm_ovfl); 24911da177e4SLinus Torvalds } 24921da177e4SLinus Torvalds if (left-- == 0) 24931da177e4SLinus Torvalds return sprintf(page, "PCI errors: parity_detect=%u, " 24941da177e4SLinus Torvalds "master_abort=%u, master_target_abort=%u,\n", 24951da177e4SLinus Torvalds lanai->stats.pcierr_parity_detect, 24961da177e4SLinus Torvalds lanai->stats.pcierr_serr_set, 24971da177e4SLinus Torvalds lanai->stats.pcierr_m_target_abort); 24981da177e4SLinus Torvalds if (left-- == 0) 24991da177e4SLinus Torvalds return sprintf(page, " slave_target_abort=%u, " 25001da177e4SLinus Torvalds "master_parity=%u\n", lanai->stats.pcierr_s_target_abort, 25011da177e4SLinus Torvalds lanai->stats.pcierr_master_parity); 25021da177e4SLinus Torvalds if (left-- == 0) 25031da177e4SLinus Torvalds return sprintf(page, " no_tx=%u, " 25041da177e4SLinus Torvalds "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx, 25051da177e4SLinus Torvalds lanai->stats.service_notx, 25061da177e4SLinus Torvalds lanai->stats.service_rxnotaal5); 25071da177e4SLinus Torvalds if (left-- == 0) 25081da177e4SLinus Torvalds return sprintf(page, "resets: dma=%u, card=%u\n", 25091da177e4SLinus Torvalds lanai->stats.dma_reenable, lanai->stats.card_reset); 25101da177e4SLinus Torvalds /* At this point, "left" should be the VCI we're looking for */ 25111da177e4SLinus Torvalds read_lock(&vcc_sklist_lock); 25121da177e4SLinus Torvalds for (; ; left++) { 25131da177e4SLinus Torvalds if (left >= NUM_VCI) { 25141da177e4SLinus Torvalds left = 0; 25151da177e4SLinus Torvalds goto out; 25161da177e4SLinus Torvalds } 25171da177e4SLinus Torvalds if ((lvcc = lanai->vccs[left]) != NULL) 25181da177e4SLinus Torvalds break; 25191da177e4SLinus Torvalds (*pos)++; 25201da177e4SLinus Torvalds } 25211da177e4SLinus Torvalds /* Note that we re-use "left" here since we're done with it */ 25221da177e4SLinus Torvalds left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u", (vci_t) left, 25231da177e4SLinus Torvalds lvcc->nref, lvcc->stats.rx_nomem); 25241da177e4SLinus Torvalds if (lvcc->rx.atmvcc != NULL) { 25251da177e4SLinus Torvalds left += sprintf(&page[left], ",\n rx_AAL=%d", 25261da177e4SLinus Torvalds lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); 25271da177e4SLinus Torvalds if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) 25281da177e4SLinus Torvalds left += sprintf(&page[left], ", rx_buf_size=%Zu, " 25291da177e4SLinus Torvalds "rx_bad_len=%u,\n rx_service_trash=%u, " 25301da177e4SLinus Torvalds "rx_service_stream=%u, rx_bad_crc=%u", 25311da177e4SLinus Torvalds lanai_buf_size(&lvcc->rx.buf), 25321da177e4SLinus Torvalds lvcc->stats.x.aal5.rx_badlen, 25331da177e4SLinus Torvalds lvcc->stats.x.aal5.service_trash, 25341da177e4SLinus Torvalds lvcc->stats.x.aal5.service_stream, 25351da177e4SLinus Torvalds lvcc->stats.x.aal5.service_rxcrc); 25361da177e4SLinus Torvalds } 25371da177e4SLinus Torvalds if (lvcc->tx.atmvcc != NULL) 25381da177e4SLinus Torvalds left += sprintf(&page[left], ",\n tx_AAL=%d, " 25391da177e4SLinus Torvalds "tx_buf_size=%Zu, tx_qos=%cBR, tx_backlogged=%c", 25401da177e4SLinus Torvalds lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, 25411da177e4SLinus Torvalds lanai_buf_size(&lvcc->tx.buf), 25421da177e4SLinus Torvalds lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', 25431da177e4SLinus Torvalds vcc_is_backlogged(lvcc) ? 'Y' : 'N'); 25441da177e4SLinus Torvalds page[left++] = '\n'; 25451da177e4SLinus Torvalds page[left] = '\0'; 25461da177e4SLinus Torvalds out: 25471da177e4SLinus Torvalds read_unlock(&vcc_sklist_lock); 25481da177e4SLinus Torvalds return left; 25491da177e4SLinus Torvalds } 25501da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 25511da177e4SLinus Torvalds 25521da177e4SLinus Torvalds /* -------------------- HOOKS: */ 25531da177e4SLinus Torvalds 25541da177e4SLinus Torvalds static const struct atmdev_ops ops = { 25551da177e4SLinus Torvalds .dev_close = lanai_dev_close, 25561da177e4SLinus Torvalds .open = lanai_open, 25571da177e4SLinus Torvalds .close = lanai_close, 25581da177e4SLinus Torvalds .getsockopt = NULL, 25591da177e4SLinus Torvalds .setsockopt = NULL, 25601da177e4SLinus Torvalds .send = lanai_send, 25611da177e4SLinus Torvalds .phy_put = NULL, 25621da177e4SLinus Torvalds .phy_get = NULL, 25631da177e4SLinus Torvalds .change_qos = lanai_change_qos, 25641da177e4SLinus Torvalds .proc_read = lanai_proc_read, 25651da177e4SLinus Torvalds .owner = THIS_MODULE 25661da177e4SLinus Torvalds }; 25671da177e4SLinus Torvalds 25681da177e4SLinus Torvalds /* initialize one probed card */ 25691da177e4SLinus Torvalds static int __devinit lanai_init_one(struct pci_dev *pci, 25701da177e4SLinus Torvalds const struct pci_device_id *ident) 25711da177e4SLinus Torvalds { 25721da177e4SLinus Torvalds struct lanai_dev *lanai; 25731da177e4SLinus Torvalds struct atm_dev *atmdev; 25741da177e4SLinus Torvalds int result; 25751da177e4SLinus Torvalds 25765cbded58SRobert P. J. Day lanai = kmalloc(sizeof(*lanai), GFP_KERNEL); 25771da177e4SLinus Torvalds if (lanai == NULL) { 25781da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL 25791da177e4SLinus Torvalds ": couldn't allocate dev_data structure!\n"); 25801da177e4SLinus Torvalds return -ENOMEM; 25811da177e4SLinus Torvalds } 25821da177e4SLinus Torvalds 2583d9ca676bSDan Williams atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL); 25841da177e4SLinus Torvalds if (atmdev == NULL) { 25851da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL 25861da177e4SLinus Torvalds ": couldn't register atm device!\n"); 25871da177e4SLinus Torvalds kfree(lanai); 25881da177e4SLinus Torvalds return -EBUSY; 25891da177e4SLinus Torvalds } 25901da177e4SLinus Torvalds 25911da177e4SLinus Torvalds atmdev->dev_data = lanai; 25921da177e4SLinus Torvalds lanai->pci = pci; 25931da177e4SLinus Torvalds lanai->type = (enum lanai_type) ident->device; 25941da177e4SLinus Torvalds 25951da177e4SLinus Torvalds result = lanai_dev_open(atmdev); 25961da177e4SLinus Torvalds if (result != 0) { 25971da177e4SLinus Torvalds DPRINTK("lanai_start() failed, err=%d\n", -result); 25981da177e4SLinus Torvalds atm_dev_deregister(atmdev); 25991da177e4SLinus Torvalds kfree(lanai); 26001da177e4SLinus Torvalds } 26011da177e4SLinus Torvalds return result; 26021da177e4SLinus Torvalds } 26031da177e4SLinus Torvalds 26041da177e4SLinus Torvalds static struct pci_device_id lanai_pci_tbl[] = { 26051d0ed384SJiri Slaby { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) }, 26061d0ed384SJiri Slaby { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) }, 26071da177e4SLinus Torvalds { 0, } /* terminal entry */ 26081da177e4SLinus Torvalds }; 26091da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, lanai_pci_tbl); 26101da177e4SLinus Torvalds 26111da177e4SLinus Torvalds static struct pci_driver lanai_driver = { 26121da177e4SLinus Torvalds .name = DEV_LABEL, 26131da177e4SLinus Torvalds .id_table = lanai_pci_tbl, 26141da177e4SLinus Torvalds .probe = lanai_init_one, 26151da177e4SLinus Torvalds }; 26161da177e4SLinus Torvalds 26171da177e4SLinus Torvalds static int __init lanai_module_init(void) 26181da177e4SLinus Torvalds { 26191da177e4SLinus Torvalds int x; 26201da177e4SLinus Torvalds 26211da177e4SLinus Torvalds x = pci_register_driver(&lanai_driver); 26221da177e4SLinus Torvalds if (x != 0) 26231da177e4SLinus Torvalds printk(KERN_ERR DEV_LABEL ": no adapter found\n"); 26241da177e4SLinus Torvalds return x; 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds 26271da177e4SLinus Torvalds static void __exit lanai_module_exit(void) 26281da177e4SLinus Torvalds { 26291da177e4SLinus Torvalds /* We'll only get called when all the interfaces are already 26301da177e4SLinus Torvalds * gone, so there isn't much to do 26311da177e4SLinus Torvalds */ 26321da177e4SLinus Torvalds DPRINTK("cleanup_module()\n"); 2633fd22f1e0SDave Jones pci_unregister_driver(&lanai_driver); 26341da177e4SLinus Torvalds } 26351da177e4SLinus Torvalds 26361da177e4SLinus Torvalds module_init(lanai_module_init); 26371da177e4SLinus Torvalds module_exit(lanai_module_exit); 26381da177e4SLinus Torvalds 26391da177e4SLinus Torvalds MODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>"); 26401da177e4SLinus Torvalds MODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); 26411da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 2642