18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com> 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This driver supports ATM cards based on the Efficient "Lanai" 58c2ecf20Sopenharmony_ci * chipset such as the Speedstream 3010 and the ENI-25p. The 68c2ecf20Sopenharmony_ci * Speedstream 3060 is currently not supported since we don't 78c2ecf20Sopenharmony_ci * have the code to drive the on-board Alcatel DSL chipset (yet). 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Thanks to Efficient for supporting this project with hardware, 108c2ecf20Sopenharmony_ci * documentation, and by answering my questions. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Things not working yet: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * o We don't support the Speedstream 3060 yet - this card has 158c2ecf20Sopenharmony_ci * an on-board DSL modem chip by Alcatel and the driver will 168c2ecf20Sopenharmony_ci * need some extra code added to handle it 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * o Note that due to limitations of the Lanai only one VCC can be 198c2ecf20Sopenharmony_ci * in CBR at once 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * o We don't currently parse the EEPROM at all. The code is all 228c2ecf20Sopenharmony_ci * there as per the spec, but it doesn't actually work. I think 238c2ecf20Sopenharmony_ci * there may be some issues with the docs. Anyway, do NOT 248c2ecf20Sopenharmony_ci * enable it yet - bugs in that code may actually damage your 258c2ecf20Sopenharmony_ci * hardware! Because of this you should hardware an ESI before 268c2ecf20Sopenharmony_ci * trying to use this in a LANE or MPOA environment. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * o AAL0 is stubbed in but the actual rx/tx path isn't written yet: 298c2ecf20Sopenharmony_ci * vcc_tx_aal0() needs to send or queue a SKB 308c2ecf20Sopenharmony_ci * vcc_tx_unqueue_aal0() needs to attempt to send queued SKBs 318c2ecf20Sopenharmony_ci * vcc_rx_aal0() needs to handle AAL0 interrupts 328c2ecf20Sopenharmony_ci * This isn't too much work - I just wanted to get other things 338c2ecf20Sopenharmony_ci * done first. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * o lanai_change_qos() isn't written yet 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * o There aren't any ioctl's yet -- I'd like to eventually support 388c2ecf20Sopenharmony_ci * setting loopback and LED modes that way. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * o If the segmentation engine or DMA gets shut down we should restart 418c2ecf20Sopenharmony_ci * card as per section 17.0i. (see lanai_reset) 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * o setsockopt(SO_CIRANGE) isn't done (although despite what the 448c2ecf20Sopenharmony_ci * API says it isn't exactly commonly implemented) 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Version history: 488c2ecf20Sopenharmony_ci * v.1.00 -- 26-JUL-2003 -- PCI/DMA updates 498c2ecf20Sopenharmony_ci * v.0.02 -- 11-JAN-2000 -- Endian fixes 508c2ecf20Sopenharmony_ci * v.0.01 -- 30-NOV-1999 -- Initial release 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <linux/module.h> 548c2ecf20Sopenharmony_ci#include <linux/slab.h> 558c2ecf20Sopenharmony_ci#include <linux/mm.h> 568c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 578c2ecf20Sopenharmony_ci#include <asm/io.h> 588c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 598c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 608c2ecf20Sopenharmony_ci#include <linux/pci.h> 618c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 628c2ecf20Sopenharmony_ci#include <linux/init.h> 638c2ecf20Sopenharmony_ci#include <linux/delay.h> 648c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* -------------------- TUNABLE PARAMATERS: */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Maximum number of VCIs per card. Setting it lower could theoretically 708c2ecf20Sopenharmony_ci * save some memory, but since we allocate our vcc list with get_free_pages, 718c2ecf20Sopenharmony_ci * it's not really likely for most architectures 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define NUM_VCI (1024) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * Enable extra debugging 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define DEBUG 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * Debug _all_ register operations with card, except the memory test. 818c2ecf20Sopenharmony_ci * Also disables the timed poll to prevent extra chattiness. This 828c2ecf20Sopenharmony_ci * isn't for normal use 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci#undef DEBUG_RW 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * The programming guide specifies a full test of the on-board SRAM 888c2ecf20Sopenharmony_ci * at initialization time. Undefine to remove this 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci#define FULL_MEMORY_TEST 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * This is the number of (4 byte) service entries that we will 948c2ecf20Sopenharmony_ci * try to allocate at startup. Note that we will end up with 958c2ecf20Sopenharmony_ci * one PAGE_SIZE's worth regardless of what this is set to 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci#define SERVICE_ENTRIES (1024) 988c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * We normally read the onboard EEPROM in order to discover our MAC 1028c2ecf20Sopenharmony_ci * address. Undefine to _not_ do this 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci/* #define READ_EEPROM */ /* ***DONT ENABLE YET*** */ 1058c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option (also) */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* 1088c2ecf20Sopenharmony_ci * Depth of TX fifo (in 128 byte units; range 2-31) 1098c2ecf20Sopenharmony_ci * Smaller numbers are better for network latency 1108c2ecf20Sopenharmony_ci * Larger numbers are better for PCI latency 1118c2ecf20Sopenharmony_ci * I'm really sure where the best tradeoff is, but the BSD driver uses 1128c2ecf20Sopenharmony_ci * 7 and it seems to work ok. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci#define TX_FIFO_DEPTH (7) 1158c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * How often (in jiffies) we will try to unstick stuck connections - 1198c2ecf20Sopenharmony_ci * shouldn't need to happen much 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci#define LANAI_POLL_PERIOD (10*HZ) 1228c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * When allocating an AAL5 receiving buffer, try to make it at least 1268c2ecf20Sopenharmony_ci * large enough to hold this many max_sdu sized PDUs 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci#define AAL5_RX_MULTIPLIER (3) 1298c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Same for transmitting buffer 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci#define AAL5_TX_MULTIPLIER (3) 1358c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * When allocating an AAL0 transmiting buffer, how many cells should fit. 1398c2ecf20Sopenharmony_ci * Remember we'll end up with a PAGE_SIZE of them anyway, so this isn't 1408c2ecf20Sopenharmony_ci * really critical 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci#define AAL0_TX_MULTIPLIER (40) 1438c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * How large should we make the AAL0 receiving buffer. Remember that this 1478c2ecf20Sopenharmony_ci * is shared between all AAL0 VC's 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci#define AAL0_RX_BUFFER_SIZE (PAGE_SIZE) 1508c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * Should we use Lanai's "powerdown" feature when no vcc's are bound? 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci/* #define USE_POWERDOWN */ 1568c2ecf20Sopenharmony_ci/* TODO: make above a module load-time option (also) */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* -------------------- DEBUGGING AIDS: */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#define DEV_LABEL "lanai" 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#ifdef DEBUG 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define DPRINTK(format, args...) \ 1658c2ecf20Sopenharmony_ci printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 1668c2ecf20Sopenharmony_ci#define APRINTK(truth, format, args...) \ 1678c2ecf20Sopenharmony_ci do { \ 1688c2ecf20Sopenharmony_ci if (unlikely(!(truth))) \ 1698c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": " format, ##args); \ 1708c2ecf20Sopenharmony_ci } while (0) 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#else /* !DEBUG */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define DPRINTK(format, args...) 1758c2ecf20Sopenharmony_ci#define APRINTK(truth, format, args...) 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#endif /* DEBUG */ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#ifdef DEBUG_RW 1808c2ecf20Sopenharmony_ci#define RWDEBUG(format, args...) \ 1818c2ecf20Sopenharmony_ci printk(KERN_DEBUG DEV_LABEL ": " format, ##args) 1828c2ecf20Sopenharmony_ci#else /* !DEBUG_RW */ 1838c2ecf20Sopenharmony_ci#define RWDEBUG(format, args...) 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* -------------------- DATA DEFINITIONS: */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define LANAI_MAPPING_SIZE (0x40000) 1898c2ecf20Sopenharmony_ci#define LANAI_EEPROM_SIZE (128) 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_citypedef int vci_t; 1928c2ecf20Sopenharmony_citypedef void __iomem *bus_addr_t; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* DMA buffer in host memory for TX, RX, or service list. */ 1958c2ecf20Sopenharmony_cistruct lanai_buffer { 1968c2ecf20Sopenharmony_ci u32 *start; /* From get_free_pages */ 1978c2ecf20Sopenharmony_ci u32 *end; /* One past last byte */ 1988c2ecf20Sopenharmony_ci u32 *ptr; /* Pointer to current host location */ 1998c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistruct lanai_vcc_stats { 2038c2ecf20Sopenharmony_ci unsigned rx_nomem; 2048c2ecf20Sopenharmony_ci union { 2058c2ecf20Sopenharmony_ci struct { 2068c2ecf20Sopenharmony_ci unsigned rx_badlen; 2078c2ecf20Sopenharmony_ci unsigned service_trash; 2088c2ecf20Sopenharmony_ci unsigned service_stream; 2098c2ecf20Sopenharmony_ci unsigned service_rxcrc; 2108c2ecf20Sopenharmony_ci } aal5; 2118c2ecf20Sopenharmony_ci struct { 2128c2ecf20Sopenharmony_ci } aal0; 2138c2ecf20Sopenharmony_ci } x; 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistruct lanai_dev; /* Forward declaration */ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * This is the card-specific per-vcc data. Note that unlike some other 2208c2ecf20Sopenharmony_ci * drivers there is NOT a 1-to-1 correspondance between these and 2218c2ecf20Sopenharmony_ci * atm_vcc's - each one of these represents an actual 2-way vcc, but 2228c2ecf20Sopenharmony_ci * an atm_vcc can be 1-way and share with a 1-way vcc in the other 2238c2ecf20Sopenharmony_ci * direction. To make it weirder, there can even be 0-way vccs 2248c2ecf20Sopenharmony_ci * bound to us, waiting to do a change_qos 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistruct lanai_vcc { 2278c2ecf20Sopenharmony_ci bus_addr_t vbase; /* Base of VCC's registers */ 2288c2ecf20Sopenharmony_ci struct lanai_vcc_stats stats; 2298c2ecf20Sopenharmony_ci int nref; /* # of atm_vcc's who reference us */ 2308c2ecf20Sopenharmony_ci vci_t vci; 2318c2ecf20Sopenharmony_ci struct { 2328c2ecf20Sopenharmony_ci struct lanai_buffer buf; 2338c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc; /* atm_vcc who is receiver */ 2348c2ecf20Sopenharmony_ci } rx; 2358c2ecf20Sopenharmony_ci struct { 2368c2ecf20Sopenharmony_ci struct lanai_buffer buf; 2378c2ecf20Sopenharmony_ci struct atm_vcc *atmvcc; /* atm_vcc who is transmitter */ 2388c2ecf20Sopenharmony_ci int endptr; /* last endptr from service entry */ 2398c2ecf20Sopenharmony_ci struct sk_buff_head backlog; 2408c2ecf20Sopenharmony_ci void (*unqueue)(struct lanai_dev *, struct lanai_vcc *, int); 2418c2ecf20Sopenharmony_ci } tx; 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cienum lanai_type { 2458c2ecf20Sopenharmony_ci lanai2 = PCI_DEVICE_ID_EF_ATM_LANAI2, 2468c2ecf20Sopenharmony_ci lanaihb = PCI_DEVICE_ID_EF_ATM_LANAIHB 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistruct lanai_dev_stats { 2508c2ecf20Sopenharmony_ci unsigned ovfl_trash; /* # of cells dropped - buffer overflow */ 2518c2ecf20Sopenharmony_ci unsigned vci_trash; /* # of cells dropped - closed vci */ 2528c2ecf20Sopenharmony_ci unsigned hec_err; /* # of cells dropped - bad HEC */ 2538c2ecf20Sopenharmony_ci unsigned atm_ovfl; /* # of cells dropped - rx fifo overflow */ 2548c2ecf20Sopenharmony_ci unsigned pcierr_parity_detect; 2558c2ecf20Sopenharmony_ci unsigned pcierr_serr_set; 2568c2ecf20Sopenharmony_ci unsigned pcierr_master_abort; 2578c2ecf20Sopenharmony_ci unsigned pcierr_m_target_abort; 2588c2ecf20Sopenharmony_ci unsigned pcierr_s_target_abort; 2598c2ecf20Sopenharmony_ci unsigned pcierr_master_parity; 2608c2ecf20Sopenharmony_ci unsigned service_notx; 2618c2ecf20Sopenharmony_ci unsigned service_norx; 2628c2ecf20Sopenharmony_ci unsigned service_rxnotaal5; 2638c2ecf20Sopenharmony_ci unsigned dma_reenable; 2648c2ecf20Sopenharmony_ci unsigned card_reset; 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistruct lanai_dev { 2688c2ecf20Sopenharmony_ci bus_addr_t base; 2698c2ecf20Sopenharmony_ci struct lanai_dev_stats stats; 2708c2ecf20Sopenharmony_ci struct lanai_buffer service; 2718c2ecf20Sopenharmony_ci struct lanai_vcc **vccs; 2728c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 2738c2ecf20Sopenharmony_ci int nbound; /* number of bound vccs */ 2748c2ecf20Sopenharmony_ci#endif 2758c2ecf20Sopenharmony_ci enum lanai_type type; 2768c2ecf20Sopenharmony_ci vci_t num_vci; /* Currently just NUM_VCI */ 2778c2ecf20Sopenharmony_ci u8 eeprom[LANAI_EEPROM_SIZE]; 2788c2ecf20Sopenharmony_ci u32 serialno, magicno; 2798c2ecf20Sopenharmony_ci struct pci_dev *pci; 2808c2ecf20Sopenharmony_ci DECLARE_BITMAP(backlog_vccs, NUM_VCI); /* VCCs with tx backlog */ 2818c2ecf20Sopenharmony_ci DECLARE_BITMAP(transmit_ready, NUM_VCI); /* VCCs with transmit space */ 2828c2ecf20Sopenharmony_ci struct timer_list timer; 2838c2ecf20Sopenharmony_ci int naal0; 2848c2ecf20Sopenharmony_ci struct lanai_buffer aal0buf; /* AAL0 RX buffers */ 2858c2ecf20Sopenharmony_ci u32 conf1, conf2; /* CONFIG[12] registers */ 2868c2ecf20Sopenharmony_ci u32 status; /* STATUS register */ 2878c2ecf20Sopenharmony_ci spinlock_t endtxlock; 2888c2ecf20Sopenharmony_ci spinlock_t servicelock; 2898c2ecf20Sopenharmony_ci struct atm_vcc *cbrvcc; 2908c2ecf20Sopenharmony_ci int number; 2918c2ecf20Sopenharmony_ci int board_rev; 2928c2ecf20Sopenharmony_ci/* TODO - look at race conditions with maintence of conf1/conf2 */ 2938c2ecf20Sopenharmony_ci/* TODO - transmit locking: should we use _irq not _irqsave? */ 2948c2ecf20Sopenharmony_ci/* TODO - organize above in some rational fashion (see <asm/cache.h>) */ 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Each device has two bitmaps for each VCC (baclog_vccs and transmit_ready) 2998c2ecf20Sopenharmony_ci * This function iterates one of these, calling a given function for each 3008c2ecf20Sopenharmony_ci * vci with their bit set 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic void vci_bitfield_iterate(struct lanai_dev *lanai, 3038c2ecf20Sopenharmony_ci const unsigned long *lp, 3048c2ecf20Sopenharmony_ci void (*func)(struct lanai_dev *,vci_t vci)) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci vci_t vci; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci for_each_set_bit(vci, lp, NUM_VCI) 3098c2ecf20Sopenharmony_ci func(lanai, vci); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* -------------------- BUFFER UTILITIES: */ 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes - 3168c2ecf20Sopenharmony_ci * usually any page allocation will do. Just to be safe in case 3178c2ecf20Sopenharmony_ci * PAGE_SIZE is insanely tiny, though... 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci#define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024) 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * Allocate a buffer in host RAM for service list, RX, or TX 3238c2ecf20Sopenharmony_ci * Returns buf->start==NULL if no memory 3248c2ecf20Sopenharmony_ci * Note that the size will be rounded up 2^n bytes, and 3258c2ecf20Sopenharmony_ci * if we can't allocate that we'll settle for something smaller 3268c2ecf20Sopenharmony_ci * until minbytes 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cistatic void lanai_buf_allocate(struct lanai_buffer *buf, 3298c2ecf20Sopenharmony_ci size_t bytes, size_t minbytes, struct pci_dev *pci) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int size; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (bytes > (128 * 1024)) /* max lanai buffer size */ 3348c2ecf20Sopenharmony_ci bytes = 128 * 1024; 3358c2ecf20Sopenharmony_ci for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2) 3368c2ecf20Sopenharmony_ci ; 3378c2ecf20Sopenharmony_ci if (minbytes < LANAI_PAGE_SIZE) 3388c2ecf20Sopenharmony_ci minbytes = LANAI_PAGE_SIZE; 3398c2ecf20Sopenharmony_ci do { 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * Technically we could use non-consistent mappings for 3428c2ecf20Sopenharmony_ci * everything, but the way the lanai uses DMA memory would 3438c2ecf20Sopenharmony_ci * make that a terrific pain. This is much simpler. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci buf->start = dma_alloc_coherent(&pci->dev, 3468c2ecf20Sopenharmony_ci size, &buf->dmaaddr, GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (buf->start != NULL) { /* Success */ 3488c2ecf20Sopenharmony_ci /* Lanai requires 256-byte alignment of DMA bufs */ 3498c2ecf20Sopenharmony_ci APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, 3508c2ecf20Sopenharmony_ci "bad dmaaddr: 0x%lx\n", 3518c2ecf20Sopenharmony_ci (unsigned long) buf->dmaaddr); 3528c2ecf20Sopenharmony_ci buf->ptr = buf->start; 3538c2ecf20Sopenharmony_ci buf->end = (u32 *) 3548c2ecf20Sopenharmony_ci (&((unsigned char *) buf->start)[size]); 3558c2ecf20Sopenharmony_ci memset(buf->start, 0, size); 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci size /= 2; 3598c2ecf20Sopenharmony_ci } while (size >= minbytes); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* size of buffer in bytes */ 3638c2ecf20Sopenharmony_cistatic inline size_t lanai_buf_size(const struct lanai_buffer *buf) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci return ((unsigned long) buf->end) - ((unsigned long) buf->start); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void lanai_buf_deallocate(struct lanai_buffer *buf, 3698c2ecf20Sopenharmony_ci struct pci_dev *pci) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci if (buf->start != NULL) { 3728c2ecf20Sopenharmony_ci dma_free_coherent(&pci->dev, lanai_buf_size(buf), 3738c2ecf20Sopenharmony_ci buf->start, buf->dmaaddr); 3748c2ecf20Sopenharmony_ci buf->start = buf->end = buf->ptr = NULL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* size of buffer as "card order" (0=1k .. 7=128k) */ 3798c2ecf20Sopenharmony_cistatic int lanai_buf_size_cardorder(const struct lanai_buffer *buf) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* This can only happen if PAGE_SIZE is gigantic, but just in case */ 3848c2ecf20Sopenharmony_ci if (order > 7) 3858c2ecf20Sopenharmony_ci order = 7; 3868c2ecf20Sopenharmony_ci return order; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* -------------------- PORT I/O UTILITIES: */ 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* Registers (and their bit-fields) */ 3928c2ecf20Sopenharmony_cienum lanai_register { 3938c2ecf20Sopenharmony_ci Reset_Reg = 0x00, /* Reset; read for chip type; bits: */ 3948c2ecf20Sopenharmony_ci#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */ 3958c2ecf20Sopenharmony_ci#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */ 3968c2ecf20Sopenharmony_ci#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */ 3978c2ecf20Sopenharmony_ci Endian_Reg = 0x04, /* Endian setting */ 3988c2ecf20Sopenharmony_ci IntStatus_Reg = 0x08, /* Interrupt status */ 3998c2ecf20Sopenharmony_ci IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */ 4008c2ecf20Sopenharmony_ci IntAck_Reg = 0x10, /* Interrupt acknowledge */ 4018c2ecf20Sopenharmony_ci IntAckMasked_Reg = 0x14, /* Interrupt acknowledge (masked) */ 4028c2ecf20Sopenharmony_ci IntStatusSet_Reg = 0x18, /* Get status + enable/disable */ 4038c2ecf20Sopenharmony_ci IntStatusSetMasked_Reg = 0x1C, /* Get status + en/di (masked) */ 4048c2ecf20Sopenharmony_ci IntControlEna_Reg = 0x20, /* Interrupt control enable */ 4058c2ecf20Sopenharmony_ci IntControlDis_Reg = 0x24, /* Interrupt control disable */ 4068c2ecf20Sopenharmony_ci Status_Reg = 0x28, /* Status */ 4078c2ecf20Sopenharmony_ci#define STATUS_PROMDATA (0x00000001) /* PROM_DATA pin */ 4088c2ecf20Sopenharmony_ci#define STATUS_WAITING (0x00000002) /* Interrupt being delayed */ 4098c2ecf20Sopenharmony_ci#define STATUS_SOOL (0x00000004) /* SOOL alarm */ 4108c2ecf20Sopenharmony_ci#define STATUS_LOCD (0x00000008) /* LOCD alarm */ 4118c2ecf20Sopenharmony_ci#define STATUS_LED (0x00000010) /* LED (HAPPI) output */ 4128c2ecf20Sopenharmony_ci#define STATUS_GPIN (0x00000020) /* GPIN pin */ 4138c2ecf20Sopenharmony_ci#define STATUS_BUTTBUSY (0x00000040) /* Butt register is pending */ 4148c2ecf20Sopenharmony_ci Config1_Reg = 0x2C, /* Config word 1; bits: */ 4158c2ecf20Sopenharmony_ci#define CONFIG1_PROMDATA (0x00000001) /* PROM_DATA pin */ 4168c2ecf20Sopenharmony_ci#define CONFIG1_PROMCLK (0x00000002) /* PROM_CLK pin */ 4178c2ecf20Sopenharmony_ci#define CONFIG1_SET_READMODE(x) ((x)*0x004) /* PCI BM reads; values: */ 4188c2ecf20Sopenharmony_ci#define READMODE_PLAIN (0) /* Plain memory read */ 4198c2ecf20Sopenharmony_ci#define READMODE_LINE (2) /* Memory read line */ 4208c2ecf20Sopenharmony_ci#define READMODE_MULTIPLE (3) /* Memory read multiple */ 4218c2ecf20Sopenharmony_ci#define CONFIG1_DMA_ENABLE (0x00000010) /* Turn on DMA */ 4228c2ecf20Sopenharmony_ci#define CONFIG1_POWERDOWN (0x00000020) /* Turn off clocks */ 4238c2ecf20Sopenharmony_ci#define CONFIG1_SET_LOOPMODE(x) ((x)*0x080) /* Clock&loop mode; values: */ 4248c2ecf20Sopenharmony_ci#define LOOPMODE_NORMAL (0) /* Normal - no loop */ 4258c2ecf20Sopenharmony_ci#define LOOPMODE_TIME (1) 4268c2ecf20Sopenharmony_ci#define LOOPMODE_DIAG (2) 4278c2ecf20Sopenharmony_ci#define LOOPMODE_LINE (3) 4288c2ecf20Sopenharmony_ci#define CONFIG1_MASK_LOOPMODE (0x00000180) 4298c2ecf20Sopenharmony_ci#define CONFIG1_SET_LEDMODE(x) ((x)*0x0200) /* Mode of LED; values: */ 4308c2ecf20Sopenharmony_ci#define LEDMODE_NOT_SOOL (0) /* !SOOL */ 4318c2ecf20Sopenharmony_ci#define LEDMODE_OFF (1) /* 0 */ 4328c2ecf20Sopenharmony_ci#define LEDMODE_ON (2) /* 1 */ 4338c2ecf20Sopenharmony_ci#define LEDMODE_NOT_LOCD (3) /* !LOCD */ 4348c2ecf20Sopenharmony_ci#define LEDMORE_GPIN (4) /* GPIN */ 4358c2ecf20Sopenharmony_ci#define LEDMODE_NOT_GPIN (7) /* !GPIN */ 4368c2ecf20Sopenharmony_ci#define CONFIG1_MASK_LEDMODE (0x00000E00) 4378c2ecf20Sopenharmony_ci#define CONFIG1_GPOUT1 (0x00001000) /* Toggle for reset */ 4388c2ecf20Sopenharmony_ci#define CONFIG1_GPOUT2 (0x00002000) /* Loopback PHY */ 4398c2ecf20Sopenharmony_ci#define CONFIG1_GPOUT3 (0x00004000) /* Loopback lanai */ 4408c2ecf20Sopenharmony_ci Config2_Reg = 0x30, /* Config word 2; bits: */ 4418c2ecf20Sopenharmony_ci#define CONFIG2_HOWMANY (0x00000001) /* >512 VCIs? */ 4428c2ecf20Sopenharmony_ci#define CONFIG2_PTI7_MODE (0x00000002) /* Make PTI=7 RM, not OAM */ 4438c2ecf20Sopenharmony_ci#define CONFIG2_VPI_CHK_DIS (0x00000004) /* Ignore RX VPI value */ 4448c2ecf20Sopenharmony_ci#define CONFIG2_HEC_DROP (0x00000008) /* Drop cells w/ HEC errors */ 4458c2ecf20Sopenharmony_ci#define CONFIG2_VCI0_NORMAL (0x00000010) /* Treat VCI=0 normally */ 4468c2ecf20Sopenharmony_ci#define CONFIG2_CBR_ENABLE (0x00000020) /* Deal with CBR traffic */ 4478c2ecf20Sopenharmony_ci#define CONFIG2_TRASH_ALL (0x00000040) /* Trashing incoming cells */ 4488c2ecf20Sopenharmony_ci#define CONFIG2_TX_DISABLE (0x00000080) /* Trashing outgoing cells */ 4498c2ecf20Sopenharmony_ci#define CONFIG2_SET_TRASH (0x00000100) /* Turn trashing on */ 4508c2ecf20Sopenharmony_ci Statistics_Reg = 0x34, /* Statistics; bits: */ 4518c2ecf20Sopenharmony_ci#define STATS_GET_FIFO_OVFL(x) (((x)>> 0)&0xFF) /* FIFO overflowed */ 4528c2ecf20Sopenharmony_ci#define STATS_GET_HEC_ERR(x) (((x)>> 8)&0xFF) /* HEC was bad */ 4538c2ecf20Sopenharmony_ci#define STATS_GET_BAD_VCI(x) (((x)>>16)&0xFF) /* VCI not open */ 4548c2ecf20Sopenharmony_ci#define STATS_GET_BUF_OVFL(x) (((x)>>24)&0xFF) /* VCC buffer full */ 4558c2ecf20Sopenharmony_ci ServiceStuff_Reg = 0x38, /* Service stuff; bits: */ 4568c2ecf20Sopenharmony_ci#define SSTUFF_SET_SIZE(x) ((x)*0x20000000) /* size of service buffer */ 4578c2ecf20Sopenharmony_ci#define SSTUFF_SET_ADDR(x) ((x)>>8) /* set address of buffer */ 4588c2ecf20Sopenharmony_ci ServWrite_Reg = 0x3C, /* ServWrite Pointer */ 4598c2ecf20Sopenharmony_ci ServRead_Reg = 0x40, /* ServRead Pointer */ 4608c2ecf20Sopenharmony_ci TxDepth_Reg = 0x44, /* FIFO Transmit Depth */ 4618c2ecf20Sopenharmony_ci Butt_Reg = 0x48, /* Butt register */ 4628c2ecf20Sopenharmony_ci CBR_ICG_Reg = 0x50, 4638c2ecf20Sopenharmony_ci CBR_PTR_Reg = 0x54, 4648c2ecf20Sopenharmony_ci PingCount_Reg = 0x58, /* Ping count */ 4658c2ecf20Sopenharmony_ci DMA_Addr_Reg = 0x5C /* DMA address */ 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic inline bus_addr_t reg_addr(const struct lanai_dev *lanai, 4698c2ecf20Sopenharmony_ci enum lanai_register reg) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci return lanai->base + reg; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic inline u32 reg_read(const struct lanai_dev *lanai, 4758c2ecf20Sopenharmony_ci enum lanai_register reg) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci u32 t; 4788c2ecf20Sopenharmony_ci t = readl(reg_addr(lanai, reg)); 4798c2ecf20Sopenharmony_ci RWDEBUG("R [0x%08X] 0x%02X = 0x%08X\n", (unsigned int) lanai->base, 4808c2ecf20Sopenharmony_ci (int) reg, t); 4818c2ecf20Sopenharmony_ci return t; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic inline void reg_write(const struct lanai_dev *lanai, u32 val, 4858c2ecf20Sopenharmony_ci enum lanai_register reg) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci RWDEBUG("W [0x%08X] 0x%02X < 0x%08X\n", (unsigned int) lanai->base, 4888c2ecf20Sopenharmony_ci (int) reg, val); 4898c2ecf20Sopenharmony_ci writel(val, reg_addr(lanai, reg)); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic inline void conf1_write(const struct lanai_dev *lanai) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci reg_write(lanai, lanai->conf1, Config1_Reg); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic inline void conf2_write(const struct lanai_dev *lanai) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci reg_write(lanai, lanai->conf2, Config2_Reg); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* Same as conf2_write(), but defers I/O if we're powered down */ 5038c2ecf20Sopenharmony_cistatic inline void conf2_write_if_powerup(const struct lanai_dev *lanai) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 5068c2ecf20Sopenharmony_ci if (unlikely((lanai->conf1 & CONFIG1_POWERDOWN) != 0)) 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci#endif /* USE_POWERDOWN */ 5098c2ecf20Sopenharmony_ci conf2_write(lanai); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic inline void reset_board(const struct lanai_dev *lanai) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci DPRINTK("about to reset board\n"); 5158c2ecf20Sopenharmony_ci reg_write(lanai, 0, Reset_Reg); 5168c2ecf20Sopenharmony_ci /* 5178c2ecf20Sopenharmony_ci * If we don't delay a little while here then we can end up 5188c2ecf20Sopenharmony_ci * leaving the card in a VERY weird state and lock up the 5198c2ecf20Sopenharmony_ci * PCI bus. This isn't documented anywhere but I've convinced 5208c2ecf20Sopenharmony_ci * myself after a lot of painful experimentation 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci udelay(5); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/* -------------------- CARD SRAM UTILITIES: */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/* The SRAM is mapped into normal PCI memory space - the only catch is 5288c2ecf20Sopenharmony_ci * that it is only 16-bits wide but must be accessed as 32-bit. The 5298c2ecf20Sopenharmony_ci * 16 high bits will be zero. We don't hide this, since they get 5308c2ecf20Sopenharmony_ci * programmed mostly like discrete registers anyway 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci#define SRAM_START (0x20000) 5338c2ecf20Sopenharmony_ci#define SRAM_BYTES (0x20000) /* Again, half don't really exist */ 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic inline bus_addr_t sram_addr(const struct lanai_dev *lanai, int offset) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci return lanai->base + SRAM_START + offset; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic inline u32 sram_read(const struct lanai_dev *lanai, int offset) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci return readl(sram_addr(lanai, offset)); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic inline void sram_write(const struct lanai_dev *lanai, 5468c2ecf20Sopenharmony_ci u32 val, int offset) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci writel(val, sram_addr(lanai, offset)); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int sram_test_word(const struct lanai_dev *lanai, int offset, 5528c2ecf20Sopenharmony_ci u32 pattern) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci u32 readback; 5558c2ecf20Sopenharmony_ci sram_write(lanai, pattern, offset); 5568c2ecf20Sopenharmony_ci readback = sram_read(lanai, offset); 5578c2ecf20Sopenharmony_ci if (likely(readback == pattern)) 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL 5608c2ecf20Sopenharmony_ci "(itf %d): SRAM word at %d bad: wrote 0x%X, read 0x%X\n", 5618c2ecf20Sopenharmony_ci lanai->number, offset, 5628c2ecf20Sopenharmony_ci (unsigned int) pattern, (unsigned int) readback); 5638c2ecf20Sopenharmony_ci return -EIO; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int sram_test_pass(const struct lanai_dev *lanai, u32 pattern) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci int offset, result = 0; 5698c2ecf20Sopenharmony_ci for (offset = 0; offset < SRAM_BYTES && result == 0; offset += 4) 5708c2ecf20Sopenharmony_ci result = sram_test_word(lanai, offset, pattern); 5718c2ecf20Sopenharmony_ci return result; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int sram_test_and_clear(const struct lanai_dev *lanai) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci#ifdef FULL_MEMORY_TEST 5778c2ecf20Sopenharmony_ci int result; 5788c2ecf20Sopenharmony_ci DPRINTK("testing SRAM\n"); 5798c2ecf20Sopenharmony_ci if ((result = sram_test_pass(lanai, 0x5555)) != 0) 5808c2ecf20Sopenharmony_ci return result; 5818c2ecf20Sopenharmony_ci if ((result = sram_test_pass(lanai, 0xAAAA)) != 0) 5828c2ecf20Sopenharmony_ci return result; 5838c2ecf20Sopenharmony_ci#endif 5848c2ecf20Sopenharmony_ci DPRINTK("clearing SRAM\n"); 5858c2ecf20Sopenharmony_ci return sram_test_pass(lanai, 0x0000); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* -------------------- CARD-BASED VCC TABLE UTILITIES: */ 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* vcc table */ 5918c2ecf20Sopenharmony_cienum lanai_vcc_offset { 5928c2ecf20Sopenharmony_ci vcc_rxaddr1 = 0x00, /* Location1, plus bits: */ 5938c2ecf20Sopenharmony_ci#define RXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of RX buffer */ 5948c2ecf20Sopenharmony_ci#define RXADDR1_SET_RMMODE(x) ((x)*0x00800) /* RM cell action; values: */ 5958c2ecf20Sopenharmony_ci#define RMMODE_TRASH (0) /* discard */ 5968c2ecf20Sopenharmony_ci#define RMMODE_PRESERVE (1) /* input as AAL0 */ 5978c2ecf20Sopenharmony_ci#define RMMODE_PIPE (2) /* pipe to coscheduler */ 5988c2ecf20Sopenharmony_ci#define RMMODE_PIPEALL (3) /* pipe non-RM too */ 5998c2ecf20Sopenharmony_ci#define RXADDR1_OAM_PRESERVE (0x00002000) /* Input OAM cells as AAL0 */ 6008c2ecf20Sopenharmony_ci#define RXADDR1_SET_MODE(x) ((x)*0x0004000) /* Reassembly mode */ 6018c2ecf20Sopenharmony_ci#define RXMODE_TRASH (0) /* discard */ 6028c2ecf20Sopenharmony_ci#define RXMODE_AAL0 (1) /* non-AAL5 mode */ 6038c2ecf20Sopenharmony_ci#define RXMODE_AAL5 (2) /* AAL5, intr. each PDU */ 6048c2ecf20Sopenharmony_ci#define RXMODE_AAL5_STREAM (3) /* AAL5 w/o per-PDU intr */ 6058c2ecf20Sopenharmony_ci vcc_rxaddr2 = 0x04, /* Location2 */ 6068c2ecf20Sopenharmony_ci vcc_rxcrc1 = 0x08, /* RX CRC claculation space */ 6078c2ecf20Sopenharmony_ci vcc_rxcrc2 = 0x0C, 6088c2ecf20Sopenharmony_ci vcc_rxwriteptr = 0x10, /* RX writeptr, plus bits: */ 6098c2ecf20Sopenharmony_ci#define RXWRITEPTR_LASTEFCI (0x00002000) /* Last PDU had EFCI bit */ 6108c2ecf20Sopenharmony_ci#define RXWRITEPTR_DROPPING (0x00004000) /* Had error, dropping */ 6118c2ecf20Sopenharmony_ci#define RXWRITEPTR_TRASHING (0x00008000) /* Trashing */ 6128c2ecf20Sopenharmony_ci vcc_rxbufstart = 0x14, /* RX bufstart, plus bits: */ 6138c2ecf20Sopenharmony_ci#define RXBUFSTART_CLP (0x00004000) 6148c2ecf20Sopenharmony_ci#define RXBUFSTART_CI (0x00008000) 6158c2ecf20Sopenharmony_ci vcc_rxreadptr = 0x18, /* RX readptr */ 6168c2ecf20Sopenharmony_ci vcc_txicg = 0x1C, /* TX ICG */ 6178c2ecf20Sopenharmony_ci vcc_txaddr1 = 0x20, /* Location1, plus bits: */ 6188c2ecf20Sopenharmony_ci#define TXADDR1_SET_SIZE(x) ((x)*0x0000100) /* size of TX buffer */ 6198c2ecf20Sopenharmony_ci#define TXADDR1_ABR (0x00008000) /* use ABR (doesn't work) */ 6208c2ecf20Sopenharmony_ci vcc_txaddr2 = 0x24, /* Location2 */ 6218c2ecf20Sopenharmony_ci vcc_txcrc1 = 0x28, /* TX CRC claculation space */ 6228c2ecf20Sopenharmony_ci vcc_txcrc2 = 0x2C, 6238c2ecf20Sopenharmony_ci vcc_txreadptr = 0x30, /* TX Readptr, plus bits: */ 6248c2ecf20Sopenharmony_ci#define TXREADPTR_GET_PTR(x) ((x)&0x01FFF) 6258c2ecf20Sopenharmony_ci#define TXREADPTR_MASK_DELTA (0x0000E000) /* ? */ 6268c2ecf20Sopenharmony_ci vcc_txendptr = 0x34, /* TX Endptr, plus bits: */ 6278c2ecf20Sopenharmony_ci#define TXENDPTR_CLP (0x00002000) 6288c2ecf20Sopenharmony_ci#define TXENDPTR_MASK_PDUMODE (0x0000C000) /* PDU mode; values: */ 6298c2ecf20Sopenharmony_ci#define PDUMODE_AAL0 (0*0x04000) 6308c2ecf20Sopenharmony_ci#define PDUMODE_AAL5 (2*0x04000) 6318c2ecf20Sopenharmony_ci#define PDUMODE_AAL5STREAM (3*0x04000) 6328c2ecf20Sopenharmony_ci vcc_txwriteptr = 0x38, /* TX Writeptr */ 6338c2ecf20Sopenharmony_ci#define TXWRITEPTR_GET_PTR(x) ((x)&0x1FFF) 6348c2ecf20Sopenharmony_ci vcc_txcbr_next = 0x3C /* # of next CBR VCI in ring */ 6358c2ecf20Sopenharmony_ci#define TXCBR_NEXT_BOZO (0x00008000) /* "bozo bit" */ 6368c2ecf20Sopenharmony_ci}; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci#define CARDVCC_SIZE (0x40) 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic inline bus_addr_t cardvcc_addr(const struct lanai_dev *lanai, 6418c2ecf20Sopenharmony_ci vci_t vci) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci return sram_addr(lanai, vci * CARDVCC_SIZE); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic inline u32 cardvcc_read(const struct lanai_vcc *lvcc, 6478c2ecf20Sopenharmony_ci enum lanai_vcc_offset offset) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci u32 val; 6508c2ecf20Sopenharmony_ci APRINTK(lvcc->vbase != NULL, "cardvcc_read: unbound vcc!\n"); 6518c2ecf20Sopenharmony_ci val= readl(lvcc->vbase + offset); 6528c2ecf20Sopenharmony_ci RWDEBUG("VR vci=%04d 0x%02X = 0x%08X\n", 6538c2ecf20Sopenharmony_ci lvcc->vci, (int) offset, val); 6548c2ecf20Sopenharmony_ci return val; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic inline void cardvcc_write(const struct lanai_vcc *lvcc, 6588c2ecf20Sopenharmony_ci u32 val, enum lanai_vcc_offset offset) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci APRINTK(lvcc->vbase != NULL, "cardvcc_write: unbound vcc!\n"); 6618c2ecf20Sopenharmony_ci APRINTK((val & ~0xFFFF) == 0, 6628c2ecf20Sopenharmony_ci "cardvcc_write: bad val 0x%X (vci=%d, addr=0x%02X)\n", 6638c2ecf20Sopenharmony_ci (unsigned int) val, lvcc->vci, (unsigned int) offset); 6648c2ecf20Sopenharmony_ci RWDEBUG("VW vci=%04d 0x%02X > 0x%08X\n", 6658c2ecf20Sopenharmony_ci lvcc->vci, (unsigned int) offset, (unsigned int) val); 6668c2ecf20Sopenharmony_ci writel(val, lvcc->vbase + offset); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* -------------------- COMPUTE SIZE OF AN AAL5 PDU: */ 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* How many bytes will an AAL5 PDU take to transmit - remember that: 6728c2ecf20Sopenharmony_ci * o we need to add 8 bytes for length, CPI, UU, and CRC 6738c2ecf20Sopenharmony_ci * o we need to round up to 48 bytes for cells 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_cistatic inline int aal5_size(int size) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci int cells = (size + 8 + 47) / 48; 6788c2ecf20Sopenharmony_ci return cells * 48; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* -------------------- FREE AN ATM SKB: */ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci if (atmvcc->pop != NULL) 6868c2ecf20Sopenharmony_ci atmvcc->pop(atmvcc, skb); 6878c2ecf20Sopenharmony_ci else 6888c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci/* -------------------- TURN VCCS ON AND OFF: */ 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic void host_vcc_start_rx(const struct lanai_vcc *lvcc) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci u32 addr1; 6968c2ecf20Sopenharmony_ci if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) { 6978c2ecf20Sopenharmony_ci dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr; 6988c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1); 6998c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2); 7008c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxwriteptr); 7018c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxbufstart); 7028c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxreadptr); 7038c2ecf20Sopenharmony_ci cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_rxaddr2); 7048c2ecf20Sopenharmony_ci addr1 = ((dmaaddr >> 8) & 0xFF) | 7058c2ecf20Sopenharmony_ci RXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->rx.buf))| 7068c2ecf20Sopenharmony_ci RXADDR1_SET_RMMODE(RMMODE_TRASH) | /* ??? */ 7078c2ecf20Sopenharmony_ci /* RXADDR1_OAM_PRESERVE | --- no OAM support yet */ 7088c2ecf20Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_AAL5); 7098c2ecf20Sopenharmony_ci } else 7108c2ecf20Sopenharmony_ci addr1 = RXADDR1_SET_RMMODE(RMMODE_PRESERVE) | /* ??? */ 7118c2ecf20Sopenharmony_ci RXADDR1_OAM_PRESERVE | /* ??? */ 7128c2ecf20Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_AAL0); 7138c2ecf20Sopenharmony_ci /* This one must be last! */ 7148c2ecf20Sopenharmony_ci cardvcc_write(lvcc, addr1, vcc_rxaddr1); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic void host_vcc_start_tx(const struct lanai_vcc *lvcc) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr; 7208c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txicg); 7218c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1); 7228c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2); 7238c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txreadptr); 7248c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txendptr); 7258c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txwriteptr); 7268c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 7278c2ecf20Sopenharmony_ci (lvcc->tx.atmvcc->qos.txtp.traffic_class == ATM_CBR) ? 7288c2ecf20Sopenharmony_ci TXCBR_NEXT_BOZO | lvcc->vci : 0, vcc_txcbr_next); 7298c2ecf20Sopenharmony_ci cardvcc_write(lvcc, (dmaaddr >> 16) & 0xFFFF, vcc_txaddr2); 7308c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 7318c2ecf20Sopenharmony_ci ((dmaaddr >> 8) & 0xFF) | 7328c2ecf20Sopenharmony_ci TXADDR1_SET_SIZE(lanai_buf_size_cardorder(&lvcc->tx.buf)), 7338c2ecf20Sopenharmony_ci vcc_txaddr1); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci/* Shutdown receiving on card */ 7378c2ecf20Sopenharmony_cistatic void lanai_shutdown_rx_vci(const struct lanai_vcc *lvcc) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 7408c2ecf20Sopenharmony_ci return; 7418c2ecf20Sopenharmony_ci /* 15.1.1 - set to trashing, wait one cell time (15us) */ 7428c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 7438c2ecf20Sopenharmony_ci RXADDR1_SET_RMMODE(RMMODE_TRASH) | 7448c2ecf20Sopenharmony_ci RXADDR1_SET_MODE(RXMODE_TRASH), vcc_rxaddr1); 7458c2ecf20Sopenharmony_ci udelay(15); 7468c2ecf20Sopenharmony_ci /* 15.1.2 - clear rest of entries */ 7478c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxaddr2); 7488c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxcrc1); 7498c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxcrc2); 7508c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxwriteptr); 7518c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxbufstart); 7528c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_rxreadptr); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* Shutdown transmitting on card. 7568c2ecf20Sopenharmony_ci * Unfortunately the lanai needs us to wait until all the data 7578c2ecf20Sopenharmony_ci * drains out of the buffer before we can dealloc it, so this 7588c2ecf20Sopenharmony_ci * can take awhile -- up to 370ms for a full 128KB buffer 7598c2ecf20Sopenharmony_ci * assuming everone else is quiet. In theory the time is 7608c2ecf20Sopenharmony_ci * boundless if there's a CBR VCC holding things up. 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_cistatic void lanai_shutdown_tx_vci(struct lanai_dev *lanai, 7638c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct sk_buff *skb; 7668c2ecf20Sopenharmony_ci unsigned long flags, timeout; 7678c2ecf20Sopenharmony_ci int read, write, lastread = -1; 7688c2ecf20Sopenharmony_ci APRINTK(!in_interrupt(), 7698c2ecf20Sopenharmony_ci "lanai_shutdown_tx_vci called w/o process context!\n"); 7708c2ecf20Sopenharmony_ci if (lvcc->vbase == NULL) /* We were never bound to a VCI */ 7718c2ecf20Sopenharmony_ci return; 7728c2ecf20Sopenharmony_ci /* 15.2.1 - wait for queue to drain */ 7738c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&lvcc->tx.backlog)) != NULL) 7748c2ecf20Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 7758c2ecf20Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 7768c2ecf20Sopenharmony_ci __clear_bit(lvcc->vci, lanai->backlog_vccs); 7778c2ecf20Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * We need to wait for the VCC to drain but don't wait forever. We 7808c2ecf20Sopenharmony_ci * give each 1K of buffer size 1/128th of a second to clear out. 7818c2ecf20Sopenharmony_ci * TODO: maybe disable CBR if we're about to timeout? 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci timeout = jiffies + 7848c2ecf20Sopenharmony_ci (((lanai_buf_size(&lvcc->tx.buf) / 1024) * HZ) >> 7); 7858c2ecf20Sopenharmony_ci write = TXWRITEPTR_GET_PTR(cardvcc_read(lvcc, vcc_txwriteptr)); 7868c2ecf20Sopenharmony_ci for (;;) { 7878c2ecf20Sopenharmony_ci read = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 7888c2ecf20Sopenharmony_ci if (read == write && /* Is TX buffer empty? */ 7898c2ecf20Sopenharmony_ci (lvcc->tx.atmvcc->qos.txtp.traffic_class != ATM_CBR || 7908c2ecf20Sopenharmony_ci (cardvcc_read(lvcc, vcc_txcbr_next) & 7918c2ecf20Sopenharmony_ci TXCBR_NEXT_BOZO) == 0)) 7928c2ecf20Sopenharmony_ci break; 7938c2ecf20Sopenharmony_ci if (read != lastread) { /* Has there been any progress? */ 7948c2ecf20Sopenharmony_ci lastread = read; 7958c2ecf20Sopenharmony_ci timeout += HZ / 10; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci if (unlikely(time_after(jiffies, timeout))) { 7988c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): Timed out on " 7998c2ecf20Sopenharmony_ci "backlog closing vci %d\n", 8008c2ecf20Sopenharmony_ci lvcc->tx.atmvcc->dev->number, lvcc->vci); 8018c2ecf20Sopenharmony_ci DPRINTK("read, write = %d, %d\n", read, write); 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci msleep(40); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci /* 15.2.2 - clear out all tx registers */ 8078c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txreadptr); 8088c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txwriteptr); 8098c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txendptr); 8108c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txcrc1); 8118c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txcrc2); 8128c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txaddr2); 8138c2ecf20Sopenharmony_ci cardvcc_write(lvcc, 0, vcc_txaddr1); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci/* -------------------- MANAGING AAL0 RX BUFFER: */ 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic inline int aal0_buffer_allocate(struct lanai_dev *lanai) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n"); 8218c2ecf20Sopenharmony_ci lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80, 8228c2ecf20Sopenharmony_ci lanai->pci); 8238c2ecf20Sopenharmony_ci return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic inline void aal0_buffer_free(struct lanai_dev *lanai) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n"); 8298c2ecf20Sopenharmony_ci lanai_buf_deallocate(&lanai->aal0buf, lanai->pci); 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/* -------------------- EEPROM UTILITIES: */ 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* Offsets of data in the EEPROM */ 8358c2ecf20Sopenharmony_ci#define EEPROM_COPYRIGHT (0) 8368c2ecf20Sopenharmony_ci#define EEPROM_COPYRIGHT_LEN (44) 8378c2ecf20Sopenharmony_ci#define EEPROM_CHECKSUM (62) 8388c2ecf20Sopenharmony_ci#define EEPROM_CHECKSUM_REV (63) 8398c2ecf20Sopenharmony_ci#define EEPROM_MAC (64) 8408c2ecf20Sopenharmony_ci#define EEPROM_MAC_REV (70) 8418c2ecf20Sopenharmony_ci#define EEPROM_SERIAL (112) 8428c2ecf20Sopenharmony_ci#define EEPROM_SERIAL_REV (116) 8438c2ecf20Sopenharmony_ci#define EEPROM_MAGIC (120) 8448c2ecf20Sopenharmony_ci#define EEPROM_MAGIC_REV (124) 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci#define EEPROM_MAGIC_VALUE (0x5AB478D2) 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci#ifndef READ_EEPROM 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci/* Stub functions to use if EEPROM reading is disabled */ 8518c2ecf20Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): *NOT* reading EEPROM\n", 8548c2ecf20Sopenharmony_ci lanai->number); 8558c2ecf20Sopenharmony_ci memset(&lanai->eeprom[EEPROM_MAC], 0, 6); 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci lanai->serialno = 0; 8628c2ecf20Sopenharmony_ci lanai->magicno = EEPROM_MAGIC_VALUE; 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci#else /* READ_EEPROM */ 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int eeprom_read(struct lanai_dev *lanai) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci int i, address; 8718c2ecf20Sopenharmony_ci u8 data; 8728c2ecf20Sopenharmony_ci u32 tmp; 8738c2ecf20Sopenharmony_ci#define set_config1(x) do { lanai->conf1 = x; conf1_write(lanai); \ 8748c2ecf20Sopenharmony_ci } while (0) 8758c2ecf20Sopenharmony_ci#define clock_h() set_config1(lanai->conf1 | CONFIG1_PROMCLK) 8768c2ecf20Sopenharmony_ci#define clock_l() set_config1(lanai->conf1 &~ CONFIG1_PROMCLK) 8778c2ecf20Sopenharmony_ci#define data_h() set_config1(lanai->conf1 | CONFIG1_PROMDATA) 8788c2ecf20Sopenharmony_ci#define data_l() set_config1(lanai->conf1 &~ CONFIG1_PROMDATA) 8798c2ecf20Sopenharmony_ci#define pre_read() do { data_h(); clock_h(); udelay(5); } while (0) 8808c2ecf20Sopenharmony_ci#define read_pin() (reg_read(lanai, Status_Reg) & STATUS_PROMDATA) 8818c2ecf20Sopenharmony_ci#define send_stop() do { data_l(); udelay(5); clock_h(); udelay(5); \ 8828c2ecf20Sopenharmony_ci data_h(); udelay(5); } while (0) 8838c2ecf20Sopenharmony_ci /* start with both clock and data high */ 8848c2ecf20Sopenharmony_ci data_h(); clock_h(); udelay(5); 8858c2ecf20Sopenharmony_ci for (address = 0; address < LANAI_EEPROM_SIZE; address++) { 8868c2ecf20Sopenharmony_ci data = (address << 1) | 1; /* Command=read + address */ 8878c2ecf20Sopenharmony_ci /* send start bit */ 8888c2ecf20Sopenharmony_ci data_l(); udelay(5); 8898c2ecf20Sopenharmony_ci clock_l(); udelay(5); 8908c2ecf20Sopenharmony_ci for (i = 128; i != 0; i >>= 1) { /* write command out */ 8918c2ecf20Sopenharmony_ci tmp = (lanai->conf1 & ~CONFIG1_PROMDATA) | 8928c2ecf20Sopenharmony_ci ((data & i) ? CONFIG1_PROMDATA : 0); 8938c2ecf20Sopenharmony_ci if (lanai->conf1 != tmp) { 8948c2ecf20Sopenharmony_ci set_config1(tmp); 8958c2ecf20Sopenharmony_ci udelay(5); /* Let new data settle */ 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci clock_h(); udelay(5); clock_l(); udelay(5); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci /* look for ack */ 9008c2ecf20Sopenharmony_ci data_h(); clock_h(); udelay(5); 9018c2ecf20Sopenharmony_ci if (read_pin() != 0) 9028c2ecf20Sopenharmony_ci goto error; /* No ack seen */ 9038c2ecf20Sopenharmony_ci clock_l(); udelay(5); 9048c2ecf20Sopenharmony_ci /* read back result */ 9058c2ecf20Sopenharmony_ci for (data = 0, i = 7; i >= 0; i--) { 9068c2ecf20Sopenharmony_ci data_h(); clock_h(); udelay(5); 9078c2ecf20Sopenharmony_ci data = (data << 1) | !!read_pin(); 9088c2ecf20Sopenharmony_ci clock_l(); udelay(5); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci /* look again for ack */ 9118c2ecf20Sopenharmony_ci data_h(); clock_h(); udelay(5); 9128c2ecf20Sopenharmony_ci if (read_pin() == 0) 9138c2ecf20Sopenharmony_ci goto error; /* Spurious ack */ 9148c2ecf20Sopenharmony_ci clock_l(); udelay(5); 9158c2ecf20Sopenharmony_ci send_stop(); 9168c2ecf20Sopenharmony_ci lanai->eeprom[address] = data; 9178c2ecf20Sopenharmony_ci DPRINTK("EEPROM 0x%04X %02X\n", 9188c2ecf20Sopenharmony_ci (unsigned int) address, (unsigned int) data); 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci error: 9228c2ecf20Sopenharmony_ci clock_l(); udelay(5); /* finish read */ 9238c2ecf20Sopenharmony_ci send_stop(); 9248c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): error reading EEPROM byte %d\n", 9258c2ecf20Sopenharmony_ci lanai->number, address); 9268c2ecf20Sopenharmony_ci return -EIO; 9278c2ecf20Sopenharmony_ci#undef set_config1 9288c2ecf20Sopenharmony_ci#undef clock_h 9298c2ecf20Sopenharmony_ci#undef clock_l 9308c2ecf20Sopenharmony_ci#undef data_h 9318c2ecf20Sopenharmony_ci#undef data_l 9328c2ecf20Sopenharmony_ci#undef pre_read 9338c2ecf20Sopenharmony_ci#undef read_pin 9348c2ecf20Sopenharmony_ci#undef send_stop 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci/* read a big-endian 4-byte value out of eeprom */ 9388c2ecf20Sopenharmony_cistatic inline u32 eeprom_be4(const struct lanai_dev *lanai, int address) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci return be32_to_cpup((const u32 *) &lanai->eeprom[address]); 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/* Checksum/validate EEPROM contents */ 9448c2ecf20Sopenharmony_cistatic int eeprom_validate(struct lanai_dev *lanai) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci int i, s; 9478c2ecf20Sopenharmony_ci u32 v; 9488c2ecf20Sopenharmony_ci const u8 *e = lanai->eeprom; 9498c2ecf20Sopenharmony_ci#ifdef DEBUG 9508c2ecf20Sopenharmony_ci /* First, see if we can get an ASCIIZ string out of the copyright */ 9518c2ecf20Sopenharmony_ci for (i = EEPROM_COPYRIGHT; 9528c2ecf20Sopenharmony_ci i < (EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN); i++) 9538c2ecf20Sopenharmony_ci if (e[i] < 0x20 || e[i] > 0x7E) 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci if ( i != EEPROM_COPYRIGHT && 9568c2ecf20Sopenharmony_ci i != EEPROM_COPYRIGHT + EEPROM_COPYRIGHT_LEN && e[i] == '\0') 9578c2ecf20Sopenharmony_ci DPRINTK("eeprom: copyright = \"%s\"\n", 9588c2ecf20Sopenharmony_ci (char *) &e[EEPROM_COPYRIGHT]); 9598c2ecf20Sopenharmony_ci else 9608c2ecf20Sopenharmony_ci DPRINTK("eeprom: copyright not found\n"); 9618c2ecf20Sopenharmony_ci#endif 9628c2ecf20Sopenharmony_ci /* Validate checksum */ 9638c2ecf20Sopenharmony_ci for (i = s = 0; i < EEPROM_CHECKSUM; i++) 9648c2ecf20Sopenharmony_ci s += e[i]; 9658c2ecf20Sopenharmony_ci s &= 0xFF; 9668c2ecf20Sopenharmony_ci if (s != e[EEPROM_CHECKSUM]) { 9678c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM checksum bad " 9688c2ecf20Sopenharmony_ci "(wanted 0x%02X, got 0x%02X)\n", lanai->number, 9698c2ecf20Sopenharmony_ci (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM]); 9708c2ecf20Sopenharmony_ci return -EIO; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci s ^= 0xFF; 9738c2ecf20Sopenharmony_ci if (s != e[EEPROM_CHECKSUM_REV]) { 9748c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM inverse checksum " 9758c2ecf20Sopenharmony_ci "bad (wanted 0x%02X, got 0x%02X)\n", lanai->number, 9768c2ecf20Sopenharmony_ci (unsigned int) s, (unsigned int) e[EEPROM_CHECKSUM_REV]); 9778c2ecf20Sopenharmony_ci return -EIO; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci /* Verify MAC address */ 9808c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 9818c2ecf20Sopenharmony_ci if ((e[EEPROM_MAC + i] ^ e[EEPROM_MAC_REV + i]) != 0xFF) { 9828c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL 9838c2ecf20Sopenharmony_ci "(itf %d) : EEPROM MAC addresses don't match " 9848c2ecf20Sopenharmony_ci "(0x%02X, inverse 0x%02X)\n", lanai->number, 9858c2ecf20Sopenharmony_ci (unsigned int) e[EEPROM_MAC + i], 9868c2ecf20Sopenharmony_ci (unsigned int) e[EEPROM_MAC_REV + i]); 9878c2ecf20Sopenharmony_ci return -EIO; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci DPRINTK("eeprom: MAC address = %pM\n", &e[EEPROM_MAC]); 9908c2ecf20Sopenharmony_ci /* Verify serial number */ 9918c2ecf20Sopenharmony_ci lanai->serialno = eeprom_be4(lanai, EEPROM_SERIAL); 9928c2ecf20Sopenharmony_ci v = eeprom_be4(lanai, EEPROM_SERIAL_REV); 9938c2ecf20Sopenharmony_ci if ((lanai->serialno ^ v) != 0xFFFFFFFF) { 9948c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM serial numbers " 9958c2ecf20Sopenharmony_ci "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 9968c2ecf20Sopenharmony_ci (unsigned int) lanai->serialno, (unsigned int) v); 9978c2ecf20Sopenharmony_ci return -EIO; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci DPRINTK("eeprom: Serial number = %d\n", (unsigned int) lanai->serialno); 10008c2ecf20Sopenharmony_ci /* Verify magic number */ 10018c2ecf20Sopenharmony_ci lanai->magicno = eeprom_be4(lanai, EEPROM_MAGIC); 10028c2ecf20Sopenharmony_ci v = eeprom_be4(lanai, EEPROM_MAGIC_REV); 10038c2ecf20Sopenharmony_ci if ((lanai->magicno ^ v) != 0xFFFFFFFF) { 10048c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): EEPROM magic numbers " 10058c2ecf20Sopenharmony_ci "don't match (0x%08X, inverse 0x%08X)\n", lanai->number, 10068c2ecf20Sopenharmony_ci lanai->magicno, v); 10078c2ecf20Sopenharmony_ci return -EIO; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci DPRINTK("eeprom: Magic number = 0x%08X\n", lanai->magicno); 10108c2ecf20Sopenharmony_ci if (lanai->magicno != EEPROM_MAGIC_VALUE) 10118c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): warning - EEPROM " 10128c2ecf20Sopenharmony_ci "magic not what expected (got 0x%08X, not 0x%08X)\n", 10138c2ecf20Sopenharmony_ci lanai->number, (unsigned int) lanai->magicno, 10148c2ecf20Sopenharmony_ci (unsigned int) EEPROM_MAGIC_VALUE); 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci#endif /* READ_EEPROM */ 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic inline const u8 *eeprom_mac(const struct lanai_dev *lanai) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci return &lanai->eeprom[EEPROM_MAC]; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci/* -------------------- INTERRUPT HANDLING UTILITIES: */ 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci/* Interrupt types */ 10288c2ecf20Sopenharmony_ci#define INT_STATS (0x00000002) /* Statistics counter overflow */ 10298c2ecf20Sopenharmony_ci#define INT_SOOL (0x00000004) /* SOOL changed state */ 10308c2ecf20Sopenharmony_ci#define INT_LOCD (0x00000008) /* LOCD changed state */ 10318c2ecf20Sopenharmony_ci#define INT_LED (0x00000010) /* LED (HAPPI) changed state */ 10328c2ecf20Sopenharmony_ci#define INT_GPIN (0x00000020) /* GPIN changed state */ 10338c2ecf20Sopenharmony_ci#define INT_PING (0x00000040) /* PING_COUNT fulfilled */ 10348c2ecf20Sopenharmony_ci#define INT_WAKE (0x00000080) /* Lanai wants bus */ 10358c2ecf20Sopenharmony_ci#define INT_CBR0 (0x00000100) /* CBR sched hit VCI 0 */ 10368c2ecf20Sopenharmony_ci#define INT_LOCK (0x00000200) /* Service list overflow */ 10378c2ecf20Sopenharmony_ci#define INT_MISMATCH (0x00000400) /* TX magic list mismatch */ 10388c2ecf20Sopenharmony_ci#define INT_AAL0_STR (0x00000800) /* Non-AAL5 buffer half filled */ 10398c2ecf20Sopenharmony_ci#define INT_AAL0 (0x00001000) /* Non-AAL5 data available */ 10408c2ecf20Sopenharmony_ci#define INT_SERVICE (0x00002000) /* Service list entries available */ 10418c2ecf20Sopenharmony_ci#define INT_TABORTSENT (0x00004000) /* Target abort sent by lanai */ 10428c2ecf20Sopenharmony_ci#define INT_TABORTBM (0x00008000) /* Abort rcv'd as bus master */ 10438c2ecf20Sopenharmony_ci#define INT_TIMEOUTBM (0x00010000) /* No response to bus master */ 10448c2ecf20Sopenharmony_ci#define INT_PCIPARITY (0x00020000) /* Parity error on PCI */ 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* Sets of the above */ 10478c2ecf20Sopenharmony_ci#define INT_ALL (0x0003FFFE) /* All interrupts */ 10488c2ecf20Sopenharmony_ci#define INT_STATUS (0x0000003C) /* Some status pin changed */ 10498c2ecf20Sopenharmony_ci#define INT_DMASHUT (0x00038000) /* DMA engine got shut down */ 10508c2ecf20Sopenharmony_ci#define INT_SEGSHUT (0x00000700) /* Segmentation got shut down */ 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic inline u32 intr_pending(const struct lanai_dev *lanai) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci return reg_read(lanai, IntStatusMasked_Reg); 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic inline void intr_enable(const struct lanai_dev *lanai, u32 i) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci reg_write(lanai, i, IntControlEna_Reg); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic inline void intr_disable(const struct lanai_dev *lanai, u32 i) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci reg_write(lanai, i, IntControlDis_Reg); 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci/* -------------------- CARD/PCI STATUS: */ 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic void status_message(int itf, const char *name, int status) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci static const char *onoff[2] = { "off to on", "on to off" }; 10728c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", 10738c2ecf20Sopenharmony_ci itf, name, onoff[!status]); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic void lanai_check_status(struct lanai_dev *lanai) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci u32 new = reg_read(lanai, Status_Reg); 10798c2ecf20Sopenharmony_ci u32 changes = new ^ lanai->status; 10808c2ecf20Sopenharmony_ci lanai->status = new; 10818c2ecf20Sopenharmony_ci#define e(flag, name) \ 10828c2ecf20Sopenharmony_ci if (changes & flag) \ 10838c2ecf20Sopenharmony_ci status_message(lanai->number, name, new & flag) 10848c2ecf20Sopenharmony_ci e(STATUS_SOOL, "SOOL"); 10858c2ecf20Sopenharmony_ci e(STATUS_LOCD, "LOCD"); 10868c2ecf20Sopenharmony_ci e(STATUS_LED, "LED"); 10878c2ecf20Sopenharmony_ci e(STATUS_GPIN, "GPIN"); 10888c2ecf20Sopenharmony_ci#undef e 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic void pcistatus_got(int itf, const char *name) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name); 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void pcistatus_check(struct lanai_dev *lanai, int clearonly) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci u16 s; 10998c2ecf20Sopenharmony_ci int result; 11008c2ecf20Sopenharmony_ci result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); 11018c2ecf20Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) { 11028c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " 11038c2ecf20Sopenharmony_ci "%d\n", lanai->number, result); 11048c2ecf20Sopenharmony_ci return; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | 11078c2ecf20Sopenharmony_ci PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | 11088c2ecf20Sopenharmony_ci PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; 11098c2ecf20Sopenharmony_ci if (s == 0) 11108c2ecf20Sopenharmony_ci return; 11118c2ecf20Sopenharmony_ci result = pci_write_config_word(lanai->pci, PCI_STATUS, s); 11128c2ecf20Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) 11138c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " 11148c2ecf20Sopenharmony_ci "%d\n", lanai->number, result); 11158c2ecf20Sopenharmony_ci if (clearonly) 11168c2ecf20Sopenharmony_ci return; 11178c2ecf20Sopenharmony_ci#define e(flag, name, stat) \ 11188c2ecf20Sopenharmony_ci if (s & flag) { \ 11198c2ecf20Sopenharmony_ci pcistatus_got(lanai->number, name); \ 11208c2ecf20Sopenharmony_ci ++lanai->stats.pcierr_##stat; \ 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); 11238c2ecf20Sopenharmony_ci e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); 11248c2ecf20Sopenharmony_ci e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); 11258c2ecf20Sopenharmony_ci e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); 11268c2ecf20Sopenharmony_ci e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); 11278c2ecf20Sopenharmony_ci e(PCI_STATUS_PARITY, "master parity", master_parity); 11288c2ecf20Sopenharmony_ci#undef e 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci/* -------------------- VCC TX BUFFER UTILITIES: */ 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci/* space left in tx buffer in bytes */ 11348c2ecf20Sopenharmony_cistatic inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci int r; 11378c2ecf20Sopenharmony_ci r = endptr * 16; 11388c2ecf20Sopenharmony_ci r -= ((unsigned long) lvcc->tx.buf.ptr) - 11398c2ecf20Sopenharmony_ci ((unsigned long) lvcc->tx.buf.start); 11408c2ecf20Sopenharmony_ci r -= 16; /* Leave "bubble" - if start==end it looks empty */ 11418c2ecf20Sopenharmony_ci if (r < 0) 11428c2ecf20Sopenharmony_ci r += lanai_buf_size(&lvcc->tx.buf); 11438c2ecf20Sopenharmony_ci return r; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/* test if VCC is currently backlogged */ 11478c2ecf20Sopenharmony_cistatic inline int vcc_is_backlogged(const struct lanai_vcc *lvcc) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci return !skb_queue_empty(&lvcc->tx.backlog); 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* Bit fields in the segmentation buffer descriptor */ 11538c2ecf20Sopenharmony_ci#define DESCRIPTOR_MAGIC (0xD0000000) 11548c2ecf20Sopenharmony_ci#define DESCRIPTOR_AAL5 (0x00008000) 11558c2ecf20Sopenharmony_ci#define DESCRIPTOR_AAL5_STREAM (0x00004000) 11568c2ecf20Sopenharmony_ci#define DESCRIPTOR_CLP (0x00002000) 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/* Add 32-bit descriptor with its padding */ 11598c2ecf20Sopenharmony_cistatic inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, 11608c2ecf20Sopenharmony_ci u32 flags, int len) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci int pos; 11638c2ecf20Sopenharmony_ci APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, 11648c2ecf20Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); 11658c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ 11668c2ecf20Sopenharmony_ci pos = ((unsigned char *) lvcc->tx.buf.ptr) - 11678c2ecf20Sopenharmony_ci (unsigned char *) lvcc->tx.buf.start; 11688c2ecf20Sopenharmony_ci APRINTK((pos & ~0x0001FFF0) == 0, 11698c2ecf20Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " 11708c2ecf20Sopenharmony_ci "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 11718c2ecf20Sopenharmony_ci lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 11728c2ecf20Sopenharmony_ci pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); 11738c2ecf20Sopenharmony_ci APRINTK((pos & ~0x0001FFF0) == 0, 11748c2ecf20Sopenharmony_ci "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " 11758c2ecf20Sopenharmony_ci "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, 11768c2ecf20Sopenharmony_ci lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); 11778c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr[-1] = 11788c2ecf20Sopenharmony_ci cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | 11798c2ecf20Sopenharmony_ci ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? 11808c2ecf20Sopenharmony_ci DESCRIPTOR_CLP : 0) | flags | pos >> 4); 11818c2ecf20Sopenharmony_ci if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 11828c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr = lvcc->tx.buf.start; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci/* Add 32-bit AAL5 trailer and leave room for its CRC */ 11868c2ecf20Sopenharmony_cistatic inline void vcc_tx_add_aal5_trailer(struct lanai_vcc *lvcc, 11878c2ecf20Sopenharmony_ci int len, int cpi, int uu) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, 11908c2ecf20Sopenharmony_ci "vcc_tx_add_aal5_trailer: bad ptr=%p\n", lvcc->tx.buf.ptr); 11918c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr += 2; 11928c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); 11938c2ecf20Sopenharmony_ci if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) 11948c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr = lvcc->tx.buf.start; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, 11988c2ecf20Sopenharmony_ci const unsigned char *src, int n) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci unsigned char *e; 12018c2ecf20Sopenharmony_ci int m; 12028c2ecf20Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 12038c2ecf20Sopenharmony_ci m = e - (unsigned char *) lvcc->tx.buf.end; 12048c2ecf20Sopenharmony_ci if (m < 0) 12058c2ecf20Sopenharmony_ci m = 0; 12068c2ecf20Sopenharmony_ci memcpy(lvcc->tx.buf.ptr, src, n - m); 12078c2ecf20Sopenharmony_ci if (m != 0) { 12088c2ecf20Sopenharmony_ci memcpy(lvcc->tx.buf.start, src + n - m, m); 12098c2ecf20Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.start) + m; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr = (u32 *) e; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci unsigned char *e; 12178c2ecf20Sopenharmony_ci int m; 12188c2ecf20Sopenharmony_ci if (n == 0) 12198c2ecf20Sopenharmony_ci return; 12208c2ecf20Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.ptr) + n; 12218c2ecf20Sopenharmony_ci m = e - (unsigned char *) lvcc->tx.buf.end; 12228c2ecf20Sopenharmony_ci if (m < 0) 12238c2ecf20Sopenharmony_ci m = 0; 12248c2ecf20Sopenharmony_ci memset(lvcc->tx.buf.ptr, 0, n - m); 12258c2ecf20Sopenharmony_ci if (m != 0) { 12268c2ecf20Sopenharmony_ci memset(lvcc->tx.buf.start, 0, m); 12278c2ecf20Sopenharmony_ci e = ((unsigned char *) lvcc->tx.buf.start) + m; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci lvcc->tx.buf.ptr = (u32 *) e; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/* Update "butt" register to specify new WritePtr */ 12338c2ecf20Sopenharmony_cistatic inline void lanai_endtx(struct lanai_dev *lanai, 12348c2ecf20Sopenharmony_ci const struct lanai_vcc *lvcc) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - 12378c2ecf20Sopenharmony_ci (unsigned char *) lvcc->tx.buf.start; 12388c2ecf20Sopenharmony_ci APRINTK((ptr & ~0x0001FFF0) == 0, 12398c2ecf20Sopenharmony_ci "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", 12408c2ecf20Sopenharmony_ci ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, 12418c2ecf20Sopenharmony_ci lvcc->tx.buf.end); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* 12448c2ecf20Sopenharmony_ci * Since the "butt register" is a shared resounce on the card we 12458c2ecf20Sopenharmony_ci * serialize all accesses to it through this spinlock. This is 12468c2ecf20Sopenharmony_ci * mostly just paranoia since the register is rarely "busy" anyway 12478c2ecf20Sopenharmony_ci * but is needed for correctness. 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_ci spin_lock(&lanai->endtxlock); 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * We need to check if the "butt busy" bit is set before 12528c2ecf20Sopenharmony_ci * updating the butt register. In theory this should 12538c2ecf20Sopenharmony_ci * never happen because the ATM card is plenty fast at 12548c2ecf20Sopenharmony_ci * updating the register. Still, we should make sure 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_ci for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { 12578c2ecf20Sopenharmony_ci if (unlikely(i > 50)) { 12588c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): butt register " 12598c2ecf20Sopenharmony_ci "always busy!\n", lanai->number); 12608c2ecf20Sopenharmony_ci break; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci udelay(5); 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * Before we tall the card to start work we need to be sure 100% of 12668c2ecf20Sopenharmony_ci * the info in the service buffer has been written before we tell 12678c2ecf20Sopenharmony_ci * the card about it 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci wmb(); 12708c2ecf20Sopenharmony_ci reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg); 12718c2ecf20Sopenharmony_ci spin_unlock(&lanai->endtxlock); 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci/* 12758c2ecf20Sopenharmony_ci * Add one AAL5 PDU to lvcc's transmit buffer. Caller garauntees there's 12768c2ecf20Sopenharmony_ci * space available. "pdusize" is the number of bytes the PDU will take 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_cistatic void lanai_send_one_aal5(struct lanai_dev *lanai, 12798c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc, struct sk_buff *skb, int pdusize) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci int pad; 12828c2ecf20Sopenharmony_ci APRINTK(pdusize == aal5_size(skb->len), 12838c2ecf20Sopenharmony_ci "lanai_send_one_aal5: wrong size packet (%d != %d)\n", 12848c2ecf20Sopenharmony_ci pdusize, aal5_size(skb->len)); 12858c2ecf20Sopenharmony_ci vcc_tx_add_aal5_descriptor(lvcc, 0, pdusize); 12868c2ecf20Sopenharmony_ci pad = pdusize - skb->len - 8; 12878c2ecf20Sopenharmony_ci APRINTK(pad >= 0, "pad is negative (%d)\n", pad); 12888c2ecf20Sopenharmony_ci APRINTK(pad < 48, "pad is too big (%d)\n", pad); 12898c2ecf20Sopenharmony_ci vcc_tx_memcpy(lvcc, skb->data, skb->len); 12908c2ecf20Sopenharmony_ci vcc_tx_memzero(lvcc, pad); 12918c2ecf20Sopenharmony_ci vcc_tx_add_aal5_trailer(lvcc, skb->len, 0, 0); 12928c2ecf20Sopenharmony_ci lanai_endtx(lanai, lvcc); 12938c2ecf20Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 12948c2ecf20Sopenharmony_ci atomic_inc(&lvcc->tx.atmvcc->stats->tx); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci/* Try to fill the buffer - don't call unless there is backlog */ 12988c2ecf20Sopenharmony_cistatic void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, 12998c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc, int endptr) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci int n; 13028c2ecf20Sopenharmony_ci struct sk_buff *skb; 13038c2ecf20Sopenharmony_ci int space = vcc_tx_space(lvcc, endptr); 13048c2ecf20Sopenharmony_ci APRINTK(vcc_is_backlogged(lvcc), 13058c2ecf20Sopenharmony_ci "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", 13068c2ecf20Sopenharmony_ci lvcc->vci); 13078c2ecf20Sopenharmony_ci while (space >= 64) { 13088c2ecf20Sopenharmony_ci skb = skb_dequeue(&lvcc->tx.backlog); 13098c2ecf20Sopenharmony_ci if (skb == NULL) 13108c2ecf20Sopenharmony_ci goto no_backlog; 13118c2ecf20Sopenharmony_ci n = aal5_size(skb->len); 13128c2ecf20Sopenharmony_ci if (n + 16 > space) { 13138c2ecf20Sopenharmony_ci /* No room for this packet - put it back on queue */ 13148c2ecf20Sopenharmony_ci skb_queue_head(&lvcc->tx.backlog, skb); 13158c2ecf20Sopenharmony_ci return; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci lanai_send_one_aal5(lanai, lvcc, skb, n); 13188c2ecf20Sopenharmony_ci space -= n + 16; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci if (!vcc_is_backlogged(lvcc)) { 13218c2ecf20Sopenharmony_ci no_backlog: 13228c2ecf20Sopenharmony_ci __clear_bit(lvcc->vci, lanai->backlog_vccs); 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/* Given an skb that we want to transmit either send it now or queue */ 13278c2ecf20Sopenharmony_cistatic void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 13288c2ecf20Sopenharmony_ci struct sk_buff *skb) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci int space, n; 13318c2ecf20Sopenharmony_ci if (vcc_is_backlogged(lvcc)) /* Already backlogged */ 13328c2ecf20Sopenharmony_ci goto queue_it; 13338c2ecf20Sopenharmony_ci space = vcc_tx_space(lvcc, 13348c2ecf20Sopenharmony_ci TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr))); 13358c2ecf20Sopenharmony_ci n = aal5_size(skb->len); 13368c2ecf20Sopenharmony_ci APRINTK(n + 16 >= 64, "vcc_tx_aal5: n too small (%d)\n", n); 13378c2ecf20Sopenharmony_ci if (space < n + 16) { /* No space for this PDU */ 13388c2ecf20Sopenharmony_ci __set_bit(lvcc->vci, lanai->backlog_vccs); 13398c2ecf20Sopenharmony_ci queue_it: 13408c2ecf20Sopenharmony_ci skb_queue_tail(&lvcc->tx.backlog, skb); 13418c2ecf20Sopenharmony_ci return; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci lanai_send_one_aal5(lanai, lvcc, skb, n); 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cistatic void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, 13478c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc, int endptr) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL 13508c2ecf20Sopenharmony_ci ": vcc_tx_unqueue_aal0: not implemented\n"); 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 13548c2ecf20Sopenharmony_ci struct sk_buff *skb) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); 13578c2ecf20Sopenharmony_ci /* Remember to increment lvcc->tx.atmvcc->stats->tx */ 13588c2ecf20Sopenharmony_ci lanai_free_skb(lvcc->tx.atmvcc, skb); 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci/* -------------------- VCC RX BUFFER UTILITIES: */ 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci/* unlike the _tx_ cousins, this doesn't update ptr */ 13648c2ecf20Sopenharmony_cistatic inline void vcc_rx_memcpy(unsigned char *dest, 13658c2ecf20Sopenharmony_ci const struct lanai_vcc *lvcc, int n) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - 13688c2ecf20Sopenharmony_ci ((const unsigned char *) (lvcc->rx.buf.end)); 13698c2ecf20Sopenharmony_ci if (m < 0) 13708c2ecf20Sopenharmony_ci m = 0; 13718c2ecf20Sopenharmony_ci memcpy(dest, lvcc->rx.buf.ptr, n - m); 13728c2ecf20Sopenharmony_ci memcpy(dest + n - m, lvcc->rx.buf.start, m); 13738c2ecf20Sopenharmony_ci /* Make sure that these copies don't get reordered */ 13748c2ecf20Sopenharmony_ci barrier(); 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci/* Receive AAL5 data on a VCC with a particular endptr */ 13788c2ecf20Sopenharmony_cistatic void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci int size; 13818c2ecf20Sopenharmony_ci struct sk_buff *skb; 13828c2ecf20Sopenharmony_ci const u32 *x; 13838c2ecf20Sopenharmony_ci u32 *end = &lvcc->rx.buf.start[endptr * 4]; 13848c2ecf20Sopenharmony_ci int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); 13858c2ecf20Sopenharmony_ci if (n < 0) 13868c2ecf20Sopenharmony_ci n += lanai_buf_size(&lvcc->rx.buf); 13878c2ecf20Sopenharmony_ci APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), 13888c2ecf20Sopenharmony_ci "vcc_rx_aal5: n out of range (%d/%zu)\n", 13898c2ecf20Sopenharmony_ci n, lanai_buf_size(&lvcc->rx.buf)); 13908c2ecf20Sopenharmony_ci /* Recover the second-to-last word to get true pdu length */ 13918c2ecf20Sopenharmony_ci if ((x = &end[-2]) < lvcc->rx.buf.start) 13928c2ecf20Sopenharmony_ci x = &lvcc->rx.buf.end[-2]; 13938c2ecf20Sopenharmony_ci /* 13948c2ecf20Sopenharmony_ci * Before we actually read from the buffer, make sure the memory 13958c2ecf20Sopenharmony_ci * changes have arrived 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci rmb(); 13988c2ecf20Sopenharmony_ci size = be32_to_cpup(x) & 0xffff; 13998c2ecf20Sopenharmony_ci if (unlikely(n != aal5_size(size))) { 14008c2ecf20Sopenharmony_ci /* Make sure size matches padding */ 14018c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " 14028c2ecf20Sopenharmony_ci "on vci=%d - size=%d n=%d\n", 14038c2ecf20Sopenharmony_ci lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); 14048c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen++; 14058c2ecf20Sopenharmony_ci goto out; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); 14088c2ecf20Sopenharmony_ci if (unlikely(skb == NULL)) { 14098c2ecf20Sopenharmony_ci lvcc->stats.rx_nomem++; 14108c2ecf20Sopenharmony_ci goto out; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci skb_put(skb, size); 14138c2ecf20Sopenharmony_ci vcc_rx_memcpy(skb->data, lvcc, size); 14148c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = lvcc->rx.atmvcc; 14158c2ecf20Sopenharmony_ci __net_timestamp(skb); 14168c2ecf20Sopenharmony_ci lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); 14178c2ecf20Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx); 14188c2ecf20Sopenharmony_ci out: 14198c2ecf20Sopenharmony_ci lvcc->rx.buf.ptr = end; 14208c2ecf20Sopenharmony_ci cardvcc_write(lvcc, endptr, vcc_rxreadptr); 14218c2ecf20Sopenharmony_ci} 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_cistatic void vcc_rx_aal0(struct lanai_dev *lanai) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); 14268c2ecf20Sopenharmony_ci /* Remember to get read_lock(&vcc_sklist_lock) while looking up VC */ 14278c2ecf20Sopenharmony_ci /* Remember to increment lvcc->rx.atmvcc->stats->rx */ 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci/* -------------------- MANAGING HOST-BASED VCC TABLE: */ 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci/* Decide whether to use vmalloc or get_zeroed_page for VCC table */ 14338c2ecf20Sopenharmony_ci#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE 14348c2ecf20Sopenharmony_ci#define VCCTABLE_GETFREEPAGE 14358c2ecf20Sopenharmony_ci#else 14368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 14378c2ecf20Sopenharmony_ci#endif 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cistatic int vcc_table_allocate(struct lanai_dev *lanai) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE 14428c2ecf20Sopenharmony_ci APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, 14438c2ecf20Sopenharmony_ci "vcc table > PAGE_SIZE!"); 14448c2ecf20Sopenharmony_ci lanai->vccs = (struct lanai_vcc **) get_zeroed_page(GFP_KERNEL); 14458c2ecf20Sopenharmony_ci return (lanai->vccs == NULL) ? -ENOMEM : 0; 14468c2ecf20Sopenharmony_ci#else 14478c2ecf20Sopenharmony_ci int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); 14488c2ecf20Sopenharmony_ci lanai->vccs = vzalloc(bytes); 14498c2ecf20Sopenharmony_ci if (unlikely(lanai->vccs == NULL)) 14508c2ecf20Sopenharmony_ci return -ENOMEM; 14518c2ecf20Sopenharmony_ci return 0; 14528c2ecf20Sopenharmony_ci#endif 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic inline void vcc_table_deallocate(const struct lanai_dev *lanai) 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci#ifdef VCCTABLE_GETFREEPAGE 14588c2ecf20Sopenharmony_ci free_page((unsigned long) lanai->vccs); 14598c2ecf20Sopenharmony_ci#else 14608c2ecf20Sopenharmony_ci vfree(lanai->vccs); 14618c2ecf20Sopenharmony_ci#endif 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci/* Allocate a fresh lanai_vcc, with the appropriate things cleared */ 14658c2ecf20Sopenharmony_cistatic inline struct lanai_vcc *new_lanai_vcc(void) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc; 14688c2ecf20Sopenharmony_ci lvcc = kzalloc(sizeof(*lvcc), GFP_KERNEL); 14698c2ecf20Sopenharmony_ci if (likely(lvcc != NULL)) { 14708c2ecf20Sopenharmony_ci skb_queue_head_init(&lvcc->tx.backlog); 14718c2ecf20Sopenharmony_ci#ifdef DEBUG 14728c2ecf20Sopenharmony_ci lvcc->vci = -1; 14738c2ecf20Sopenharmony_ci#endif 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci return lvcc; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic int lanai_get_sized_buffer(struct lanai_dev *lanai, 14798c2ecf20Sopenharmony_ci struct lanai_buffer *buf, int max_sdu, int multiplier, 14808c2ecf20Sopenharmony_ci const char *name) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci int size; 14838c2ecf20Sopenharmony_ci if (unlikely(max_sdu < 1)) 14848c2ecf20Sopenharmony_ci max_sdu = 1; 14858c2ecf20Sopenharmony_ci max_sdu = aal5_size(max_sdu); 14868c2ecf20Sopenharmony_ci size = (max_sdu + 16) * multiplier + 16; 14878c2ecf20Sopenharmony_ci lanai_buf_allocate(buf, size, max_sdu + 32, lanai->pci); 14888c2ecf20Sopenharmony_ci if (unlikely(buf->start == NULL)) 14898c2ecf20Sopenharmony_ci return -ENOMEM; 14908c2ecf20Sopenharmony_ci if (unlikely(lanai_buf_size(buf) < size)) 14918c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " 14928c2ecf20Sopenharmony_ci "for %s buffer, got only %zu\n", lanai->number, size, 14938c2ecf20Sopenharmony_ci name, lanai_buf_size(buf)); 14948c2ecf20Sopenharmony_ci DPRINTK("Allocated %zu byte %s buffer\n", lanai_buf_size(buf), name); 14958c2ecf20Sopenharmony_ci return 0; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci/* Setup a RX buffer for a currently unbound AAL5 vci */ 14998c2ecf20Sopenharmony_cistatic inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai, 15008c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc, const struct atm_qos *qos) 15018c2ecf20Sopenharmony_ci{ 15028c2ecf20Sopenharmony_ci return lanai_get_sized_buffer(lanai, &lvcc->rx.buf, 15038c2ecf20Sopenharmony_ci qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, "RX"); 15048c2ecf20Sopenharmony_ci} 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci/* Setup a TX buffer for a currently unbound AAL5 vci */ 15078c2ecf20Sopenharmony_cistatic int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc, 15088c2ecf20Sopenharmony_ci const struct atm_qos *qos) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci int max_sdu, multiplier; 15118c2ecf20Sopenharmony_ci if (qos->aal == ATM_AAL0) { 15128c2ecf20Sopenharmony_ci lvcc->tx.unqueue = vcc_tx_unqueue_aal0; 15138c2ecf20Sopenharmony_ci max_sdu = ATM_CELL_SIZE - 1; 15148c2ecf20Sopenharmony_ci multiplier = AAL0_TX_MULTIPLIER; 15158c2ecf20Sopenharmony_ci } else { 15168c2ecf20Sopenharmony_ci lvcc->tx.unqueue = vcc_tx_unqueue_aal5; 15178c2ecf20Sopenharmony_ci max_sdu = qos->txtp.max_sdu; 15188c2ecf20Sopenharmony_ci multiplier = AAL5_TX_MULTIPLIER; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu, 15218c2ecf20Sopenharmony_ci multiplier, "TX"); 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic inline void host_vcc_bind(struct lanai_dev *lanai, 15258c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc, vci_t vci) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci if (lvcc->vbase != NULL) 15288c2ecf20Sopenharmony_ci return; /* We already were bound in the other direction */ 15298c2ecf20Sopenharmony_ci DPRINTK("Binding vci %d\n", vci); 15308c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 15318c2ecf20Sopenharmony_ci if (lanai->nbound++ == 0) { 15328c2ecf20Sopenharmony_ci DPRINTK("Coming out of powerdown\n"); 15338c2ecf20Sopenharmony_ci lanai->conf1 &= ~CONFIG1_POWERDOWN; 15348c2ecf20Sopenharmony_ci conf1_write(lanai); 15358c2ecf20Sopenharmony_ci conf2_write(lanai); 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci#endif 15388c2ecf20Sopenharmony_ci lvcc->vbase = cardvcc_addr(lanai, vci); 15398c2ecf20Sopenharmony_ci lanai->vccs[lvcc->vci = vci] = lvcc; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic inline void host_vcc_unbind(struct lanai_dev *lanai, 15438c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci if (lvcc->vbase == NULL) 15468c2ecf20Sopenharmony_ci return; /* This vcc was never bound */ 15478c2ecf20Sopenharmony_ci DPRINTK("Unbinding vci %d\n", lvcc->vci); 15488c2ecf20Sopenharmony_ci lvcc->vbase = NULL; 15498c2ecf20Sopenharmony_ci lanai->vccs[lvcc->vci] = NULL; 15508c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 15518c2ecf20Sopenharmony_ci if (--lanai->nbound == 0) { 15528c2ecf20Sopenharmony_ci DPRINTK("Going into powerdown\n"); 15538c2ecf20Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 15548c2ecf20Sopenharmony_ci conf1_write(lanai); 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci#endif 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci/* -------------------- RESET CARD: */ 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic void lanai_reset(struct lanai_dev *lanai) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* resetting - not " 15648c2ecf20Sopenharmony_ci "implemented\n", lanai->number); 15658c2ecf20Sopenharmony_ci /* TODO */ 15668c2ecf20Sopenharmony_ci /* The following is just a hack until we write the real 15678c2ecf20Sopenharmony_ci * resetter - at least ack whatever interrupt sent us 15688c2ecf20Sopenharmony_ci * here 15698c2ecf20Sopenharmony_ci */ 15708c2ecf20Sopenharmony_ci reg_write(lanai, INT_ALL, IntAck_Reg); 15718c2ecf20Sopenharmony_ci lanai->stats.card_reset++; 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci/* -------------------- SERVICE LIST UTILITIES: */ 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci/* 15778c2ecf20Sopenharmony_ci * Allocate service buffer and tell card about it 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_cistatic int service_buffer_allocate(struct lanai_dev *lanai) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8, 15828c2ecf20Sopenharmony_ci lanai->pci); 15838c2ecf20Sopenharmony_ci if (unlikely(lanai->service.start == NULL)) 15848c2ecf20Sopenharmony_ci return -ENOMEM; 15858c2ecf20Sopenharmony_ci DPRINTK("allocated service buffer at %p, size %zu(%d)\n", 15868c2ecf20Sopenharmony_ci lanai->service.start, 15878c2ecf20Sopenharmony_ci lanai_buf_size(&lanai->service), 15888c2ecf20Sopenharmony_ci lanai_buf_size_cardorder(&lanai->service)); 15898c2ecf20Sopenharmony_ci /* Clear ServWrite register to be safe */ 15908c2ecf20Sopenharmony_ci reg_write(lanai, 0, ServWrite_Reg); 15918c2ecf20Sopenharmony_ci /* ServiceStuff register contains size and address of buffer */ 15928c2ecf20Sopenharmony_ci reg_write(lanai, 15938c2ecf20Sopenharmony_ci SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | 15948c2ecf20Sopenharmony_ci SSTUFF_SET_ADDR(lanai->service.dmaaddr), 15958c2ecf20Sopenharmony_ci ServiceStuff_Reg); 15968c2ecf20Sopenharmony_ci return 0; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic inline void service_buffer_deallocate(struct lanai_dev *lanai) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci lanai_buf_deallocate(&lanai->service, lanai->pci); 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci/* Bitfields in service list */ 16058c2ecf20Sopenharmony_ci#define SERVICE_TX (0x80000000) /* Was from transmission */ 16068c2ecf20Sopenharmony_ci#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */ 16078c2ecf20Sopenharmony_ci#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */ 16088c2ecf20Sopenharmony_ci#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */ 16098c2ecf20Sopenharmony_ci#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */ 16108c2ecf20Sopenharmony_ci#define SERVICE_STREAM (0x04000000) /* RX Stream mode */ 16118c2ecf20Sopenharmony_ci#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF) 16128c2ecf20Sopenharmony_ci#define SERVICE_GET_END(x) ((x)&0x1FFF) 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci/* Handle one thing from the service list - returns true if it marked a 16158c2ecf20Sopenharmony_ci * VCC ready for xmit 16168c2ecf20Sopenharmony_ci */ 16178c2ecf20Sopenharmony_cistatic int handle_service(struct lanai_dev *lanai, u32 s) 16188c2ecf20Sopenharmony_ci{ 16198c2ecf20Sopenharmony_ci vci_t vci = SERVICE_GET_VCI(s); 16208c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc; 16218c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 16228c2ecf20Sopenharmony_ci lvcc = lanai->vccs[vci]; 16238c2ecf20Sopenharmony_ci if (unlikely(lvcc == NULL)) { 16248c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16258c2ecf20Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for nonexistent " 16268c2ecf20Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 16278c2ecf20Sopenharmony_ci if (s & SERVICE_TX) 16288c2ecf20Sopenharmony_ci lanai->stats.service_notx++; 16298c2ecf20Sopenharmony_ci else 16308c2ecf20Sopenharmony_ci lanai->stats.service_norx++; 16318c2ecf20Sopenharmony_ci return 0; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci if (s & SERVICE_TX) { /* segmentation interrupt */ 16348c2ecf20Sopenharmony_ci if (unlikely(lvcc->tx.atmvcc == NULL)) { 16358c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16368c2ecf20Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for non-TX " 16378c2ecf20Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 16388c2ecf20Sopenharmony_ci lanai->stats.service_notx++; 16398c2ecf20Sopenharmony_ci return 0; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci __set_bit(vci, lanai->transmit_ready); 16428c2ecf20Sopenharmony_ci lvcc->tx.endptr = SERVICE_GET_END(s); 16438c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16448c2ecf20Sopenharmony_ci return 1; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci if (unlikely(lvcc->rx.atmvcc == NULL)) { 16478c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16488c2ecf20Sopenharmony_ci DPRINTK("(itf %d) got service entry 0x%X for non-RX " 16498c2ecf20Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 16508c2ecf20Sopenharmony_ci lanai->stats.service_norx++; 16518c2ecf20Sopenharmony_ci return 0; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci if (unlikely(lvcc->rx.atmvcc->qos.aal != ATM_AAL5)) { 16548c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16558c2ecf20Sopenharmony_ci DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " 16568c2ecf20Sopenharmony_ci "vcc %d\n", lanai->number, (unsigned int) s, vci); 16578c2ecf20Sopenharmony_ci lanai->stats.service_rxnotaal5++; 16588c2ecf20Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16598c2ecf20Sopenharmony_ci return 0; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci if (likely(!(s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)))) { 16628c2ecf20Sopenharmony_ci vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); 16638c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16648c2ecf20Sopenharmony_ci return 0; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci if (s & SERVICE_TRASH) { 16678c2ecf20Sopenharmony_ci int bytes; 16688c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16698c2ecf20Sopenharmony_ci DPRINTK("got trashed rx pdu on vci %d\n", vci); 16708c2ecf20Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16718c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_trash++; 16728c2ecf20Sopenharmony_ci bytes = (SERVICE_GET_END(s) * 16) - 16738c2ecf20Sopenharmony_ci (((unsigned long) lvcc->rx.buf.ptr) - 16748c2ecf20Sopenharmony_ci ((unsigned long) lvcc->rx.buf.start)) + 47; 16758c2ecf20Sopenharmony_ci if (bytes < 0) 16768c2ecf20Sopenharmony_ci bytes += lanai_buf_size(&lvcc->rx.buf); 16778c2ecf20Sopenharmony_ci lanai->stats.ovfl_trash += (bytes / 48); 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci if (s & SERVICE_STREAM) { 16818c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16828c2ecf20Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16838c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_stream++; 16848c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " 16858c2ecf20Sopenharmony_ci "PDU on VCI %d!\n", lanai->number, vci); 16868c2ecf20Sopenharmony_ci lanai_reset(lanai); 16878c2ecf20Sopenharmony_ci return 0; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci DPRINTK("got rx crc error on vci %d\n", vci); 16908c2ecf20Sopenharmony_ci atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); 16918c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc++; 16928c2ecf20Sopenharmony_ci lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; 16938c2ecf20Sopenharmony_ci cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); 16948c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 16958c2ecf20Sopenharmony_ci return 0; 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci/* Try transmitting on all VCIs that we marked ready to serve */ 16998c2ecf20Sopenharmony_cistatic void iter_transmit(struct lanai_dev *lanai, vci_t vci) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc = lanai->vccs[vci]; 17028c2ecf20Sopenharmony_ci if (vcc_is_backlogged(lvcc)) 17038c2ecf20Sopenharmony_ci lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr); 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci/* Run service queue -- called from interrupt context or with 17078c2ecf20Sopenharmony_ci * interrupts otherwise disabled and with the lanai->servicelock 17088c2ecf20Sopenharmony_ci * lock held 17098c2ecf20Sopenharmony_ci */ 17108c2ecf20Sopenharmony_cistatic void run_service(struct lanai_dev *lanai) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci int ntx = 0; 17138c2ecf20Sopenharmony_ci u32 wreg = reg_read(lanai, ServWrite_Reg); 17148c2ecf20Sopenharmony_ci const u32 *end = lanai->service.start + wreg; 17158c2ecf20Sopenharmony_ci while (lanai->service.ptr != end) { 17168c2ecf20Sopenharmony_ci ntx += handle_service(lanai, 17178c2ecf20Sopenharmony_ci le32_to_cpup(lanai->service.ptr++)); 17188c2ecf20Sopenharmony_ci if (lanai->service.ptr >= lanai->service.end) 17198c2ecf20Sopenharmony_ci lanai->service.ptr = lanai->service.start; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci reg_write(lanai, wreg, ServRead_Reg); 17228c2ecf20Sopenharmony_ci if (ntx != 0) { 17238c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 17248c2ecf20Sopenharmony_ci vci_bitfield_iterate(lanai, lanai->transmit_ready, 17258c2ecf20Sopenharmony_ci iter_transmit); 17268c2ecf20Sopenharmony_ci bitmap_zero(lanai->transmit_ready, NUM_VCI); 17278c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci/* -------------------- GATHER STATISTICS: */ 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic void get_statistics(struct lanai_dev *lanai) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci u32 statreg = reg_read(lanai, Statistics_Reg); 17368c2ecf20Sopenharmony_ci lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); 17378c2ecf20Sopenharmony_ci lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); 17388c2ecf20Sopenharmony_ci lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); 17398c2ecf20Sopenharmony_ci lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg); 17408c2ecf20Sopenharmony_ci} 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci/* -------------------- POLLING TIMER: */ 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci#ifndef DEBUG_RW 17458c2ecf20Sopenharmony_ci/* Try to undequeue 1 backlogged vcc */ 17468c2ecf20Sopenharmony_cistatic void iter_dequeue(struct lanai_dev *lanai, vci_t vci) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc = lanai->vccs[vci]; 17498c2ecf20Sopenharmony_ci int endptr; 17508c2ecf20Sopenharmony_ci if (lvcc == NULL || lvcc->tx.atmvcc == NULL || 17518c2ecf20Sopenharmony_ci !vcc_is_backlogged(lvcc)) { 17528c2ecf20Sopenharmony_ci __clear_bit(vci, lanai->backlog_vccs); 17538c2ecf20Sopenharmony_ci return; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); 17568c2ecf20Sopenharmony_ci lvcc->tx.unqueue(lanai, lvcc, endptr); 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci#endif /* !DEBUG_RW */ 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic void lanai_timed_poll(struct timer_list *t) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct lanai_dev *lanai = from_timer(lanai, t, timer); 17638c2ecf20Sopenharmony_ci#ifndef DEBUG_RW 17648c2ecf20Sopenharmony_ci unsigned long flags; 17658c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 17668c2ecf20Sopenharmony_ci if (lanai->conf1 & CONFIG1_POWERDOWN) 17678c2ecf20Sopenharmony_ci return; 17688c2ecf20Sopenharmony_ci#endif /* USE_POWERDOWN */ 17698c2ecf20Sopenharmony_ci local_irq_save(flags); 17708c2ecf20Sopenharmony_ci /* If we can grab the spinlock, check if any services need to be run */ 17718c2ecf20Sopenharmony_ci if (spin_trylock(&lanai->servicelock)) { 17728c2ecf20Sopenharmony_ci run_service(lanai); 17738c2ecf20Sopenharmony_ci spin_unlock(&lanai->servicelock); 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci /* ...and see if any backlogged VCs can make progress */ 17768c2ecf20Sopenharmony_ci /* unfortunately linux has no read_trylock() currently */ 17778c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 17788c2ecf20Sopenharmony_ci vci_bitfield_iterate(lanai, lanai->backlog_vccs, iter_dequeue); 17798c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 17808c2ecf20Sopenharmony_ci local_irq_restore(flags); 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci get_statistics(lanai); 17838c2ecf20Sopenharmony_ci#endif /* !DEBUG_RW */ 17848c2ecf20Sopenharmony_ci mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD); 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic inline void lanai_timed_poll_start(struct lanai_dev *lanai) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci timer_setup(&lanai->timer, lanai_timed_poll, 0); 17908c2ecf20Sopenharmony_ci lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; 17918c2ecf20Sopenharmony_ci add_timer(&lanai->timer); 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cistatic inline void lanai_timed_poll_stop(struct lanai_dev *lanai) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci del_timer_sync(&lanai->timer); 17978c2ecf20Sopenharmony_ci} 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci/* -------------------- INTERRUPT SERVICE: */ 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_cistatic inline void lanai_int_1(struct lanai_dev *lanai, u32 reason) 18028c2ecf20Sopenharmony_ci{ 18038c2ecf20Sopenharmony_ci u32 ack = 0; 18048c2ecf20Sopenharmony_ci if (reason & INT_SERVICE) { 18058c2ecf20Sopenharmony_ci ack = INT_SERVICE; 18068c2ecf20Sopenharmony_ci spin_lock(&lanai->servicelock); 18078c2ecf20Sopenharmony_ci run_service(lanai); 18088c2ecf20Sopenharmony_ci spin_unlock(&lanai->servicelock); 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci if (reason & (INT_AAL0_STR | INT_AAL0)) { 18118c2ecf20Sopenharmony_ci ack |= reason & (INT_AAL0_STR | INT_AAL0); 18128c2ecf20Sopenharmony_ci vcc_rx_aal0(lanai); 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci /* The rest of the interrupts are pretty rare */ 18158c2ecf20Sopenharmony_ci if (ack == reason) 18168c2ecf20Sopenharmony_ci goto done; 18178c2ecf20Sopenharmony_ci if (reason & INT_STATS) { 18188c2ecf20Sopenharmony_ci reason &= ~INT_STATS; /* No need to ack */ 18198c2ecf20Sopenharmony_ci get_statistics(lanai); 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci if (reason & INT_STATUS) { 18228c2ecf20Sopenharmony_ci ack |= reason & INT_STATUS; 18238c2ecf20Sopenharmony_ci lanai_check_status(lanai); 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci if (unlikely(reason & INT_DMASHUT)) { 18268c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA " 18278c2ecf20Sopenharmony_ci "shutdown, reason=0x%08X, address=0x%08X\n", 18288c2ecf20Sopenharmony_ci lanai->number, (unsigned int) (reason & INT_DMASHUT), 18298c2ecf20Sopenharmony_ci (unsigned int) reg_read(lanai, DMA_Addr_Reg)); 18308c2ecf20Sopenharmony_ci if (reason & INT_TABORTBM) { 18318c2ecf20Sopenharmony_ci lanai_reset(lanai); 18328c2ecf20Sopenharmony_ci return; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci ack |= (reason & INT_DMASHUT); 18358c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): re-enabling DMA\n", 18368c2ecf20Sopenharmony_ci lanai->number); 18378c2ecf20Sopenharmony_ci conf1_write(lanai); 18388c2ecf20Sopenharmony_ci lanai->stats.dma_reenable++; 18398c2ecf20Sopenharmony_ci pcistatus_check(lanai, 0); 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci if (unlikely(reason & INT_TABORTSENT)) { 18428c2ecf20Sopenharmony_ci ack |= (reason & INT_TABORTSENT); 18438c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): sent PCI target abort\n", 18448c2ecf20Sopenharmony_ci lanai->number); 18458c2ecf20Sopenharmony_ci pcistatus_check(lanai, 0); 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci if (unlikely(reason & INT_SEGSHUT)) { 18488c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 18498c2ecf20Sopenharmony_ci "segmentation shutdown, reason=0x%08X\n", lanai->number, 18508c2ecf20Sopenharmony_ci (unsigned int) (reason & INT_SEGSHUT)); 18518c2ecf20Sopenharmony_ci lanai_reset(lanai); 18528c2ecf20Sopenharmony_ci return; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci if (unlikely(reason & (INT_PING | INT_WAKE))) { 18558c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): driver error - " 18568c2ecf20Sopenharmony_ci "unexpected interrupt 0x%08X, resetting\n", 18578c2ecf20Sopenharmony_ci lanai->number, 18588c2ecf20Sopenharmony_ci (unsigned int) (reason & (INT_PING | INT_WAKE))); 18598c2ecf20Sopenharmony_ci lanai_reset(lanai); 18608c2ecf20Sopenharmony_ci return; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci#ifdef DEBUG 18638c2ecf20Sopenharmony_ci if (unlikely(ack != reason)) { 18648c2ecf20Sopenharmony_ci DPRINTK("unacked ints: 0x%08X\n", 18658c2ecf20Sopenharmony_ci (unsigned int) (reason & ~ack)); 18668c2ecf20Sopenharmony_ci ack = reason; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci#endif 18698c2ecf20Sopenharmony_ci done: 18708c2ecf20Sopenharmony_ci if (ack != 0) 18718c2ecf20Sopenharmony_ci reg_write(lanai, ack, IntAck_Reg); 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic irqreturn_t lanai_int(int irq, void *devid) 18758c2ecf20Sopenharmony_ci{ 18768c2ecf20Sopenharmony_ci struct lanai_dev *lanai = devid; 18778c2ecf20Sopenharmony_ci u32 reason; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 18808c2ecf20Sopenharmony_ci /* 18818c2ecf20Sopenharmony_ci * If we're powered down we shouldn't be generating any interrupts - 18828c2ecf20Sopenharmony_ci * so assume that this is a shared interrupt line and it's for someone 18838c2ecf20Sopenharmony_ci * else 18848c2ecf20Sopenharmony_ci */ 18858c2ecf20Sopenharmony_ci if (unlikely(lanai->conf1 & CONFIG1_POWERDOWN)) 18868c2ecf20Sopenharmony_ci return IRQ_NONE; 18878c2ecf20Sopenharmony_ci#endif 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci reason = intr_pending(lanai); 18908c2ecf20Sopenharmony_ci if (reason == 0) 18918c2ecf20Sopenharmony_ci return IRQ_NONE; /* Must be for someone else */ 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci do { 18948c2ecf20Sopenharmony_ci if (unlikely(reason == 0xFFFFFFFF)) 18958c2ecf20Sopenharmony_ci break; /* Maybe we've been unplugged? */ 18968c2ecf20Sopenharmony_ci lanai_int_1(lanai, reason); 18978c2ecf20Sopenharmony_ci reason = intr_pending(lanai); 18988c2ecf20Sopenharmony_ci } while (reason != 0); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 19018c2ecf20Sopenharmony_ci} 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci/* TODO - it would be nice if we could use the "delayed interrupt" system 19048c2ecf20Sopenharmony_ci * to some advantage 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci/* -------------------- CHECK BOARD ID/REV: */ 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci/* 19108c2ecf20Sopenharmony_ci * The board id and revision are stored both in the reset register and 19118c2ecf20Sopenharmony_ci * in the PCI configuration space - the documentation says to check 19128c2ecf20Sopenharmony_ci * each of them. If revp!=NULL we store the revision there 19138c2ecf20Sopenharmony_ci */ 19148c2ecf20Sopenharmony_cistatic int check_board_id_and_rev(const char *name, u32 val, int *revp) 19158c2ecf20Sopenharmony_ci{ 19168c2ecf20Sopenharmony_ci DPRINTK("%s says board_id=%d, board_rev=%d\n", name, 19178c2ecf20Sopenharmony_ci (int) RESET_GET_BOARD_ID(val), 19188c2ecf20Sopenharmony_ci (int) RESET_GET_BOARD_REV(val)); 19198c2ecf20Sopenharmony_ci if (RESET_GET_BOARD_ID(val) != BOARD_ID_LANAI256) { 19208c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": Found %s board-id %d -- not a " 19218c2ecf20Sopenharmony_ci "Lanai 25.6\n", name, (int) RESET_GET_BOARD_ID(val)); 19228c2ecf20Sopenharmony_ci return -ENODEV; 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ci if (revp != NULL) 19258c2ecf20Sopenharmony_ci *revp = RESET_GET_BOARD_REV(val); 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci/* -------------------- PCI INITIALIZATION/SHUTDOWN: */ 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_cistatic int lanai_pci_start(struct lanai_dev *lanai) 19328c2ecf20Sopenharmony_ci{ 19338c2ecf20Sopenharmony_ci struct pci_dev *pci = lanai->pci; 19348c2ecf20Sopenharmony_ci int result; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (pci_enable_device(pci) != 0) { 19378c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable " 19388c2ecf20Sopenharmony_ci "PCI device", lanai->number); 19398c2ecf20Sopenharmony_ci return -ENXIO; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci pci_set_master(pci); 19428c2ecf20Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) { 19438c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL 19448c2ecf20Sopenharmony_ci "(itf %d): No suitable DMA available.\n", lanai->number); 19458c2ecf20Sopenharmony_ci return -EBUSY; 19468c2ecf20Sopenharmony_ci } 19478c2ecf20Sopenharmony_ci result = check_board_id_and_rev("PCI", pci->subsystem_device, NULL); 19488c2ecf20Sopenharmony_ci if (result != 0) 19498c2ecf20Sopenharmony_ci return result; 19508c2ecf20Sopenharmony_ci /* Set latency timer to zero as per lanai docs */ 19518c2ecf20Sopenharmony_ci result = pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0); 19528c2ecf20Sopenharmony_ci if (result != PCIBIOS_SUCCESSFUL) { 19538c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't write " 19548c2ecf20Sopenharmony_ci "PCI_LATENCY_TIMER: %d\n", lanai->number, result); 19558c2ecf20Sopenharmony_ci return -EINVAL; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci pcistatus_check(lanai, 1); 19588c2ecf20Sopenharmony_ci pcistatus_check(lanai, 0); 19598c2ecf20Sopenharmony_ci return 0; 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci/* -------------------- VPI/VCI ALLOCATION: */ 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci/* 19658c2ecf20Sopenharmony_ci * We _can_ use VCI==0 for normal traffic, but only for UBR (or we'll 19668c2ecf20Sopenharmony_ci * get a CBRZERO interrupt), and we can use it only if no one is receiving 19678c2ecf20Sopenharmony_ci * AAL0 traffic (since they will use the same queue) - according to the 19688c2ecf20Sopenharmony_ci * docs we shouldn't even use it for AAL0 traffic 19698c2ecf20Sopenharmony_ci */ 19708c2ecf20Sopenharmony_cistatic inline int vci0_is_ok(struct lanai_dev *lanai, 19718c2ecf20Sopenharmony_ci const struct atm_qos *qos) 19728c2ecf20Sopenharmony_ci{ 19738c2ecf20Sopenharmony_ci if (qos->txtp.traffic_class == ATM_CBR || qos->aal == ATM_AAL0) 19748c2ecf20Sopenharmony_ci return 0; 19758c2ecf20Sopenharmony_ci if (qos->rxtp.traffic_class != ATM_NONE) { 19768c2ecf20Sopenharmony_ci if (lanai->naal0 != 0) 19778c2ecf20Sopenharmony_ci return 0; 19788c2ecf20Sopenharmony_ci lanai->conf2 |= CONFIG2_VCI0_NORMAL; 19798c2ecf20Sopenharmony_ci conf2_write_if_powerup(lanai); 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci return 1; 19828c2ecf20Sopenharmony_ci} 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci/* return true if vci is currently unused, or if requested qos is 19858c2ecf20Sopenharmony_ci * compatible 19868c2ecf20Sopenharmony_ci */ 19878c2ecf20Sopenharmony_cistatic int vci_is_ok(struct lanai_dev *lanai, vci_t vci, 19888c2ecf20Sopenharmony_ci const struct atm_vcc *atmvcc) 19898c2ecf20Sopenharmony_ci{ 19908c2ecf20Sopenharmony_ci const struct atm_qos *qos = &atmvcc->qos; 19918c2ecf20Sopenharmony_ci const struct lanai_vcc *lvcc = lanai->vccs[vci]; 19928c2ecf20Sopenharmony_ci if (vci == 0 && !vci0_is_ok(lanai, qos)) 19938c2ecf20Sopenharmony_ci return 0; 19948c2ecf20Sopenharmony_ci if (unlikely(lvcc != NULL)) { 19958c2ecf20Sopenharmony_ci if (qos->rxtp.traffic_class != ATM_NONE && 19968c2ecf20Sopenharmony_ci lvcc->rx.atmvcc != NULL && lvcc->rx.atmvcc != atmvcc) 19978c2ecf20Sopenharmony_ci return 0; 19988c2ecf20Sopenharmony_ci if (qos->txtp.traffic_class != ATM_NONE && 19998c2ecf20Sopenharmony_ci lvcc->tx.atmvcc != NULL && lvcc->tx.atmvcc != atmvcc) 20008c2ecf20Sopenharmony_ci return 0; 20018c2ecf20Sopenharmony_ci if (qos->txtp.traffic_class == ATM_CBR && 20028c2ecf20Sopenharmony_ci lanai->cbrvcc != NULL && lanai->cbrvcc != atmvcc) 20038c2ecf20Sopenharmony_ci return 0; 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci if (qos->aal == ATM_AAL0 && lanai->naal0 == 0 && 20068c2ecf20Sopenharmony_ci qos->rxtp.traffic_class != ATM_NONE) { 20078c2ecf20Sopenharmony_ci const struct lanai_vcc *vci0 = lanai->vccs[0]; 20088c2ecf20Sopenharmony_ci if (vci0 != NULL && vci0->rx.atmvcc != NULL) 20098c2ecf20Sopenharmony_ci return 0; 20108c2ecf20Sopenharmony_ci lanai->conf2 &= ~CONFIG2_VCI0_NORMAL; 20118c2ecf20Sopenharmony_ci conf2_write_if_powerup(lanai); 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci return 1; 20148c2ecf20Sopenharmony_ci} 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_cistatic int lanai_normalize_ci(struct lanai_dev *lanai, 20178c2ecf20Sopenharmony_ci const struct atm_vcc *atmvcc, short *vpip, vci_t *vcip) 20188c2ecf20Sopenharmony_ci{ 20198c2ecf20Sopenharmony_ci switch (*vpip) { 20208c2ecf20Sopenharmony_ci case ATM_VPI_ANY: 20218c2ecf20Sopenharmony_ci *vpip = 0; 20228c2ecf20Sopenharmony_ci fallthrough; 20238c2ecf20Sopenharmony_ci case 0: 20248c2ecf20Sopenharmony_ci break; 20258c2ecf20Sopenharmony_ci default: 20268c2ecf20Sopenharmony_ci return -EADDRINUSE; 20278c2ecf20Sopenharmony_ci } 20288c2ecf20Sopenharmony_ci switch (*vcip) { 20298c2ecf20Sopenharmony_ci case ATM_VCI_ANY: 20308c2ecf20Sopenharmony_ci for (*vcip = ATM_NOT_RSV_VCI; *vcip < lanai->num_vci; 20318c2ecf20Sopenharmony_ci (*vcip)++) 20328c2ecf20Sopenharmony_ci if (vci_is_ok(lanai, *vcip, atmvcc)) 20338c2ecf20Sopenharmony_ci return 0; 20348c2ecf20Sopenharmony_ci return -EADDRINUSE; 20358c2ecf20Sopenharmony_ci default: 20368c2ecf20Sopenharmony_ci if (*vcip >= lanai->num_vci || *vcip < 0 || 20378c2ecf20Sopenharmony_ci !vci_is_ok(lanai, *vcip, atmvcc)) 20388c2ecf20Sopenharmony_ci return -EADDRINUSE; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci return 0; 20418c2ecf20Sopenharmony_ci} 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci/* -------------------- MANAGE CBR: */ 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci/* 20468c2ecf20Sopenharmony_ci * CBR ICG is stored as a fixed-point number with 4 fractional bits. 20478c2ecf20Sopenharmony_ci * Note that storing a number greater than 2046.0 will result in 20488c2ecf20Sopenharmony_ci * incorrect shaping 20498c2ecf20Sopenharmony_ci */ 20508c2ecf20Sopenharmony_ci#define CBRICG_FRAC_BITS (4) 20518c2ecf20Sopenharmony_ci#define CBRICG_MAX (2046 << CBRICG_FRAC_BITS) 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci/* 20548c2ecf20Sopenharmony_ci * ICG is related to PCR with the formula PCR = MAXPCR / (ICG + 1) 20558c2ecf20Sopenharmony_ci * where MAXPCR is (according to the docs) 25600000/(54*8), 20568c2ecf20Sopenharmony_ci * which is equal to (3125<<9)/27. 20578c2ecf20Sopenharmony_ci * 20588c2ecf20Sopenharmony_ci * Solving for ICG, we get: 20598c2ecf20Sopenharmony_ci * ICG = MAXPCR/PCR - 1 20608c2ecf20Sopenharmony_ci * ICG = (3125<<9)/(27*PCR) - 1 20618c2ecf20Sopenharmony_ci * ICG = ((3125<<9) - (27*PCR)) / (27*PCR) 20628c2ecf20Sopenharmony_ci * 20638c2ecf20Sopenharmony_ci * The end result is supposed to be a fixed-point number with FRAC_BITS 20648c2ecf20Sopenharmony_ci * bits of a fractional part, so we keep everything in the numerator 20658c2ecf20Sopenharmony_ci * shifted by that much as we compute 20668c2ecf20Sopenharmony_ci * 20678c2ecf20Sopenharmony_ci */ 20688c2ecf20Sopenharmony_cistatic int pcr_to_cbricg(const struct atm_qos *qos) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci int rounddown = 0; /* 1 = Round PCR down, i.e. round ICG _up_ */ 20718c2ecf20Sopenharmony_ci int x, icg, pcr = atm_pcr_goal(&qos->txtp); 20728c2ecf20Sopenharmony_ci if (pcr == 0) /* Use maximum bandwidth */ 20738c2ecf20Sopenharmony_ci return 0; 20748c2ecf20Sopenharmony_ci if (pcr < 0) { 20758c2ecf20Sopenharmony_ci rounddown = 1; 20768c2ecf20Sopenharmony_ci pcr = -pcr; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci x = pcr * 27; 20798c2ecf20Sopenharmony_ci icg = (3125 << (9 + CBRICG_FRAC_BITS)) - (x << CBRICG_FRAC_BITS); 20808c2ecf20Sopenharmony_ci if (rounddown) 20818c2ecf20Sopenharmony_ci icg += x - 1; 20828c2ecf20Sopenharmony_ci icg /= x; 20838c2ecf20Sopenharmony_ci if (icg > CBRICG_MAX) 20848c2ecf20Sopenharmony_ci icg = CBRICG_MAX; 20858c2ecf20Sopenharmony_ci DPRINTK("pcr_to_cbricg: pcr=%d rounddown=%c icg=%d\n", 20868c2ecf20Sopenharmony_ci pcr, rounddown ? 'Y' : 'N', icg); 20878c2ecf20Sopenharmony_ci return icg; 20888c2ecf20Sopenharmony_ci} 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_cistatic inline void lanai_cbr_setup(struct lanai_dev *lanai) 20918c2ecf20Sopenharmony_ci{ 20928c2ecf20Sopenharmony_ci reg_write(lanai, pcr_to_cbricg(&lanai->cbrvcc->qos), CBR_ICG_Reg); 20938c2ecf20Sopenharmony_ci reg_write(lanai, lanai->cbrvcc->vci, CBR_PTR_Reg); 20948c2ecf20Sopenharmony_ci lanai->conf2 |= CONFIG2_CBR_ENABLE; 20958c2ecf20Sopenharmony_ci conf2_write(lanai); 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cistatic inline void lanai_cbr_shutdown(struct lanai_dev *lanai) 20998c2ecf20Sopenharmony_ci{ 21008c2ecf20Sopenharmony_ci lanai->conf2 &= ~CONFIG2_CBR_ENABLE; 21018c2ecf20Sopenharmony_ci conf2_write(lanai); 21028c2ecf20Sopenharmony_ci} 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci/* -------------------- OPERATIONS: */ 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci/* setup a newly detected device */ 21078c2ecf20Sopenharmony_cistatic int lanai_dev_open(struct atm_dev *atmdev) 21088c2ecf20Sopenharmony_ci{ 21098c2ecf20Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 21108c2ecf20Sopenharmony_ci unsigned long raw_base; 21118c2ecf20Sopenharmony_ci int result; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci DPRINTK("In lanai_dev_open()\n"); 21148c2ecf20Sopenharmony_ci /* Basic device fields */ 21158c2ecf20Sopenharmony_ci lanai->number = atmdev->number; 21168c2ecf20Sopenharmony_ci lanai->num_vci = NUM_VCI; 21178c2ecf20Sopenharmony_ci bitmap_zero(lanai->backlog_vccs, NUM_VCI); 21188c2ecf20Sopenharmony_ci bitmap_zero(lanai->transmit_ready, NUM_VCI); 21198c2ecf20Sopenharmony_ci lanai->naal0 = 0; 21208c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 21218c2ecf20Sopenharmony_ci lanai->nbound = 0; 21228c2ecf20Sopenharmony_ci#endif 21238c2ecf20Sopenharmony_ci lanai->cbrvcc = NULL; 21248c2ecf20Sopenharmony_ci memset(&lanai->stats, 0, sizeof lanai->stats); 21258c2ecf20Sopenharmony_ci spin_lock_init(&lanai->endtxlock); 21268c2ecf20Sopenharmony_ci spin_lock_init(&lanai->servicelock); 21278c2ecf20Sopenharmony_ci atmdev->ci_range.vpi_bits = 0; 21288c2ecf20Sopenharmony_ci atmdev->ci_range.vci_bits = 0; 21298c2ecf20Sopenharmony_ci while (1 << atmdev->ci_range.vci_bits < lanai->num_vci) 21308c2ecf20Sopenharmony_ci atmdev->ci_range.vci_bits++; 21318c2ecf20Sopenharmony_ci atmdev->link_rate = ATM_25_PCR; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci /* 3.2: PCI initialization */ 21348c2ecf20Sopenharmony_ci if ((result = lanai_pci_start(lanai)) != 0) 21358c2ecf20Sopenharmony_ci goto error; 21368c2ecf20Sopenharmony_ci raw_base = lanai->pci->resource[0].start; 21378c2ecf20Sopenharmony_ci lanai->base = (bus_addr_t) ioremap(raw_base, LANAI_MAPPING_SIZE); 21388c2ecf20Sopenharmony_ci if (lanai->base == NULL) { 21398c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": couldn't remap I/O space\n"); 21408c2ecf20Sopenharmony_ci result = -ENOMEM; 21418c2ecf20Sopenharmony_ci goto error_pci; 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci /* 3.3: Reset lanai and PHY */ 21448c2ecf20Sopenharmony_ci reset_board(lanai); 21458c2ecf20Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg); 21468c2ecf20Sopenharmony_ci lanai->conf1 &= ~(CONFIG1_GPOUT1 | CONFIG1_POWERDOWN | 21478c2ecf20Sopenharmony_ci CONFIG1_MASK_LEDMODE); 21488c2ecf20Sopenharmony_ci lanai->conf1 |= CONFIG1_SET_LEDMODE(LEDMODE_NOT_SOOL); 21498c2ecf20Sopenharmony_ci reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 21508c2ecf20Sopenharmony_ci udelay(1000); 21518c2ecf20Sopenharmony_ci conf1_write(lanai); 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci /* 21548c2ecf20Sopenharmony_ci * 3.4: Turn on endian mode for big-endian hardware 21558c2ecf20Sopenharmony_ci * We don't actually want to do this - the actual bit fields 21568c2ecf20Sopenharmony_ci * in the endian register are not documented anywhere. 21578c2ecf20Sopenharmony_ci * Instead we do the bit-flipping ourselves on big-endian 21588c2ecf20Sopenharmony_ci * hardware. 21598c2ecf20Sopenharmony_ci * 21608c2ecf20Sopenharmony_ci * 3.5: get the board ID/rev by reading the reset register 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_ci result = check_board_id_and_rev("register", 21638c2ecf20Sopenharmony_ci reg_read(lanai, Reset_Reg), &lanai->board_rev); 21648c2ecf20Sopenharmony_ci if (result != 0) 21658c2ecf20Sopenharmony_ci goto error_unmap; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci /* 3.6: read EEPROM */ 21688c2ecf20Sopenharmony_ci if ((result = eeprom_read(lanai)) != 0) 21698c2ecf20Sopenharmony_ci goto error_unmap; 21708c2ecf20Sopenharmony_ci if ((result = eeprom_validate(lanai)) != 0) 21718c2ecf20Sopenharmony_ci goto error_unmap; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* 3.7: re-reset PHY, do loopback tests, setup PHY */ 21748c2ecf20Sopenharmony_ci reg_write(lanai, lanai->conf1 | CONFIG1_GPOUT1, Config1_Reg); 21758c2ecf20Sopenharmony_ci udelay(1000); 21768c2ecf20Sopenharmony_ci conf1_write(lanai); 21778c2ecf20Sopenharmony_ci /* TODO - loopback tests */ 21788c2ecf20Sopenharmony_ci lanai->conf1 |= (CONFIG1_GPOUT2 | CONFIG1_GPOUT3 | CONFIG1_DMA_ENABLE); 21798c2ecf20Sopenharmony_ci conf1_write(lanai); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* 3.8/3.9: test and initialize card SRAM */ 21828c2ecf20Sopenharmony_ci if ((result = sram_test_and_clear(lanai)) != 0) 21838c2ecf20Sopenharmony_ci goto error_unmap; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci /* 3.10: initialize lanai registers */ 21868c2ecf20Sopenharmony_ci lanai->conf1 |= CONFIG1_DMA_ENABLE; 21878c2ecf20Sopenharmony_ci conf1_write(lanai); 21888c2ecf20Sopenharmony_ci if ((result = service_buffer_allocate(lanai)) != 0) 21898c2ecf20Sopenharmony_ci goto error_unmap; 21908c2ecf20Sopenharmony_ci if ((result = vcc_table_allocate(lanai)) != 0) 21918c2ecf20Sopenharmony_ci goto error_service; 21928c2ecf20Sopenharmony_ci lanai->conf2 = (lanai->num_vci >= 512 ? CONFIG2_HOWMANY : 0) | 21938c2ecf20Sopenharmony_ci CONFIG2_HEC_DROP | /* ??? */ CONFIG2_PTI7_MODE; 21948c2ecf20Sopenharmony_ci conf2_write(lanai); 21958c2ecf20Sopenharmony_ci reg_write(lanai, TX_FIFO_DEPTH, TxDepth_Reg); 21968c2ecf20Sopenharmony_ci reg_write(lanai, 0, CBR_ICG_Reg); /* CBR defaults to no limit */ 21978c2ecf20Sopenharmony_ci if ((result = request_irq(lanai->pci->irq, lanai_int, IRQF_SHARED, 21988c2ecf20Sopenharmony_ci DEV_LABEL, lanai)) != 0) { 21998c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL ": can't allocate interrupt\n"); 22008c2ecf20Sopenharmony_ci goto error_vcctable; 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci mb(); /* Make sure that all that made it */ 22038c2ecf20Sopenharmony_ci intr_enable(lanai, INT_ALL & ~(INT_PING | INT_WAKE)); 22048c2ecf20Sopenharmony_ci /* 3.11: initialize loop mode (i.e. turn looping off) */ 22058c2ecf20Sopenharmony_ci lanai->conf1 = (lanai->conf1 & ~CONFIG1_MASK_LOOPMODE) | 22068c2ecf20Sopenharmony_ci CONFIG1_SET_LOOPMODE(LOOPMODE_NORMAL) | 22078c2ecf20Sopenharmony_ci CONFIG1_GPOUT2 | CONFIG1_GPOUT3; 22088c2ecf20Sopenharmony_ci conf1_write(lanai); 22098c2ecf20Sopenharmony_ci lanai->status = reg_read(lanai, Status_Reg); 22108c2ecf20Sopenharmony_ci /* We're now done initializing this card */ 22118c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 22128c2ecf20Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 22138c2ecf20Sopenharmony_ci conf1_write(lanai); 22148c2ecf20Sopenharmony_ci#endif 22158c2ecf20Sopenharmony_ci memcpy(atmdev->esi, eeprom_mac(lanai), ESI_LEN); 22168c2ecf20Sopenharmony_ci lanai_timed_poll_start(lanai); 22178c2ecf20Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d, base=%p, irq=%u " 22188c2ecf20Sopenharmony_ci "(%pMF)\n", lanai->number, (int) lanai->pci->revision, 22198c2ecf20Sopenharmony_ci lanai->base, lanai->pci->irq, atmdev->esi); 22208c2ecf20Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): LANAI%s, serialno=%u(0x%X), " 22218c2ecf20Sopenharmony_ci "board_rev=%d\n", lanai->number, 22228c2ecf20Sopenharmony_ci lanai->type==lanai2 ? "2" : "HB", (unsigned int) lanai->serialno, 22238c2ecf20Sopenharmony_ci (unsigned int) lanai->serialno, lanai->board_rev); 22248c2ecf20Sopenharmony_ci return 0; 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci error_vcctable: 22278c2ecf20Sopenharmony_ci vcc_table_deallocate(lanai); 22288c2ecf20Sopenharmony_ci error_service: 22298c2ecf20Sopenharmony_ci service_buffer_deallocate(lanai); 22308c2ecf20Sopenharmony_ci error_unmap: 22318c2ecf20Sopenharmony_ci reset_board(lanai); 22328c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 22338c2ecf20Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg) | CONFIG1_POWERDOWN; 22348c2ecf20Sopenharmony_ci conf1_write(lanai); 22358c2ecf20Sopenharmony_ci#endif 22368c2ecf20Sopenharmony_ci iounmap(lanai->base); 22378c2ecf20Sopenharmony_ci lanai->base = NULL; 22388c2ecf20Sopenharmony_ci error_pci: 22398c2ecf20Sopenharmony_ci pci_disable_device(lanai->pci); 22408c2ecf20Sopenharmony_ci error: 22418c2ecf20Sopenharmony_ci return result; 22428c2ecf20Sopenharmony_ci} 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci/* called when device is being shutdown, and all vcc's are gone - higher 22458c2ecf20Sopenharmony_ci * levels will deallocate the atm device for us 22468c2ecf20Sopenharmony_ci */ 22478c2ecf20Sopenharmony_cistatic void lanai_dev_close(struct atm_dev *atmdev) 22488c2ecf20Sopenharmony_ci{ 22498c2ecf20Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 22508c2ecf20Sopenharmony_ci if (lanai->base==NULL) 22518c2ecf20Sopenharmony_ci return; 22528c2ecf20Sopenharmony_ci printk(KERN_INFO DEV_LABEL "(itf %d): shutting down interface\n", 22538c2ecf20Sopenharmony_ci lanai->number); 22548c2ecf20Sopenharmony_ci lanai_timed_poll_stop(lanai); 22558c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 22568c2ecf20Sopenharmony_ci lanai->conf1 = reg_read(lanai, Config1_Reg) & ~CONFIG1_POWERDOWN; 22578c2ecf20Sopenharmony_ci conf1_write(lanai); 22588c2ecf20Sopenharmony_ci#endif 22598c2ecf20Sopenharmony_ci intr_disable(lanai, INT_ALL); 22608c2ecf20Sopenharmony_ci free_irq(lanai->pci->irq, lanai); 22618c2ecf20Sopenharmony_ci reset_board(lanai); 22628c2ecf20Sopenharmony_ci#ifdef USE_POWERDOWN 22638c2ecf20Sopenharmony_ci lanai->conf1 |= CONFIG1_POWERDOWN; 22648c2ecf20Sopenharmony_ci conf1_write(lanai); 22658c2ecf20Sopenharmony_ci#endif 22668c2ecf20Sopenharmony_ci pci_disable_device(lanai->pci); 22678c2ecf20Sopenharmony_ci vcc_table_deallocate(lanai); 22688c2ecf20Sopenharmony_ci service_buffer_deallocate(lanai); 22698c2ecf20Sopenharmony_ci iounmap(lanai->base); 22708c2ecf20Sopenharmony_ci kfree(lanai); 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci/* close a vcc */ 22748c2ecf20Sopenharmony_cistatic void lanai_close(struct atm_vcc *atmvcc) 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 22778c2ecf20Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 22788c2ecf20Sopenharmony_ci if (lvcc == NULL) 22798c2ecf20Sopenharmony_ci return; 22808c2ecf20Sopenharmony_ci clear_bit(ATM_VF_READY, &atmvcc->flags); 22818c2ecf20Sopenharmony_ci clear_bit(ATM_VF_PARTIAL, &atmvcc->flags); 22828c2ecf20Sopenharmony_ci if (lvcc->rx.atmvcc == atmvcc) { 22838c2ecf20Sopenharmony_ci lanai_shutdown_rx_vci(lvcc); 22848c2ecf20Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) { 22858c2ecf20Sopenharmony_ci if (--lanai->naal0 <= 0) 22868c2ecf20Sopenharmony_ci aal0_buffer_free(lanai); 22878c2ecf20Sopenharmony_ci } else 22888c2ecf20Sopenharmony_ci lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci); 22898c2ecf20Sopenharmony_ci lvcc->rx.atmvcc = NULL; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci if (lvcc->tx.atmvcc == atmvcc) { 22928c2ecf20Sopenharmony_ci if (atmvcc == lanai->cbrvcc) { 22938c2ecf20Sopenharmony_ci if (lvcc->vbase != NULL) 22948c2ecf20Sopenharmony_ci lanai_cbr_shutdown(lanai); 22958c2ecf20Sopenharmony_ci lanai->cbrvcc = NULL; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci lanai_shutdown_tx_vci(lanai, lvcc); 22988c2ecf20Sopenharmony_ci lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci); 22998c2ecf20Sopenharmony_ci lvcc->tx.atmvcc = NULL; 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci if (--lvcc->nref == 0) { 23028c2ecf20Sopenharmony_ci host_vcc_unbind(lanai, lvcc); 23038c2ecf20Sopenharmony_ci kfree(lvcc); 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci atmvcc->dev_data = NULL; 23068c2ecf20Sopenharmony_ci clear_bit(ATM_VF_ADDR, &atmvcc->flags); 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci/* open a vcc on the card to vpi/vci */ 23108c2ecf20Sopenharmony_cistatic int lanai_open(struct atm_vcc *atmvcc) 23118c2ecf20Sopenharmony_ci{ 23128c2ecf20Sopenharmony_ci struct lanai_dev *lanai; 23138c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc; 23148c2ecf20Sopenharmony_ci int result = 0; 23158c2ecf20Sopenharmony_ci int vci = atmvcc->vci; 23168c2ecf20Sopenharmony_ci short vpi = atmvcc->vpi; 23178c2ecf20Sopenharmony_ci /* we don't support partial open - it's not really useful anyway */ 23188c2ecf20Sopenharmony_ci if ((test_bit(ATM_VF_PARTIAL, &atmvcc->flags)) || 23198c2ecf20Sopenharmony_ci (vpi == ATM_VPI_UNSPEC) || (vci == ATM_VCI_UNSPEC)) 23208c2ecf20Sopenharmony_ci return -EINVAL; 23218c2ecf20Sopenharmony_ci lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 23228c2ecf20Sopenharmony_ci result = lanai_normalize_ci(lanai, atmvcc, &vpi, &vci); 23238c2ecf20Sopenharmony_ci if (unlikely(result != 0)) 23248c2ecf20Sopenharmony_ci goto out; 23258c2ecf20Sopenharmony_ci set_bit(ATM_VF_ADDR, &atmvcc->flags); 23268c2ecf20Sopenharmony_ci if (atmvcc->qos.aal != ATM_AAL0 && atmvcc->qos.aal != ATM_AAL5) 23278c2ecf20Sopenharmony_ci return -EINVAL; 23288c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n", lanai->number, 23298c2ecf20Sopenharmony_ci (int) vpi, vci); 23308c2ecf20Sopenharmony_ci lvcc = lanai->vccs[vci]; 23318c2ecf20Sopenharmony_ci if (lvcc == NULL) { 23328c2ecf20Sopenharmony_ci lvcc = new_lanai_vcc(); 23338c2ecf20Sopenharmony_ci if (unlikely(lvcc == NULL)) 23348c2ecf20Sopenharmony_ci return -ENOMEM; 23358c2ecf20Sopenharmony_ci atmvcc->dev_data = lvcc; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci lvcc->nref++; 23388c2ecf20Sopenharmony_ci if (atmvcc->qos.rxtp.traffic_class != ATM_NONE) { 23398c2ecf20Sopenharmony_ci APRINTK(lvcc->rx.atmvcc == NULL, "rx.atmvcc!=NULL, vci=%d\n", 23408c2ecf20Sopenharmony_ci vci); 23418c2ecf20Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) { 23428c2ecf20Sopenharmony_ci if (lanai->naal0 == 0) 23438c2ecf20Sopenharmony_ci result = aal0_buffer_allocate(lanai); 23448c2ecf20Sopenharmony_ci } else 23458c2ecf20Sopenharmony_ci result = lanai_setup_rx_vci_aal5( 23468c2ecf20Sopenharmony_ci lanai, lvcc, &atmvcc->qos); 23478c2ecf20Sopenharmony_ci if (unlikely(result != 0)) 23488c2ecf20Sopenharmony_ci goto out_free; 23498c2ecf20Sopenharmony_ci lvcc->rx.atmvcc = atmvcc; 23508c2ecf20Sopenharmony_ci lvcc->stats.rx_nomem = 0; 23518c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen = 0; 23528c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_trash = 0; 23538c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_stream = 0; 23548c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc = 0; 23558c2ecf20Sopenharmony_ci if (atmvcc->qos.aal == ATM_AAL0) 23568c2ecf20Sopenharmony_ci lanai->naal0++; 23578c2ecf20Sopenharmony_ci } 23588c2ecf20Sopenharmony_ci if (atmvcc->qos.txtp.traffic_class != ATM_NONE) { 23598c2ecf20Sopenharmony_ci APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n", 23608c2ecf20Sopenharmony_ci vci); 23618c2ecf20Sopenharmony_ci result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos); 23628c2ecf20Sopenharmony_ci if (unlikely(result != 0)) 23638c2ecf20Sopenharmony_ci goto out_free; 23648c2ecf20Sopenharmony_ci lvcc->tx.atmvcc = atmvcc; 23658c2ecf20Sopenharmony_ci if (atmvcc->qos.txtp.traffic_class == ATM_CBR) { 23668c2ecf20Sopenharmony_ci APRINTK(lanai->cbrvcc == NULL, 23678c2ecf20Sopenharmony_ci "cbrvcc!=NULL, vci=%d\n", vci); 23688c2ecf20Sopenharmony_ci lanai->cbrvcc = atmvcc; 23698c2ecf20Sopenharmony_ci } 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci host_vcc_bind(lanai, lvcc, vci); 23728c2ecf20Sopenharmony_ci /* 23738c2ecf20Sopenharmony_ci * Make sure everything made it to RAM before we tell the card about 23748c2ecf20Sopenharmony_ci * the VCC 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_ci wmb(); 23778c2ecf20Sopenharmony_ci if (atmvcc == lvcc->rx.atmvcc) 23788c2ecf20Sopenharmony_ci host_vcc_start_rx(lvcc); 23798c2ecf20Sopenharmony_ci if (atmvcc == lvcc->tx.atmvcc) { 23808c2ecf20Sopenharmony_ci host_vcc_start_tx(lvcc); 23818c2ecf20Sopenharmony_ci if (lanai->cbrvcc == atmvcc) 23828c2ecf20Sopenharmony_ci lanai_cbr_setup(lanai); 23838c2ecf20Sopenharmony_ci } 23848c2ecf20Sopenharmony_ci set_bit(ATM_VF_READY, &atmvcc->flags); 23858c2ecf20Sopenharmony_ci return 0; 23868c2ecf20Sopenharmony_ci out_free: 23878c2ecf20Sopenharmony_ci lanai_close(atmvcc); 23888c2ecf20Sopenharmony_ci out: 23898c2ecf20Sopenharmony_ci return result; 23908c2ecf20Sopenharmony_ci} 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_cistatic int lanai_send(struct atm_vcc *atmvcc, struct sk_buff *skb) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc = (struct lanai_vcc *) atmvcc->dev_data; 23958c2ecf20Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmvcc->dev->dev_data; 23968c2ecf20Sopenharmony_ci unsigned long flags; 23978c2ecf20Sopenharmony_ci if (unlikely(lvcc == NULL || lvcc->vbase == NULL || 23988c2ecf20Sopenharmony_ci lvcc->tx.atmvcc != atmvcc)) 23998c2ecf20Sopenharmony_ci goto einval; 24008c2ecf20Sopenharmony_ci#ifdef DEBUG 24018c2ecf20Sopenharmony_ci if (unlikely(skb == NULL)) { 24028c2ecf20Sopenharmony_ci DPRINTK("lanai_send: skb==NULL for vci=%d\n", atmvcc->vci); 24038c2ecf20Sopenharmony_ci goto einval; 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci if (unlikely(lanai == NULL)) { 24068c2ecf20Sopenharmony_ci DPRINTK("lanai_send: lanai==NULL for vci=%d\n", atmvcc->vci); 24078c2ecf20Sopenharmony_ci goto einval; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci#endif 24108c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = atmvcc; 24118c2ecf20Sopenharmony_ci switch (atmvcc->qos.aal) { 24128c2ecf20Sopenharmony_ci case ATM_AAL5: 24138c2ecf20Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 24148c2ecf20Sopenharmony_ci vcc_tx_aal5(lanai, lvcc, skb); 24158c2ecf20Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 24168c2ecf20Sopenharmony_ci return 0; 24178c2ecf20Sopenharmony_ci case ATM_AAL0: 24188c2ecf20Sopenharmony_ci if (unlikely(skb->len != ATM_CELL_SIZE-1)) 24198c2ecf20Sopenharmony_ci goto einval; 24208c2ecf20Sopenharmony_ci /* NOTE - this next line is technically invalid - we haven't unshared skb */ 24218c2ecf20Sopenharmony_ci cpu_to_be32s((u32 *) skb->data); 24228c2ecf20Sopenharmony_ci read_lock_irqsave(&vcc_sklist_lock, flags); 24238c2ecf20Sopenharmony_ci vcc_tx_aal0(lanai, lvcc, skb); 24248c2ecf20Sopenharmony_ci read_unlock_irqrestore(&vcc_sklist_lock, flags); 24258c2ecf20Sopenharmony_ci return 0; 24268c2ecf20Sopenharmony_ci } 24278c2ecf20Sopenharmony_ci DPRINTK("lanai_send: bad aal=%d on vci=%d\n", (int) atmvcc->qos.aal, 24288c2ecf20Sopenharmony_ci atmvcc->vci); 24298c2ecf20Sopenharmony_ci einval: 24308c2ecf20Sopenharmony_ci lanai_free_skb(atmvcc, skb); 24318c2ecf20Sopenharmony_ci return -EINVAL; 24328c2ecf20Sopenharmony_ci} 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cistatic int lanai_change_qos(struct atm_vcc *atmvcc, 24358c2ecf20Sopenharmony_ci /*const*/ struct atm_qos *qos, int flags) 24368c2ecf20Sopenharmony_ci{ 24378c2ecf20Sopenharmony_ci return -EBUSY; /* TODO: need to write this */ 24388c2ecf20Sopenharmony_ci} 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci#ifndef CONFIG_PROC_FS 24418c2ecf20Sopenharmony_ci#define lanai_proc_read NULL 24428c2ecf20Sopenharmony_ci#else 24438c2ecf20Sopenharmony_cistatic int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) 24448c2ecf20Sopenharmony_ci{ 24458c2ecf20Sopenharmony_ci struct lanai_dev *lanai = (struct lanai_dev *) atmdev->dev_data; 24468c2ecf20Sopenharmony_ci loff_t left = *pos; 24478c2ecf20Sopenharmony_ci struct lanai_vcc *lvcc; 24488c2ecf20Sopenharmony_ci if (left-- == 0) 24498c2ecf20Sopenharmony_ci return sprintf(page, DEV_LABEL "(itf %d): chip=LANAI%s, " 24508c2ecf20Sopenharmony_ci "serial=%u, magic=0x%08X, num_vci=%d\n", 24518c2ecf20Sopenharmony_ci atmdev->number, lanai->type==lanai2 ? "2" : "HB", 24528c2ecf20Sopenharmony_ci (unsigned int) lanai->serialno, 24538c2ecf20Sopenharmony_ci (unsigned int) lanai->magicno, lanai->num_vci); 24548c2ecf20Sopenharmony_ci if (left-- == 0) 24558c2ecf20Sopenharmony_ci return sprintf(page, "revision: board=%d, pci_if=%d\n", 24568c2ecf20Sopenharmony_ci lanai->board_rev, (int) lanai->pci->revision); 24578c2ecf20Sopenharmony_ci if (left-- == 0) 24588c2ecf20Sopenharmony_ci return sprintf(page, "EEPROM ESI: %pM\n", 24598c2ecf20Sopenharmony_ci &lanai->eeprom[EEPROM_MAC]); 24608c2ecf20Sopenharmony_ci if (left-- == 0) 24618c2ecf20Sopenharmony_ci return sprintf(page, "status: SOOL=%d, LOCD=%d, LED=%d, " 24628c2ecf20Sopenharmony_ci "GPIN=%d\n", (lanai->status & STATUS_SOOL) ? 1 : 0, 24638c2ecf20Sopenharmony_ci (lanai->status & STATUS_LOCD) ? 1 : 0, 24648c2ecf20Sopenharmony_ci (lanai->status & STATUS_LED) ? 1 : 0, 24658c2ecf20Sopenharmony_ci (lanai->status & STATUS_GPIN) ? 1 : 0); 24668c2ecf20Sopenharmony_ci if (left-- == 0) 24678c2ecf20Sopenharmony_ci return sprintf(page, "global buffer sizes: service=%zu, " 24688c2ecf20Sopenharmony_ci "aal0_rx=%zu\n", lanai_buf_size(&lanai->service), 24698c2ecf20Sopenharmony_ci lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); 24708c2ecf20Sopenharmony_ci if (left-- == 0) { 24718c2ecf20Sopenharmony_ci get_statistics(lanai); 24728c2ecf20Sopenharmony_ci return sprintf(page, "cells in error: overflow=%u, " 24738c2ecf20Sopenharmony_ci "closed_vci=%u, bad_HEC=%u, rx_fifo=%u\n", 24748c2ecf20Sopenharmony_ci lanai->stats.ovfl_trash, lanai->stats.vci_trash, 24758c2ecf20Sopenharmony_ci lanai->stats.hec_err, lanai->stats.atm_ovfl); 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci if (left-- == 0) 24788c2ecf20Sopenharmony_ci return sprintf(page, "PCI errors: parity_detect=%u, " 24798c2ecf20Sopenharmony_ci "master_abort=%u, master_target_abort=%u,\n", 24808c2ecf20Sopenharmony_ci lanai->stats.pcierr_parity_detect, 24818c2ecf20Sopenharmony_ci lanai->stats.pcierr_serr_set, 24828c2ecf20Sopenharmony_ci lanai->stats.pcierr_m_target_abort); 24838c2ecf20Sopenharmony_ci if (left-- == 0) 24848c2ecf20Sopenharmony_ci return sprintf(page, " slave_target_abort=%u, " 24858c2ecf20Sopenharmony_ci "master_parity=%u\n", lanai->stats.pcierr_s_target_abort, 24868c2ecf20Sopenharmony_ci lanai->stats.pcierr_master_parity); 24878c2ecf20Sopenharmony_ci if (left-- == 0) 24888c2ecf20Sopenharmony_ci return sprintf(page, " no_tx=%u, " 24898c2ecf20Sopenharmony_ci "no_rx=%u, bad_rx_aal=%u\n", lanai->stats.service_norx, 24908c2ecf20Sopenharmony_ci lanai->stats.service_notx, 24918c2ecf20Sopenharmony_ci lanai->stats.service_rxnotaal5); 24928c2ecf20Sopenharmony_ci if (left-- == 0) 24938c2ecf20Sopenharmony_ci return sprintf(page, "resets: dma=%u, card=%u\n", 24948c2ecf20Sopenharmony_ci lanai->stats.dma_reenable, lanai->stats.card_reset); 24958c2ecf20Sopenharmony_ci /* At this point, "left" should be the VCI we're looking for */ 24968c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 24978c2ecf20Sopenharmony_ci for (; ; left++) { 24988c2ecf20Sopenharmony_ci if (left >= NUM_VCI) { 24998c2ecf20Sopenharmony_ci left = 0; 25008c2ecf20Sopenharmony_ci goto out; 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci if ((lvcc = lanai->vccs[left]) != NULL) 25038c2ecf20Sopenharmony_ci break; 25048c2ecf20Sopenharmony_ci (*pos)++; 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci /* Note that we re-use "left" here since we're done with it */ 25078c2ecf20Sopenharmony_ci left = sprintf(page, "VCI %4d: nref=%d, rx_nomem=%u", (vci_t) left, 25088c2ecf20Sopenharmony_ci lvcc->nref, lvcc->stats.rx_nomem); 25098c2ecf20Sopenharmony_ci if (lvcc->rx.atmvcc != NULL) { 25108c2ecf20Sopenharmony_ci left += sprintf(&page[left], ",\n rx_AAL=%d", 25118c2ecf20Sopenharmony_ci lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); 25128c2ecf20Sopenharmony_ci if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) 25138c2ecf20Sopenharmony_ci left += sprintf(&page[left], ", rx_buf_size=%zu, " 25148c2ecf20Sopenharmony_ci "rx_bad_len=%u,\n rx_service_trash=%u, " 25158c2ecf20Sopenharmony_ci "rx_service_stream=%u, rx_bad_crc=%u", 25168c2ecf20Sopenharmony_ci lanai_buf_size(&lvcc->rx.buf), 25178c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.rx_badlen, 25188c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_trash, 25198c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_stream, 25208c2ecf20Sopenharmony_ci lvcc->stats.x.aal5.service_rxcrc); 25218c2ecf20Sopenharmony_ci } 25228c2ecf20Sopenharmony_ci if (lvcc->tx.atmvcc != NULL) 25238c2ecf20Sopenharmony_ci left += sprintf(&page[left], ",\n tx_AAL=%d, " 25248c2ecf20Sopenharmony_ci "tx_buf_size=%zu, tx_qos=%cBR, tx_backlogged=%c", 25258c2ecf20Sopenharmony_ci lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, 25268c2ecf20Sopenharmony_ci lanai_buf_size(&lvcc->tx.buf), 25278c2ecf20Sopenharmony_ci lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', 25288c2ecf20Sopenharmony_ci vcc_is_backlogged(lvcc) ? 'Y' : 'N'); 25298c2ecf20Sopenharmony_ci page[left++] = '\n'; 25308c2ecf20Sopenharmony_ci page[left] = '\0'; 25318c2ecf20Sopenharmony_ci out: 25328c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 25338c2ecf20Sopenharmony_ci return left; 25348c2ecf20Sopenharmony_ci} 25358c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci/* -------------------- HOOKS: */ 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_cistatic const struct atmdev_ops ops = { 25408c2ecf20Sopenharmony_ci .dev_close = lanai_dev_close, 25418c2ecf20Sopenharmony_ci .open = lanai_open, 25428c2ecf20Sopenharmony_ci .close = lanai_close, 25438c2ecf20Sopenharmony_ci .send = lanai_send, 25448c2ecf20Sopenharmony_ci .phy_put = NULL, 25458c2ecf20Sopenharmony_ci .phy_get = NULL, 25468c2ecf20Sopenharmony_ci .change_qos = lanai_change_qos, 25478c2ecf20Sopenharmony_ci .proc_read = lanai_proc_read, 25488c2ecf20Sopenharmony_ci .owner = THIS_MODULE 25498c2ecf20Sopenharmony_ci}; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci/* initialize one probed card */ 25528c2ecf20Sopenharmony_cistatic int lanai_init_one(struct pci_dev *pci, 25538c2ecf20Sopenharmony_ci const struct pci_device_id *ident) 25548c2ecf20Sopenharmony_ci{ 25558c2ecf20Sopenharmony_ci struct lanai_dev *lanai; 25568c2ecf20Sopenharmony_ci struct atm_dev *atmdev; 25578c2ecf20Sopenharmony_ci int result; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci lanai = kzalloc(sizeof(*lanai), GFP_KERNEL); 25608c2ecf20Sopenharmony_ci if (lanai == NULL) { 25618c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL 25628c2ecf20Sopenharmony_ci ": couldn't allocate dev_data structure!\n"); 25638c2ecf20Sopenharmony_ci return -ENOMEM; 25648c2ecf20Sopenharmony_ci } 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci atmdev = atm_dev_register(DEV_LABEL, &pci->dev, &ops, -1, NULL); 25678c2ecf20Sopenharmony_ci if (atmdev == NULL) { 25688c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL 25698c2ecf20Sopenharmony_ci ": couldn't register atm device!\n"); 25708c2ecf20Sopenharmony_ci kfree(lanai); 25718c2ecf20Sopenharmony_ci return -EBUSY; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci atmdev->dev_data = lanai; 25758c2ecf20Sopenharmony_ci lanai->pci = pci; 25768c2ecf20Sopenharmony_ci lanai->type = (enum lanai_type) ident->device; 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci result = lanai_dev_open(atmdev); 25798c2ecf20Sopenharmony_ci if (result != 0) { 25808c2ecf20Sopenharmony_ci DPRINTK("lanai_start() failed, err=%d\n", -result); 25818c2ecf20Sopenharmony_ci atm_dev_deregister(atmdev); 25828c2ecf20Sopenharmony_ci kfree(lanai); 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci return result; 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic const struct pci_device_id lanai_pci_tbl[] = { 25888c2ecf20Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAI2) }, 25898c2ecf20Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_LANAIHB) }, 25908c2ecf20Sopenharmony_ci { 0, } /* terminal entry */ 25918c2ecf20Sopenharmony_ci}; 25928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, lanai_pci_tbl); 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_cistatic struct pci_driver lanai_driver = { 25958c2ecf20Sopenharmony_ci .name = DEV_LABEL, 25968c2ecf20Sopenharmony_ci .id_table = lanai_pci_tbl, 25978c2ecf20Sopenharmony_ci .probe = lanai_init_one, 25988c2ecf20Sopenharmony_ci}; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_cimodule_pci_driver(lanai_driver); 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mitchell Blank Jr <mitch@sfgoth.com>"); 26038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); 26048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2605