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