18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Madge Ambassador ATM Adapter driver. 48c2ecf20Sopenharmony_ci Copyright (C) 1995-1999 Madge Networks Ltd. 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* * dedicated to the memory of Graham Gordon 1971-1998 * */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/poison.h> 208c2ecf20Sopenharmony_ci#include <linux/bitrev.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/firmware.h> 238c2ecf20Sopenharmony_ci#include <linux/ihex.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/atomic.h> 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "ambassador.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>" 338c2ecf20Sopenharmony_ci#define description_string "Madge ATM Ambassador driver" 348c2ecf20Sopenharmony_ci#define version_string "1.2.4" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline void __init show_version (void) { 378c2ecf20Sopenharmony_ci printk ("%s version %s\n", description_string, version_string); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci Theory of Operation 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci I Hardware, detection, initialisation and shutdown. 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 1. Supported Hardware 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci This driver is for the PCI ATMizer-based Ambassador card (except 498c2ecf20Sopenharmony_ci very early versions). It is not suitable for the similar EISA "TR7" 508c2ecf20Sopenharmony_ci card. Commercially, both cards are known as Collage Server ATM 518c2ecf20Sopenharmony_ci adapters. 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci The loader supports image transfer to the card, image start and few 548c2ecf20Sopenharmony_ci other miscellaneous commands. 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023. 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci The cards are big-endian. 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 2. Detection 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci Standard PCI stuff, the early cards are detected and rejected. 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 3. Initialisation 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci The cards are reset and the self-test results are checked. The 678c2ecf20Sopenharmony_ci microcode image is then transferred and started. This waits for a 688c2ecf20Sopenharmony_ci pointer to a descriptor containing details of the host-based queues 698c2ecf20Sopenharmony_ci and buffers and various parameters etc. Once they are processed 708c2ecf20Sopenharmony_ci normal operations may begin. The BIA is read using a microcode 718c2ecf20Sopenharmony_ci command. 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci 4. Shutdown 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci This may be accomplished either by a card reset or via the microcode 768c2ecf20Sopenharmony_ci shutdown command. Further investigation required. 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 5. Persistent state 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci The card reset does not affect PCI configuration (good) or the 818c2ecf20Sopenharmony_ci contents of several other "shared run-time registers" (bad) which 828c2ecf20Sopenharmony_ci include doorbell and interrupt control as well as EEPROM and PCI 838c2ecf20Sopenharmony_ci control. The driver must be careful when modifying these registers 848c2ecf20Sopenharmony_ci not to touch bits it does not use and to undo any changes at exit. 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci II Driver software 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci 0. Generalities 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci The adapter is quite intelligent (fast) and has a simple interface 918c2ecf20Sopenharmony_ci (few features). VPI is always zero, 1024 VCIs are supported. There 928c2ecf20Sopenharmony_ci is limited cell rate support. UBR channels can be capped and ABR 938c2ecf20Sopenharmony_ci (explicit rate, but not EFCI) is supported. There is no CBR or VBR 948c2ecf20Sopenharmony_ci support. 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 1. Driver <-> Adapter Communication 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci Apart from the basic loader commands, the driver communicates 998c2ecf20Sopenharmony_ci through three entities: the command queue (CQ), the transmit queue 1008c2ecf20Sopenharmony_ci pair (TXQ) and the receive queue pairs (RXQ). These three entities 1018c2ecf20Sopenharmony_ci are set up by the host and passed to the microcode just after it has 1028c2ecf20Sopenharmony_ci been started. 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci All queues are host-based circular queues. They are contiguous and 1058c2ecf20Sopenharmony_ci (due to hardware limitations) have some restrictions as to their 1068c2ecf20Sopenharmony_ci locations in (bus) memory. They are of the "full means the same as 1078c2ecf20Sopenharmony_ci empty so don't do that" variety since the adapter uses pointers 1088c2ecf20Sopenharmony_ci internally. 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci The queue pairs work as follows: one queue is for supply to the 1118c2ecf20Sopenharmony_ci adapter, items in it are pending and are owned by the adapter; the 1128c2ecf20Sopenharmony_ci other is the queue for return from the adapter, items in it have 1138c2ecf20Sopenharmony_ci been dealt with by the adapter. The host adds items to the supply 1148c2ecf20Sopenharmony_ci (TX descriptors and free RX buffer descriptors) and removes items 1158c2ecf20Sopenharmony_ci from the return (TX and RX completions). The adapter deals with out 1168c2ecf20Sopenharmony_ci of order completions. 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci Interrupts (card to host) and the doorbell (host to card) are used 1198c2ecf20Sopenharmony_ci for signalling. 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci 1. CQ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci This is to communicate "open VC", "close VC", "get stats" etc. to 1248c2ecf20Sopenharmony_ci the adapter. At most one command is retired every millisecond by the 1258c2ecf20Sopenharmony_ci card. There is no out of order completion or notification. The 1268c2ecf20Sopenharmony_ci driver needs to check the return code of the command, waiting as 1278c2ecf20Sopenharmony_ci appropriate. 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci 2. TXQ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci TX supply items are of variable length (scatter gather support) and 1328c2ecf20Sopenharmony_ci so the queue items are (more or less) pointers to the real thing. 1338c2ecf20Sopenharmony_ci Each TX supply item contains a unique, host-supplied handle (the skb 1348c2ecf20Sopenharmony_ci bus address seems most sensible as this works for Alphas as well, 1358c2ecf20Sopenharmony_ci there is no need to do any endian conversions on the handles). 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci TX return items consist of just the handles above. 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci 3. RXQ (up to 4 of these with different lengths and buffer sizes) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci RX supply items consist of a unique, host-supplied handle (the skb 1428c2ecf20Sopenharmony_ci bus address again) and a pointer to the buffer data area. 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci RX return items consist of the handle above, the VC, length and a 1458c2ecf20Sopenharmony_ci status word. This just screams "oh so easy" doesn't it? 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci Note on RX pool sizes: 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci Each pool should have enough buffers to handle a back-to-back stream 1508c2ecf20Sopenharmony_ci of minimum sized frames on a single VC. For example: 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci frame spacing = 3us (about right) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci delay = IRQ lat + RX handling + RX buffer replenish = 20 (us) (a guess) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci min number of buffers for one VC = 1 + delay/spacing (buffers) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci delay/spacing = latency = (20+2)/3 = 7 (buffers) (rounding up) 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci The 20us delay assumes that there is no need to sleep; if we need to 1618c2ecf20Sopenharmony_ci sleep to get buffers we are going to drop frames anyway. 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci In fact, each pool should have enough buffers to support the 1648c2ecf20Sopenharmony_ci simultaneous reassembly of a separate frame on each VC and cope with 1658c2ecf20Sopenharmony_ci the case in which frames complete in round robin cell fashion on 1668c2ecf20Sopenharmony_ci each VC. 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci Only one frame can complete at each cell arrival, so if "n" VCs are 1698c2ecf20Sopenharmony_ci open, the worst case is to have them all complete frames together 1708c2ecf20Sopenharmony_ci followed by all starting new frames together. 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci desired number of buffers = n + delay/spacing 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci These are the extreme requirements, however, they are "n+k" for some 1758c2ecf20Sopenharmony_ci "k" so we have only the constant to choose. This is the argument 1768c2ecf20Sopenharmony_ci rx_lats which current defaults to 7. 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci Actually, "n ? n+k : 0" is better and this is what is implemented, 1798c2ecf20Sopenharmony_ci subject to the limit given by the pool size. 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci 4. Driver locking 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci Simple spinlocks are used around the TX and RX queue mechanisms. 1848c2ecf20Sopenharmony_ci Anyone with a faster, working method is welcome to implement it. 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci The adapter command queue is protected with a spinlock. We always 1878c2ecf20Sopenharmony_ci wait for commands to complete. 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci A more complex form of locking is used around parts of the VC open 1908c2ecf20Sopenharmony_ci and close functions. There are three reasons for a lock: 1. we need 1918c2ecf20Sopenharmony_ci to do atomic rate reservation and release (not used yet), 2. Opening 1928c2ecf20Sopenharmony_ci sometimes involves two adapter commands which must not be separated 1938c2ecf20Sopenharmony_ci by another command on the same VC, 3. the changes to RX pool size 1948c2ecf20Sopenharmony_ci must be atomic. The lock needs to work over context switches, so we 1958c2ecf20Sopenharmony_ci use a semaphore. 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci III Hardware Features and Microcode Bugs 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 1. Byte Ordering 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*! 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci 2. Memory access 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci All structures that are not accessed using DMA must be 4-byte 2068c2ecf20Sopenharmony_ci aligned (not a problem) and must not cross 4MB boundaries. 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci There is a DMA memory hole at E0000000-E00000FF (groan). 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB 2118c2ecf20Sopenharmony_ci but for a hardware bug). 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci RX buffers (DMA write) must not cross 16MB boundaries and must 2148c2ecf20Sopenharmony_ci include spare trailing bytes up to the next 4-byte boundary; they 2158c2ecf20Sopenharmony_ci will be written with rubbish. 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci The PLX likes to prefetch; if reading up to 4 u32 past the end of 2188c2ecf20Sopenharmony_ci each TX fragment is not a problem, then TX can be made to go a 2198c2ecf20Sopenharmony_ci little faster by passing a flag at init that disables a prefetch 2208c2ecf20Sopenharmony_ci workaround. We do not pass this flag. (new microcode only) 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci Now we: 2238c2ecf20Sopenharmony_ci . Note that alloc_skb rounds up size to a 16byte boundary. 2248c2ecf20Sopenharmony_ci . Ensure all areas do not traverse 4MB boundaries. 2258c2ecf20Sopenharmony_ci . Ensure all areas do not start at a E00000xx bus address. 2268c2ecf20Sopenharmony_ci (I cannot be certain, but this may always hold with Linux) 2278c2ecf20Sopenharmony_ci . Make all failures cause a loud message. 2288c2ecf20Sopenharmony_ci . Discard non-conforming SKBs (causes TX failure or RX fill delay). 2298c2ecf20Sopenharmony_ci . Discard non-conforming TX fragment descriptors (the TX fails). 2308c2ecf20Sopenharmony_ci In the future we could: 2318c2ecf20Sopenharmony_ci . Allow RX areas that traverse 4MB (but not 16MB) boundaries. 2328c2ecf20Sopenharmony_ci . Segment TX areas into some/more fragments, when necessary. 2338c2ecf20Sopenharmony_ci . Relax checks for non-DMA items (ignore hole). 2348c2ecf20Sopenharmony_ci . Give scatter-gather (iovec) requirements using ???. (?) 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci 3. VC close is broken (only for new microcode) 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci The VC close adapter microcode command fails to do anything if any 2398c2ecf20Sopenharmony_ci frames have been received on the VC but none have been transmitted. 2408c2ecf20Sopenharmony_ci Frames continue to be reassembled and passed (with IRQ) to the 2418c2ecf20Sopenharmony_ci driver. 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci IV To Do List 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci . Fix bugs! 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci . Timer code may be broken. 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci . Deal with buggy VC close (somehow) in microcode 12. 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci . Handle interrupted and/or non-blocking writes - is this a job for 2528c2ecf20Sopenharmony_ci the protocol layer? 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci . Add code to break up TX fragments when they span 4MB boundaries. 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci . Add SUNI phy layer (need to know where SUNI lives on card). 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b) 2598c2ecf20Sopenharmony_ci leave extra headroom space for Ambassador TX descriptors. 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci . Understand these elements of struct atm_vcc: recvq (proto?), 2628c2ecf20Sopenharmony_ci sleep, callback, listenq, backlog_quota, reply and user_back. 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable). 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow. 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci . Decide whether RX buffer recycling is or can be made completely safe; 2698c2ecf20Sopenharmony_ci turn it back on. It looks like Werner is going to axe this. 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci . Implement QoS changes on open VCs (involves extracting parts of VC open 2728c2ecf20Sopenharmony_ci and close into separate functions and using them to make changes). 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci . Hack on command queue so that someone can issue multiple commands and wait 2758c2ecf20Sopenharmony_ci on the last one (OR only "no-op" or "wait" commands are waited for). 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci . Eliminate need for while-schedule around do_command. 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci*/ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void do_housekeeping (struct timer_list *t); 2828c2ecf20Sopenharmony_ci/********** globals **********/ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic unsigned short debug = 0; 2858c2ecf20Sopenharmony_cistatic unsigned int cmds = 8; 2868c2ecf20Sopenharmony_cistatic unsigned int txs = 32; 2878c2ecf20Sopenharmony_cistatic unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 }; 2888c2ecf20Sopenharmony_cistatic unsigned int rxs_bs[NUM_RX_POOLS] = { 4080, 12240, 36720, 65535 }; 2898c2ecf20Sopenharmony_cistatic unsigned int rx_lats = 7; 2908c2ecf20Sopenharmony_cistatic unsigned char pci_lat = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const unsigned long onegigmask = -1 << 30; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/********** access to adapter **********/ 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic inline void wr_plain (const amb_dev * dev, size_t addr, u32 data) { 2978c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x", addr, data); 2988c2ecf20Sopenharmony_ci#ifdef AMB_MMIO 2998c2ecf20Sopenharmony_ci dev->membase[addr / sizeof(u32)] = data; 3008c2ecf20Sopenharmony_ci#else 3018c2ecf20Sopenharmony_ci outl (data, dev->iobase + addr); 3028c2ecf20Sopenharmony_ci#endif 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic inline u32 rd_plain (const amb_dev * dev, size_t addr) { 3068c2ecf20Sopenharmony_ci#ifdef AMB_MMIO 3078c2ecf20Sopenharmony_ci u32 data = dev->membase[addr / sizeof(u32)]; 3088c2ecf20Sopenharmony_ci#else 3098c2ecf20Sopenharmony_ci u32 data = inl (dev->iobase + addr); 3108c2ecf20Sopenharmony_ci#endif 3118c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x", addr, data); 3128c2ecf20Sopenharmony_ci return data; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic inline void wr_mem (const amb_dev * dev, size_t addr, u32 data) { 3168c2ecf20Sopenharmony_ci __be32 be = cpu_to_be32 (data); 3178c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x b[%08x]", addr, data, be); 3188c2ecf20Sopenharmony_ci#ifdef AMB_MMIO 3198c2ecf20Sopenharmony_ci dev->membase[addr / sizeof(u32)] = be; 3208c2ecf20Sopenharmony_ci#else 3218c2ecf20Sopenharmony_ci outl (be, dev->iobase + addr); 3228c2ecf20Sopenharmony_ci#endif 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic inline u32 rd_mem (const amb_dev * dev, size_t addr) { 3268c2ecf20Sopenharmony_ci#ifdef AMB_MMIO 3278c2ecf20Sopenharmony_ci __be32 be = dev->membase[addr / sizeof(u32)]; 3288c2ecf20Sopenharmony_ci#else 3298c2ecf20Sopenharmony_ci __be32 be = inl (dev->iobase + addr); 3308c2ecf20Sopenharmony_ci#endif 3318c2ecf20Sopenharmony_ci u32 data = be32_to_cpu (be); 3328c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x b[%08x]", addr, data, be); 3338c2ecf20Sopenharmony_ci return data; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/********** dump routines **********/ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic inline void dump_registers (const amb_dev * dev) { 3398c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR 3408c2ecf20Sopenharmony_ci if (debug & DBG_REGS) { 3418c2ecf20Sopenharmony_ci size_t i; 3428c2ecf20Sopenharmony_ci PRINTD (DBG_REGS, "reading PLX control: "); 3438c2ecf20Sopenharmony_ci for (i = 0x00; i < 0x30; i += sizeof(u32)) 3448c2ecf20Sopenharmony_ci rd_mem (dev, i); 3458c2ecf20Sopenharmony_ci PRINTD (DBG_REGS, "reading mailboxes: "); 3468c2ecf20Sopenharmony_ci for (i = 0x40; i < 0x60; i += sizeof(u32)) 3478c2ecf20Sopenharmony_ci rd_mem (dev, i); 3488c2ecf20Sopenharmony_ci PRINTD (DBG_REGS, "reading doorb irqev irqen reset:"); 3498c2ecf20Sopenharmony_ci for (i = 0x60; i < 0x70; i += sizeof(u32)) 3508c2ecf20Sopenharmony_ci rd_mem (dev, i); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci#else 3538c2ecf20Sopenharmony_ci (void) dev; 3548c2ecf20Sopenharmony_ci#endif 3558c2ecf20Sopenharmony_ci return; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline void dump_loader_block (volatile loader_block * lb) { 3598c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR 3608c2ecf20Sopenharmony_ci unsigned int i; 3618c2ecf20Sopenharmony_ci PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:", 3628c2ecf20Sopenharmony_ci lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command)); 3638c2ecf20Sopenharmony_ci for (i = 0; i < MAX_COMMAND_DATA; ++i) 3648c2ecf20Sopenharmony_ci PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i])); 3658c2ecf20Sopenharmony_ci PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid)); 3668c2ecf20Sopenharmony_ci#else 3678c2ecf20Sopenharmony_ci (void) lb; 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic inline void dump_command (command * cmd) { 3738c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR 3748c2ecf20Sopenharmony_ci unsigned int i; 3758c2ecf20Sopenharmony_ci PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:", 3768c2ecf20Sopenharmony_ci cmd, /*be32_to_cpu*/ (cmd->request)); 3778c2ecf20Sopenharmony_ci for (i = 0; i < 3; ++i) 3788c2ecf20Sopenharmony_ci PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i])); 3798c2ecf20Sopenharmony_ci PRINTDE (DBG_CMD, ""); 3808c2ecf20Sopenharmony_ci#else 3818c2ecf20Sopenharmony_ci (void) cmd; 3828c2ecf20Sopenharmony_ci#endif 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) { 3878c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR 3888c2ecf20Sopenharmony_ci unsigned int i; 3898c2ecf20Sopenharmony_ci unsigned char * data = skb->data; 3908c2ecf20Sopenharmony_ci PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc); 3918c2ecf20Sopenharmony_ci for (i=0; i<skb->len && i < 256;i++) 3928c2ecf20Sopenharmony_ci PRINTDM (DBG_DATA, "%02x ", data[i]); 3938c2ecf20Sopenharmony_ci PRINTDE (DBG_DATA,""); 3948c2ecf20Sopenharmony_ci#else 3958c2ecf20Sopenharmony_ci (void) prefix; 3968c2ecf20Sopenharmony_ci (void) vc; 3978c2ecf20Sopenharmony_ci (void) skb; 3988c2ecf20Sopenharmony_ci#endif 3998c2ecf20Sopenharmony_ci return; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/********** check memory areas for use by Ambassador **********/ 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* see limitations under Hardware Features */ 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int check_area (void * start, size_t length) { 4078c2ecf20Sopenharmony_ci // assumes length > 0 4088c2ecf20Sopenharmony_ci const u32 fourmegmask = -1 << 22; 4098c2ecf20Sopenharmony_ci const u32 twofivesixmask = -1 << 8; 4108c2ecf20Sopenharmony_ci const u32 starthole = 0xE0000000; 4118c2ecf20Sopenharmony_ci u32 startaddress = virt_to_bus (start); 4128c2ecf20Sopenharmony_ci u32 lastaddress = startaddress+length-1; 4138c2ecf20Sopenharmony_ci if ((startaddress ^ lastaddress) & fourmegmask || 4148c2ecf20Sopenharmony_ci (startaddress & twofivesixmask) == starthole) { 4158c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!", 4168c2ecf20Sopenharmony_ci startaddress, lastaddress); 4178c2ecf20Sopenharmony_ci return -1; 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/********** free an skb (as per ATM device driver documentation) **********/ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void amb_kfree_skb (struct sk_buff * skb) { 4268c2ecf20Sopenharmony_ci if (ATM_SKB(skb)->vcc->pop) { 4278c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb); 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci dev_kfree_skb_any (skb); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/********** TX completion **********/ 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void tx_complete (amb_dev * dev, tx_out * tx) { 4368c2ecf20Sopenharmony_ci tx_simple * tx_descr = bus_to_virt (tx->handle); 4378c2ecf20Sopenharmony_ci struct sk_buff * skb = tx_descr->skb; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci // VC layer stats 4428c2ecf20Sopenharmony_ci atomic_inc(&ATM_SKB(skb)->vcc->stats->tx); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci // free the descriptor 4458c2ecf20Sopenharmony_ci kfree (tx_descr); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci // free the skb 4488c2ecf20Sopenharmony_ci amb_kfree_skb (skb); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dev->stats.tx_ok++; 4518c2ecf20Sopenharmony_ci return; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/********** RX completion **********/ 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void rx_complete (amb_dev * dev, rx_out * rx) { 4578c2ecf20Sopenharmony_ci struct sk_buff * skb = bus_to_virt (rx->handle); 4588c2ecf20Sopenharmony_ci u16 vc = be16_to_cpu (rx->vc); 4598c2ecf20Sopenharmony_ci // unused: u16 lec_id = be16_to_cpu (rx->lec_id); 4608c2ecf20Sopenharmony_ci u16 status = be16_to_cpu (rx->status); 4618c2ecf20Sopenharmony_ci u16 rx_len = be16_to_cpu (rx->length); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci // XXX move this in and add to VC stats ??? 4668c2ecf20Sopenharmony_ci if (!status) { 4678c2ecf20Sopenharmony_ci struct atm_vcc * atm_vcc = dev->rxer[vc]; 4688c2ecf20Sopenharmony_ci dev->stats.rx.ok++; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (atm_vcc) { 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (rx_len <= atm_vcc->qos.rxtp.max_sdu) { 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (atm_charge (atm_vcc, skb->truesize)) { 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci // prepare socket buffer 4778c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = atm_vcc; 4788c2ecf20Sopenharmony_ci skb_put (skb, rx_len); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dump_skb ("<<<", vc, skb); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci // VC layer stats 4838c2ecf20Sopenharmony_ci atomic_inc(&atm_vcc->stats->rx); 4848c2ecf20Sopenharmony_ci __net_timestamp(skb); 4858c2ecf20Sopenharmony_ci // end of our responsibility 4868c2ecf20Sopenharmony_ci atm_vcc->push (atm_vcc, skb); 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci } else { 4908c2ecf20Sopenharmony_ci // someone fix this (message), please! 4918c2ecf20Sopenharmony_ci PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu, truesize %u)", vc, skb->truesize); 4928c2ecf20Sopenharmony_ci // drop stats incremented in atm_charge 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci PRINTK (KERN_INFO, "dropped over-size frame"); 4978c2ecf20Sopenharmony_ci // should we count this? 4988c2ecf20Sopenharmony_ci atomic_inc(&atm_vcc->stats->rx_drop); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci } else { 5028c2ecf20Sopenharmony_ci PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc); 5038c2ecf20Sopenharmony_ci // this is an adapter bug, only in new version of microcode 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci dev->stats.rx.error++; 5088c2ecf20Sopenharmony_ci if (status & CRC_ERR) 5098c2ecf20Sopenharmony_ci dev->stats.rx.badcrc++; 5108c2ecf20Sopenharmony_ci if (status & LEN_ERR) 5118c2ecf20Sopenharmony_ci dev->stats.rx.toolong++; 5128c2ecf20Sopenharmony_ci if (status & ABORT_ERR) 5138c2ecf20Sopenharmony_ci dev->stats.rx.aborted++; 5148c2ecf20Sopenharmony_ci if (status & UNUSED_ERR) 5158c2ecf20Sopenharmony_ci dev->stats.rx.unused++; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci dev_kfree_skb_any (skb); 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci Note on queue handling. 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci Here "give" and "take" refer to queue entries and a queue (pair) 5278c2ecf20Sopenharmony_ci rather than frames to or from the host or adapter. Empty frame 5288c2ecf20Sopenharmony_ci buffers are given to the RX queue pair and returned unused or 5298c2ecf20Sopenharmony_ci containing RX frames. TX frames (well, pointers to TX fragment 5308c2ecf20Sopenharmony_ci lists) are given to the TX queue pair, completions are returned. 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci*/ 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/********** command queue **********/ 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci// I really don't like this, but it's the best I can do at the moment 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci// also, the callers are responsible for byte order as the microcode 5398c2ecf20Sopenharmony_ci// sometimes does 16-bit accesses (yuk yuk yuk) 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int command_do (amb_dev * dev, command * cmd) { 5428c2ecf20Sopenharmony_ci amb_cq * cq = &dev->cq; 5438c2ecf20Sopenharmony_ci volatile amb_cq_ptrs * ptrs = &cq->ptrs; 5448c2ecf20Sopenharmony_ci command * my_slot; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (test_bit (dead, &dev->flags)) 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci spin_lock (&cq->lock); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci // if not full... 5548c2ecf20Sopenharmony_ci if (cq->pending < cq->maximum) { 5558c2ecf20Sopenharmony_ci // remember my slot for later 5568c2ecf20Sopenharmony_ci my_slot = ptrs->in; 5578c2ecf20Sopenharmony_ci PRINTD (DBG_CMD, "command in slot %p", my_slot); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci dump_command (cmd); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci // copy command in 5628c2ecf20Sopenharmony_ci *ptrs->in = *cmd; 5638c2ecf20Sopenharmony_ci cq->pending++; 5648c2ecf20Sopenharmony_ci ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci // mail the command 5678c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, mb.adapter.cmd_address), virt_to_bus (ptrs->in)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (cq->pending > cq->high) 5708c2ecf20Sopenharmony_ci cq->high = cq->pending; 5718c2ecf20Sopenharmony_ci spin_unlock (&cq->lock); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci // these comments were in a while-loop before, msleep removes the loop 5748c2ecf20Sopenharmony_ci // go to sleep 5758c2ecf20Sopenharmony_ci // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout); 5768c2ecf20Sopenharmony_ci msleep(cq->pending); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci // wait for my slot to be reached (all waiters are here or above, until...) 5798c2ecf20Sopenharmony_ci while (ptrs->out != my_slot) { 5808c2ecf20Sopenharmony_ci PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out); 5818c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 5828c2ecf20Sopenharmony_ci schedule(); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci // wait on my slot (... one gets to its slot, and... ) 5868c2ecf20Sopenharmony_ci while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) { 5878c2ecf20Sopenharmony_ci PRINTD (DBG_CMD, "wait: command slot completion"); 5888c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 5898c2ecf20Sopenharmony_ci schedule(); 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci PRINTD (DBG_CMD, "command complete"); 5938c2ecf20Sopenharmony_ci // update queue (... moves the queue along to the next slot) 5948c2ecf20Sopenharmony_ci spin_lock (&cq->lock); 5958c2ecf20Sopenharmony_ci cq->pending--; 5968c2ecf20Sopenharmony_ci // copy command out 5978c2ecf20Sopenharmony_ci *cmd = *ptrs->out; 5988c2ecf20Sopenharmony_ci ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit); 5998c2ecf20Sopenharmony_ci spin_unlock (&cq->lock); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci } else { 6038c2ecf20Sopenharmony_ci cq->filled++; 6048c2ecf20Sopenharmony_ci spin_unlock (&cq->lock); 6058c2ecf20Sopenharmony_ci return -EAGAIN; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci/********** TX queue pair **********/ 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int tx_give (amb_dev * dev, tx_in * tx) { 6138c2ecf20Sopenharmony_ci amb_txq * txq = &dev->txq; 6148c2ecf20Sopenharmony_ci unsigned long flags; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (test_bit (dead, &dev->flags)) 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci spin_lock_irqsave (&txq->lock, flags); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (txq->pending < txq->maximum) { 6248c2ecf20Sopenharmony_ci PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci *txq->in.ptr = *tx; 6278c2ecf20Sopenharmony_ci txq->pending++; 6288c2ecf20Sopenharmony_ci txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit); 6298c2ecf20Sopenharmony_ci // hand over the TX and ring the bell 6308c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, mb.adapter.tx_address), virt_to_bus (txq->in.ptr)); 6318c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, doorbell), TX_FRAME); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (txq->pending > txq->high) 6348c2ecf20Sopenharmony_ci txq->high = txq->pending; 6358c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&txq->lock, flags); 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci } else { 6388c2ecf20Sopenharmony_ci txq->filled++; 6398c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&txq->lock, flags); 6408c2ecf20Sopenharmony_ci return -EAGAIN; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int tx_take (amb_dev * dev) { 6458c2ecf20Sopenharmony_ci amb_txq * txq = &dev->txq; 6468c2ecf20Sopenharmony_ci unsigned long flags; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci spin_lock_irqsave (&txq->lock, flags); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (txq->pending && txq->out.ptr->handle) { 6538c2ecf20Sopenharmony_ci // deal with TX completion 6548c2ecf20Sopenharmony_ci tx_complete (dev, txq->out.ptr); 6558c2ecf20Sopenharmony_ci // mark unused again 6568c2ecf20Sopenharmony_ci txq->out.ptr->handle = 0; 6578c2ecf20Sopenharmony_ci // remove item 6588c2ecf20Sopenharmony_ci txq->pending--; 6598c2ecf20Sopenharmony_ci txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&txq->lock, flags); 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci } else { 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&txq->lock, flags); 6668c2ecf20Sopenharmony_ci return -1; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/********** RX queue pairs **********/ 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) { 6738c2ecf20Sopenharmony_ci amb_rxq * rxq = &dev->rxq[pool]; 6748c2ecf20Sopenharmony_ci unsigned long flags; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci spin_lock_irqsave (&rxq->lock, flags); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (rxq->pending < rxq->maximum) { 6818c2ecf20Sopenharmony_ci PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci *rxq->in.ptr = *rx; 6848c2ecf20Sopenharmony_ci rxq->pending++; 6858c2ecf20Sopenharmony_ci rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit); 6868c2ecf20Sopenharmony_ci // hand over the RX buffer 6878c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, mb.adapter.rx_address[pool]), virt_to_bus (rxq->in.ptr)); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&rxq->lock, flags); 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&rxq->lock, flags); 6938c2ecf20Sopenharmony_ci return -1; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int rx_take (amb_dev * dev, unsigned char pool) { 6988c2ecf20Sopenharmony_ci amb_rxq * rxq = &dev->rxq[pool]; 6998c2ecf20Sopenharmony_ci unsigned long flags; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci spin_lock_irqsave (&rxq->lock, flags); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) { 7068c2ecf20Sopenharmony_ci // deal with RX completion 7078c2ecf20Sopenharmony_ci rx_complete (dev, rxq->out.ptr); 7088c2ecf20Sopenharmony_ci // mark unused again 7098c2ecf20Sopenharmony_ci rxq->out.ptr->status = 0; 7108c2ecf20Sopenharmony_ci rxq->out.ptr->length = 0; 7118c2ecf20Sopenharmony_ci // remove item 7128c2ecf20Sopenharmony_ci rxq->pending--; 7138c2ecf20Sopenharmony_ci rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (rxq->pending < rxq->low) 7168c2ecf20Sopenharmony_ci rxq->low = rxq->pending; 7178c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&rxq->lock, flags); 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci if (!rxq->pending && rxq->buffers_wanted) 7218c2ecf20Sopenharmony_ci rxq->emptied++; 7228c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&rxq->lock, flags); 7238c2ecf20Sopenharmony_ci return -1; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/********** RX Pool handling **********/ 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/* pre: buffers_wanted = 0, post: pending = 0 */ 7308c2ecf20Sopenharmony_cistatic void drain_rx_pool (amb_dev * dev, unsigned char pool) { 7318c2ecf20Sopenharmony_ci amb_rxq * rxq = &dev->rxq[pool]; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (test_bit (dead, &dev->flags)) 7368c2ecf20Sopenharmony_ci return; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* we are not quite like the fill pool routines as we cannot just 7398c2ecf20Sopenharmony_ci remove one buffer, we have to remove all of them, but we might as 7408c2ecf20Sopenharmony_ci well pretend... */ 7418c2ecf20Sopenharmony_ci if (rxq->pending > rxq->buffers_wanted) { 7428c2ecf20Sopenharmony_ci command cmd; 7438c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q); 7448c2ecf20Sopenharmony_ci cmd.args.flush.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT); 7458c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 7468c2ecf20Sopenharmony_ci schedule(); 7478c2ecf20Sopenharmony_ci /* the pool may also be emptied via the interrupt handler */ 7488c2ecf20Sopenharmony_ci while (rxq->pending > rxq->buffers_wanted) 7498c2ecf20Sopenharmony_ci if (rx_take (dev, pool)) 7508c2ecf20Sopenharmony_ci schedule(); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic void drain_rx_pools (amb_dev * dev) { 7578c2ecf20Sopenharmony_ci unsigned char pool; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 7628c2ecf20Sopenharmony_ci drain_rx_pool (dev, pool); 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic void fill_rx_pool (amb_dev * dev, unsigned char pool, 7668c2ecf20Sopenharmony_ci gfp_t priority) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci rx_in rx; 7698c2ecf20Sopenharmony_ci amb_rxq * rxq; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (test_bit (dead, &dev->flags)) 7748c2ecf20Sopenharmony_ci return; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci rxq = &dev->rxq[pool]; 7778c2ecf20Sopenharmony_ci while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) { 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci struct sk_buff * skb = alloc_skb (rxq->buffer_size, priority); 7808c2ecf20Sopenharmony_ci if (!skb) { 7818c2ecf20Sopenharmony_ci PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool); 7828c2ecf20Sopenharmony_ci return; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci if (check_area (skb->data, skb->truesize)) { 7858c2ecf20Sopenharmony_ci dev_kfree_skb_any (skb); 7868c2ecf20Sopenharmony_ci return; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci // cast needed as there is no %? for pointer differences 7898c2ecf20Sopenharmony_ci PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li", 7908c2ecf20Sopenharmony_ci skb, skb->head, (long) skb_end_offset(skb)); 7918c2ecf20Sopenharmony_ci rx.handle = virt_to_bus (skb); 7928c2ecf20Sopenharmony_ci rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); 7938c2ecf20Sopenharmony_ci if (rx_give (dev, &rx, pool)) 7948c2ecf20Sopenharmony_ci dev_kfree_skb_any (skb); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci// top up all RX pools 8028c2ecf20Sopenharmony_cistatic void fill_rx_pools (amb_dev * dev) { 8038c2ecf20Sopenharmony_ci unsigned char pool; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 8088c2ecf20Sopenharmony_ci fill_rx_pool (dev, pool, GFP_ATOMIC); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/********** enable host interrupts **********/ 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void interrupts_on (amb_dev * dev) { 8168c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, interrupt_control), 8178c2ecf20Sopenharmony_ci rd_plain (dev, offsetof(amb_mem, interrupt_control)) 8188c2ecf20Sopenharmony_ci | AMB_INTERRUPT_BITS); 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/********** disable host interrupts **********/ 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void interrupts_off (amb_dev * dev) { 8248c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, interrupt_control), 8258c2ecf20Sopenharmony_ci rd_plain (dev, offsetof(amb_mem, interrupt_control)) 8268c2ecf20Sopenharmony_ci &~ AMB_INTERRUPT_BITS); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci/********** interrupt handling **********/ 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic irqreturn_t interrupt_handler(int irq, void *dev_id) { 8328c2ecf20Sopenharmony_ci amb_dev * dev = dev_id; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci { 8378c2ecf20Sopenharmony_ci u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt)); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci // for us or someone else sharing the same interrupt 8408c2ecf20Sopenharmony_ci if (!interrupt) { 8418c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ, "irq not for me: %d", irq); 8428c2ecf20Sopenharmony_ci return IRQ_NONE; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci // definitely for us 8468c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt); 8478c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, interrupt), -1); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci { 8518c2ecf20Sopenharmony_ci unsigned int irq_work = 0; 8528c2ecf20Sopenharmony_ci unsigned char pool; 8538c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 8548c2ecf20Sopenharmony_ci while (!rx_take (dev, pool)) 8558c2ecf20Sopenharmony_ci ++irq_work; 8568c2ecf20Sopenharmony_ci while (!tx_take (dev)) 8578c2ecf20Sopenharmony_ci ++irq_work; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (irq_work) { 8608c2ecf20Sopenharmony_ci fill_rx_pools (dev); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ, "work done: %u", irq_work); 8638c2ecf20Sopenharmony_ci } else { 8648c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ|DBG_WARN, "no work done"); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id); 8698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci/********** make rate (not quite as much fun as Horizon) **********/ 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistatic int make_rate (unsigned int rate, rounding r, 8758c2ecf20Sopenharmony_ci u16 * bits, unsigned int * actual) { 8768c2ecf20Sopenharmony_ci unsigned char exp = -1; // hush gcc 8778c2ecf20Sopenharmony_ci unsigned int man = -1; // hush gcc 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci // rates in cells per second, ITU format (nasty 16-bit floating-point) 8828c2ecf20Sopenharmony_ci // given 5-bit e and 9-bit m: 8838c2ecf20Sopenharmony_ci // rate = EITHER (1+m/2^9)*2^e OR 0 8848c2ecf20Sopenharmony_ci // bits = EITHER 1<<14 | e<<9 | m OR 0 8858c2ecf20Sopenharmony_ci // (bit 15 is "reserved", bit 14 "non-zero") 8868c2ecf20Sopenharmony_ci // smallest rate is 0 (special representation) 8878c2ecf20Sopenharmony_ci // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1) 8888c2ecf20Sopenharmony_ci // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0) 8898c2ecf20Sopenharmony_ci // simple algorithm: 8908c2ecf20Sopenharmony_ci // find position of top bit, this gives e 8918c2ecf20Sopenharmony_ci // remove top bit and shift (rounding if feeling clever) by 9-e 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci // ucode bug: please don't set bit 14! so 0 rate not representable 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (rate > 0xffc00000U) { 8968c2ecf20Sopenharmony_ci // larger than largest representable rate 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (r == round_up) { 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci } else { 9018c2ecf20Sopenharmony_ci exp = 31; 9028c2ecf20Sopenharmony_ci man = 511; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci } else if (rate) { 9068c2ecf20Sopenharmony_ci // representable rate 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci exp = 31; 9098c2ecf20Sopenharmony_ci man = rate; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci // invariant: rate = man*2^(exp-31) 9128c2ecf20Sopenharmony_ci while (!(man & (1<<31))) { 9138c2ecf20Sopenharmony_ci exp = exp - 1; 9148c2ecf20Sopenharmony_ci man = man<<1; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci // man has top bit set 9188c2ecf20Sopenharmony_ci // rate = (2^31+(man-2^31))*2^(exp-31) 9198c2ecf20Sopenharmony_ci // rate = (1+(man-2^31)/2^31)*2^exp 9208c2ecf20Sopenharmony_ci man = man<<1; 9218c2ecf20Sopenharmony_ci man &= 0xffffffffU; // a nop on 32-bit systems 9228c2ecf20Sopenharmony_ci // rate = (1+man/2^32)*2^exp 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci // exp is in the range 0 to 31, man is in the range 0 to 2^32-1 9258c2ecf20Sopenharmony_ci // time to lose significance... we want m in the range 0 to 2^9-1 9268c2ecf20Sopenharmony_ci // rounding presents a minor problem... we first decide which way 9278c2ecf20Sopenharmony_ci // we are rounding (based on given rounding direction and possibly 9288c2ecf20Sopenharmony_ci // the bits of the mantissa that are to be discarded). 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci switch (r) { 9318c2ecf20Sopenharmony_ci case round_down: { 9328c2ecf20Sopenharmony_ci // just truncate 9338c2ecf20Sopenharmony_ci man = man>>(32-9); 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci case round_up: { 9378c2ecf20Sopenharmony_ci // check all bits that we are discarding 9388c2ecf20Sopenharmony_ci if (man & (~0U>>9)) { 9398c2ecf20Sopenharmony_ci man = (man>>(32-9)) + 1; 9408c2ecf20Sopenharmony_ci if (man == (1<<9)) { 9418c2ecf20Sopenharmony_ci // no need to check for round up outside of range 9428c2ecf20Sopenharmony_ci man = 0; 9438c2ecf20Sopenharmony_ci exp += 1; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci } else { 9468c2ecf20Sopenharmony_ci man = (man>>(32-9)); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci case round_nearest: { 9518c2ecf20Sopenharmony_ci // check msb that we are discarding 9528c2ecf20Sopenharmony_ci if (man & (1<<(32-9-1))) { 9538c2ecf20Sopenharmony_ci man = (man>>(32-9)) + 1; 9548c2ecf20Sopenharmony_ci if (man == (1<<9)) { 9558c2ecf20Sopenharmony_ci // no need to check for round up outside of range 9568c2ecf20Sopenharmony_ci man = 0; 9578c2ecf20Sopenharmony_ci exp += 1; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } else { 9608c2ecf20Sopenharmony_ci man = (man>>(32-9)); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci break; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci } else { 9678c2ecf20Sopenharmony_ci // zero rate - not representable 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci if (r == round_down) { 9708c2ecf20Sopenharmony_ci return -EINVAL; 9718c2ecf20Sopenharmony_ci } else { 9728c2ecf20Sopenharmony_ci exp = 0; 9738c2ecf20Sopenharmony_ci man = 0; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (bits) 9818c2ecf20Sopenharmony_ci *bits = /* (1<<14) | */ (exp<<9) | man; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (actual) 9848c2ecf20Sopenharmony_ci *actual = (exp >= 9) 9858c2ecf20Sopenharmony_ci ? (1 << exp) + (man << (exp-9)) 9868c2ecf20Sopenharmony_ci : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp)); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/********** Linux ATM Operations **********/ 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci// some are not yet implemented while others do not make sense for 9948c2ecf20Sopenharmony_ci// this device 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci/********** Open a VC **********/ 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic int amb_open (struct atm_vcc * atm_vcc) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci int error; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci struct atm_qos * qos; 10038c2ecf20Sopenharmony_ci struct atm_trafprm * txtp; 10048c2ecf20Sopenharmony_ci struct atm_trafprm * rxtp; 10058c2ecf20Sopenharmony_ci u16 tx_rate_bits = -1; // hush gcc 10068c2ecf20Sopenharmony_ci u16 tx_vc_bits = -1; // hush gcc 10078c2ecf20Sopenharmony_ci u16 tx_frame_bits = -1; // hush gcc 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci amb_dev * dev = AMB_DEV(atm_vcc->dev); 10108c2ecf20Sopenharmony_ci amb_vcc * vcc; 10118c2ecf20Sopenharmony_ci unsigned char pool = -1; // hush gcc 10128c2ecf20Sopenharmony_ci short vpi = atm_vcc->vpi; 10138c2ecf20Sopenharmony_ci int vci = atm_vcc->vci; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci#ifdef ATM_VPI_UNSPEC 10188c2ecf20Sopenharmony_ci // UNSPEC is deprecated, remove this code eventually 10198c2ecf20Sopenharmony_ci if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) { 10208c2ecf20Sopenharmony_ci PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)"); 10218c2ecf20Sopenharmony_ci return -EINVAL; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci#endif 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (!(0 <= vpi && vpi < (1<<NUM_VPI_BITS) && 10268c2ecf20Sopenharmony_ci 0 <= vci && vci < (1<<NUM_VCI_BITS))) { 10278c2ecf20Sopenharmony_ci PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci); 10288c2ecf20Sopenharmony_ci return -EINVAL; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci qos = &atm_vcc->qos; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (qos->aal != ATM_AAL5) { 10348c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "AAL not supported"); 10358c2ecf20Sopenharmony_ci return -EINVAL; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci // traffic parameters 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "TX:"); 10418c2ecf20Sopenharmony_ci txtp = &qos->txtp; 10428c2ecf20Sopenharmony_ci if (txtp->traffic_class != ATM_NONE) { 10438c2ecf20Sopenharmony_ci switch (txtp->traffic_class) { 10448c2ecf20Sopenharmony_ci case ATM_UBR: { 10458c2ecf20Sopenharmony_ci // we take "the PCR" as a rate-cap 10468c2ecf20Sopenharmony_ci int pcr = atm_pcr_goal (txtp); 10478c2ecf20Sopenharmony_ci if (!pcr) { 10488c2ecf20Sopenharmony_ci // no rate cap 10498c2ecf20Sopenharmony_ci tx_rate_bits = 0; 10508c2ecf20Sopenharmony_ci tx_vc_bits = TX_UBR; 10518c2ecf20Sopenharmony_ci tx_frame_bits = TX_FRAME_NOTCAP; 10528c2ecf20Sopenharmony_ci } else { 10538c2ecf20Sopenharmony_ci rounding r; 10548c2ecf20Sopenharmony_ci if (pcr < 0) { 10558c2ecf20Sopenharmony_ci r = round_down; 10568c2ecf20Sopenharmony_ci pcr = -pcr; 10578c2ecf20Sopenharmony_ci } else { 10588c2ecf20Sopenharmony_ci r = round_up; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci error = make_rate (pcr, r, &tx_rate_bits, NULL); 10618c2ecf20Sopenharmony_ci if (error) 10628c2ecf20Sopenharmony_ci return error; 10638c2ecf20Sopenharmony_ci tx_vc_bits = TX_UBR_CAPPED; 10648c2ecf20Sopenharmony_ci tx_frame_bits = TX_FRAME_CAPPED; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci break; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci#if 0 10698c2ecf20Sopenharmony_ci case ATM_ABR: { 10708c2ecf20Sopenharmony_ci pcr = atm_pcr_goal (txtp); 10718c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "pcr goal = %d", pcr); 10728c2ecf20Sopenharmony_ci break; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci#endif 10758c2ecf20Sopenharmony_ci default: { 10768c2ecf20Sopenharmony_ci // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); 10778c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "request for non-UBR denied"); 10788c2ecf20Sopenharmony_ci return -EINVAL; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx", 10828c2ecf20Sopenharmony_ci tx_rate_bits, tx_vc_bits); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "RX:"); 10868c2ecf20Sopenharmony_ci rxtp = &qos->rxtp; 10878c2ecf20Sopenharmony_ci if (rxtp->traffic_class == ATM_NONE) { 10888c2ecf20Sopenharmony_ci // do nothing 10898c2ecf20Sopenharmony_ci } else { 10908c2ecf20Sopenharmony_ci // choose an RX pool (arranged in increasing size) 10918c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 10928c2ecf20Sopenharmony_ci if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) { 10938c2ecf20Sopenharmony_ci PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)", 10948c2ecf20Sopenharmony_ci pool, rxtp->max_sdu, dev->rxq[pool].buffer_size); 10958c2ecf20Sopenharmony_ci break; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci if (pool == NUM_RX_POOLS) { 10988c2ecf20Sopenharmony_ci PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL, 10998c2ecf20Sopenharmony_ci "no pool suitable for VC (RX max_sdu %d is too large)", 11008c2ecf20Sopenharmony_ci rxtp->max_sdu); 11018c2ecf20Sopenharmony_ci return -EINVAL; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci switch (rxtp->traffic_class) { 11058c2ecf20Sopenharmony_ci case ATM_UBR: { 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci#if 0 11098c2ecf20Sopenharmony_ci case ATM_ABR: { 11108c2ecf20Sopenharmony_ci pcr = atm_pcr_goal (rxtp); 11118c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "pcr goal = %d", pcr); 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci#endif 11158c2ecf20Sopenharmony_ci default: { 11168c2ecf20Sopenharmony_ci // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); 11178c2ecf20Sopenharmony_ci PRINTD (DBG_QOS, "request for non-UBR denied"); 11188c2ecf20Sopenharmony_ci return -EINVAL; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci // get space for our vcc stuff 11248c2ecf20Sopenharmony_ci vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL); 11258c2ecf20Sopenharmony_ci if (!vcc) { 11268c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "out of memory!"); 11278c2ecf20Sopenharmony_ci return -ENOMEM; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci atm_vcc->dev_data = (void *) vcc; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci // no failures beyond this point 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci // we are not really "immediately before allocating the connection 11348c2ecf20Sopenharmony_ci // identifier in hardware", but it will just have to do! 11358c2ecf20Sopenharmony_ci set_bit(ATM_VF_ADDR,&atm_vcc->flags); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (txtp->traffic_class != ATM_NONE) { 11388c2ecf20Sopenharmony_ci command cmd; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci vcc->tx_frame_bits = tx_frame_bits; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci mutex_lock(&dev->vcc_sf); 11438c2ecf20Sopenharmony_ci if (dev->rxer[vci]) { 11448c2ecf20Sopenharmony_ci // RXer on the channel already, just modify rate... 11458c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); 11468c2ecf20Sopenharmony_ci cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 11478c2ecf20Sopenharmony_ci cmd.args.modify_rate.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT); 11488c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 11498c2ecf20Sopenharmony_ci schedule(); 11508c2ecf20Sopenharmony_ci // ... and TX flags, preserving the RX pool 11518c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); 11528c2ecf20Sopenharmony_ci cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 11538c2ecf20Sopenharmony_ci cmd.args.modify_flags.flags = cpu_to_be32 11548c2ecf20Sopenharmony_ci ( (AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT) 11558c2ecf20Sopenharmony_ci | (tx_vc_bits << SRB_FLAGS_SHIFT) ); 11568c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 11578c2ecf20Sopenharmony_ci schedule(); 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci // no RXer on the channel, just open (with pool zero) 11608c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_OPEN_VC); 11618c2ecf20Sopenharmony_ci cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 11628c2ecf20Sopenharmony_ci cmd.args.open.flags = cpu_to_be32 (tx_vc_bits << SRB_FLAGS_SHIFT); 11638c2ecf20Sopenharmony_ci cmd.args.open.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT); 11648c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 11658c2ecf20Sopenharmony_ci schedule(); 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci dev->txer[vci].tx_present = 1; 11688c2ecf20Sopenharmony_ci mutex_unlock(&dev->vcc_sf); 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (rxtp->traffic_class != ATM_NONE) { 11728c2ecf20Sopenharmony_ci command cmd; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci vcc->rx_info.pool = pool; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci mutex_lock(&dev->vcc_sf); 11778c2ecf20Sopenharmony_ci /* grow RX buffer pool */ 11788c2ecf20Sopenharmony_ci if (!dev->rxq[pool].buffers_wanted) 11798c2ecf20Sopenharmony_ci dev->rxq[pool].buffers_wanted = rx_lats; 11808c2ecf20Sopenharmony_ci dev->rxq[pool].buffers_wanted += 1; 11818c2ecf20Sopenharmony_ci fill_rx_pool (dev, pool, GFP_KERNEL); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (dev->txer[vci].tx_present) { 11848c2ecf20Sopenharmony_ci // TXer on the channel already 11858c2ecf20Sopenharmony_ci // switch (from pool zero) to this pool, preserving the TX bits 11868c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); 11878c2ecf20Sopenharmony_ci cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 11888c2ecf20Sopenharmony_ci cmd.args.modify_flags.flags = cpu_to_be32 11898c2ecf20Sopenharmony_ci ( (pool << SRB_POOL_SHIFT) 11908c2ecf20Sopenharmony_ci | (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT) ); 11918c2ecf20Sopenharmony_ci } else { 11928c2ecf20Sopenharmony_ci // no TXer on the channel, open the VC (with no rate info) 11938c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_OPEN_VC); 11948c2ecf20Sopenharmony_ci cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 11958c2ecf20Sopenharmony_ci cmd.args.open.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT); 11968c2ecf20Sopenharmony_ci cmd.args.open.rate = cpu_to_be32 (0); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 11998c2ecf20Sopenharmony_ci schedule(); 12008c2ecf20Sopenharmony_ci // this link allows RX frames through 12018c2ecf20Sopenharmony_ci dev->rxer[vci] = atm_vcc; 12028c2ecf20Sopenharmony_ci mutex_unlock(&dev->vcc_sf); 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci // indicate readiness 12068c2ecf20Sopenharmony_ci set_bit(ATM_VF_READY,&atm_vcc->flags); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/********** Close a VC **********/ 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic void amb_close (struct atm_vcc * atm_vcc) { 12148c2ecf20Sopenharmony_ci amb_dev * dev = AMB_DEV (atm_vcc->dev); 12158c2ecf20Sopenharmony_ci amb_vcc * vcc = AMB_VCC (atm_vcc); 12168c2ecf20Sopenharmony_ci u16 vci = atm_vcc->vci; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci PRINTD (DBG_VCC|DBG_FLOW, "amb_close"); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci // indicate unreadiness 12218c2ecf20Sopenharmony_ci clear_bit(ATM_VF_READY,&atm_vcc->flags); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci // disable TXing 12248c2ecf20Sopenharmony_ci if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) { 12258c2ecf20Sopenharmony_ci command cmd; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci mutex_lock(&dev->vcc_sf); 12288c2ecf20Sopenharmony_ci if (dev->rxer[vci]) { 12298c2ecf20Sopenharmony_ci // RXer still on the channel, just modify rate... XXX not really needed 12308c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); 12318c2ecf20Sopenharmony_ci cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 12328c2ecf20Sopenharmony_ci cmd.args.modify_rate.rate = cpu_to_be32 (0); 12338c2ecf20Sopenharmony_ci // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool 12348c2ecf20Sopenharmony_ci } else { 12358c2ecf20Sopenharmony_ci // no RXer on the channel, close channel 12368c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_CLOSE_VC); 12378c2ecf20Sopenharmony_ci cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci dev->txer[vci].tx_present = 0; 12408c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 12418c2ecf20Sopenharmony_ci schedule(); 12428c2ecf20Sopenharmony_ci mutex_unlock(&dev->vcc_sf); 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci // disable RXing 12468c2ecf20Sopenharmony_ci if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) { 12478c2ecf20Sopenharmony_ci command cmd; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci // this is (the?) one reason why we need the amb_vcc struct 12508c2ecf20Sopenharmony_ci unsigned char pool = vcc->rx_info.pool; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci mutex_lock(&dev->vcc_sf); 12538c2ecf20Sopenharmony_ci if (dev->txer[vci].tx_present) { 12548c2ecf20Sopenharmony_ci // TXer still on the channel, just go to pool zero XXX not really needed 12558c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); 12568c2ecf20Sopenharmony_ci cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 12578c2ecf20Sopenharmony_ci cmd.args.modify_flags.flags = cpu_to_be32 12588c2ecf20Sopenharmony_ci (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT); 12598c2ecf20Sopenharmony_ci } else { 12608c2ecf20Sopenharmony_ci // no TXer on the channel, close the VC 12618c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_CLOSE_VC); 12628c2ecf20Sopenharmony_ci cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci // forget the rxer - no more skbs will be pushed 12658c2ecf20Sopenharmony_ci if (atm_vcc != dev->rxer[vci]) 12668c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p", 12678c2ecf20Sopenharmony_ci "arghhh! we're going to die!", 12688c2ecf20Sopenharmony_ci vcc, dev->rxer[vci]); 12698c2ecf20Sopenharmony_ci dev->rxer[vci] = NULL; 12708c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) 12718c2ecf20Sopenharmony_ci schedule(); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci /* shrink RX buffer pool */ 12748c2ecf20Sopenharmony_ci dev->rxq[pool].buffers_wanted -= 1; 12758c2ecf20Sopenharmony_ci if (dev->rxq[pool].buffers_wanted == rx_lats) { 12768c2ecf20Sopenharmony_ci dev->rxq[pool].buffers_wanted = 0; 12778c2ecf20Sopenharmony_ci drain_rx_pool (dev, pool); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci mutex_unlock(&dev->vcc_sf); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci // free our structure 12838c2ecf20Sopenharmony_ci kfree (vcc); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci // say the VPI/VCI is free again 12868c2ecf20Sopenharmony_ci clear_bit(ATM_VF_ADDR,&atm_vcc->flags); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci return; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci/********** Send **********/ 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { 12948c2ecf20Sopenharmony_ci amb_dev * dev = AMB_DEV(atm_vcc->dev); 12958c2ecf20Sopenharmony_ci amb_vcc * vcc = AMB_VCC(atm_vcc); 12968c2ecf20Sopenharmony_ci u16 vc = atm_vcc->vci; 12978c2ecf20Sopenharmony_ci unsigned int tx_len = skb->len; 12988c2ecf20Sopenharmony_ci unsigned char * tx_data = skb->data; 12998c2ecf20Sopenharmony_ci tx_simple * tx_descr; 13008c2ecf20Sopenharmony_ci tx_in tx; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (test_bit (dead, &dev->flags)) 13038c2ecf20Sopenharmony_ci return -EIO; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u", 13068c2ecf20Sopenharmony_ci vc, tx_data, tx_len); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci dump_skb (">>>", vc, skb); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (!dev->txer[vc].tx_present) { 13118c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc); 13128c2ecf20Sopenharmony_ci return -EBADFD; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci // this is a driver private field so we have to set it ourselves, 13168c2ecf20Sopenharmony_ci // despite the fact that we are _required_ to use it to check for a 13178c2ecf20Sopenharmony_ci // pop function 13188c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = atm_vcc; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) { 13218c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping..."); 13228c2ecf20Sopenharmony_ci return -EIO; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (check_area (skb->data, skb->len)) { 13268c2ecf20Sopenharmony_ci atomic_inc(&atm_vcc->stats->tx_err); 13278c2ecf20Sopenharmony_ci return -ENOMEM; // ? 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci // allocate memory for fragments 13318c2ecf20Sopenharmony_ci tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL); 13328c2ecf20Sopenharmony_ci if (!tx_descr) { 13338c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "could not allocate TX descriptor"); 13348c2ecf20Sopenharmony_ci return -ENOMEM; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci if (check_area (tx_descr, sizeof(tx_simple))) { 13378c2ecf20Sopenharmony_ci kfree (tx_descr); 13388c2ecf20Sopenharmony_ci return -ENOMEM; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci tx_descr->skb = skb; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len); 13458c2ecf20Sopenharmony_ci tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data)); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr); 13488c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.vc = 0; 13498c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.next_descriptor_length = 0; 13508c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.next_descriptor = 0; 13518c2ecf20Sopenharmony_ci#ifdef AMB_NEW_MICROCODE 13528c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.cpcs_uu = 0; 13538c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.cpi = 0; 13548c2ecf20Sopenharmony_ci tx_descr->tx_frag_end.pad = 0; 13558c2ecf20Sopenharmony_ci#endif 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci tx.vc = cpu_to_be16 (vcc->tx_frame_bits | vc); 13588c2ecf20Sopenharmony_ci tx.tx_descr_length = cpu_to_be16 (sizeof(tx_frag)+sizeof(tx_frag_end)); 13598c2ecf20Sopenharmony_ci tx.tx_descr_addr = cpu_to_be32 (virt_to_bus (&tx_descr->tx_frag)); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci while (tx_give (dev, &tx)) 13628c2ecf20Sopenharmony_ci schedule(); 13638c2ecf20Sopenharmony_ci return 0; 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci/********** Change QoS on a VC **********/ 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci// int amb_change_qos (struct atm_vcc * atm_vcc, struct atm_qos * qos, int flags); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci/********** Free RX Socket Buffer **********/ 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci#if 0 13738c2ecf20Sopenharmony_cistatic void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) { 13748c2ecf20Sopenharmony_ci amb_dev * dev = AMB_DEV (atm_vcc->dev); 13758c2ecf20Sopenharmony_ci amb_vcc * vcc = AMB_VCC (atm_vcc); 13768c2ecf20Sopenharmony_ci unsigned char pool = vcc->rx_info.pool; 13778c2ecf20Sopenharmony_ci rx_in rx; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci // This may be unsafe for various reasons that I cannot really guess 13808c2ecf20Sopenharmony_ci // at. However, I note that the ATM layer calls kfree_skb rather 13818c2ecf20Sopenharmony_ci // than dev_kfree_skb at this point so we are least covered as far 13828c2ecf20Sopenharmony_ci // as buffer locking goes. There may be bugs if pcap clones RX skbs. 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)", 13858c2ecf20Sopenharmony_ci skb, atm_vcc, vcc); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci rx.handle = virt_to_bus (skb); 13888c2ecf20Sopenharmony_ci rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci skb->data = skb->head; 13918c2ecf20Sopenharmony_ci skb_reset_tail_pointer(skb); 13928c2ecf20Sopenharmony_ci skb->len = 0; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci if (!rx_give (dev, &rx, pool)) { 13958c2ecf20Sopenharmony_ci // success 13968c2ecf20Sopenharmony_ci PRINTD (DBG_SKB|DBG_POOL, "recycled skb for pool %hu", pool); 13978c2ecf20Sopenharmony_ci return; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci // just do what the ATM layer would have done 14018c2ecf20Sopenharmony_ci dev_kfree_skb_any (skb); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci return; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci#endif 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci/********** Proc File Output **********/ 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic int amb_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) { 14108c2ecf20Sopenharmony_ci amb_dev * dev = AMB_DEV (atm_dev); 14118c2ecf20Sopenharmony_ci int left = *pos; 14128c2ecf20Sopenharmony_ci unsigned char pool; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW, "amb_proc_read"); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* more diagnostics here? */ 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (!left--) { 14198c2ecf20Sopenharmony_ci amb_stats * s = &dev->stats; 14208c2ecf20Sopenharmony_ci return sprintf (page, 14218c2ecf20Sopenharmony_ci "frames: TX OK %lu, RX OK %lu, RX bad %lu " 14228c2ecf20Sopenharmony_ci "(CRC %lu, long %lu, aborted %lu, unused %lu).\n", 14238c2ecf20Sopenharmony_ci s->tx_ok, s->rx.ok, s->rx.error, 14248c2ecf20Sopenharmony_ci s->rx.badcrc, s->rx.toolong, 14258c2ecf20Sopenharmony_ci s->rx.aborted, s->rx.unused); 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (!left--) { 14298c2ecf20Sopenharmony_ci amb_cq * c = &dev->cq; 14308c2ecf20Sopenharmony_ci return sprintf (page, "cmd queue [cur/hi/max]: %u/%u/%u. ", 14318c2ecf20Sopenharmony_ci c->pending, c->high, c->maximum); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (!left--) { 14358c2ecf20Sopenharmony_ci amb_txq * t = &dev->txq; 14368c2ecf20Sopenharmony_ci return sprintf (page, "TX queue [cur/max high full]: %u/%u %u %u.\n", 14378c2ecf20Sopenharmony_ci t->pending, t->maximum, t->high, t->filled); 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (!left--) { 14418c2ecf20Sopenharmony_ci unsigned int count = sprintf (page, "RX queues [cur/max/req low empty]:"); 14428c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) { 14438c2ecf20Sopenharmony_ci amb_rxq * r = &dev->rxq[pool]; 14448c2ecf20Sopenharmony_ci count += sprintf (page+count, " %u/%u/%u %u %u", 14458c2ecf20Sopenharmony_ci r->pending, r->maximum, r->buffers_wanted, r->low, r->emptied); 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci count += sprintf (page+count, ".\n"); 14488c2ecf20Sopenharmony_ci return count; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci if (!left--) { 14528c2ecf20Sopenharmony_ci unsigned int count = sprintf (page, "RX buffer sizes:"); 14538c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) { 14548c2ecf20Sopenharmony_ci amb_rxq * r = &dev->rxq[pool]; 14558c2ecf20Sopenharmony_ci count += sprintf (page+count, " %u", r->buffer_size); 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci count += sprintf (page+count, ".\n"); 14588c2ecf20Sopenharmony_ci return count; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci#if 0 14628c2ecf20Sopenharmony_ci if (!left--) { 14638c2ecf20Sopenharmony_ci // suni block etc? 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci#endif 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci/********** Operation Structure **********/ 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic const struct atmdev_ops amb_ops = { 14738c2ecf20Sopenharmony_ci .open = amb_open, 14748c2ecf20Sopenharmony_ci .close = amb_close, 14758c2ecf20Sopenharmony_ci .send = amb_send, 14768c2ecf20Sopenharmony_ci .proc_read = amb_proc_read, 14778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14788c2ecf20Sopenharmony_ci}; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci/********** housekeeping **********/ 14818c2ecf20Sopenharmony_cistatic void do_housekeeping (struct timer_list *t) { 14828c2ecf20Sopenharmony_ci amb_dev * dev = from_timer(dev, t, housekeeping); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci // could collect device-specific (not driver/atm-linux) stats here 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci // last resort refill once every ten seconds 14878c2ecf20Sopenharmony_ci fill_rx_pools (dev); 14888c2ecf20Sopenharmony_ci mod_timer(&dev->housekeeping, jiffies + 10*HZ); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci return; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/********** creation of communication queues **********/ 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic int create_queues(amb_dev *dev, unsigned int cmds, unsigned int txs, 14968c2ecf20Sopenharmony_ci unsigned int *rxs, unsigned int *rx_buffer_sizes) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci unsigned char pool; 14998c2ecf20Sopenharmony_ci size_t total = 0; 15008c2ecf20Sopenharmony_ci void * memory; 15018c2ecf20Sopenharmony_ci void * limit; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW, "create_queues %p", dev); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci total += cmds * sizeof(command); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci total += txs * (sizeof(tx_in) + sizeof(tx_out)); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 15108c2ecf20Sopenharmony_ci total += rxs[pool] * (sizeof(rx_in) + sizeof(rx_out)); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci memory = kmalloc (total, GFP_KERNEL); 15138c2ecf20Sopenharmony_ci if (!memory) { 15148c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "could not allocate queues"); 15158c2ecf20Sopenharmony_ci return -ENOMEM; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci if (check_area (memory, total)) { 15188c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "queues allocated in nasty area"); 15198c2ecf20Sopenharmony_ci kfree (memory); 15208c2ecf20Sopenharmony_ci return -ENOMEM; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci limit = memory + total; 15248c2ecf20Sopenharmony_ci PRINTD (DBG_INIT, "queues from %p to %p", memory, limit); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci PRINTD (DBG_CMD, "command queue at %p", memory); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci { 15298c2ecf20Sopenharmony_ci command * cmd = memory; 15308c2ecf20Sopenharmony_ci amb_cq * cq = &dev->cq; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci cq->pending = 0; 15338c2ecf20Sopenharmony_ci cq->high = 0; 15348c2ecf20Sopenharmony_ci cq->maximum = cmds - 1; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci cq->ptrs.start = cmd; 15378c2ecf20Sopenharmony_ci cq->ptrs.in = cmd; 15388c2ecf20Sopenharmony_ci cq->ptrs.out = cmd; 15398c2ecf20Sopenharmony_ci cq->ptrs.limit = cmd + cmds; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci memory = cq->ptrs.limit; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci PRINTD (DBG_TX, "TX queue pair at %p", memory); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci { 15478c2ecf20Sopenharmony_ci tx_in * in = memory; 15488c2ecf20Sopenharmony_ci tx_out * out; 15498c2ecf20Sopenharmony_ci amb_txq * txq = &dev->txq; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci txq->pending = 0; 15528c2ecf20Sopenharmony_ci txq->high = 0; 15538c2ecf20Sopenharmony_ci txq->filled = 0; 15548c2ecf20Sopenharmony_ci txq->maximum = txs - 1; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci txq->in.start = in; 15578c2ecf20Sopenharmony_ci txq->in.ptr = in; 15588c2ecf20Sopenharmony_ci txq->in.limit = in + txs; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci memory = txq->in.limit; 15618c2ecf20Sopenharmony_ci out = memory; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci txq->out.start = out; 15648c2ecf20Sopenharmony_ci txq->out.ptr = out; 15658c2ecf20Sopenharmony_ci txq->out.limit = out + txs; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci memory = txq->out.limit; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci PRINTD (DBG_RX, "RX queue pairs at %p", memory); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) { 15738c2ecf20Sopenharmony_ci rx_in * in = memory; 15748c2ecf20Sopenharmony_ci rx_out * out; 15758c2ecf20Sopenharmony_ci amb_rxq * rxq = &dev->rxq[pool]; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci rxq->buffer_size = rx_buffer_sizes[pool]; 15788c2ecf20Sopenharmony_ci rxq->buffers_wanted = 0; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci rxq->pending = 0; 15818c2ecf20Sopenharmony_ci rxq->low = rxs[pool] - 1; 15828c2ecf20Sopenharmony_ci rxq->emptied = 0; 15838c2ecf20Sopenharmony_ci rxq->maximum = rxs[pool] - 1; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci rxq->in.start = in; 15868c2ecf20Sopenharmony_ci rxq->in.ptr = in; 15878c2ecf20Sopenharmony_ci rxq->in.limit = in + rxs[pool]; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci memory = rxq->in.limit; 15908c2ecf20Sopenharmony_ci out = memory; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci rxq->out.start = out; 15938c2ecf20Sopenharmony_ci rxq->out.ptr = out; 15948c2ecf20Sopenharmony_ci rxq->out.limit = out + rxs[pool]; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci memory = rxq->out.limit; 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (memory == limit) { 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci } else { 16028c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "bad queue alloc %p != %p (tell maintainer)", memory, limit); 16038c2ecf20Sopenharmony_ci kfree (limit - total); 16048c2ecf20Sopenharmony_ci return -ENOMEM; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci} 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci/********** destruction of communication queues **********/ 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic void destroy_queues (amb_dev * dev) { 16128c2ecf20Sopenharmony_ci // all queues assumed empty 16138c2ecf20Sopenharmony_ci void * memory = dev->cq.ptrs.start; 16148c2ecf20Sopenharmony_ci // includes txq.in, txq.out, rxq[].in and rxq[].out 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW, "destroy_queues %p", dev); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci PRINTD (DBG_INIT, "freeing queues at %p", memory); 16198c2ecf20Sopenharmony_ci kfree (memory); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci/********** basic loader commands and error handling **********/ 16258c2ecf20Sopenharmony_ci// centisecond timeouts - guessing away here 16268c2ecf20Sopenharmony_cistatic unsigned int command_timeouts [] = { 16278c2ecf20Sopenharmony_ci [host_memory_test] = 15, 16288c2ecf20Sopenharmony_ci [read_adapter_memory] = 2, 16298c2ecf20Sopenharmony_ci [write_adapter_memory] = 2, 16308c2ecf20Sopenharmony_ci [adapter_start] = 50, 16318c2ecf20Sopenharmony_ci [get_version_number] = 10, 16328c2ecf20Sopenharmony_ci [interrupt_host] = 1, 16338c2ecf20Sopenharmony_ci [flash_erase_sector] = 1, 16348c2ecf20Sopenharmony_ci [adap_download_block] = 1, 16358c2ecf20Sopenharmony_ci [adap_erase_flash] = 1, 16368c2ecf20Sopenharmony_ci [adap_run_in_iram] = 1, 16378c2ecf20Sopenharmony_ci [adap_end_download] = 1 16388c2ecf20Sopenharmony_ci}; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_cistatic unsigned int command_successes [] = { 16428c2ecf20Sopenharmony_ci [host_memory_test] = COMMAND_PASSED_TEST, 16438c2ecf20Sopenharmony_ci [read_adapter_memory] = COMMAND_READ_DATA_OK, 16448c2ecf20Sopenharmony_ci [write_adapter_memory] = COMMAND_WRITE_DATA_OK, 16458c2ecf20Sopenharmony_ci [adapter_start] = COMMAND_COMPLETE, 16468c2ecf20Sopenharmony_ci [get_version_number] = COMMAND_COMPLETE, 16478c2ecf20Sopenharmony_ci [interrupt_host] = COMMAND_COMPLETE, 16488c2ecf20Sopenharmony_ci [flash_erase_sector] = COMMAND_COMPLETE, 16498c2ecf20Sopenharmony_ci [adap_download_block] = COMMAND_COMPLETE, 16508c2ecf20Sopenharmony_ci [adap_erase_flash] = COMMAND_COMPLETE, 16518c2ecf20Sopenharmony_ci [adap_run_in_iram] = COMMAND_COMPLETE, 16528c2ecf20Sopenharmony_ci [adap_end_download] = COMMAND_COMPLETE 16538c2ecf20Sopenharmony_ci}; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_cistatic int decode_loader_result (loader_command cmd, u32 result) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci int res; 16588c2ecf20Sopenharmony_ci const char *msg; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (result == command_successes[cmd]) 16618c2ecf20Sopenharmony_ci return 0; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci switch (result) { 16648c2ecf20Sopenharmony_ci case BAD_COMMAND: 16658c2ecf20Sopenharmony_ci res = -EINVAL; 16668c2ecf20Sopenharmony_ci msg = "bad command"; 16678c2ecf20Sopenharmony_ci break; 16688c2ecf20Sopenharmony_ci case COMMAND_IN_PROGRESS: 16698c2ecf20Sopenharmony_ci res = -ETIMEDOUT; 16708c2ecf20Sopenharmony_ci msg = "command in progress"; 16718c2ecf20Sopenharmony_ci break; 16728c2ecf20Sopenharmony_ci case COMMAND_PASSED_TEST: 16738c2ecf20Sopenharmony_ci res = 0; 16748c2ecf20Sopenharmony_ci msg = "command passed test"; 16758c2ecf20Sopenharmony_ci break; 16768c2ecf20Sopenharmony_ci case COMMAND_FAILED_TEST: 16778c2ecf20Sopenharmony_ci res = -EIO; 16788c2ecf20Sopenharmony_ci msg = "command failed test"; 16798c2ecf20Sopenharmony_ci break; 16808c2ecf20Sopenharmony_ci case COMMAND_READ_DATA_OK: 16818c2ecf20Sopenharmony_ci res = 0; 16828c2ecf20Sopenharmony_ci msg = "command read data ok"; 16838c2ecf20Sopenharmony_ci break; 16848c2ecf20Sopenharmony_ci case COMMAND_READ_BAD_ADDRESS: 16858c2ecf20Sopenharmony_ci res = -EINVAL; 16868c2ecf20Sopenharmony_ci msg = "command read bad address"; 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci case COMMAND_WRITE_DATA_OK: 16898c2ecf20Sopenharmony_ci res = 0; 16908c2ecf20Sopenharmony_ci msg = "command write data ok"; 16918c2ecf20Sopenharmony_ci break; 16928c2ecf20Sopenharmony_ci case COMMAND_WRITE_BAD_ADDRESS: 16938c2ecf20Sopenharmony_ci res = -EINVAL; 16948c2ecf20Sopenharmony_ci msg = "command write bad address"; 16958c2ecf20Sopenharmony_ci break; 16968c2ecf20Sopenharmony_ci case COMMAND_WRITE_FLASH_FAILURE: 16978c2ecf20Sopenharmony_ci res = -EIO; 16988c2ecf20Sopenharmony_ci msg = "command write flash failure"; 16998c2ecf20Sopenharmony_ci break; 17008c2ecf20Sopenharmony_ci case COMMAND_COMPLETE: 17018c2ecf20Sopenharmony_ci res = 0; 17028c2ecf20Sopenharmony_ci msg = "command complete"; 17038c2ecf20Sopenharmony_ci break; 17048c2ecf20Sopenharmony_ci case COMMAND_FLASH_ERASE_FAILURE: 17058c2ecf20Sopenharmony_ci res = -EIO; 17068c2ecf20Sopenharmony_ci msg = "command flash erase failure"; 17078c2ecf20Sopenharmony_ci break; 17088c2ecf20Sopenharmony_ci case COMMAND_WRITE_BAD_DATA: 17098c2ecf20Sopenharmony_ci res = -EINVAL; 17108c2ecf20Sopenharmony_ci msg = "command write bad data"; 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci default: 17138c2ecf20Sopenharmony_ci res = -EINVAL; 17148c2ecf20Sopenharmony_ci msg = "unknown error"; 17158c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD|DBG_ERR, 17168c2ecf20Sopenharmony_ci "decode_loader_result got %d=%x !", 17178c2ecf20Sopenharmony_ci result, result); 17188c2ecf20Sopenharmony_ci break; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "%s", msg); 17228c2ecf20Sopenharmony_ci return res; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic int do_loader_command(volatile loader_block *lb, const amb_dev *dev, 17268c2ecf20Sopenharmony_ci loader_command cmd) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci unsigned long timeout; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "do_loader_command"); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* do a command 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci Set the return value to zero, set the command type and set the 17368c2ecf20Sopenharmony_ci valid entry to the right magic value. The payload is already 17378c2ecf20Sopenharmony_ci correctly byte-ordered so we leave it alone. Hit the doorbell 17388c2ecf20Sopenharmony_ci with the bus address of this structure. 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci */ 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci lb->result = 0; 17438c2ecf20Sopenharmony_ci lb->command = cpu_to_be32 (cmd); 17448c2ecf20Sopenharmony_ci lb->valid = cpu_to_be32 (DMA_VALID); 17458c2ecf20Sopenharmony_ci // dump_registers (dev); 17468c2ecf20Sopenharmony_ci // dump_loader_block (lb); 17478c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (lb) & ~onegigmask); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci timeout = command_timeouts[cmd] * 10; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci while (!lb->result || lb->result == cpu_to_be32 (COMMAND_IN_PROGRESS)) 17528c2ecf20Sopenharmony_ci if (timeout) { 17538c2ecf20Sopenharmony_ci timeout = msleep_interruptible(timeout); 17548c2ecf20Sopenharmony_ci } else { 17558c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD|DBG_ERR, "command %d timed out", cmd); 17568c2ecf20Sopenharmony_ci dump_registers (dev); 17578c2ecf20Sopenharmony_ci dump_loader_block (lb); 17588c2ecf20Sopenharmony_ci return -ETIMEDOUT; 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (cmd == adapter_start) { 17628c2ecf20Sopenharmony_ci // wait for start command to acknowledge... 17638c2ecf20Sopenharmony_ci timeout = 100; 17648c2ecf20Sopenharmony_ci while (rd_plain (dev, offsetof(amb_mem, doorbell))) 17658c2ecf20Sopenharmony_ci if (timeout) { 17668c2ecf20Sopenharmony_ci timeout = msleep_interruptible(timeout); 17678c2ecf20Sopenharmony_ci } else { 17688c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD|DBG_ERR, "start command did not clear doorbell, res=%08x", 17698c2ecf20Sopenharmony_ci be32_to_cpu (lb->result)); 17708c2ecf20Sopenharmony_ci dump_registers (dev); 17718c2ecf20Sopenharmony_ci return -ETIMEDOUT; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci return 0; 17748c2ecf20Sopenharmony_ci } else { 17758c2ecf20Sopenharmony_ci return decode_loader_result (cmd, be32_to_cpu (lb->result)); 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci/* loader: determine loader version */ 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic int get_loader_version(loader_block *lb, const amb_dev *dev, 17838c2ecf20Sopenharmony_ci u32 *version) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci int res; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version"); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci res = do_loader_command (lb, dev, get_version_number); 17908c2ecf20Sopenharmony_ci if (res) 17918c2ecf20Sopenharmony_ci return res; 17928c2ecf20Sopenharmony_ci if (version) 17938c2ecf20Sopenharmony_ci *version = be32_to_cpu (lb->payload.version); 17948c2ecf20Sopenharmony_ci return 0; 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci/* loader: write memory data blocks */ 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_cistatic int loader_write(loader_block *lb, const amb_dev *dev, 18008c2ecf20Sopenharmony_ci const struct ihex_binrec *rec) 18018c2ecf20Sopenharmony_ci{ 18028c2ecf20Sopenharmony_ci transfer_block * tb = &lb->payload.transfer; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "loader_write"); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci tb->address = rec->addr; 18078c2ecf20Sopenharmony_ci tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4); 18088c2ecf20Sopenharmony_ci memcpy(tb->data, rec->data, be16_to_cpu(rec->len)); 18098c2ecf20Sopenharmony_ci return do_loader_command (lb, dev, write_adapter_memory); 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci/* loader: verify memory data blocks */ 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic int loader_verify(loader_block *lb, const amb_dev *dev, 18158c2ecf20Sopenharmony_ci const struct ihex_binrec *rec) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci transfer_block * tb = &lb->payload.transfer; 18188c2ecf20Sopenharmony_ci int res; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify"); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci tb->address = rec->addr; 18238c2ecf20Sopenharmony_ci tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4); 18248c2ecf20Sopenharmony_ci res = do_loader_command (lb, dev, read_adapter_memory); 18258c2ecf20Sopenharmony_ci if (!res && memcmp(tb->data, rec->data, be16_to_cpu(rec->len))) 18268c2ecf20Sopenharmony_ci res = -EINVAL; 18278c2ecf20Sopenharmony_ci return res; 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci/* loader: start microcode */ 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic int loader_start(loader_block *lb, const amb_dev *dev, u32 address) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "loader_start"); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci lb->payload.start = cpu_to_be32 (address); 18378c2ecf20Sopenharmony_ci return do_loader_command (lb, dev, adapter_start); 18388c2ecf20Sopenharmony_ci} 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci/********** reset card **********/ 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_cistatic inline void sf (const char * msg) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "self-test failed: %s", msg); 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cistatic int amb_reset (amb_dev * dev, int diags) { 18488c2ecf20Sopenharmony_ci u32 word; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset"); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci word = rd_plain (dev, offsetof(amb_mem, reset_control)); 18538c2ecf20Sopenharmony_ci // put card into reset state 18548c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, reset_control), word | AMB_RESET_BITS); 18558c2ecf20Sopenharmony_ci // wait a short while 18568c2ecf20Sopenharmony_ci udelay (10); 18578c2ecf20Sopenharmony_ci#if 1 18588c2ecf20Sopenharmony_ci // put card into known good state 18598c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, interrupt_control), AMB_DOORBELL_BITS); 18608c2ecf20Sopenharmony_ci // clear all interrupts just in case 18618c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, interrupt), -1); 18628c2ecf20Sopenharmony_ci#endif 18638c2ecf20Sopenharmony_ci // clear self-test done flag 18648c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, mb.loader.ready), 0); 18658c2ecf20Sopenharmony_ci // take card out of reset state 18668c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, reset_control), word &~ AMB_RESET_BITS); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (diags) { 18698c2ecf20Sopenharmony_ci unsigned long timeout; 18708c2ecf20Sopenharmony_ci // 4.2 second wait 18718c2ecf20Sopenharmony_ci msleep(4200); 18728c2ecf20Sopenharmony_ci // half second time-out 18738c2ecf20Sopenharmony_ci timeout = 500; 18748c2ecf20Sopenharmony_ci while (!rd_plain (dev, offsetof(amb_mem, mb.loader.ready))) 18758c2ecf20Sopenharmony_ci if (timeout) { 18768c2ecf20Sopenharmony_ci timeout = msleep_interruptible(timeout); 18778c2ecf20Sopenharmony_ci } else { 18788c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD|DBG_ERR, "reset timed out"); 18798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci // get results of self-test 18838c2ecf20Sopenharmony_ci // XXX double check byte-order 18848c2ecf20Sopenharmony_ci word = rd_mem (dev, offsetof(amb_mem, mb.loader.result)); 18858c2ecf20Sopenharmony_ci if (word & SELF_TEST_FAILURE) { 18868c2ecf20Sopenharmony_ci if (word & GPINT_TST_FAILURE) 18878c2ecf20Sopenharmony_ci sf ("interrupt"); 18888c2ecf20Sopenharmony_ci if (word & SUNI_DATA_PATTERN_FAILURE) 18898c2ecf20Sopenharmony_ci sf ("SUNI data pattern"); 18908c2ecf20Sopenharmony_ci if (word & SUNI_DATA_BITS_FAILURE) 18918c2ecf20Sopenharmony_ci sf ("SUNI data bits"); 18928c2ecf20Sopenharmony_ci if (word & SUNI_UTOPIA_FAILURE) 18938c2ecf20Sopenharmony_ci sf ("SUNI UTOPIA interface"); 18948c2ecf20Sopenharmony_ci if (word & SUNI_FIFO_FAILURE) 18958c2ecf20Sopenharmony_ci sf ("SUNI cell buffer FIFO"); 18968c2ecf20Sopenharmony_ci if (word & SRAM_FAILURE) 18978c2ecf20Sopenharmony_ci sf ("bad SRAM"); 18988c2ecf20Sopenharmony_ci // better return value? 18998c2ecf20Sopenharmony_ci return -EIO; 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci return 0; 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci/********** transfer and start the microcode **********/ 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_cistatic int ucode_init(loader_block *lb, amb_dev *dev) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci const struct firmware *fw; 19118c2ecf20Sopenharmony_ci unsigned long start_address; 19128c2ecf20Sopenharmony_ci const struct ihex_binrec *rec; 19138c2ecf20Sopenharmony_ci const char *errmsg = NULL; 19148c2ecf20Sopenharmony_ci int res; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci res = request_ihex_firmware(&fw, "atmsar11.fw", &dev->pci_dev->dev); 19178c2ecf20Sopenharmony_ci if (res) { 19188c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "Cannot load microcode data"); 19198c2ecf20Sopenharmony_ci return res; 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci /* First record contains just the start address */ 19238c2ecf20Sopenharmony_ci rec = (const struct ihex_binrec *)fw->data; 19248c2ecf20Sopenharmony_ci if (be16_to_cpu(rec->len) != sizeof(__be32) || be32_to_cpu(rec->addr)) { 19258c2ecf20Sopenharmony_ci errmsg = "no start record"; 19268c2ecf20Sopenharmony_ci goto fail; 19278c2ecf20Sopenharmony_ci } 19288c2ecf20Sopenharmony_ci start_address = be32_to_cpup((__be32 *)rec->data); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci rec = ihex_next_binrec(rec); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init"); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci while (rec) { 19358c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD, "starting region (%x, %u)", be32_to_cpu(rec->addr), 19368c2ecf20Sopenharmony_ci be16_to_cpu(rec->len)); 19378c2ecf20Sopenharmony_ci if (be16_to_cpu(rec->len) > 4 * MAX_TRANSFER_DATA) { 19388c2ecf20Sopenharmony_ci errmsg = "record too long"; 19398c2ecf20Sopenharmony_ci goto fail; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci if (be16_to_cpu(rec->len) & 3) { 19428c2ecf20Sopenharmony_ci errmsg = "odd number of bytes"; 19438c2ecf20Sopenharmony_ci goto fail; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci res = loader_write(lb, dev, rec); 19468c2ecf20Sopenharmony_ci if (res) 19478c2ecf20Sopenharmony_ci break; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci res = loader_verify(lb, dev, rec); 19508c2ecf20Sopenharmony_ci if (res) 19518c2ecf20Sopenharmony_ci break; 19528c2ecf20Sopenharmony_ci rec = ihex_next_binrec(rec); 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci release_firmware(fw); 19558c2ecf20Sopenharmony_ci if (!res) 19568c2ecf20Sopenharmony_ci res = loader_start(lb, dev, start_address); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci return res; 19598c2ecf20Sopenharmony_cifail: 19608c2ecf20Sopenharmony_ci release_firmware(fw); 19618c2ecf20Sopenharmony_ci PRINTK(KERN_ERR, "Bad microcode data (%s)", errmsg); 19628c2ecf20Sopenharmony_ci return -EINVAL; 19638c2ecf20Sopenharmony_ci} 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci/********** give adapter parameters **********/ 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic inline __be32 bus_addr(void * addr) { 19688c2ecf20Sopenharmony_ci return cpu_to_be32 (virt_to_bus (addr)); 19698c2ecf20Sopenharmony_ci} 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_cistatic int amb_talk(amb_dev *dev) 19728c2ecf20Sopenharmony_ci{ 19738c2ecf20Sopenharmony_ci adap_talk_block a; 19748c2ecf20Sopenharmony_ci unsigned char pool; 19758c2ecf20Sopenharmony_ci unsigned long timeout; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW, "amb_talk %p", dev); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci a.command_start = bus_addr (dev->cq.ptrs.start); 19808c2ecf20Sopenharmony_ci a.command_end = bus_addr (dev->cq.ptrs.limit); 19818c2ecf20Sopenharmony_ci a.tx_start = bus_addr (dev->txq.in.start); 19828c2ecf20Sopenharmony_ci a.tx_end = bus_addr (dev->txq.in.limit); 19838c2ecf20Sopenharmony_ci a.txcom_start = bus_addr (dev->txq.out.start); 19848c2ecf20Sopenharmony_ci a.txcom_end = bus_addr (dev->txq.out.limit); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) { 19878c2ecf20Sopenharmony_ci // the other "a" items are set up by the adapter 19888c2ecf20Sopenharmony_ci a.rec_struct[pool].buffer_start = bus_addr (dev->rxq[pool].in.start); 19898c2ecf20Sopenharmony_ci a.rec_struct[pool].buffer_end = bus_addr (dev->rxq[pool].in.limit); 19908c2ecf20Sopenharmony_ci a.rec_struct[pool].rx_start = bus_addr (dev->rxq[pool].out.start); 19918c2ecf20Sopenharmony_ci a.rec_struct[pool].rx_end = bus_addr (dev->rxq[pool].out.limit); 19928c2ecf20Sopenharmony_ci a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size); 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci#ifdef AMB_NEW_MICROCODE 19968c2ecf20Sopenharmony_ci // disable fast PLX prefetching 19978c2ecf20Sopenharmony_ci a.init_flags = 0; 19988c2ecf20Sopenharmony_ci#endif 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci // pass the structure 20018c2ecf20Sopenharmony_ci wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (&a)); 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci // 2.2 second wait (must not touch doorbell during 2 second DMA test) 20048c2ecf20Sopenharmony_ci msleep(2200); 20058c2ecf20Sopenharmony_ci // give the adapter another half second? 20068c2ecf20Sopenharmony_ci timeout = 500; 20078c2ecf20Sopenharmony_ci while (rd_plain (dev, offsetof(amb_mem, doorbell))) 20088c2ecf20Sopenharmony_ci if (timeout) { 20098c2ecf20Sopenharmony_ci timeout = msleep_interruptible(timeout); 20108c2ecf20Sopenharmony_ci } else { 20118c2ecf20Sopenharmony_ci PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out"); 20128c2ecf20Sopenharmony_ci return -ETIMEDOUT; 20138c2ecf20Sopenharmony_ci } 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci return 0; 20168c2ecf20Sopenharmony_ci} 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci// get microcode version 20198c2ecf20Sopenharmony_cistatic void amb_ucode_version(amb_dev *dev) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci u32 major; 20228c2ecf20Sopenharmony_ci u32 minor; 20238c2ecf20Sopenharmony_ci command cmd; 20248c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_GET_VERSION); 20258c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) { 20268c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 20278c2ecf20Sopenharmony_ci schedule(); 20288c2ecf20Sopenharmony_ci } 20298c2ecf20Sopenharmony_ci major = be32_to_cpu (cmd.args.version.major); 20308c2ecf20Sopenharmony_ci minor = be32_to_cpu (cmd.args.version.minor); 20318c2ecf20Sopenharmony_ci PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor); 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci// get end station address 20358c2ecf20Sopenharmony_cistatic void amb_esi(amb_dev *dev, u8 *esi) 20368c2ecf20Sopenharmony_ci{ 20378c2ecf20Sopenharmony_ci u32 lower4; 20388c2ecf20Sopenharmony_ci u16 upper2; 20398c2ecf20Sopenharmony_ci command cmd; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci cmd.request = cpu_to_be32 (SRB_GET_BIA); 20428c2ecf20Sopenharmony_ci while (command_do (dev, &cmd)) { 20438c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 20448c2ecf20Sopenharmony_ci schedule(); 20458c2ecf20Sopenharmony_ci } 20468c2ecf20Sopenharmony_ci lower4 = be32_to_cpu (cmd.args.bia.lower4); 20478c2ecf20Sopenharmony_ci upper2 = be32_to_cpu (cmd.args.bia.upper2); 20488c2ecf20Sopenharmony_ci PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci if (esi) { 20518c2ecf20Sopenharmony_ci unsigned int i; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci PRINTDB (DBG_INIT, "ESI:"); 20548c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN; ++i) { 20558c2ecf20Sopenharmony_ci if (i < 4) 20568c2ecf20Sopenharmony_ci esi[i] = bitrev8(lower4>>(8*i)); 20578c2ecf20Sopenharmony_ci else 20588c2ecf20Sopenharmony_ci esi[i] = bitrev8(upper2>>(8*(i-4))); 20598c2ecf20Sopenharmony_ci PRINTDM (DBG_INIT, " %02x", esi[i]); 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci PRINTDE (DBG_INIT, ""); 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci return; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic void fixup_plx_window (amb_dev *dev, loader_block *lb) 20698c2ecf20Sopenharmony_ci{ 20708c2ecf20Sopenharmony_ci // fix up the PLX-mapped window base address to match the block 20718c2ecf20Sopenharmony_ci unsigned long blb; 20728c2ecf20Sopenharmony_ci u32 mapreg; 20738c2ecf20Sopenharmony_ci blb = virt_to_bus(lb); 20748c2ecf20Sopenharmony_ci // the kernel stack had better not ever cross a 1Gb boundary! 20758c2ecf20Sopenharmony_ci mapreg = rd_plain (dev, offsetof(amb_mem, stuff[10])); 20768c2ecf20Sopenharmony_ci mapreg &= ~onegigmask; 20778c2ecf20Sopenharmony_ci mapreg |= blb & onegigmask; 20788c2ecf20Sopenharmony_ci wr_plain (dev, offsetof(amb_mem, stuff[10]), mapreg); 20798c2ecf20Sopenharmony_ci return; 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_cistatic int amb_init(amb_dev *dev) 20838c2ecf20Sopenharmony_ci{ 20848c2ecf20Sopenharmony_ci loader_block lb; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci u32 version; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci if (amb_reset (dev, 1)) { 20898c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "card reset failed!"); 20908c2ecf20Sopenharmony_ci } else { 20918c2ecf20Sopenharmony_ci fixup_plx_window (dev, &lb); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (get_loader_version (&lb, dev, &version)) { 20948c2ecf20Sopenharmony_ci PRINTK (KERN_INFO, "failed to get loader version"); 20958c2ecf20Sopenharmony_ci } else { 20968c2ecf20Sopenharmony_ci PRINTK (KERN_INFO, "loader version is %08x", version); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci if (ucode_init (&lb, dev)) { 20998c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "microcode failure"); 21008c2ecf20Sopenharmony_ci } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) { 21018c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "failed to get memory for queues"); 21028c2ecf20Sopenharmony_ci } else { 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (amb_talk (dev)) { 21058c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "adapter did not accept queues"); 21068c2ecf20Sopenharmony_ci } else { 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci amb_ucode_version (dev); 21098c2ecf20Sopenharmony_ci return 0; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci } /* amb_talk */ 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci destroy_queues (dev); 21148c2ecf20Sopenharmony_ci } /* create_queues, ucode_init */ 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci amb_reset (dev, 0); 21178c2ecf20Sopenharmony_ci } /* get_loader_version */ 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci } /* amb_reset */ 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci return -EINVAL; 21228c2ecf20Sopenharmony_ci} 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic void setup_dev(amb_dev *dev, struct pci_dev *pci_dev) 21258c2ecf20Sopenharmony_ci{ 21268c2ecf20Sopenharmony_ci unsigned char pool; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci // set up known dev items straight away 21298c2ecf20Sopenharmony_ci dev->pci_dev = pci_dev; 21308c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, dev); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci dev->iobase = pci_resource_start (pci_dev, 1); 21338c2ecf20Sopenharmony_ci dev->irq = pci_dev->irq; 21348c2ecf20Sopenharmony_ci dev->membase = bus_to_virt(pci_resource_start(pci_dev, 0)); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci // flags (currently only dead) 21378c2ecf20Sopenharmony_ci dev->flags = 0; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci // Allocate cell rates (fibre) 21408c2ecf20Sopenharmony_ci // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53 21418c2ecf20Sopenharmony_ci // to be really pedantic, this should be ATM_OC3c_PCR 21428c2ecf20Sopenharmony_ci dev->tx_avail = ATM_OC3_PCR; 21438c2ecf20Sopenharmony_ci dev->rx_avail = ATM_OC3_PCR; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci // semaphore for txer/rxer modifications - we cannot use a 21468c2ecf20Sopenharmony_ci // spinlock as the critical region needs to switch processes 21478c2ecf20Sopenharmony_ci mutex_init(&dev->vcc_sf); 21488c2ecf20Sopenharmony_ci // queue manipulation spinlocks; we want atomic reads and 21498c2ecf20Sopenharmony_ci // writes to the queue descriptors (handles IRQ and SMP) 21508c2ecf20Sopenharmony_ci // consider replacing "int pending" -> "atomic_t available" 21518c2ecf20Sopenharmony_ci // => problem related to who gets to move queue pointers 21528c2ecf20Sopenharmony_ci spin_lock_init (&dev->cq.lock); 21538c2ecf20Sopenharmony_ci spin_lock_init (&dev->txq.lock); 21548c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 21558c2ecf20Sopenharmony_ci spin_lock_init (&dev->rxq[pool].lock); 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic void setup_pci_dev(struct pci_dev *pci_dev) 21598c2ecf20Sopenharmony_ci{ 21608c2ecf20Sopenharmony_ci unsigned char lat; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci // enable bus master accesses 21638c2ecf20Sopenharmony_ci pci_set_master(pci_dev); 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci // frobnicate latency (upwards, usually) 21668c2ecf20Sopenharmony_ci pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat); 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (!pci_lat) 21698c2ecf20Sopenharmony_ci pci_lat = (lat < MIN_PCI_LATENCY) ? MIN_PCI_LATENCY : lat; 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci if (lat != pci_lat) { 21728c2ecf20Sopenharmony_ci PRINTK (KERN_INFO, "Changing PCI latency timer from %hu to %hu", 21738c2ecf20Sopenharmony_ci lat, pci_lat); 21748c2ecf20Sopenharmony_ci pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat); 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_ci} 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_cistatic int amb_probe(struct pci_dev *pci_dev, 21798c2ecf20Sopenharmony_ci const struct pci_device_id *pci_ent) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci amb_dev * dev; 21828c2ecf20Sopenharmony_ci int err; 21838c2ecf20Sopenharmony_ci unsigned int irq; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci err = pci_enable_device(pci_dev); 21868c2ecf20Sopenharmony_ci if (err < 0) { 21878c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card"); 21888c2ecf20Sopenharmony_ci goto out; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci // read resources from PCI configuration space 21928c2ecf20Sopenharmony_ci irq = pci_dev->irq; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) { 21958c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card"); 21968c2ecf20Sopenharmony_ci err = -EINVAL; 21978c2ecf20Sopenharmony_ci goto out_disable; 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at" 22018c2ecf20Sopenharmony_ci " IO %llx, IRQ %u, MEM %p", 22028c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_start(pci_dev, 1), 22038c2ecf20Sopenharmony_ci irq, bus_to_virt(pci_resource_start(pci_dev, 0))); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci // check IO region 22068c2ecf20Sopenharmony_ci err = pci_request_region(pci_dev, 1, DEV_LABEL); 22078c2ecf20Sopenharmony_ci if (err < 0) { 22088c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "IO range already in use!"); 22098c2ecf20Sopenharmony_ci goto out_disable; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(amb_dev), GFP_KERNEL); 22138c2ecf20Sopenharmony_ci if (!dev) { 22148c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "out of memory!"); 22158c2ecf20Sopenharmony_ci err = -ENOMEM; 22168c2ecf20Sopenharmony_ci goto out_release; 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci setup_dev(dev, pci_dev); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci err = amb_init(dev); 22228c2ecf20Sopenharmony_ci if (err < 0) { 22238c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "adapter initialisation failure"); 22248c2ecf20Sopenharmony_ci goto out_free; 22258c2ecf20Sopenharmony_ci } 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci setup_pci_dev(pci_dev); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci // grab (but share) IRQ and install handler 22308c2ecf20Sopenharmony_ci err = request_irq(irq, interrupt_handler, IRQF_SHARED, DEV_LABEL, dev); 22318c2ecf20Sopenharmony_ci if (err < 0) { 22328c2ecf20Sopenharmony_ci PRINTK (KERN_ERR, "request IRQ failed!"); 22338c2ecf20Sopenharmony_ci goto out_reset; 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci dev->atm_dev = atm_dev_register (DEV_LABEL, &pci_dev->dev, &amb_ops, -1, 22378c2ecf20Sopenharmony_ci NULL); 22388c2ecf20Sopenharmony_ci if (!dev->atm_dev) { 22398c2ecf20Sopenharmony_ci PRINTD (DBG_ERR, "failed to register Madge ATM adapter"); 22408c2ecf20Sopenharmony_ci err = -EINVAL; 22418c2ecf20Sopenharmony_ci goto out_free_irq; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p", 22458c2ecf20Sopenharmony_ci dev->atm_dev->number, dev, dev->atm_dev); 22468c2ecf20Sopenharmony_ci dev->atm_dev->dev_data = (void *) dev; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci // register our address 22498c2ecf20Sopenharmony_ci amb_esi (dev, dev->atm_dev->esi); 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci // 0 bits for vpi, 10 bits for vci 22528c2ecf20Sopenharmony_ci dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS; 22538c2ecf20Sopenharmony_ci dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci timer_setup(&dev->housekeeping, do_housekeeping, 0); 22568c2ecf20Sopenharmony_ci mod_timer(&dev->housekeeping, jiffies); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci // enable host interrupts 22598c2ecf20Sopenharmony_ci interrupts_on (dev); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ciout: 22628c2ecf20Sopenharmony_ci return err; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ciout_free_irq: 22658c2ecf20Sopenharmony_ci free_irq(irq, dev); 22668c2ecf20Sopenharmony_ciout_reset: 22678c2ecf20Sopenharmony_ci amb_reset(dev, 0); 22688c2ecf20Sopenharmony_ciout_free: 22698c2ecf20Sopenharmony_ci kfree(dev); 22708c2ecf20Sopenharmony_ciout_release: 22718c2ecf20Sopenharmony_ci pci_release_region(pci_dev, 1); 22728c2ecf20Sopenharmony_ciout_disable: 22738c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 22748c2ecf20Sopenharmony_ci goto out; 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_cistatic void amb_remove_one(struct pci_dev *pci_dev) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci struct amb_dev *dev; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci dev = pci_get_drvdata(pci_dev); 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev); 22858c2ecf20Sopenharmony_ci del_timer_sync(&dev->housekeeping); 22868c2ecf20Sopenharmony_ci // the drain should not be necessary 22878c2ecf20Sopenharmony_ci drain_rx_pools(dev); 22888c2ecf20Sopenharmony_ci interrupts_off(dev); 22898c2ecf20Sopenharmony_ci amb_reset(dev, 0); 22908c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 22918c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 22928c2ecf20Sopenharmony_ci destroy_queues(dev); 22938c2ecf20Sopenharmony_ci atm_dev_deregister(dev->atm_dev); 22948c2ecf20Sopenharmony_ci kfree(dev); 22958c2ecf20Sopenharmony_ci pci_release_region(pci_dev, 1); 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic void __init amb_check_args (void) { 22998c2ecf20Sopenharmony_ci unsigned char pool; 23008c2ecf20Sopenharmony_ci unsigned int max_rx_size; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR 23038c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK); 23048c2ecf20Sopenharmony_ci#else 23058c2ecf20Sopenharmony_ci if (debug) 23068c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "no debugging support"); 23078c2ecf20Sopenharmony_ci#endif 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci if (cmds < MIN_QUEUE_SIZE) 23108c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "cmds has been raised to %u", 23118c2ecf20Sopenharmony_ci cmds = MIN_QUEUE_SIZE); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci if (txs < MIN_QUEUE_SIZE) 23148c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "txs has been raised to %u", 23158c2ecf20Sopenharmony_ci txs = MIN_QUEUE_SIZE); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 23188c2ecf20Sopenharmony_ci if (rxs[pool] < MIN_QUEUE_SIZE) 23198c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "rxs[%hu] has been raised to %u", 23208c2ecf20Sopenharmony_ci pool, rxs[pool] = MIN_QUEUE_SIZE); 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci // buffers sizes should be greater than zero and strictly increasing 23238c2ecf20Sopenharmony_ci max_rx_size = 0; 23248c2ecf20Sopenharmony_ci for (pool = 0; pool < NUM_RX_POOLS; ++pool) 23258c2ecf20Sopenharmony_ci if (rxs_bs[pool] <= max_rx_size) 23268c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "useless pool (rxs_bs[%hu] = %u)", 23278c2ecf20Sopenharmony_ci pool, rxs_bs[pool]); 23288c2ecf20Sopenharmony_ci else 23298c2ecf20Sopenharmony_ci max_rx_size = rxs_bs[pool]; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (rx_lats < MIN_RX_BUFFERS) 23328c2ecf20Sopenharmony_ci PRINTK (KERN_NOTICE, "rx_lats has been raised to %u", 23338c2ecf20Sopenharmony_ci rx_lats = MIN_RX_BUFFERS); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci return; 23368c2ecf20Sopenharmony_ci} 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci/********** module stuff **********/ 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ciMODULE_AUTHOR(maintainer_string); 23418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(description_string); 23428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 23438c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmsar11.fw"); 23448c2ecf20Sopenharmony_cimodule_param(debug, ushort, 0644); 23458c2ecf20Sopenharmony_cimodule_param(cmds, uint, 0); 23468c2ecf20Sopenharmony_cimodule_param(txs, uint, 0); 23478c2ecf20Sopenharmony_cimodule_param_array(rxs, uint, NULL, 0); 23488c2ecf20Sopenharmony_cimodule_param_array(rxs_bs, uint, NULL, 0); 23498c2ecf20Sopenharmony_cimodule_param(rx_lats, uint, 0); 23508c2ecf20Sopenharmony_cimodule_param(pci_lat, byte, 0); 23518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "debug bitmap, see .h file"); 23528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cmds, "number of command queue entries"); 23538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(txs, "number of TX queue entries"); 23548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rxs, "number of RX queue entries [" __MODULE_STRING(NUM_RX_POOLS) "]"); 23558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rxs_bs, "size of RX buffers [" __MODULE_STRING(NUM_RX_POOLS) "]"); 23568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_lats, "number of extra buffers to cope with RX latencies"); 23578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles"); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci/********** module entry **********/ 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_cistatic const struct pci_device_id amb_pci_tbl[] = { 23628c2ecf20Sopenharmony_ci { PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR), 0 }, 23638c2ecf20Sopenharmony_ci { PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD), 0 }, 23648c2ecf20Sopenharmony_ci { 0, } 23658c2ecf20Sopenharmony_ci}; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amb_pci_tbl); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_cistatic struct pci_driver amb_driver = { 23708c2ecf20Sopenharmony_ci .name = "amb", 23718c2ecf20Sopenharmony_ci .probe = amb_probe, 23728c2ecf20Sopenharmony_ci .remove = amb_remove_one, 23738c2ecf20Sopenharmony_ci .id_table = amb_pci_tbl, 23748c2ecf20Sopenharmony_ci}; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_cistatic int __init amb_module_init (void) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_INIT, "init_module"); 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(amb_mem) != 4*16 + 4*12); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci show_version(); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci amb_check_args(); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci // get the juice 23878c2ecf20Sopenharmony_ci return pci_register_driver(&amb_driver); 23888c2ecf20Sopenharmony_ci} 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci/********** module exit **********/ 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_cistatic void __exit amb_module_exit (void) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module"); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci pci_unregister_driver(&amb_driver); 23978c2ecf20Sopenharmony_ci} 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_cimodule_init(amb_module_init); 24008c2ecf20Sopenharmony_cimodule_exit(amb_module_exit); 2401