GIT 0a2a493507805369ff5d92ea5dad032fa27bfab1 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git#ALL commit a0b8ed4f168a604001ee492468c79c20edfac2f4 Author: Ayaz Abdulla Date: Tue Jan 23 12:27:21 2007 -0500 forcedeth: statistics optimization This patch optimizes the data paths that can support hw counters. It removes the sw counted statistics. This is the last patch for the optimization set. Bumping up version of driver. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 9d0a6e3d18d19fd44232c2aa876f60a17c7a81bb Author: Ayaz Abdulla Date: Tue Jan 23 12:27:00 2007 -0500 forcedeth: statistics supported This patch introduces hw statistics for older devices that supported it. It breaks up the counters supported into separate versions. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 4ffc5e20dae5f59bcd5ad4d9b6dc558f458fa348 Author: Ayaz Abdulla Date: Tue Jan 23 12:00:56 2007 -0500 forcedeth: tx max work This patch adds a limit to how much tx work can be done in each iteration of tx processing. If the max limit is reached, remaining tx completions will be handled by timer interrupt. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 0eb8fece8571d848dd9fbe7adb6e3486ffc040dd Author: Scott Feldman Date: Wed Nov 9 02:18:52 2005 -0500 [netdrvr e100] experiment with doing RX in a similar manner to eepro100 I was going to say that eepro100's speedo_rx_link() does the same DMA abuse as e100, but then I noticed one little detail: eepro100 sets both EL (end of list) and S (suspend) bits in the RFD as it chains it to the RFD list. e100 was only setting the EL bit. Hmmm, that's interesting. That means that if HW reads a RFD with the S-bit set, it'll process that RFD and then suspend the receive unit. The receive unit will resume when SW clears the S-bit. There is no need for SW to restart the receive unit. Which means a lot of the receive unit state tracking code in the driver goes away. So here's a patch against 2.6.14. (Sorry for inlining it; the mailer I'm using now will mess with the word wrap). I can't test this on XScale (unless someone has an e100 module for Gumstix :) . It should be doing exactly what eepro100 does with RFDs. I don't believe this change will introduce a performance hit because the S-bit and EL-bit go hand-in-hand meaning if we're going to suspend because of the S- bit, we're on the last resource anyway, so we'll have to wait for SW to replenish. (cherry picked from 29e79da9495261119e3b2e4e7c72507348e75976 commit) commit a5fa5e328116303d92c69f6724e69b664d10e480 Author: Ayaz Abdulla Date: Sun Jan 21 18:10:57 2007 -0500 forcedeth: irq data path optimization This patch optimizes the irq data paths and cleans up the code. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 721c8d1944f87b8f5ce88a123f680dfca5138ace Author: Ayaz Abdulla Date: Sun Jan 21 18:10:52 2007 -0500 forcedeth: rx data path optimization This patch optimizes the rx data paths and cleans up the code. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 2b640425edd030dffb0cb030df1c03e9b832b1b8 Author: Ayaz Abdulla Date: Sun Jan 21 18:10:47 2007 -0500 forcedeth: tx data path optimization This patch optimizes the tx data paths and cleans up the code (removes vlan from descr1/2 since only valid for desc3, changes to make code easier to read, etc). Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit eacfe63d474b233dc9184113860f318e695d9684 Author: Ayaz Abdulla Date: Sun Jan 21 18:10:42 2007 -0500 forcedeth: tx limiting This patch optimizes the logic for tx limiting. It adds a flag to check on the completion side instead of recalculating the number of empty slots. Also, it removes the fields that were previous used for limiting since they have no value. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit f3ad98e208fd87fa846d7ce969295ec015af8040 Author: Ayaz Abdulla Date: Sun Jan 21 18:10:37 2007 -0500 forcedeth: optimized routines This patch breaks up the routines into two versions, one for legacy descriptor versions (ver 1 and ver 2) and one for desc ver 3. This will make the new desc functions more leaner and further reductions will be made in next few patches. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit d779011a049ee0517f45416859b5c0967bae5120 Author: Jay Vosburgh Date: Fri Jan 19 18:15:56 2007 -0800 bonding: update version Update version number to reflect recent changes. Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik commit 49aef8e07ef782f86d396b891393621559654a15 Author: Jay Vosburgh Date: Fri Jan 19 18:15:47 2007 -0800 bonding: modify sysfs support to permit multiple loads The existing code would blindly attempt to create the bonding_masters file (in /sys/class/net) every time the module was loaded. When the module is loaded multiple times (which is the historical method used by initscripts and sysconfig to create multiple bonding interfaces), this caused load failure of the second module load attempt, as the creation request would fail. This changes the code to note the failure, arrange to not remove the bonding_masters file upon module exit, and then return success. Bonding interfaces created by the second or subsequent loads of the module will not exist in bonding_masters. This is not a significant change, as previously only the interfaces from the most recent load of the module would be listed. Both situations are less than optimal, but this case permits compatibility with existing distro configuration scripts, and is consistent. Note that previously, the sysfs create request would overwrite the exsting bonding_masters file and succeed, allowing multiple loads of the module. The sysfs code has recently changed to return an error if the file being created already exists. Patrick McHardy , who reported this problem, observed crashes on the old kernel (before sysfs checked for duplicates). I did not experience such crashes, but this change should resolve them. Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik commit 1af69a5feafcd7e16ad7e5869bfd2bfcc0e4b23a Author: Jay Vosburgh Date: Fri Jan 19 18:15:38 2007 -0800 bonding: fix error check in sysfs creation The existing code did not correctly handle failures to create the per-interface sysfs group for bonding. Modified code to notice errors, and correctly unwind. Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik commit 22246909fec07f5affc8af2c2eda7be448a907f9 Author: Jay Vosburgh Date: Fri Jan 19 18:15:31 2007 -0800 bonding: fix device name allocation error The code to select names for the bonding interfaces was, for the non-sysfs creation case, always using a hard-coded set of bond0, bond1, etc, up to max_bonds. This caused conflicts for the second or subsequent loads of the module. Changed the code to obtain device names from dev_alloc_name(). Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik commit 9feb340365737644f5109ccd13e66011864e5ef1 Author: Akinobu Mita Date: Tue Dec 19 13:09:08 2006 -0800 net: use bitrev8 Use bitrev8 for bmac, mace, macmace, macsonic, and skfp drivers. [akpm@osdl.org: use the API, not the array] Cc: Jeff Garzik Cc: Paul Mackerras Cc: Mirko Lindner Cc: Thomas Bogendoerfer Signed-off-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 6cc93a6467682a3c35035166d6e71ee4572adce4 Author: Adrian Bunk Date: Tue Dec 12 17:24:39 2006 +0100 bonding.h: "extern inline" -> "static inline" "extern inline" generates a warning with -Wmissing-prototypes and I'm currently working on getting the kernel cleaned up for adding this to the CFLAGS since it will help us to avoid a nasty class of runtime errors. If there are places that really need a forced inline, __always_inline would be the correct solution. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik commit 5961daba283e401cb69be9621ea4cb677edcf560 Author: Ron Mercer Date: Wed Jan 3 16:26:08 2007 -0800 qla3xxx: Add support for Qlogic 4032 chip. Qlogic 4032 chip is an incremental change from the 4022. Signed-off-by: Ron Mercer Signed-off-by: Jeff Garzik commit 9c2a1a01bbdcc7f7c516f24f86d6d607cd4388e5 Author: Adrian Bunk Date: Thu Jan 4 19:53:30 2007 +0100 remove the broken OAKNET driver The OAKNET driver: - has been marked as BROKEN for more than two years and - is still marked as BROKEN. Drivers that had been marked as BROKEN for such a long time seem to be unlikely to be revived in the forseeable future. But if anyone wants to ever revive this driver, the code is still present in the older kernel releases. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik commit f14d50be0f7616b58211602d961e200319d80a01 Author: Stephen Hemminger Date: Mon Jan 8 11:26:12 2007 -0800 chelsio: more rx speedup Cleanup receive processing some more: * do the reserve padding of skb during setup * don't pass constants to get_packet * do smart prefetch of skb * make copybreak a module parameter Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit bd1d1aec84dc17d2fb4fc9e8a28412aff58b550b Author: Stephen Hemminger Date: Mon Jan 8 11:26:30 2007 -0800 chelsio: NAPI speed improvement Speedup and cleanup the receive processing by eliminating the mmio read and a lock round trip. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 261216b8aa621f6c56042105f1347fc6a5a4bd17 Author: Divy Le Ray Date: Thu Jan 18 22:04:14 2007 -0500 Add support for the latest 1G/10G Chelsio adapter, T3. This driver is required by the Chelsio T3 RDMA driver posted by Steve Wise. Signed-off-by: Divy Le Ray Signed-off-by: Jeff Garzik commit 223710f251320ae94a9fd615b3347c752c9af29a Author: Adrian Bunk Date: Thu Jan 11 14:48:59 2007 +0100 make hdlc_setup() static again hdlc_setup was exported, but this export was never used. If a driver using it actually shows up it can still be exported again. Signed-off-by: Adrian Bunk Acked-by: Krzysztof Halasa Signed-off-by: Jeff Garzik commit 5010d46975f8ffb3886214ba6862bad02ad991bb Author: Ayaz Abdulla Date: Tue Jan 9 13:30:13 2007 -0500 forcedeth: rx skb recycle This patch removes the code that recycled the skb on error. This will help in reducing the branches in the main data paths. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 8d08857b3ba8831d88910b221cb23d3563d95415 Author: Ayaz Abdulla Date: Tue Jan 9 13:30:10 2007 -0500 forcedeth: tx locking This patch reduces the amount of code within the lock to only the critical sections. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit ffaa0476174e05ff70771cc462fa75ccff0fdd97 Author: Ayaz Abdulla Date: Tue Jan 9 13:30:07 2007 -0500 forcedeth: ring access This patch modifys ring access by using pointers. This avoids computing the current index and avoids accessing the base address of the rings. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit de96d74156d7b9c937bb4a2ff668eb952e0d48eb Author: Ayaz Abdulla Date: Tue Jan 9 13:30:02 2007 -0500 forcedeth: dma access This patch allows the hardware to fetch the tx and rx ring descriptors with 64 bytes per access instead of 32 bytes. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik commit 920d9a58b26998e4fcc883f5a7af2b9770c85ed2 Author: Adrian Bunk Date: Tue Dec 19 13:08:48 2006 -0800 remove the broken SKMC driver The SKMC driver has: - already been marked as BROKEN in 2.6.0 three years ago and - is still marked as BROKEN. Drivers that had been marked as BROKEN for such a long time seem to be unlikely to be revived in the forseeable future. But if anyone wants to ever revive this driver, the code is still present in the older kernel releases. Signed-off-by: Adrian Bunk Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit 43ace47fed244f9ea91eb60ca4f25eb3c8e3b777 Author: Cesar Eduardo Barros Date: Tue Dec 19 13:08:47 2006 -0800 driver for Silan SC92031 netdev This is a driver for the Silan SC92031/Rsltek 8139D NIC chip. This chip is found on at least one counterfeit Encore ENL832-TX-RENT NIC [1], which came with a mini-CD with the 2.4 driver. A slightly older version of the driver was found at [2]. The main difference between them is that the newer one has a small bugfix in the RX path, a lot of gratuitous renaming of functions, all the printable strings changed to show as a "Rsltek 8139D" [sic], and a PCI ID of 8139 instead of 2031. The driver on this patch is a rewrite of the vendor drivers (based mostly on the older one). Changes from the previous patch sent to netdev: - Use MMIO instead of PIO - Changed TX bounce buffers allocation - Use skb_copy_and_csum_dev - Several small bug fixes - Tested for more than just a few minutes each time [1] See http://www.encore-usa.com/faq.php under ENL832-TX-RENT for more information [2] Look for SL_LINUX.ZIP (which is really a .tar.gz) at http://broadbandforum.in/dataone_Intex_LAN_cardlinux-t4207-s15.html [3] To compile on 2.6.17, simply add back the last argument to the interrupt handler in two places, and copy the boolean declarations from 2.6.19 [akpm@osdl.org: build fixes] Signed-off-by: Cesar Eduardo Barros Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik commit e41fb7e3f1cd5c7b15835c461e654aa5b118b892 Author: Stephen Hemminger Date: Wed Dec 20 13:06:36 2006 -0800 sky2: better power state management Improve power management and error handling by using pci_set_power_state(), instead of driver doing PCI PM register changes in the driver. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik commit 864ebf5a302c4f4f085cc1ee3a2f9cbc74b2e7d6 Author: Arjan van de Ven Date: Tue Dec 12 14:06:23 2006 +0100 remove NETIF_F_TSO ifdefery Remove the NETIF_F_TSO #ifdef-ery in drivers/net; this was for old-old-2.4 compat (even current 2.4 has NETIF_F_TSO) but it's time to get rid of it by now. Signed-off-by: Arjan van de Ven Signed-off-by: Jeff Garzik commit 6fb58c1a7c54ee7a0d7749d71ce6161d4e1c46e9 Author: Linas Vepstas Date: Wed Dec 13 15:23:56 2006 -0600 Spidernet RX Debugging printout Add some debugging and error printing. The show_rx_chain() prints out the status of the rx chain, which shows that the status of the descriptors gets messed up after the second & subsequent RX ramfulls. Print out contents of bad packets if error occurs. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 7f9a83d20620de2d2a03cb8b97b6e3964f146ee2 Author: Linas Vepstas Date: Wed Dec 13 15:23:01 2006 -0600 Spidernet Avoid possible RX chain corruption Delete possible source of chain corruption; the hardware already knows the location of the tail, and writing it again is likely to mess it up. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit fb4cc3d766d30cc930802f07313e62175d2cf650 Author: Linas Vepstas Date: Wed Dec 13 15:22:04 2006 -0600 Spidernet Memory barrier Add memory barrier to make sure that the rest of the RX descriptor state is flushed to memory before we tell the hardware that its ready to go. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit c213916885ec16bf5fd6a2a5022d448425bfab50 Author: Linas Vepstas Date: Wed Dec 13 15:20:59 2006 -0600 Spidernet RX Chain tail Tell the hardware the location of the rx ring tail. More punctuation cleanup. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit a38acdd325823ef4a30f4425990edd2c8125de1e Author: Linas Vepstas Date: Wed Dec 13 15:19:54 2006 -0600 Spidernet Remove unused variable Remove unused variable; this makes code easier to read. Tweak commentary. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 46adec79a47c6e94ea1d81f0e87ecd0d9c9d1f6a Author: Linas Vepstas Date: Wed Dec 13 15:18:52 2006 -0600 Spidernet RX Refill The invocation of the rx ring refill routine is haphazard, it can be called from a central location. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 6aa64833c3d16bf1aed52e7833021da4fa4ae1bc Author: Linas Vepstas Date: Wed Dec 13 15:17:39 2006 -0600 Spidernet Cleanup return codes Simplify the somewhat convoluted use of return codes in the rx buffer handling. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 21577516a78f5aad4ab540507f9724c599e9304e Author: Linas Vepstas Date: Wed Dec 13 15:16:18 2006 -0600 Spidernet another skb mem leak Another skb leak in an error branch. Fix this by adding call to dev_kfree_skb_irq() after moving to a more appropriate spot. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 5b5c9879babf5e449fda42b949443d96266c9078 Author: Linas Vepstas Date: Wed Dec 13 15:15:15 2006 -0600 Spidernet RX skb mem leak One of the unlikely error branches has an skb memory leak. Fix this by handling the error conditions consistently. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 2918e808e1f2b8a9c300eeea3883e15d83d32239 Author: Linas Vepstas Date: Wed Dec 13 15:12:26 2006 -0600 Spidernet cleanup un-needed API There is no need to pass a flag into spider_net_decode_one_descr() so remove this, and perform some othre minor cleanup. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit c81e5ff514d45c45b676f32c4e6c5edd138b0f54 Author: Linas Vepstas Date: Wed Dec 13 15:10:06 2006 -0600 Spidernet remove rxramfull tasklet Get rid of the rxramfull tasklet, and let the NAPI poll routine deal with this situation. (The rxramfull interrupt is simply stating that the h/w has run out of room for incoming packets). Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit f933191df94afbc73f35c3a252d7b12f3512e6eb Author: Linas Vepstas Date: Wed Dec 13 15:08:25 2006 -0600 Spidernet add net_ratelimit to suppress long output This patch adds net_ratelimit to many of the printks in order to limit extraneous warning messages (created in response to Bug 28554). This patch supercedes all previous ratelimit patches. This has been tested, please apply. From: James K Lewis Signed-off-by: James K Lewis Signed-off-by: Linas Vepstas Signed-off-by: Jeff Garzik commit 3ce646555f15e09b5382a494c6b7659520bffefa Author: Linas Vepstas Date: Wed Dec 13 15:06:59 2006 -0600 Spidernet DMA coalescing The current driver code performs 512 DMA mappings of a bunch of 32-byte ring descriptor structures. This is silly, as they are all in contiguous memory. This patch changes the code to dma_map_coherent() each rx/tx ring as a whole. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann Signed-off-by: Jeff Garzik commit 5551368c4296e1e922a7a36fa01d1e609a381279 Author: Francois Romieu Date: Tue Dec 12 00:13:48 2006 +0100 chelsio: tabulate the update of the statistic counters Let's try to avoid some code duplication. - cxgb2 The data are contiguous. Use plain memcpy. - ixf1010/pm3393/vsc7326 The cast of &mac->stats to (u64 *) is not wonderful but it is not clear if it is worth to add an ad-hoc union under the struct cmac_statistics. vsc7326_reg.h suggests that more statistics could be available. Signed-off-by: Francois Romieu commit 57967e328977f85c66301ddf7153c5e64f60f646 Author: Francois Romieu Date: Tue Dec 5 23:57:41 2006 +0100 chelsio: misc cleanups in sge - duplicated code in sge::free_cmdQ_buffers ; - NET_IP_ALIGN is already defined in (included) ; - pci_alloc_consistent() returns void * ; - pci_alloc_consistent() returns a zeroed chunk of memory ; - early return in restart_tx_queues. Signed-off-by: Francois Romieu commit c3f310c0567c0e8537c9b9000a05f48dfcc9c1e9 Author: Francois Romieu Date: Tue Dec 5 23:19:06 2006 +0100 chelsio: useless test in cxgb2::remove_one pci_get_drvadata() is necessarily distinct from NULL if cxgb2::init_one succeeded. cxgb2::remove_one is solely issued through the PCI device callback. Signed-off-by: Francois Romieu commit 59d058d9408b76e4a2f92286e1d3a947d4d4eb4f Author: Francois Romieu Date: Mon Dec 11 23:49:13 2006 +0100 chelsio: useless curly braces Signed-off-by: Francois Romieu commit f4e7aa0ab306c99f4f9f139573798be4bbe62d05 Author: Francois Romieu Date: Mon Dec 11 23:47:00 2006 +0100 chelsio: spaces, tabs and friends Signed-off-by: Francois Romieu commit 56c4a5a46f8e0264a96485d4f3b9a301f70e6d7e Author: Francois Romieu Date: Mon Dec 11 23:41:36 2006 +0100 chelsio: the return statement is not a function Signed-off-by: Francois Romieu commit dd1eb1308ca52e6ddb121cba66ffd6e426ac4ce8 Author: Francois Romieu Date: Tue Dec 5 22:38:00 2006 +0100 chelsio: move return, break and continue statements on their own line Signed-off-by: Francois Romieu commit d697fe74ac0455e41fc323887390ec0f8fb4406d Author: Zhu Yi Date: Thu Jan 11 17:32:54 2007 +0800 [PATCH] ipw2200: add iwconfig rts/frag auto support This patch add ipw2200 support for iwconfig rts/frag auto. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville commit da69fa9164356c641fb5b3dd234448eebe60be04 Author: Daniel Drake Date: Thu Jan 4 03:33:54 2007 +0000 [PATCH] zd1211rw: Add ID for ZyXEL ZyAIR G-220 v2 Tested by Marijn Schouten zd1211b chip 0586:340f v4810 high 00-13-49 AL2230_RF pa0 g--- FCC ID: I88G220V2 Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit c17b45e4cd79939d0dbe154cb578f85ce09f5c04 Author: John W. Linville Date: Tue Jan 2 21:22:05 2007 -0500 [PATCH] softmac: avoid assert in ieee80211softmac_wx_get_rate Unconfigured bcm43xx device can hit an assert() during wx_get_rate queries. This is because bcm43xx calls ieee80211softmac_start late (i.e. during open instead of probe). bcm43xx_net_open -> bcm43xx_init_board -> bcm43xx_select_wireless_core -> ieee80211softmac_start Fix is to check that device is running before completing ieee80211softmac_wx_get_rate. Signed-off-by: John W. Linville commit 274b0b2c3e600123829604ab3b9b7d3e520792b4 Author: Kai Engert Date: Tue Dec 12 21:09:41 2006 +0100 [PATCH] prism54: add ethtool -i interface Add support for "ethtool -i" to prism54 driver. ethtool -i queries the specified device for associated driver information. This helps tools like Fedora's system-config-network to provide GUI management of network devices. I learned how to write this patch by reading the ipw2100 driver code. Signed-off-by: Kai Engert Signed-off-by: John W. Linville commit 601e3cdfba73ce3efbbac42b41762ae2fcb0b8a8 Author: Larry Finger Date: Sat Dec 30 23:30:32 2006 -0600 [PATCH] bcm43xx: Interrogate hardware-enable switch and update LEDs The current bcm43xx driver ignores any wireless-enable switches on mini-PCI and mini-PCI-E cards. This patch implements a new routine to interrogate the radio hardware enabled bit in the interface, logs the initial state and any changes in the switch (if debugging enabled), activates the LED to show the state, and changes the periodic work handler to provide 1 second response to switch changes and to account for changes in the periodic work specs. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit d58b6b011772ff5ea428157493b5c917c39b481f Author: Daniel Drake Date: Sat Dec 30 22:38:23 2006 +0000 [PATCH] zd1211rw: Add ID for Linksys WUSBF54G Tested by Henrik Hjelte zd1211b chip 13b1:0024 v4802 high 00-14-bf AL2230_RF pa0 ---- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit ccd04e8492144a72c6b80e7588a1e5620c39d0b2 Author: Michael Buesch Date: Fri Dec 15 21:32:44 2006 +0100 [PATCH] Update Prism54 MAINTAINERS entry prism54-private@prism54.org bounces with SMTP error from remote mailer after RCPT TO:: host mx1.tuxfamily.net [212.85.158.8]: 550 unknown user developers@islsm.org seems to be the new mailing list. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4c14b51c7716aa5bf11647308f5679e25a768e75 Author: Daniel Drake Date: Tue Dec 12 01:26:11 2006 +0000 [PATCH] zd1211rw: Remove addressing abstraction Instead of passing our own custom 32-bit addresses around and translating them, this patch makes all our register address constants absolute and removes the translation. There are two ugly parts: - fw_reg_addr() is needed to compute addresses of firmware registers, as this is dynamic based upon firmware - inc_addr() needs a small hack to handle byte vs word addressing However, both of those are only small, and we don't use fw_regs a whole lot anyway. The bonuses here include simplicity and improved driver readability. Also, the fact that registers are now referenced by 16-bit absolute addresses (as opposed to 32-bit pseudo addresses) means that over 2kb compiled code size has been shaved off. Includes some touchups and sparse fixes from Ulrich Kunitz. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 4c85640796f96ce1f2a7243a88125b5c86d1337b Author: Daniel Drake Date: Tue Dec 12 01:25:52 2006 +0000 [PATCH] zd1211rw: Consistency for address space constants The zd1211rw address space has confused me once too many times. This patch introduces the following naming notation: Memory space is split into segments (cr, fw, eeprom) and segments may contain components (e.g. boot code inside eeprom). These names are arbitrary and only for the description below: x_START: Absolute address of segment start (previously these were named such as CR_BASE_OFFSET, but they weren't really offsets unless you were considering them as an offset to 0) x_LEN: Segment length x_y_LEN: Length of component y of segment x x_y_OFFSET: Relative address of component y into segment x. The absolute address for this component is (x_START + x_y_OFFSET) I also renamed EEPROM registers to EEPROM data. These 'registers' can't be written to using standard I/O and really represent predefined data from the vendor. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 9ff88f6ba8212be972276a314e9153913e2068e5 Author: Daniel Drake Date: Tue Dec 12 01:25:37 2006 +0000 [PATCH] zd1211rw: 2 new ZD1211B device ID's Philips SNU5600, tested by unibrow zd1211b chip 0471:1236 v4810 high 00-12-bf AL2230_RF pa0 g-- SMC Ez Connect 802.11g (SMCWUSB-G), tested by Victorino Sanz Prat zd1211b chip 083a:4505 v4810 full 00-13-f7 AL2230_RF pa0 g--N Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 4683efa0c4987f8c874064cc84c2c8529b5cc352 Author: Daniel Drake Date: Tue Dec 12 01:25:13 2006 +0000 [PATCH] zd1211rw: Generic HMAC initialization Many of the registers written during ZD1211 HMAC initialization are duplicated exactly for ZD1211B. Move the identical ones into a generic part, and write the hardware-specific ones separately. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 0f227611b045139bac96b88ee7a7efbc4f07e81b Author: Jay Cliburn Date: Tue Jan 23 00:55:09 2007 -0500 Add Attansic L1 ethernet driver. This driver is a modified version of the Attansic reference driver for the L1 ethernet adapter. Attansic has granted permission for its inclusion in the mainline kernel. Signed-off-by: Jeff Garzik commit 244b55a141d47f4e05c20795c9d49cbf0d59e5ba Author: Auke Kok Date: Thu Jan 18 09:25:33 2007 -0800 e1000: update version to 7.3.20-k2 Signed-off-by: Auke Kok commit 2ba8af5ab03f4a63df3665eb638ede645a599607 Author: Jesse Brandeburg Date: Thu Jan 18 09:25:31 2007 -0800 e1000: tune our dynamic itr transmit packet accounting The driver was still mis-calculating the number of bytes sent during transmit, now the driver computes what appears to be exactly 100% correct byte counts (not including CRC) when figuring out how many bytes and frames were sent during the current transmit packet. commit 77c54723f08b62170b5f18c13e00ae445892296f Author: Bruce Allan Date: Thu Jan 18 09:25:28 2007 -0800 e1000: clear ip csum info from context descriptor Since the driver sets the IP checksum insertion bit (IXSM in Status field) in transmit context descriptors, it should clear the IP checksum bits of any garbage so as not to confuse the hardware. Signed-off-by: Bruce Allan Signed-off-by: Auke Kok commit f9e6f156c17bae743d6a1b6114edb0efca32c4ba Author: Auke Kok Date: Thu Jan 18 09:25:26 2007 -0800 e1000: display flow control of link status at link up Print RX/TX flow control setting at link up time to display the actual link FC properties instead of the advertised values. Signed-off-by: Auke Kok commit 151d11fb5bdce5e27d59cf5616a95490d0c084e0 Author: Jesse Brandeburg Date: Thu Jan 18 09:25:23 2007 -0800 e1000: fix NAPI performance on 4-port adapters This fix attempts to solve a customer (IBM) reported issue with NAPI enabled e1000 having bad performance when transmitting simultaneously on four ports. The issue comes down to an interaction between NAPI, hardware interrupt balancing, and the driver rescheduling poll on the same processor. Try to fix by allowing the driver to re-enable interrupts sooner instead of polling one more time, when there was recently all the work completed in cleanup. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok commit f39668f782de716823978534ea0ad1791dd17f66 Author: Jesse Brandeburg Date: Thu Jan 18 09:25:21 2007 -0800 e1000: Fix MSI only interrupt handler routine Unfortunately the read-free MSI interrupt handler needs to flush write the icr register and thus we can't be read-free. Our MSI irq routine thus becomes a lot more simpler since we don't need to track link state anymore. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok commit 9455a51b63f31f7747b64bdefe64d13385cf8fb0 Author: Auke Kok Date: Thu Jan 18 09:25:18 2007 -0800 e1000: clean up debug output defines Remove unused MSGOUT macro and add "\n" to function debug output. Signed-off-by: Auke Kok commit 951c9a69efe280807e6a8dbb1c46759e357be382 Author: Jesse Brandeburg Date: Thu Jan 18 09:25:15 2007 -0800 e1000: simplify case handling gigabit at half duplex Remvoe duplicate code handling erraneous user supplied wrong case of gigabit speed with half duplex. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok Signed-off-by: Andrew Morton --- MAINTAINERS | 2 drivers/net/Kconfig | 67 drivers/net/Makefile | 6 drivers/net/Space.c | 4 drivers/net/atl1/Makefile | 2 drivers/net/atl1/atl1.h | 288 + drivers/net/atl1/atl1_ethtool.c | 436 ++ drivers/net/atl1/atl1_hw.c | 728 +++ drivers/net/atl1/atl1_hw.h | 965 ++++ drivers/net/atl1/atl1_main.c | 2490 +++++++++++ drivers/net/atl1/atl1_param.c | 223 + drivers/net/bmac.c | 20 drivers/net/bnx2.c | 12 drivers/net/bonding/bond_main.c | 23 drivers/net/bonding/bond_sysfs.c | 15 drivers/net/bonding/bonding.h | 4 drivers/net/chelsio/common.h | 2 drivers/net/chelsio/cpl5_cmd.h | 18 drivers/net/chelsio/cxgb2.c | 151 drivers/net/chelsio/elmer0.h | 40 drivers/net/chelsio/espi.c | 44 drivers/net/chelsio/fpga_defs.h | 6 drivers/net/chelsio/gmac.h | 11 drivers/net/chelsio/ixf1010.c | 100 drivers/net/chelsio/mv88e1xxx.c | 27 drivers/net/chelsio/my3126.c | 16 drivers/net/chelsio/pm3393.c | 91 drivers/net/chelsio/sge.c | 328 - drivers/net/chelsio/subr.c | 89 drivers/net/chelsio/tp.c | 60 drivers/net/chelsio/vsc7326.c | 135 drivers/net/chelsio/vsc7326_reg.h | 143 drivers/net/chelsio/vsc8244.c | 41 drivers/net/cxgb3/Makefile | 8 drivers/net/cxgb3/adapter.h | 255 + drivers/net/cxgb3/ael1002.c | 231 + drivers/net/cxgb3/common.h | 709 +++ drivers/net/cxgb3/cxgb3_ctl_defs.h | 142 drivers/net/cxgb3/cxgb3_defs.h | 99 drivers/net/cxgb3/cxgb3_ioctl.h | 165 drivers/net/cxgb3/cxgb3_main.c | 2474 +++++++++++ drivers/net/cxgb3/cxgb3_offload.c | 1222 +++++ drivers/net/cxgb3/cxgb3_offload.h | 193 drivers/net/cxgb3/firmware_exports.h | 144 drivers/net/cxgb3/l2t.c | 450 ++ drivers/net/cxgb3/l2t.h | 143 drivers/net/cxgb3/mc5.c | 453 ++ drivers/net/cxgb3/regs.h | 2195 ++++++++++ drivers/net/cxgb3/sge.c | 2702 ++++++++++++ drivers/net/cxgb3/sge_defs.h | 251 + drivers/net/cxgb3/t3_cpl.h | 1426 ++++++ drivers/net/cxgb3/t3_hw.c | 3354 ++++++++++++++++ drivers/net/cxgb3/t3cdev.h | 72 drivers/net/cxgb3/version.h | 24 drivers/net/cxgb3/vsc8211.c | 208 drivers/net/cxgb3/xgmac.c | 389 + drivers/net/e100.c | 72 drivers/net/e1000/e1000.h | 7 drivers/net/e1000/e1000_ethtool.c | 6 drivers/net/e1000/e1000_main.c | 128 drivers/net/e1000/e1000_osdep.h | 4 drivers/net/e1000/e1000_param.c | 15 drivers/net/forcedeth.c | 1368 ++++-- drivers/net/ixgb/ixgb.h | 2 drivers/net/ixgb/ixgb_ethtool.c | 6 drivers/net/ixgb/ixgb_main.c | 4 drivers/net/mace.c | 16 drivers/net/macmace.c | 18 drivers/net/macsonic.c | 6 drivers/net/myri10ge/myri10ge.c | 10 drivers/net/oaknet.c | 666 --- drivers/net/qla3xxx.c | 363 + drivers/net/qla3xxx.h | 88 drivers/net/s2io.c | 8 drivers/net/sc92031.c | 1620 +++++++ drivers/net/sk_mca.c | 1216 ----- drivers/net/sk_mca.h | 170 drivers/net/skfp/can.c | 83 drivers/net/skfp/drvfbi.c | 24 drivers/net/skfp/fplustm.c | 4 drivers/net/skfp/smt.c | 10 drivers/net/sky2.c | 140 drivers/net/sky2.h | 1 drivers/net/spider_net.c | 311 - drivers/net/spider_net.h | 20 drivers/net/spider_net_ethtool.c | 4 drivers/net/tg3.c | 32 drivers/net/wan/hdlc.c | 3 drivers/net/wireless/bcm43xx/bcm43xx.h | 7 drivers/net/wireless/bcm43xx/bcm43xx_leds.c | 11 drivers/net/wireless/bcm43xx/bcm43xx_main.c | 36 drivers/net/wireless/bcm43xx/bcm43xx_radio.c | 2 drivers/net/wireless/bcm43xx/bcm43xx_radio.h | 16 drivers/net/wireless/ipw2200.c | 4 drivers/net/wireless/prism54/islpci_dev.c | 13 drivers/net/wireless/prism54/islpci_dev.h | 4 drivers/net/wireless/prism54/islpci_hotplug.c | 3 drivers/net/wireless/zd1211rw/zd_chip.c | 126 drivers/net/wireless/zd1211rw/zd_chip.h | 154 drivers/net/wireless/zd1211rw/zd_def.h | 2 drivers/net/wireless/zd1211rw/zd_ieee80211.h | 1 drivers/net/wireless/zd1211rw/zd_rf.h | 2 drivers/net/wireless/zd1211rw/zd_types.h | 71 drivers/net/wireless/zd1211rw/zd_usb.c | 128 drivers/net/wireless/zd1211rw/zd_usb.h | 6 net/ieee80211/softmac/ieee80211softmac_wx.c | 6 106 files changed, 26642 insertions(+), 4271 deletions(-) diff -puN MAINTAINERS~git-netdev-all MAINTAINERS --- a/MAINTAINERS~git-netdev-all +++ a/MAINTAINERS @@ -2658,7 +2658,7 @@ S: Supported PRISM54 WIRELESS DRIVER P: Prism54 Development Team -M: prism54-private@prism54.org +M: developers@islsm.org L: netdev@vger.kernel.org W: http://prism54.org S: Maintained diff -puN drivers/net/Kconfig~git-netdev-all drivers/net/Kconfig --- a/drivers/net/Kconfig~git-netdev-all +++ a/drivers/net/Kconfig @@ -235,16 +235,6 @@ config BMAC To compile this driver as a module, choose M here: the module will be called bmac. -config OAKNET - tristate "National DP83902AV (Oak ethernet) support" - depends on NET_ETHERNET && PPC && BROKEN - select CRC32 - help - Say Y if your machine has this type of Ethernet network card. - - To compile this driver as a module, choose M here: the module - will be called oaknet. - config ARIADNE tristate "Ariadne support" depends on NET_ETHERNET && ZORRO @@ -1155,21 +1145,6 @@ config SEEQ8005 . The module will be called seeq8005. -config SKMC - tristate "SKnet MCA support" - depends on NET_ETHERNET && MCA && BROKEN - ---help--- - These are Micro Channel Ethernet adapters. You need to say Y to "MCA - support" in order to use this driver. Supported cards are the SKnet - Junior MC2 and the SKnet MC2(+). The driver automatically - distinguishes between the two cards. Note that using multiple boards - of different type hasn't been tested with this driver. Say Y if you - have one of these Ethernet adapters. - - To compile this driver as a module, choose M here and read - . The module - will be called sk_mca. - config NE2_MCA tristate "NE/2 (ne2000 MCA version) support" depends on NET_ETHERNET && MCA_LEGACY @@ -1788,6 +1763,18 @@ config LAN_SAA9730 workstations. See . +config SC92031 + tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)" + depends on NET_PCI && PCI && EXPERIMENTAL + select CRC32 + ---help--- + This is a driver for the Fast Ethernet PCI network cards based on + the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you + have one of these, say Y here. + + To compile this driver as a module, choose M here: the module + will be called sc92031. This is recommended. + config NET_POCKET bool "Pocket and portable adapters" depends on NET_ETHERNET && PARPORT @@ -2348,6 +2335,17 @@ config QLA3XXX To compile this driver as a module, choose M here: the module will be called qla3xxx. +config ATL1 + tristate "Attansic L1 Gigabit Ethernet support (EXPERIMENTAL)" + depends on NET_PCI && PCI && EXPERIMENTAL + select CRC32 + select MII + help + This driver supports the Attansic L1 gigabit ethernet adapter. + + To compile this driver as a module, choose M here. The module + will be called atl1. + endmenu # @@ -2392,6 +2390,24 @@ config CHELSIO_T1_NAPI NAPI is a driver API designed to reduce CPU and interrupt load when the driver is receiving lots of packets from the card. +config CHELSIO_T3 + tristate "Chelsio Communications T3 10Gb Ethernet support" + depends on PCI + help + This driver supports Chelsio T3-based gigabit and 10Gb Ethernet + adapters. + + For general information about Chelsio and our products, visit + our website at . + + For customer support, please visit our customer support page at + . + + Please send feedback to . + + To compile this driver as a module, choose M here: the module + will be called cxgb3. + config EHEA tristate "eHEA Ethernet support" depends on IBMEBUS @@ -2561,6 +2577,7 @@ config DEFXX_MMIO config SKFP tristate "SysKonnect FDDI PCI support" depends on FDDI && PCI + select BITREVERSE ---help--- Say Y here if you have a SysKonnect FDDI PCI adapter. The following adapters are supported by this driver: diff -puN drivers/net/Makefile~git-netdev-all drivers/net/Makefile --- a/drivers/net/Makefile~git-netdev-all +++ a/drivers/net/Makefile @@ -6,8 +6,10 @@ obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IXGB) += ixgb/ obj-$(CONFIG_CHELSIO_T1) += chelsio/ +obj-$(CONFIG_CHELSIO_T3) += cxgb3/ obj-$(CONFIG_EHEA) += ehea/ obj-$(CONFIG_BONDING) += bonding/ +obj-$(CONFIG_ATL1) += atl1/ obj-$(CONFIG_GIANFAR) += gianfar_driver.o gianfar_driver-objs := gianfar.o \ @@ -36,8 +38,6 @@ obj-$(CONFIG_CASSINI) += cassini.o obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o -obj-$(CONFIG_OAKNET) += oaknet.o 8390.o - obj-$(CONFIG_DGRS) += dgrs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o @@ -137,7 +137,6 @@ obj-$(CONFIG_AT1700) += at1700.o obj-$(CONFIG_EL1) += 3c501.o obj-$(CONFIG_EL16) += 3c507.o obj-$(CONFIG_ELMC) += 3c523.o -obj-$(CONFIG_SKMC) += sk_mca.o obj-$(CONFIG_IBMLANA) += ibmlana.o obj-$(CONFIG_ELMC_II) += 3c527.o obj-$(CONFIG_EL3) += 3c509.o @@ -160,6 +159,7 @@ obj-$(CONFIG_APRICOT) += 82596.o obj-$(CONFIG_LASI_82596) += lasi_82596.o obj-$(CONFIG_MVME16x_NET) += 82596.o obj-$(CONFIG_BVME6000_NET) += 82596.o +obj-$(CONFIG_SC92031) += sc92031.o # This is also a 82596 and should probably be merged obj-$(CONFIG_LP486E) += lp486e.o diff -puN drivers/net/Space.c~git-netdev-all drivers/net/Space.c --- a/drivers/net/Space.c~git-netdev-all +++ a/drivers/net/Space.c @@ -59,7 +59,6 @@ extern struct net_device *wavelan_probe( extern struct net_device *arlan_probe(int unit); extern struct net_device *el16_probe(int unit); extern struct net_device *elmc_probe(int unit); -extern struct net_device *skmca_probe(int unit); extern struct net_device *elplus_probe(int unit); extern struct net_device *ac3200_probe(int unit); extern struct net_device *es_probe(int unit); @@ -153,9 +152,6 @@ static struct devprobe2 mca_probes[] __i #ifdef CONFIG_ELMC_II /* 3c527 */ {mc32_probe, 0}, #endif -#ifdef CONFIG_SKMC /* SKnet Microchannel */ - {skmca_probe, 0}, -#endif {NULL, 0}, }; diff -puN /dev/null drivers/net/atl1/Makefile --- /dev/null +++ a/drivers/net/atl1/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ATL1) += atl1.o +atl1-y += atl1_main.o atl1_hw.o atl1_ethtool.o atl1_param.o diff -puN /dev/null drivers/net/atl1/atl1.h --- /dev/null +++ a/drivers/net/atl1/atl1.h @@ -0,0 +1,288 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + **/ + +#ifndef _ATL1_H_ +#define _ATL1_H_ + +#include +#include +#include + +#include "atl1_hw.h" + +/* function prototypes needed by multiple files */ +s32 atl1_up(struct atl1_adapter *adapter); +void atl1_down(struct atl1_adapter *adapter); +int atl1_reset(struct atl1_adapter *adapter); +s32 atl1_setup_ring_resources(struct atl1_adapter *adapter); +void atl1_free_ring_resources(struct atl1_adapter *adapter); + +struct atl1_adapter; + +#define ATL1_MAX_INTR 3 + +#define ATL1_DEFAULT_TPD 256 +#define ATL1_MAX_TPD 1023 +#define ATL1_MIN_TPD 64 +#define ATL1_DEFAULT_RFD 512 +#define ATL1_MIN_RFD 128 +#define ATL1_MAX_RFD 2047 + +#define ATL1_GET_DESC(R, i, type) (&(((type *)((R)->desc))[i])) +#define ATL1_RFD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_free_desc) +#define ATL1_TPD_DESC(R, i) ATL1_GET_DESC(R, i, struct tx_packet_desc) +#define ATL1_RRD_DESC(R, i) ATL1_GET_DESC(R, i, struct rx_return_desc) + +/** + * Some workarounds require millisecond delays and are run during interrupt + * context. Most notably, when establishing link, the phy may need tweaking + * but cannot process phy register reads/writes faster than millisecond + * intervals...and we establish link due to a "link status change" interrupt. + **/ + +/** + * wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer + **/ +struct atl1_buffer { + struct sk_buff *skb; + u16 length; + u16 alloced; + dma_addr_t dma; +}; + +#define MAX_TX_BUF_LEN 0x3000 /* 12KB */ + +struct atl1_tpd_ring { + void *desc; /* pointer to the descriptor ring memory */ + dma_addr_t dma; /* physical adress of the descriptor ring */ + u16 size; /* length of descriptor ring in bytes */ + u16 count; /* number of descriptors in the ring */ + + u16 hw_idx; /* hardware index */ + atomic_t next_to_clean; + atomic_t next_to_use; + struct atl1_buffer *buffer_info; +}; + +struct atl1_rfd_ring { + void *desc; + dma_addr_t dma; + u16 size; + u16 count; + atomic_t next_to_use; + u16 next_to_clean; + struct atl1_buffer *buffer_info; +}; + +struct atl1_rrd_ring { + void *desc; + dma_addr_t dma; + unsigned int size; + u16 count; + u16 next_to_use; + atomic_t next_to_clean; +}; + +struct atl1_ring_header { + /* pointer to the descriptor ring memory */ + void *desc; + /* physical adress of the descriptor ring */ + dma_addr_t dma; + /* length of descriptor ring in bytes */ + unsigned int size; +}; + +struct atl1_cmb { + struct coals_msg_block *cmb; + dma_addr_t dma; +}; + +struct atl1_smb { + struct stats_msg_block *smb; + dma_addr_t dma; +}; + +/* Statistics counters */ +struct atl1_sft_stats { + u64 rx_packets; + u64 tx_packets; + u64 rx_bytes; + u64 tx_bytes; + u64 multicast; + u64 collisions; + u64 rx_errors; + u64 rx_length_errors; + u64 rx_crc_errors; + u64 rx_frame_errors; + u64 rx_fifo_errors; + u64 rx_missed_errors; + u64 tx_errors; + u64 tx_fifo_errors; + u64 tx_aborted_errors; + u64 tx_window_errors; + u64 tx_carrier_errors; + + u64 tx_pause; /* The number of Pause packet transmitted. */ + u64 excecol; /* The number of transmit packets aborted due to excessive collisions. */ + u64 deffer; /* The number of packets transmitted that is deferred. */ + u64 scc; /* The number of packets subsequently transmitted successfully with a single prior collision. */ + u64 mcc; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ + u64 latecol; /* The number of packets transmitted with late collisions. */ + u64 tx_underun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ + u64 tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU, regardless if it is truncated by Selene or not. + * (The name is not really reflects the meaning in this case here.) */ + u64 rx_pause; /* The number of Pause packet received. */ + u64 rx_rrd_ov; + u64 rx_trunc; +}; + +/* board specific private data structure */ +#define ATL1_REGS_LEN 8 + +/* Structure containing variables used by the shared code */ +struct atl1_hw { + u8 __iomem *hw_addr; + struct atl1_adapter *back; + + enum atl1_dma_order dma_ord; + enum atl1_dma_rcb rcb_value; + enum atl1_dma_req_block dmar_block; + enum atl1_dma_req_block dmaw_block; + + u8 preamble_len; + u8 max_retry; /* Retransmission maximum , afterwards the packet will be discarded. */ + u8 jam_ipg; /* IPG to start JAM for collision based flow control in half-duplex mode. In unit of 8-bit time. */ + u8 ipgt; /* Desired back to back inter-packet gap. The default is 96-bit time. */ + u8 min_ifg; /* Minimum number of IFG to enforce in between RX frames. Frame gap below such IFP is dropped. */ + u8 ipgr1; /* 64bit Carrier-Sense window */ + u8 ipgr2; /* 96-bit IPG window */ + + u8 tpd_burst; /* Number of TPD to prefetch in a cache-aligned burst. Each TPD is 16-byte in length. */ + u8 rfd_burst; /* Number of RFD to prefetch in a cache-aligned burst. Each RFD is 12-byte in length. */ + u8 rfd_fetch_gap; + u8 rrd_burst; /* Threshold of number of RRDs can be retired in a burst. Each RRD is 16-byte in length. */ + u8 tpd_fetch_th; + u8 tpd_fetch_gap; + u16 tx_jumbo_task_th; + u16 txf_burst; /* Number of data byte to read in a cache-aligned burst. Each SRAM entry is 8-byte in length. */ + u16 rx_jumbo_th; /* Jumbo packet size for non-VLAN packet. VLAN packet should add 4-byte */ + u16 rx_jumbo_lkah; + u16 rrd_ret_timer; /* RRD retirement timer. Decrement by 1 after every 512ns passes. */ + u16 lcol; /* Collision Window */ + + u16 cmb_tpd; + u16 cmb_rrd; + u16 cmb_rx_timer; + u16 cmb_tx_timer; + u32 smb_timer; + u16 media_type; + u16 autoneg_advertised; + u16 pci_cmd_word; + + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg; + + u32 mem_rang; + u32 txcw; + u32 max_frame_size; + u32 min_frame_size; + u32 mc_filter_type; + u32 num_mc_addrs; + u32 collision_delta; + u32 tx_packet_delta; + u16 phy_spd_default; + + u16 dev_rev; + u8 revision_id; + + /* spi flash */ + u8 flash_vendor; + + u8 dma_fairness; + u8 mac_addr[NODE_ADDRESS_SIZE]; + u8 perm_mac_addr[NODE_ADDRESS_SIZE]; + + /* bool phy_preamble_sup; */ + bool phy_configured; +}; + +struct atl1_adapter { + /* OS defined structs */ + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; + struct atl1_sft_stats soft_stats; + + struct vlan_group *vlgrp; + u32 rx_buffer_len; + u32 wol; + u16 link_speed; + u16 link_duplex; + spinlock_t lock; + atomic_t irq_sem; + struct work_struct tx_timeout_task; + struct work_struct link_chg_task; + struct work_struct pcie_dma_to_rst_task; + struct timer_list watchdog_timer; + struct timer_list phy_config_timer; + bool phy_timer_pending; + + bool mac_disabled; + + /* All descriptor rings' memory */ + struct atl1_ring_header ring_header; + + /* TX */ + struct atl1_tpd_ring tpd_ring; + spinlock_t mb_lock; + + /* RX */ + struct atl1_rfd_ring rfd_ring; + struct atl1_rrd_ring rrd_ring; + u64 hw_csum_err; + u64 hw_csum_good; + + u32 gorcl; + u64 gorcl_old; + + /* Interrupt Moderator timer ( 2us resolution) */ + u16 imt; + /* Interrupt Clear timer (2us resolution) */ + u16 ict; + + /* MII interface info */ + struct mii_if_info mii; + + /* structs defined in atl1_hw.h */ + u32 bd_number; /* board number */ + bool pci_using_64; + struct atl1_hw hw; + struct atl1_smb smb; + struct atl1_cmb cmb; + + int enable_msi; + u32 pci_state[16]; +}; + +#endif /* _ATL1_H_ */ diff -puN /dev/null drivers/net/atl1/atl1_ethtool.c --- /dev/null +++ a/drivers/net/atl1/atl1_ethtool.c @@ -0,0 +1,436 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + **/ + +#include +#include +#include +#include +#include +#include + +#include "atl1.h" + + +extern char atl1_driver_name[]; +extern char atl1_driver_version[]; + +struct atl1_stats { + char stat_string[ETH_GSTRING_LEN]; + int sizeof_stat; + int stat_offset; +}; + +#define ATL1_STAT(m) sizeof(((struct atl1_adapter *)0)->m), \ + offsetof(struct atl1_adapter, m) + +static struct atl1_stats atl1_gstrings_stats[] = { + {"rx_packets", ATL1_STAT(soft_stats.rx_packets)}, + {"tx_packets", ATL1_STAT(soft_stats.tx_packets)}, + {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)}, + {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)}, + {"rx_errors", ATL1_STAT(soft_stats.rx_errors)}, + {"tx_errors", ATL1_STAT(soft_stats.tx_errors)}, + {"rx_dropped", ATL1_STAT(net_stats.rx_dropped)}, + {"tx_dropped", ATL1_STAT(net_stats.tx_dropped)}, + {"multicast", ATL1_STAT(soft_stats.multicast)}, + {"collisions", ATL1_STAT(soft_stats.collisions)}, + {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)}, + {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, + {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)}, + {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)}, + {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)}, + {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)}, + {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)}, + {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)}, + {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)}, + {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)}, + {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)}, + {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)}, + {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)}, + {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)}, + {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)}, + {"tx_underun", ATL1_STAT(soft_stats.tx_underun)}, + {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)}, + {"tx_pause", ATL1_STAT(soft_stats.tx_pause)}, + {"rx_pause", ATL1_STAT(soft_stats.rx_pause)}, + {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)}, + {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)} +}; + +static void atl1_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + int i; + char *p; + + for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { + p = (char *)adapter+atl1_gstrings_stats[i].stat_offset; + data[i] = (atl1_gstrings_stats[i].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + +} + +static int atl1_get_stats_count(struct net_device *netdev) +{ + return ARRAY_SIZE(atl1_gstrings_stats); +} + +static int atl1_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP); + ecmd->advertising = ADVERTISED_TP; + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + ecmd->advertising |= ADVERTISED_Autoneg; + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) { + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->advertising |= + (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + } else { + ecmd->advertising |= (ADVERTISED_1000baseT_Full); + } + } + ecmd->port = PORT_TP; + ecmd->phy_address = 0; + ecmd->transceiver = XCVR_INTERNAL; + + if (netif_carrier_ok(adapter->netdev)) { + u16 link_speed, link_duplex; + atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex); + ecmd->speed = link_speed; + if (link_duplex == FULL_DUPLEX) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; + } else { + ecmd->speed = -1; + ecmd->duplex = -1; + } + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + ecmd->autoneg = AUTONEG_ENABLE; + } else { + ecmd->autoneg = AUTONEG_DISABLE; + } + + return 0; +} + +static int atl1_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + u16 phy_data; + int ret_val = 0; + u16 old_media_type = hw->media_type; + + if (netif_running(adapter->netdev)) { + printk(KERN_DEBUG "%s: ethtool shutting down link adapter\n", + atl1_driver_name); + atl1_down(adapter); + } + + if (ecmd->autoneg == AUTONEG_ENABLE) { + hw->media_type = MEDIA_TYPE_AUTO_SENSOR; + } else { + if (ecmd->speed == SPEED_1000) { + if (ecmd->duplex != DUPLEX_FULL) { + printk(KERN_WARNING + "%s: can't force to 1000M half duplex\n", + atl1_driver_name); + ret_val = -EINVAL; + goto exit_sset; + } + hw->media_type = MEDIA_TYPE_1000M_FULL; + } else if (ecmd->speed == SPEED_100) { + if (ecmd->duplex == DUPLEX_FULL) { + hw->media_type = MEDIA_TYPE_100M_FULL; + } else { + hw->media_type = MEDIA_TYPE_100M_HALF; + } + } else { + if (ecmd->duplex == DUPLEX_FULL) { + hw->media_type = MEDIA_TYPE_10M_FULL; + } else { + hw->media_type = MEDIA_TYPE_10M_HALF; + } + } + } + switch (hw->media_type) { + case MEDIA_TYPE_AUTO_SENSOR: + ecmd->advertising = + ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_TP; + break; + case MEDIA_TYPE_1000M_FULL: + ecmd->advertising = + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_TP; + break; + default: + ecmd->advertising = 0; + break; + } + if (atl1_phy_setup_autoneg_adv(hw)) { + ret_val = -EINVAL; + printk(KERN_WARNING + "%s: invalid ethtool speed/duplex setting\n", + atl1_driver_name); + goto exit_sset; + } + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; + } else { + switch (hw->media_type) { + case MEDIA_TYPE_100M_FULL: + phy_data = + MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | + MII_CR_RESET; + break; + case MEDIA_TYPE_100M_HALF: + phy_data = MII_CR_SPEED_100 | MII_CR_RESET; + break; + case MEDIA_TYPE_10M_FULL: + phy_data = + MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; + break; + default: /* MEDIA_TYPE_10M_HALF: */ + phy_data = MII_CR_SPEED_10 | MII_CR_RESET; + break; + } + } + atl1_write_phy_reg(hw, MII_BMCR, phy_data); +exit_sset: + if (ret_val) { + hw->media_type = old_media_type; + } + if (netif_running(adapter->netdev)) { + printk(KERN_DEBUG "%s: ethtool starting link adapter\n", + atl1_driver_name); + atl1_up(adapter); + } else if (!ret_val) { + printk(KERN_DEBUG "%s: ethtool resetting link adapter\n", + atl1_driver_name); + atl1_reset(adapter); + } + return ret_val; +} + +static void atl1_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + + strncpy(drvinfo->driver, atl1_driver_name, 32); + strncpy(drvinfo->version, atl1_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->eedump_len = 48; +} + +static void atl1_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + + wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; + wol->wolopts = 0; + if (adapter->wol & ATL1_WUFC_EX) + wol->wolopts |= WAKE_UCAST; + if (adapter->wol & ATL1_WUFC_MC) + wol->wolopts |= WAKE_MCAST; + if (adapter->wol & ATL1_WUFC_BC) + wol->wolopts |= WAKE_BCAST; + if (adapter->wol & ATL1_WUFC_MAG) + wol->wolopts |= WAKE_MAGIC; + return; +} + +static int atl1_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + + if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + adapter->wol = 0; + if (wol->wolopts & WAKE_UCAST) + adapter->wol |= ATL1_WUFC_EX; + if (wol->wolopts & WAKE_MCAST) + adapter->wol |= ATL1_WUFC_MC; + if (wol->wolopts & WAKE_BCAST) + adapter->wol |= ATL1_WUFC_BC; + if (wol->wolopts & WAKE_MAGIC) + adapter->wol |= ATL1_WUFC_MAG; + return 0; +} + +static void atl1_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_tpd_ring *txdr = &adapter->tpd_ring; + struct atl1_rfd_ring *rxdr = &adapter->rfd_ring; + + ring->rx_max_pending = 2048; + ring->tx_max_pending = 1024; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = rxdr->count; + ring->tx_pending = txdr->count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static void atl1_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *epause) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + epause->autoneg = AUTONEG_ENABLE; + } else { + epause->autoneg = AUTONEG_DISABLE; + } + epause->rx_pause = 1; + epause->tx_pause = 1; +} + +static int atl1_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *epause) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + epause->autoneg = AUTONEG_ENABLE; + } else { + epause->autoneg = AUTONEG_DISABLE; + } + + epause->rx_pause = 1; + epause->tx_pause = 1; + + return 0; +} + +static u32 atl1_get_rx_csum(struct net_device *netdev) +{ + return 1; +} + +static void atl1_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) { + memcpy(p, atl1_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + } +} + +static int atl1_nway_reset(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + + if (netif_running(netdev)) { + u16 phy_data; + atl1_down(adapter); + + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; + } else { + switch (hw->media_type) { + case MEDIA_TYPE_100M_FULL: + phy_data = MII_CR_FULL_DUPLEX | + MII_CR_SPEED_100 | MII_CR_RESET; + break; + case MEDIA_TYPE_100M_HALF: + phy_data = MII_CR_SPEED_100 | MII_CR_RESET; + break; + case MEDIA_TYPE_10M_FULL: + phy_data = MII_CR_FULL_DUPLEX | + MII_CR_SPEED_10 | MII_CR_RESET; + break; + default: /* MEDIA_TYPE_10M_HALF */ + phy_data = MII_CR_SPEED_10 | MII_CR_RESET; + } + } + atl1_write_phy_reg(hw, MII_BMCR, phy_data); + atl1_up(adapter); + } + return 0; +} + +const struct ethtool_ops atl1_ethtool_ops = { + .get_settings = atl1_get_settings, + .set_settings = atl1_set_settings, + .get_drvinfo = atl1_get_drvinfo, + .get_wol = atl1_get_wol, + .set_wol = atl1_set_wol, + .get_ringparam = atl1_get_ringparam, + .get_pauseparam = atl1_get_pauseparam, + .set_pauseparam = atl1_set_pauseparam, + .get_rx_csum = atl1_get_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_hw_csum, + .get_link = ethtool_op_get_link, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_strings = atl1_get_strings, + .nway_reset = atl1_nway_reset, + .get_ethtool_stats = atl1_get_ethtool_stats, + .get_stats_count = atl1_get_stats_count, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, +}; diff -puN /dev/null drivers/net/atl1/atl1_hw.c --- /dev/null +++ a/drivers/net/atl1/atl1_hw.c @@ -0,0 +1,728 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + **/ + +#include +#include +#include +#include +#include +#include +#include +#include "atl1.h" + + +extern char atl1_driver_name[]; + +/** + * Reset the transmit and receive units; mask and clear all interrupts. + * hw - Struct containing variables accessed by shared code + * return : ATL1_SUCCESS or idle status (if error) + **/ +s32 atl1_reset_hw(struct atl1_hw * hw) +{ + u32 icr; + int i; + + /** + * Clear Interrupt mask to stop board from generating + * interrupts & Clear any pending interrupt events + **/ + /** + * iowrite32(0, hw->hw_addr + REG_IMR); + * iowrite32(0xffffffff, hw->hw_addr + REG_ISR); + **/ + + /** + * Issue Soft Reset to the MAC. This will reset the chip's + * transmit, receive, DMA. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + **/ + iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL); + ioread32(hw->hw_addr + REG_MASTER_CTRL); + + iowrite16(1, hw->hw_addr + REG_GPHY_ENABLE); + ioread16(hw->hw_addr + REG_GPHY_ENABLE); + + msleep(1); /* delay about 1ms */ + + /* Wait at least 10ms for All module to be Idle */ + for (i = 0; i < 10; i++) { + icr = ioread32(hw->hw_addr + REG_IDLE_STATUS); + if (!icr) + break; + msleep(1); /* delay 1 ms */ + cpu_relax(); /* FIXME: is this still the right way to do this? */ + } + + if (icr) { + printk (KERN_DEBUG "icr = %x\n", icr); + return icr; + } + + return ATL1_SUCCESS; +} + +/** function about EEPROM + * + * check_eeprom_exist + * return 0 if eeprom exist + **/ +static int atl1_check_eeprom_exist(struct atl1_hw *hw) +{ + u32 value; + value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); + if (value & SPI_FLASH_CTRL_EN_VPD) { + value &= ~SPI_FLASH_CTRL_EN_VPD; + iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); + } + + value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST); + return ((value & 0xFF00) == 0x6C00) ? 0 : 1; +} + +static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 * p_value) +{ + int i; + u32 control; + + if (offset & 3) + return false; /* address do not align */ + + iowrite32(0, hw->hw_addr + REG_VPD_DATA); + control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT; + iowrite32(control, hw->hw_addr + REG_VPD_CAP); + ioread32(hw->hw_addr + REG_VPD_CAP); + + for (i = 0; i < 10; i++) { + msleep(2); + control = ioread32(hw->hw_addr + REG_VPD_CAP); + if (control & VPD_CAP_VPD_FLAG) + break; + } + if (control & VPD_CAP_VPD_FLAG) { + *p_value = ioread32(hw->hw_addr + REG_VPD_DATA); + return true; + } + return false; /* timeout */ +} + +/** + * Reads the value from a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to read + **/ +s32 atl1_read_phy_reg(struct atl1_hw * hw, u16 reg_addr, u16 * phy_data) +{ + u32 val; + int i; + + val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT | + MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 << + MDIO_CLK_SEL_SHIFT; + iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); + ioread32(hw->hw_addr + REG_MDIO_CTRL); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = ioread32(hw->hw_addr + REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) { + break; + } + } + if (!(val & (MDIO_START | MDIO_BUSY))) { + *phy_data = (u16) val; + return ATL1_SUCCESS; + } + return ATL1_ERR_PHY; +} + +#define CUSTOM_SPI_CS_SETUP 2 +#define CUSTOM_SPI_CLK_HI 2 +#define CUSTOM_SPI_CLK_LO 2 +#define CUSTOM_SPI_CS_HOLD 2 +#define CUSTOM_SPI_CS_HI 3 + +static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 * buf) +{ + int i; + u32 value; + + iowrite32(0, hw->hw_addr + REG_SPI_DATA); + iowrite32(addr, hw->hw_addr + REG_SPI_ADDR); + + value = SPI_FLASH_CTRL_WAIT_READY | + (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) << + SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI & + SPI_FLASH_CTRL_CLK_HI_MASK) << + SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO & + SPI_FLASH_CTRL_CLK_LO_MASK) << + SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD & + SPI_FLASH_CTRL_CS_HOLD_MASK) << + SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI & + SPI_FLASH_CTRL_CS_HI_MASK) << + SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) << + SPI_FLASH_CTRL_INS_SHIFT; + + iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); + + value |= SPI_FLASH_CTRL_START; + iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL); + ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); + + for (i = 0; i < 10; i++) { + msleep(1); /* 1ms */ + value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL); + if (!(value & SPI_FLASH_CTRL_START)) + break; + } + + if (value & SPI_FLASH_CTRL_START) + return false; + + *buf = ioread32(hw->hw_addr + REG_SPI_DATA); + + return true; +} + +/** + * get_permanent_address + * return 0 if get valid mac address, + **/ +int atl1_get_permanent_address(struct atl1_hw *hw) +{ + u32 addr[2]; + u32 i, control; + u16 reg; + u8 eth_addr[NODE_ADDRESS_SIZE]; + bool key_valid; + + if (is_valid_ether_addr(hw->perm_mac_addr)) + return 0; + + /* init */ + addr[0] = addr[1] = 0; + + if (!atl1_check_eeprom_exist(hw)) { /* eeprom exist */ + reg = 0; + key_valid = false; + /* Read out all EEPROM content */ + i = 0; + while (1) { + if (atl1_read_eeprom(hw, i + 0x100, &control)) { + if (key_valid) { + if (reg == REG_MAC_STA_ADDR) + addr[0] = control; + else if (reg == (REG_MAC_STA_ADDR + 4)) { + addr[1] = control; + } + key_valid = false; + } else if ((control & 0xff) == 0x5A) { + key_valid = true; + reg = (u16) (control >> 16); + } else { + break; /* assume data end while encount an invalid KEYWORD */ + } + } else { + break; /* read error */ + } + i += 4; + } + +/** + * The following 2 lines are the Attansic originals. Saving for posterity. + * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]); + * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]); + **/ + *(u32 *) & eth_addr[2] = swab32(addr[0]); + *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]); + + if (is_valid_ether_addr(eth_addr)) { + memcpy(hw->perm_mac_addr, eth_addr, NODE_ADDRESS_SIZE); + return 0; + } + return 1; + } + + /* see if SPI FLAGS exist ? */ + addr[0] = addr[1] = 0; + reg = 0; + key_valid = false; + i = 0; + while (1) { + if (atl1_spi_read(hw, i + 0x1f000, &control)) { + if (key_valid) { + if (reg == REG_MAC_STA_ADDR) + addr[0] = control; + else if (reg == (REG_MAC_STA_ADDR + 4)) { + addr[1] = control; + } + key_valid = false; + } else if ((control & 0xff) == 0x5A) { + key_valid = true; + reg = (u16) (control >> 16); + } else { + break; /* data end */ + } + } else { + break; /* read error */ + } + i += 4; + } + +/** + * The following 2 lines are the Attansic originals. Saving for posterity. + * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]); + * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]); + **/ + *(u32 *) & eth_addr[2] = swab32(addr[0]); + *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]); + if (is_valid_ether_addr(eth_addr)) { + memcpy(hw->perm_mac_addr, eth_addr, NODE_ADDRESS_SIZE); + return 0; + } + return 1; +} + +/** + * Reads the adapter's MAC address from the EEPROM + * hw - Struct containing variables accessed by shared code + **/ +s32 atl1_read_mac_addr(struct atl1_hw * hw) +{ + u16 i; + + if (atl1_get_permanent_address(hw)) + random_ether_addr(hw->perm_mac_addr); + + for (i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + return ATL1_SUCCESS; +} + +/** + * Hashes an address to determine its location in the multicast table + * hw - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + * + * atl1_hash_mc_addr + * purpose + * set hash value for a multicast address + * hash calcu processing : + * 1. calcu 32bit CRC for multicast address + * 2. reverse crc with MSB to LSB + **/ +u32 atl1_hash_mc_addr(struct atl1_hw * hw, u8 * mc_addr) +{ + u32 crc32, value = 0; + int i; + + crc32 = ether_crc_le(6, mc_addr); + crc32 = ~crc32; + for (i = 0; i < 32; i++) + value |= (((crc32 >> i) & 1) << (31 - i)); + + return value; +} + +/** + * Sets the bit in the multicast table corresponding to the hash value. + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + **/ +void atl1_hash_set(struct atl1_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg; + u32 mta; + + /** + * The HASH Table is a register array of 2 32-bit registers. + * It is treated like an array of 64 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + **/ + hash_reg = (hash_value >> 31) & 0x1; + hash_bit = (hash_value >> 26) & 0x1F; + mta = ioread32((hw + REG_RX_HASH_TABLE) + (hash_reg << 2)); + mta |= (1 << hash_bit); + iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); +} + +/** + * Writes a value to a PHY register + * hw - Struct containing variables accessed by shared code + * reg_addr - address of the PHY register to write + * data - data to write to the PHY + **/ +s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data) +{ + int i; + u32 val; + + val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT | + (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT | + MDIO_SUP_PREAMBLE | + MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT; + iowrite32(val, hw->hw_addr + REG_MDIO_CTRL); + ioread32(hw->hw_addr + REG_MDIO_CTRL); + + for (i = 0; i < MDIO_WAIT_TIMES; i++) { + udelay(2); + val = ioread32(hw->hw_addr + REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if (!(val & (MDIO_START | MDIO_BUSY))) + return ATL1_SUCCESS; + + return ATL1_ERR_PHY; +} + +/** + * Make L001's PHY out of Power Saving State (bug) + * hw - Struct containing variables accessed by shared code + * when power on, L001's PHY always on Power saving State + * (Gigabit Link forbidden) + **/ +static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw) +{ + s32 ret; + ret = atl1_write_phy_reg(hw, 29, 0x0029); + if (ret) + return ret; + return atl1_write_phy_reg(hw, 30, 0); +} + +/** + *TODO: do something or get rid of this + **/ +s32 atl1_phy_enter_power_saving(struct atl1_hw * hw) +{ +/* s32 ret_val; + * u16 phy_data; + */ + +/* + ret_val = atl1_write_phy_reg(hw, ...); + ret_val = atl1_write_phy_reg(hw, ...); + .... +*/ + return ATL1_SUCCESS; +} + +/** + * Resets the PHY and make all config validate + * hw - Struct containing variables accessed by shared code + * + * Sets bit 15 and 12 of the MII Control regiser (for F001 bug) + **/ +static s32 atl1_phy_reset(struct atl1_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR || + hw->media_type == MEDIA_TYPE_1000M_FULL) { + phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN; + } else { + switch (hw->media_type) { + case MEDIA_TYPE_100M_FULL: + phy_data = + MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | + MII_CR_RESET; + break; + case MEDIA_TYPE_100M_HALF: + phy_data = MII_CR_SPEED_100 | MII_CR_RESET; + break; + case MEDIA_TYPE_10M_FULL: + phy_data = + MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; + break; + default: /* MEDIA_TYPE_10M_HALF: */ + phy_data = MII_CR_SPEED_10 | MII_CR_RESET; + break; + } + } + + ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data); + if (ret_val) { + u32 val; + int i; + /************************************** + * pcie serdes link may be down ! + **************************************/ + printk(KERN_DEBUG "%s: autoneg caused pcie phy link down\n", + atl1_driver_name); + + for (i = 0; i < 25; i++) { + msleep(1); + val = ioread32(hw->hw_addr + REG_MDIO_CTRL); + if (!(val & (MDIO_START | MDIO_BUSY))) + break; + } + + if ((val & (MDIO_START | MDIO_BUSY)) != 0) { + printk(KERN_WARNING + "%s: pcie link down at least for 25ms\n", + atl1_driver_name); + return ret_val; + } + } + return ATL1_SUCCESS; +} + +/** + * Configures PHY autoneg and flow control advertisement settings + * hw - Struct containing variables accessed by shared code + **/ +s32 atl1_phy_setup_autoneg_adv(struct atl1_hw * hw) +{ + s32 ret_val; + s16 mii_autoneg_adv_reg; + s16 mii_1000t_ctrl_reg; + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK; + + /* Read the MII 1000Base-T Control Register (Address 9). */ + mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK; + + /** + * First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + **/ + mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK; + mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK; + + /** + * Need to parse media_type and set up + * the appropriate PHY registers. + **/ + switch (hw->media_type) { + case MEDIA_TYPE_AUTO_SENSOR: + mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS | + MII_AR_10T_FD_CAPS | + MII_AR_100TX_HD_CAPS | + MII_AR_100TX_FD_CAPS); + mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS; + break; + + case MEDIA_TYPE_1000M_FULL: + mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS; + break; + + case MEDIA_TYPE_100M_FULL: + mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; + break; + + case MEDIA_TYPE_100M_HALF: + mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; + break; + + case MEDIA_TYPE_10M_FULL: + mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; + break; + + default: + mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; + break; + } + + /* flow control fixed to enable all */ + mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); + + hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; + hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; + + ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + ret_val = atl1_write_phy_reg(hw, MII_AT001_CR, mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + + return ATL1_SUCCESS; +} + +/** + * Configures link settings. + * hw - Struct containing variables accessed by shared code + * Assumes the hardware has previously been reset and the + * transmitter and receiver are not enabled. + **/ +static s32 atl1_setup_link(struct atl1_hw *hw) +{ + s32 ret_val; + + /** + * Options: + * PHY will advertise value(s) parsed from + * autoneg_advertised and fc + * no matter what autoneg is , We will not wait link result. + **/ + ret_val = atl1_phy_setup_autoneg_adv(hw); + if (ret_val) { + printk(KERN_DEBUG "%s: error setting up autonegotiation\n", + atl1_driver_name); + return ret_val; + } + /* SW.Reset , En-Auto-Neg if needed */ + ret_val = atl1_phy_reset(hw); + if (ret_val) { + printk(KERN_DEBUG "%s: error resetting the phy\n", atl1_driver_name); + return ret_val; + } + hw->phy_configured = true; + return ret_val; +} + +struct atl1_spi_flash_dev flash_table[] = { +/* MFR_NAME WRSR READ PRGM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */ + {"Atmel", 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62}, + {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60}, + {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7}, +}; + +static void atl1_init_flash_opcode(struct atl1_hw *hw) +{ + if (hw->flash_vendor >= sizeof(flash_table) / sizeof(flash_table[0])) + hw->flash_vendor = 0; /* ATMEL */ + + /* Init OP table */ + iowrite8(flash_table[hw->flash_vendor].cmd_program, + hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM); + iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase, + hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE); + iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase, + hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE); + iowrite8(flash_table[hw->flash_vendor].cmd_rdid, + hw->hw_addr + REG_SPI_FLASH_OP_RDID); + iowrite8(flash_table[hw->flash_vendor].cmd_wren, + hw->hw_addr + REG_SPI_FLASH_OP_WREN); + iowrite8(flash_table[hw->flash_vendor].cmd_rdsr, + hw->hw_addr + REG_SPI_FLASH_OP_RDSR); + iowrite8(flash_table[hw->flash_vendor].cmd_wrsr, + hw->hw_addr + REG_SPI_FLASH_OP_WRSR); + iowrite8(flash_table[hw->flash_vendor].cmd_read, + hw->hw_addr + REG_SPI_FLASH_OP_READ); +} + +/** + * Performs basic configuration of the adapter. + * hw - Struct containing variables accessed by shared code + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes multicast table, + * and Calls routines to setup link + * Leaves the transmit and receive units disabled and uninitialized. + **/ +s32 atl1_init_hw(struct atl1_hw *hw) +{ + u32 ret_val = 0; + + /* Zero out the Multicast HASH table */ + iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); + /* clear the old settings from the multicast hash table */ + iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); + + atl1_init_flash_opcode(hw); + + if (!hw->phy_configured) { + /* enable GPHY LinkChange Interrrupt */ + ret_val = atl1_write_phy_reg(hw, 18, 0xC00); + if (ret_val) + return ret_val; + /* make PHY out of power-saving state */ + ret_val = atl1_phy_leave_power_saving(hw); + if (ret_val) + return ret_val; + /* Call a subroutine to configure the link */ + ret_val = atl1_setup_link(hw); + } + return ret_val; +} + +/** + * Detects the current speed and duplex settings of the hardware. + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + **/ +s32 atl1_get_speed_and_duplex(struct atl1_hw * hw, u16 * speed, u16 * duplex) +{ + s32 ret_val; + u16 phy_data; + + /* ; --- Read PHY Specific Status Register (17) */ + ret_val = atl1_read_phy_reg(hw, MII_AT001_PSSR, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED)) + return ATL1_ERR_PHY_RES; + + switch (phy_data & MII_AT001_PSSR_SPEED) { + case MII_AT001_PSSR_1000MBS: + *speed = SPEED_1000; + break; + case MII_AT001_PSSR_100MBS: + *speed = SPEED_100; + break; + case MII_AT001_PSSR_10MBS: + *speed = SPEED_10; + break; + default: + printk(KERN_DEBUG "%s: error getting speed\n", + atl1_driver_name); + return ATL1_ERR_PHY_SPEED; + break; + } + if (phy_data & MII_AT001_PSSR_DPLX) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + return ATL1_SUCCESS; +} + +void atl1_set_mac_addr(struct atl1_hw *hw) +{ + u32 value; + /** + * 00-0B-6A-F6-00-DC + * 0: 6AF600DC 1: 000B + * low dword + **/ + value = (((u32) hw->mac_addr[2]) << 24) | + (((u32) hw->mac_addr[3]) << 16) | + (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5])); + iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); + /* high dword */ + value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); + iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2)); +} diff -puN /dev/null drivers/net/atl1/atl1_hw.h --- /dev/null +++ a/drivers/net/atl1/atl1_hw.h @@ -0,0 +1,965 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * There are a lot of defines in here that are unused and/or have cryptic + * names. Please leave them alone, as they're the closest thing we have + * to a spec from Attansic at present. *ahem* -- CHS + **/ + +#ifndef _ATL1_HW_H_ +#define _ATL1_HW_H_ + +struct atl1_adapter; +struct atl1_hw; + +/* function prototypes needed by multiple files */ +s32 atl1_phy_setup_autoneg_adv(struct atl1_hw *hw); +s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data); +s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 * speed, u16 * duplex); +s32 atl1_read_mac_addr(struct atl1_hw *hw); +s32 atl1_init_hw(struct atl1_hw *hw); +s32 atl1_get_speed_and_duplex(struct atl1_hw *hw, u16 * speed, u16 * duplex); +s32 atl1_set_speed_and_duplex(struct atl1_hw *hw, u16 speed, u16 duplex); +u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 * mc_addr); +void atl1_hash_set(struct atl1_hw *hw, u32 hash_value); +s32 atl1_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 * phy_data); +void atl1_set_mac_addr(struct atl1_hw *hw); +s32 atl1_phy_enter_power_saving(struct atl1_hw *hw); +s32 atl1_reset_hw(struct atl1_hw *hw); +void atl1_check_options(struct atl1_adapter *adapter); + +/* register definitions */ +#define REG_PCIE_CAP_LIST 0x58 + +#define REG_VPD_CAP 0x6C +#define VPD_CAP_ID_MASK 0xff +#define VPD_CAP_ID_SHIFT 0 +#define VPD_CAP_NEXT_PTR_MASK 0xFF +#define VPD_CAP_NEXT_PTR_SHIFT 8 +#define VPD_CAP_VPD_ADDR_MASK 0x7FFF +#define VPD_CAP_VPD_ADDR_SHIFT 16 +#define VPD_CAP_VPD_FLAG 0x80000000 + +#define REG_VPD_DATA 0x70 + +#define REG_SPI_FLASH_CTRL 0x200 +#define SPI_FLASH_CTRL_STS_NON_RDY 0x1 +#define SPI_FLASH_CTRL_STS_WEN 0x2 +#define SPI_FLASH_CTRL_STS_WPEN 0x80 +#define SPI_FLASH_CTRL_DEV_STS_MASK 0xFF +#define SPI_FLASH_CTRL_DEV_STS_SHIFT 0 +#define SPI_FLASH_CTRL_INS_MASK 0x7 +#define SPI_FLASH_CTRL_INS_SHIFT 8 +#define SPI_FLASH_CTRL_START 0x800 +#define SPI_FLASH_CTRL_EN_VPD 0x2000 +#define SPI_FLASH_CTRL_LDSTART 0x8000 +#define SPI_FLASH_CTRL_CS_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HI_SHIFT 16 +#define SPI_FLASH_CTRL_CS_HOLD_MASK 0x3 +#define SPI_FLASH_CTRL_CS_HOLD_SHIFT 18 +#define SPI_FLASH_CTRL_CLK_LO_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_LO_SHIFT 20 +#define SPI_FLASH_CTRL_CLK_HI_MASK 0x3 +#define SPI_FLASH_CTRL_CLK_HI_SHIFT 22 +#define SPI_FLASH_CTRL_CS_SETUP_MASK 0x3 +#define SPI_FLASH_CTRL_CS_SETUP_SHIFT 24 +#define SPI_FLASH_CTRL_EROM_PGSZ_MASK 0x3 +#define SPI_FLASH_CTRL_EROM_PGSZ_SHIFT 26 +#define SPI_FLASH_CTRL_WAIT_READY 0x10000000 + +#define REG_SPI_ADDR 0x204 + +#define REG_SPI_DATA 0x208 + +#define REG_SPI_FLASH_CONFIG 0x20C +#define SPI_FLASH_CONFIG_LD_ADDR_MASK 0xFFFFFF +#define SPI_FLASH_CONFIG_LD_ADDR_SHIFT 0 +#define SPI_FLASH_CONFIG_VPD_ADDR_MASK 0x3 +#define SPI_FLASH_CONFIG_VPD_ADDR_SHIFT 24 +#define SPI_FLASH_CONFIG_LD_EXIST 0x4000000 + +#define REG_SPI_FLASH_OP_PROGRAM 0x210 +#define REG_SPI_FLASH_OP_SC_ERASE 0x211 +#define REG_SPI_FLASH_OP_CHIP_ERASE 0x212 +#define REG_SPI_FLASH_OP_RDID 0x213 +#define REG_SPI_FLASH_OP_WREN 0x214 +#define REG_SPI_FLASH_OP_RDSR 0x215 +#define REG_SPI_FLASH_OP_WRSR 0x216 +#define REG_SPI_FLASH_OP_READ 0x217 + +#define REG_TWSI_CTRL 0x218 +#define TWSI_CTRL_LD_OFFSET_MASK 0xFF +#define TWSI_CTRL_LD_OFFSET_SHIFT 0 +#define TWSI_CTRL_LD_SLV_ADDR_MASK 0x7 +#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8 +#define TWSI_CTRL_SW_LDSTART 0x800 +#define TWSI_CTRL_HW_LDSTART 0x1000 +#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F +#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15 +#define TWSI_CTRL_LD_EXIST 0x400000 +#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_READ_FREQ_SEL_SHIFT 23 +#define TWSI_CTRL_FREQ_SEL_100K 0 +#define TWSI_CTRL_FREQ_SEL_200K 1 +#define TWSI_CTRL_FREQ_SEL_300K 2 +#define TWSI_CTRL_FREQ_SEL_400K 3 +#define TWSI_CTRL_SMB_SLV_ADDR +#define TWSI_CTRL_WRITE_FREQ_SEL_MASK 0x3 +#define TWSI_CTRL_WRITE_FREQ_SEL_SHIFT 24 + +#define REG_PCIE_DEV_MISC_CTRL 0x21C +#define PCIE_DEV_MISC_CTRL_EXT_PIPE 0x2 +#define PCIE_DEV_MISC_CTRL_RETRY_BUFDIS 0x1 +#define PCIE_DEV_MISC_CTRL_SPIROM_EXIST 0x4 +#define PCIE_DEV_MISC_CTRL_SERDES_ENDIAN 0x8 +#define PCIE_DEV_MISC_CTRL_SERDES_SEL_DIN 0x10 + +/* Selene Master Control Register */ +#define REG_MASTER_CTRL 0x1400 +#define MASTER_CTRL_SOFT_RST 0x1 +#define MASTER_CTRL_MTIMER_EN 0x2 +#define MASTER_CTRL_ITIMER_EN 0x4 +#define MASTER_CTRL_MANUAL_INT 0x8 +#define MASTER_CTRL_REV_NUM_SHIFT 16 +#define MASTER_CTRL_REV_NUM_MASK 0xff +#define MASTER_CTRL_DEV_ID_SHIFT 24 +#define MASTER_CTRL_DEV_ID_MASK 0xff + +/* Timer Initial Value Register */ +#define REG_MANUAL_TIMER_INIT 0x1404 + +/* IRQ ModeratorTimer Initial Value Register */ +#define REG_IRQ_MODU_TIMER_INIT 0x1408 + +#define REG_GPHY_ENABLE 0x140C + +/* IRQ Anti-Lost Timer Initial Value Register */ +#define REG_CMBDISDMA_TIMER 0x140E + +/* Block IDLE Status Register */ +#define REG_IDLE_STATUS 0x1410 +#define IDLE_STATUS_RXMAC 1 +#define IDLE_STATUS_TXMAC 2 +#define IDLE_STATUS_RXQ 4 +#define IDLE_STATUS_TXQ 8 +#define IDLE_STATUS_DMAR 0x10 +#define IDLE_STATUS_DMAW 0x20 +#define IDLE_STATUS_SMB 0x40 +#define IDLE_STATUS_CMB 0x80 + +/* MDIO Control Register */ +#define REG_MDIO_CTRL 0x1414 +#define MDIO_DATA_MASK 0xffff +#define MDIO_DATA_SHIFT 0 +#define MDIO_REG_ADDR_MASK 0x1f +#define MDIO_REG_ADDR_SHIFT 16 +#define MDIO_RW 0x200000 +#define MDIO_SUP_PREAMBLE 0x400000 +#define MDIO_START 0x800000 +#define MDIO_CLK_SEL_SHIFT 24 +#define MDIO_CLK_25_4 0 +#define MDIO_CLK_25_6 2 +#define MDIO_CLK_25_8 3 +#define MDIO_CLK_25_10 4 +#define MDIO_CLK_25_14 5 +#define MDIO_CLK_25_20 6 +#define MDIO_CLK_25_28 7 +#define MDIO_BUSY 0x8000000 +#define MDIO_WAIT_TIMES 30 + +/* MII PHY Status Register */ +#define REG_PHY_STATUS 0x1418 + +/* BIST Control and Status Register0 (for the Packet Memory) */ +#define REG_BIST0_CTRL 0x141c +#define BIST0_NOW 0x1 +#define BIST0_SRAM_FAIL 0x2 +#define BIST0_FUSE_FLAG 0x4 +#define REG_BIST1_CTRL 0x1420 +#define BIST1_NOW 0x1 +#define BIST1_SRAM_FAIL 0x2 +#define BIST1_FUSE_FLAG 0x4 + +/* MAC Control Register */ +#define REG_MAC_CTRL 0x1480 +#define MAC_CTRL_TX_EN 1 +#define MAC_CTRL_RX_EN 2 +#define MAC_CTRL_TX_FLOW 4 +#define MAC_CTRL_RX_FLOW 8 +#define MAC_CTRL_LOOPBACK 0x10 +#define MAC_CTRL_DUPLX 0x20 +#define MAC_CTRL_ADD_CRC 0x40 +#define MAC_CTRL_PAD 0x80 +#define MAC_CTRL_LENCHK 0x100 +#define MAC_CTRL_HUGE_EN 0x200 +#define MAC_CTRL_PRMLEN_SHIFT 10 +#define MAC_CTRL_PRMLEN_MASK 0xf +#define MAC_CTRL_RMV_VLAN 0x4000 +#define MAC_CTRL_PROMIS_EN 0x8000 +#define MAC_CTRL_TX_PAUSE 0x10000 +#define MAC_CTRL_SCNT 0x20000 +#define MAC_CTRL_SRST_TX 0x40000 +#define MAC_CTRL_TX_SIMURST 0x80000 +#define MAC_CTRL_SPEED_SHIFT 20 +#define MAC_CTRL_SPEED_MASK 0x300000 +#define MAC_CTRL_SPEED_1000 2 +#define MAC_CTRL_SPEED_10_100 1 +#define MAC_CTRL_DBG_TX_BKPRESURE 0x400000 +#define MAC_CTRL_TX_HUGE 0x800000 +#define MAC_CTRL_RX_CHKSUM_EN 0x1000000 +#define MAC_CTRL_MC_ALL_EN 0x2000000 +#define MAC_CTRL_BC_EN 0x4000000 +#define MAC_CTRL_DBG 0x8000000 + +/* MAC IPG/IFG Control Register */ +#define REG_MAC_IPG_IFG 0x1484 +#define MAC_IPG_IFG_IPGT_SHIFT 0 +#define MAC_IPG_IFG_IPGT_MASK 0x7f +#define MAC_IPG_IFG_MIFG_SHIFT 8 +#define MAC_IPG_IFG_MIFG_MASK 0xff +#define MAC_IPG_IFG_IPGR1_SHIFT 16 +#define MAC_IPG_IFG_IPGR1_MASK 0x7f +#define MAC_IPG_IFG_IPGR2_SHIFT 24 +#define MAC_IPG_IFG_IPGR2_MASK 0x7f + +/* MAC STATION ADDRESS */ +#define REG_MAC_STA_ADDR 0x1488 + +/* Hash table for multicast address */ +#define REG_RX_HASH_TABLE 0x1490 + +/* MAC Half-Duplex Control Register */ +#define REG_MAC_HALF_DUPLX_CTRL 0x1498 +#define MAC_HALF_DUPLX_CTRL_LCOL_SHIFT 0 +#define MAC_HALF_DUPLX_CTRL_LCOL_MASK 0x3ff +#define MAC_HALF_DUPLX_CTRL_RETRY_SHIFT 12 +#define MAC_HALF_DUPLX_CTRL_RETRY_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_EXC_DEF_EN 0x10000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_C 0x20000 +#define MAC_HALF_DUPLX_CTRL_NO_BACK_P 0x40000 +#define MAC_HALF_DUPLX_CTRL_ABEBE 0x80000 +#define MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT 20 +#define MAC_HALF_DUPLX_CTRL_ABEBT_MASK 0xf +#define MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT 24 +#define MAC_HALF_DUPLX_CTRL_JAMIPG_MASK 0xf + +/* Maximum Frame Length Control Register */ +#define REG_MTU 0x149c + +/* Wake-On-Lan control register */ +#define REG_WOL_CTRL 0x14a0 +#define WOL_PATTERN_EN 0x00000001 +#define WOL_PATTERN_PME_EN 0x00000002 +#define WOL_MAGIC_EN 0x00000004 +#define WOL_MAGIC_PME_EN 0x00000008 +#define WOL_LINK_CHG_EN 0x00000010 +#define WOL_LINK_CHG_PME_EN 0x00000020 +#define WOL_PATTERN_ST 0x00000100 +#define WOL_MAGIC_ST 0x00000200 +#define WOL_LINKCHG_ST 0x00000400 +#define WOL_CLK_SWITCH_EN 0x00008000 +#define WOL_PT0_EN 0x00010000 +#define WOL_PT1_EN 0x00020000 +#define WOL_PT2_EN 0x00040000 +#define WOL_PT3_EN 0x00080000 +#define WOL_PT4_EN 0x00100000 +#define WOL_PT5_EN 0x00200000 +#define WOL_PT6_EN 0x00400000 + +/* WOL Length ( 2 DWORD ) */ +#define REG_WOL_PATTERN_LEN 0x14a4 +#define WOL_PT_LEN_MASK 0x7f +#define WOL_PT0_LEN_SHIFT 0 +#define WOL_PT1_LEN_SHIFT 8 +#define WOL_PT2_LEN_SHIFT 16 +#define WOL_PT3_LEN_SHIFT 24 +#define WOL_PT4_LEN_SHIFT 0 +#define WOL_PT5_LEN_SHIFT 8 +#define WOL_PT6_LEN_SHIFT 16 + +/* Internal SRAM Partition Register */ +#define REG_SRAM_RFD_ADDR 0x1500 +#define REG_SRAM_RFD_LEN (REG_SRAM_RFD_ADDR+ 4) +#define REG_SRAM_RRD_ADDR (REG_SRAM_RFD_ADDR+ 8) +#define REG_SRAM_RRD_LEN (REG_SRAM_RFD_ADDR+12) +#define REG_SRAM_TPD_ADDR (REG_SRAM_RFD_ADDR+16) +#define REG_SRAM_TPD_LEN (REG_SRAM_RFD_ADDR+20) +#define REG_SRAM_TRD_ADDR (REG_SRAM_RFD_ADDR+24) +#define REG_SRAM_TRD_LEN (REG_SRAM_RFD_ADDR+28) +#define REG_SRAM_RXF_ADDR (REG_SRAM_RFD_ADDR+32) +#define REG_SRAM_RXF_LEN (REG_SRAM_RFD_ADDR+36) +#define REG_SRAM_TXF_ADDR (REG_SRAM_RFD_ADDR+40) +#define REG_SRAM_TXF_LEN (REG_SRAM_RFD_ADDR+44) +#define REG_SRAM_TCPH_PATH_ADDR (REG_SRAM_RFD_ADDR+48) +#define SRAM_TCPH_ADDR_MASK 0x0fff +#define SRAM_TCPH_ADDR_SHIFT 0 +#define SRAM_PATH_ADDR_MASK 0x0fff +#define SRAM_PATH_ADDR_SHIFT 16 + +/* Load Ptr Register */ +#define REG_LOAD_PTR (REG_SRAM_RFD_ADDR+52) + +/* Descriptor Control register */ +#define REG_DESC_BASE_ADDR_HI 0x1540 +#define REG_DESC_RFD_ADDR_LO (REG_DESC_BASE_ADDR_HI+4) +#define REG_DESC_RRD_ADDR_LO (REG_DESC_BASE_ADDR_HI+8) +#define REG_DESC_TPD_ADDR_LO (REG_DESC_BASE_ADDR_HI+12) +#define REG_DESC_CMB_ADDR_LO (REG_DESC_BASE_ADDR_HI+16) +#define REG_DESC_SMB_ADDR_LO (REG_DESC_BASE_ADDR_HI+20) +#define REG_DESC_RFD_RRD_RING_SIZE (REG_DESC_BASE_ADDR_HI+24) +#define DESC_RFD_RING_SIZE_MASK 0x7ff +#define DESC_RFD_RING_SIZE_SHIFT 0 +#define DESC_RRD_RING_SIZE_MASK 0x7ff +#define DESC_RRD_RING_SIZE_SHIFT 16 +#define REG_DESC_TPD_RING_SIZE (REG_DESC_BASE_ADDR_HI+28) +#define DESC_TPD_RING_SIZE_MASK 0x3ff +#define DESC_TPD_RING_SIZE_SHIFT 0 + +/* TXQ Control Register */ +#define REG_TXQ_CTRL 0x1580 +#define TXQ_CTRL_TPD_BURST_NUM_SHIFT 0 +#define TXQ_CTRL_TPD_BURST_NUM_MASK 0x1f +#define TXQ_CTRL_EN 0x20 +#define TXQ_CTRL_ENH_MODE 0x40 +#define TXQ_CTRL_TPD_FETCH_TH_SHIFT 8 +#define TXQ_CTRL_TPD_FETCH_TH_MASK 0x3f +#define TXQ_CTRL_TXF_BURST_NUM_SHIFT 16 +#define TXQ_CTRL_TXF_BURST_NUM_MASK 0xffff + +/* Jumbo packet Threshold for task offload */ +#define REG_TX_JUMBO_TASK_TH_TPD_IPG 0x1584 +#define TX_JUMBO_TASK_TH_MASK 0x7ff +#define TX_JUMBO_TASK_TH_SHIFT 0 +#define TX_TPD_MIN_IPG_MASK 0x1f +#define TX_TPD_MIN_IPG_SHIFT 16 + +/* RXQ Control Register */ +#define REG_RXQ_CTRL 0x15a0 +#define RXQ_CTRL_RFD_BURST_NUM_SHIFT 0 +#define RXQ_CTRL_RFD_BURST_NUM_MASK 0xff +#define RXQ_CTRL_RRD_BURST_THRESH_SHIFT 8 +#define RXQ_CTRL_RRD_BURST_THRESH_MASK 0xff +#define RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT 16 +#define RXQ_CTRL_RFD_PREF_MIN_IPG_MASK 0x1f +#define RXQ_CTRL_CUT_THRU_EN 0x40000000 +#define RXQ_CTRL_EN 0x80000000 + +/* Rx jumbo packet threshold and rrd retirement timer */ +#define REG_RXQ_JMBOSZ_RRDTIM (REG_RXQ_CTRL+ 4) +#define RXQ_JMBOSZ_TH_MASK 0x7ff +#define RXQ_JMBOSZ_TH_SHIFT 0 +#define RXQ_JMBO_LKAH_MASK 0xf +#define RXQ_JMBO_LKAH_SHIFT 11 +#define RXQ_RRD_TIMER_MASK 0xffff +#define RXQ_RRD_TIMER_SHIFT 16 + +/* RFD flow control register */ +#define REG_RXQ_RXF_PAUSE_THRESH (REG_RXQ_CTRL+ 8) +#define RXQ_RXF_PAUSE_TH_HI_SHIFT 16 +#define RXQ_RXF_PAUSE_TH_HI_MASK 0xfff +#define RXQ_RXF_PAUSE_TH_LO_SHIFT 0 +#define RXQ_RXF_PAUSE_TH_LO_MASK 0xfff + +/* RRD flow control register */ +#define REG_RXQ_RRD_PAUSE_THRESH (REG_RXQ_CTRL+12) +#define RXQ_RRD_PAUSE_TH_HI_SHIFT 0 +#define RXQ_RRD_PAUSE_TH_HI_MASK 0xfff +#define RXQ_RRD_PAUSE_TH_LO_SHIFT 16 +#define RXQ_RRD_PAUSE_TH_LO_MASK 0xfff + +/* DMA Engine Control Register */ +#define REG_DMA_CTRL 0x15c0 +#define DMA_CTRL_DMAR_IN_ORDER 0x1 +#define DMA_CTRL_DMAR_ENH_ORDER 0x2 +#define DMA_CTRL_DMAR_OUT_ORDER 0x4 +#define DMA_CTRL_RCB_VALUE 0x8 +#define DMA_CTRL_DMAR_BURST_LEN_SHIFT 4 +#define DMA_CTRL_DMAR_BURST_LEN_MASK 7 +#define DMA_CTRL_DMAW_BURST_LEN_SHIFT 7 +#define DMA_CTRL_DMAW_BURST_LEN_MASK 7 +#define DMA_CTRL_DMAR_EN 0x400 +#define DMA_CTRL_DMAW_EN 0x800 + +/* CMB/SMB Control Register */ +#define REG_CSMB_CTRL 0x15d0 +#define CSMB_CTRL_CMB_NOW 1 +#define CSMB_CTRL_SMB_NOW 2 +#define CSMB_CTRL_CMB_EN 4 +#define CSMB_CTRL_SMB_EN 8 + +/* CMB DMA Write Threshold Register */ +#define REG_CMB_WRITE_TH (REG_CSMB_CTRL+ 4) +#define CMB_RRD_TH_SHIFT 0 +#define CMB_RRD_TH_MASK 0x7ff +#define CMB_TPD_TH_SHIFT 16 +#define CMB_TPD_TH_MASK 0x7ff + +/* RX/TX count-down timer to trigger CMB-write. 2us resolution. */ +#define REG_CMB_WRITE_TIMER (REG_CSMB_CTRL+ 8) +#define CMB_RX_TM_SHIFT 0 +#define CMB_RX_TM_MASK 0xffff +#define CMB_TX_TM_SHIFT 16 +#define CMB_TX_TM_MASK 0xffff + +/* Number of packet received since last CMB write */ +#define REG_CMB_RX_PKT_CNT (REG_CSMB_CTRL+12) + +/* Number of packet transmitted since last CMB write */ +#define REG_CMB_TX_PKT_CNT (REG_CSMB_CTRL+16) + +/* SMB auto DMA timer register */ +#define REG_SMB_TIMER (REG_CSMB_CTRL+20) + +/* Mailbox Register */ +#define REG_MAILBOX 0x15f0 +#define MB_RFD_PROD_INDX_SHIFT 0 +#define MB_RFD_PROD_INDX_MASK 0x7ff +#define MB_RRD_CONS_INDX_SHIFT 11 +#define MB_RRD_CONS_INDX_MASK 0x7ff +#define MB_TPD_PROD_INDX_SHIFT 22 +#define MB_TPD_PROD_INDX_MASK 0x3ff + +/* Interrupt Status Register */ +#define REG_ISR 0x1600 +#define ISR_SMB 1 +#define ISR_TIMER 2 +#define ISR_MANUAL 4 +#define ISR_RXF_OV 8 +#define ISR_RFD_UNRUN 0x10 +#define ISR_RRD_OV 0x20 +#define ISR_TXF_UNRUN 0x40 +#define ISR_LINK 0x80 +#define ISR_HOST_RFD_UNRUN 0x100 +#define ISR_HOST_RRD_OV 0x200 +#define ISR_DMAR_TO_RST 0x400 +#define ISR_DMAW_TO_RST 0x800 +#define ISR_GPHY 0x1000 +#define ISR_RX_PKT 0x10000 +#define ISR_TX_PKT 0x20000 +#define ISR_TX_DMA 0x40000 +#define ISR_RX_DMA 0x80000 +#define ISR_CMB_RX 0x100000 +#define ISR_CMB_TX 0x200000 +#define ISR_MAC_RX 0x400000 +#define ISR_MAC_TX 0x800000 +#define ISR_UR_DETECTED 0x1000000 +#define ISR_FERR_DETECTED 0x2000000 +#define ISR_NFERR_DETECTED 0x4000000 +#define ISR_CERR_DETECTED 0x8000000 +#define ISR_PHY_LINKDOWN 0x10000000 +#define ISR_DIS_SMB 0x20000000 +#define ISR_DIS_DMA 0x40000000 +#define ISR_DIS_INT 0x80000000 + +/* Interrupt Mask Register */ +#define REG_IMR 0x1604 + +/* Normal Interrupt mask */ +#define IMR_NORMAL_MASK (\ + ISR_SMB |\ + ISR_GPHY |\ + ISR_PHY_LINKDOWN|\ + ISR_DMAR_TO_RST |\ + ISR_DMAW_TO_RST |\ + ISR_CMB_TX |\ + ISR_CMB_RX ) + +/* Debug Interrupt Mask (enable all interrupt) */ +#define IMR_DEBUG_MASK (\ + ISR_SMB |\ + ISR_TIMER |\ + ISR_MANUAL |\ + ISR_RXF_OV |\ + ISR_RFD_UNRUN |\ + ISR_RRD_OV |\ + ISR_TXF_UNRUN |\ + ISR_LINK |\ + ISR_CMB_TX |\ + ISR_CMB_RX |\ + ISR_RX_PKT |\ + ISR_TX_PKT |\ + ISR_MAC_RX |\ + ISR_MAC_TX ) + +/* Interrupt Status Register */ +#define REG_RFD_RRD_IDX 0x1800 +#define REG_TPD_IDX 0x1804 + +/* MII definition */ +/* PHY Common Register */ +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 +#define MII_ADVERTISE 0x04 +#define MII_LPA 0x05 +#define MII_EXPANSION 0x06 +#define MII_AT001_CR 0x09 +#define MII_AT001_SR 0x0A +#define MII_AT001_ESR 0x0F +#define MII_AT001_PSCR 0x10 +#define MII_AT001_PSSR 0x11 +#define MII_DCOUNTER 0x12 +#define MII_FCSCOUNTER 0x13 +#define MII_NWAYTEST 0x14 +#define MII_RERRCOUNTER 0x15 +#define MII_SREVISION 0x16 +#define MII_RESV1 0x17 +#define MII_LBRERROR 0x18 +#define MII_PHYADDR 0x19 +#define MII_RESV2 0x1a +#define MII_TPISTATUS 0x1b +#define MII_NCONFIG 0x1c + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_MASK 0x2040 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Link partner ability register. */ +#define MII_LPA_SLCT 0x001f /* Same as advertise selector */ +#define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */ +#define MII_LPA_PAUSE 0x0400 /* PAUSE */ +#define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ +#define MII_LPA_RFAULT 0x2000 /* Link partner faulted */ +#define MII_LPA_LPACK 0x4000 /* Link partner acked us */ +#define MII_LPA_NPAGE 0x8000 /* Next page bit */ + +/* Autoneg Advertisement Register */ +#define MII_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define MII_AR_PAUSE 0x0400 /* Pause operation desired */ +#define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ +#define MII_AR_SPEED_MASK 0x01E0 +#define MII_AR_DEFAULT_CAP_MASK 0x0DE0 + +/* 1000BASE-T Control Register */ +#define MII_AT001_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define MII_AT001_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define MII_AT001_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port, 0=DTE device */ +#define MII_AT001_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master, 0=Configure PHY as Slave */ +#define MII_AT001_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value, 0=Automatic Master/Slave config */ +#define MII_AT001_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define MII_AT001_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define MII_AT001_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define MII_AT001_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ +#define MII_AT001_CR_1000T_SPEED_MASK 0x0300 +#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK 0x0300 + +/* 1000BASE-T Status Register */ +#define MII_AT001_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define MII_AT001_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define MII_AT001_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define MII_AT001_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define MII_AT001_SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define MII_AT001_SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define MII_AT001_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define MII_AT001_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define MII_AT001_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define MII_AT001_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +/* AT001 PHY Specific Control Register */ +#define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define MII_AT001_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define MII_AT001_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define MII_AT001_PSCR_MAC_POWERDOWN 0x0008 +#define MII_AT001_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, 0=CLK125 toggling */ +#define MII_AT001_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5, Manual MDI configuration */ +#define MII_AT001_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define MII_AT001_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */ +#define MII_AT001_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled all speeds. */ +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE 0x0080 /* 1=Enable Extended 10BASE-T distance (Lower 10BASE-T RX Threshold), 0=Normal 10BASE-T RX Threshold */ +#define MII_AT001_PSCR_MII_5BIT_ENABLE 0x0100 /* 1=5-Bit interface in 100BASE-TX, 0=MII interface in 100BASE-TX */ +#define MII_AT001_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define MII_AT001_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define MII_AT001_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ +#define MII_AT001_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define MII_AT001_PSCR_AUTO_X_MODE_SHIFT 5 +#define MII_AT001_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* AT001 PHY Specific Status Register */ +#define MII_AT001_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define MII_AT001_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define MII_AT001_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define MII_AT001_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define MII_AT001_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define MII_AT001_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +/* PCI Command Register Bit Definitions */ +#define PCI_REG_COMMAND 0x04 /* PCI Command Register */ +#define CMD_IO_SPACE 0x0001 +#define CMD_MEMORY_SPACE 0x0002 +#define CMD_BUS_MASTER 0x0004 + +/* Wake Up Filter Control */ +#define ATL1_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define ATL1_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define ATL1_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define ATL1_WUFC_MC 0x00000008 /* Multicast Wakeup Enable */ +#define ATL1_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ + +/* Error Codes */ +#define ATL1_SUCCESS 0 +#define ATL1_ERR_EEPROM 1 +#define ATL1_ERR_PHY 2 +#define ATL1_ERR_CONFIG 3 +#define ATL1_ERR_PARAM 4 +#define ATL1_ERR_MAC_TYPE 5 +#define ATL1_ERR_PHY_TYPE 6 +#define ATL1_ERR_PHY_SPEED 7 +#define ATL1_ERR_PHY_RES 8 + +#define SPEED_0 0xffff +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define MEDIA_TYPE_AUTO_SENSOR 0 +#define MEDIA_TYPE_1000M_FULL 1 +#define MEDIA_TYPE_100M_FULL 2 +#define MEDIA_TYPE_100M_HALF 3 +#define MEDIA_TYPE_10M_FULL 4 +#define MEDIA_TYPE_10M_HALF 5 + +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 +#define ADVERTISE_1000_FULL 0x0020 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds */ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds */ + +/* The size (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* with FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* with FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x2800 + +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ + +/* For checksumming , the sum of all words in the EEPROM should equal 0xBABA */ +#define EEPROM_SUM 0xBABA +#define NODE_ADDRESS_SIZE 6 + +/* Statistics counters collected by the MAC */ +struct stats_msg_block { + /* rx */ + u32 rx_ok; /* The number of good packet received. */ + u32 rx_bcast; /* The number of good broadcast packet received. */ + u32 rx_mcast; /* The number of good multicast packet received. */ + u32 rx_pause; /* The number of Pause packet received. */ + u32 rx_ctrl; /* The number of Control packet received other than Pause frame. */ + u32 rx_fcs_err; /* The number of packets with bad FCS. */ + u32 rx_len_err; /* The number of packets with mismatch of length field and actual size. */ + u32 rx_byte_cnt; /* The number of bytes of good packet received. FCS is NOT included. */ + u32 rx_runt; /* The number of packets received that are less than 64 byte long and with good FCS. */ + u32 rx_frag; /* The number of packets received that are less than 64 byte long and with bad FCS. */ + u32 rx_sz_64; /* The number of good and bad packets received that are 64 byte long. */ + u32 rx_sz_65_127; /* The number of good and bad packets received that are between 65 and 127-byte long. */ + u32 rx_sz_128_255; /* The number of good and bad packets received that are between 128 and 255-byte long. */ + u32 rx_sz_256_511; /* The number of good and bad packets received that are between 256 and 511-byte long. */ + u32 rx_sz_512_1023; /* The number of good and bad packets received that are between 512 and 1023-byte long. */ + u32 rx_sz_1024_1518; /* The number of good and bad packets received that are between 1024 and 1518-byte long. */ + u32 rx_sz_1519_max; /* The number of good and bad packets received that are between 1519-byte and MTU. */ + u32 rx_sz_ov; /* The number of good and bad packets received that are more than MTU size Å¡C truncated by Selene. */ + u32 rx_rxf_ov; /* The number of frame dropped due to occurrence of RX FIFO overflow. */ + u32 rx_rrd_ov; /* The number of frame dropped due to occurrence of RRD overflow. */ + u32 rx_align_err; /* Alignment Error */ + u32 rx_bcast_byte_cnt; /* The byte count of broadcast packet received, excluding FCS. */ + u32 rx_mcast_byte_cnt; /* The byte count of multicast packet received, excluding FCS. */ + u32 rx_err_addr; /* The number of packets dropped due to address filtering. */ + + /* tx */ + u32 tx_ok; /* The number of good packet transmitted. */ + u32 tx_bcast; /* The number of good broadcast packet transmitted. */ + u32 tx_mcast; /* The number of good multicast packet transmitted. */ + u32 tx_pause; /* The number of Pause packet transmitted. */ + u32 tx_exc_defer; /* The number of packets transmitted with excessive deferral. */ + u32 tx_ctrl; /* The number of packets transmitted is a control frame, excluding Pause frame. */ + u32 tx_defer; /* The number of packets transmitted that is deferred. */ + u32 tx_byte_cnt; /* The number of bytes of data transmitted. FCS is NOT included. */ + u32 tx_sz_64; /* The number of good and bad packets transmitted that are 64 byte long. */ + u32 tx_sz_65_127; /* The number of good and bad packets transmitted that are between 65 and 127-byte long. */ + u32 tx_sz_128_255; /* The number of good and bad packets transmitted that are between 128 and 255-byte long. */ + u32 tx_sz_256_511; /* The number of good and bad packets transmitted that are between 256 and 511-byte long. */ + u32 tx_sz_512_1023; /* The number of good and bad packets transmitted that are between 512 and 1023-byte long. */ + u32 tx_sz_1024_1518; /* The number of good and bad packets transmitted that are between 1024 and 1518-byte long. */ + u32 tx_sz_1519_max; /* The number of good and bad packets transmitted that are between 1519-byte and MTU. */ + u32 tx_1_col; /* The number of packets subsequently transmitted successfully with a single prior collision. */ + u32 tx_2_col; /* The number of packets subsequently transmitted successfully with multiple prior collisions. */ + u32 tx_late_col; /* The number of packets transmitted with late collisions. */ + u32 tx_abort_col; /* The number of transmit packets aborted due to excessive collisions. */ + u32 tx_underrun; /* The number of transmit packets aborted due to transmit FIFO underrun, or TRD FIFO underrun */ + u32 tx_rd_eop; /* The number of times that read beyond the EOP into the next frame area when TRD was not written timely */ + u32 tx_len_err; /* The number of transmit packets with length field does NOT match the actual frame size. */ + u32 tx_trunc; /* The number of transmit packets truncated due to size exceeding MTU. */ + u32 tx_bcast_byte; /* The byte count of broadcast packet transmitted, excluding FCS. */ + u32 tx_mcast_byte; /* The byte count of multicast packet transmitted, excluding FCS. */ + u32 smb_updated; /* 1: SMB Updated. This is used by software as the indication of the statistics update. + * Software should clear this bit as soon as retrieving the statistics information. */ +}; + +/* Coalescing Message Block */ +struct coals_msg_block { + u32 int_stats; /* interrupt status */ + u16 rrd_prod_idx; /* TRD Producer Index. */ + u16 rfd_cons_idx; /* RFD Consumer Index. */ + u16 update; /* Selene sets this bit every time it DMA the CMB to host memory. + * Software supposes to clear this bit when CMB information is processed. */ + u16 tpd_cons_idx; /* TPD Consumer Index. */ +}; + +/* RRD descriptor */ +struct rx_return_desc { + u8 num_buf; /* Number of RFD buffers used by the received packet */ + u8 resved; + u16 buf_indx; /* RFD Index of the first buffer */ + union { + u32 valid; + struct { + u16 rx_chksum; + u16 pkt_size; + } xsum_sz; + } xsz; + + u16 pkt_flg; /* Packet flags */ + u16 err_flg; /* Error flags */ + u16 resved2; + u16 vlan_tag; /* VLAN TAG */ +}; + +#define PACKET_FLAG_ETH_TYPE 0x0080 +#define PACKET_FLAG_VLAN_INS 0x0100 +#define PACKET_FLAG_ERR 0x0200 +#define PACKET_FLAG_IPV4 0x0400 +#define PACKET_FLAG_UDP 0x0800 +#define PACKET_FLAG_TCP 0x1000 +#define PACKET_FLAG_BCAST 0x2000 +#define PACKET_FLAG_MCAST 0x4000 +#define PACKET_FLAG_PAUSE 0x8000 + +#define ERR_FLAG_CRC 0x0001 +#define ERR_FLAG_CODE 0x0002 +#define ERR_FLAG_DRIBBLE 0x0004 +#define ERR_FLAG_RUNT 0x0008 +#define ERR_FLAG_OV 0x0010 +#define ERR_FLAG_TRUNC 0x0020 +#define ERR_FLAG_IP_CHKSUM 0x0040 +#define ERR_FLAG_L4_CHKSUM 0x0080 +#define ERR_FLAG_LEN 0x0100 +#define ERR_FLAG_DES_ADDR 0x0200 + +/* RFD descriptor */ +struct rx_free_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + __le16 buf_len; /* Size of the receive buffer in host memory, in byte */ + u16 coalese; /* Update consumer index to host after the reception of this frame */ + /* __attribute__ ((packed)) is required */ +} __attribute__ ((packed)); + +/* tsopu defines */ +#define TSO_PARAM_BUFLEN_MASK 0x3FFF +#define TSO_PARAM_BUFLEN_SHIFT 0 +#define TSO_PARAM_DMAINT_MASK 0x0001 +#define TSO_PARAM_DMAINT_SHIFT 14 +#define TSO_PARAM_PKTNT_MASK 0x0001 +#define TSO_PARAM_PKTINT_SHIFT 15 +#define TSO_PARAM_VLANTAG_MASK 0xFFFF +#define TSO_PARAM_VLAN_SHIFT 16 + +/* tsopl defines */ +#define TSO_PARAM_EOP_MASK 0x0001 +#define TSO_PARAM_EOP_SHIFT 0 +#define TSO_PARAM_COALESCE_MASK 0x0001 +#define TSO_PARAM_COALESCE_SHIFT 1 +#define TSO_PARAM_INSVLAG_MASK 0x0001 +#define TSO_PARAM_INSVLAG_SHIFT 2 +#define TSO_PARAM_CUSTOMCKSUM_MASK 0x0001 +#define TSO_PARAM_CUSTOMCKSUM_SHIFT 3 +#define TSO_PARAM_SEGMENT_MASK 0x0001 +#define TSO_PARAM_SEGMENT_SHIFT 4 +#define TSO_PARAM_IPCKSUM_MASK 0x0001 +#define TSO_PARAM_IPCKSUM_SHIFT 5 +#define TSO_PARAM_TCPCKSUM_MASK 0x0001 +#define TSO_PARAM_TCPCKSUM_SHIFT 6 +#define TSO_PARAM_UDPCKSUM_MASK 0x0001 +#define TSO_PARAM_UDPCKSUM_SHIFT 7 +#define TSO_PARAM_VLANTAGGED_MASK 0x0001 +#define TSO_PARAM_VLANTAGGED_SHIFT 8 +#define TSO_PARAM_ETHTYPE_MASK 0x0001 +#define TSO_PARAM_ETHTYPE_SHIFT 9 +#define TSO_PARAM_IPHL_MASK 0x000F +#define TSO_PARAM_IPHL_SHIFT 10 +#define TSO_PARAM_TCPHDRLEN_MASK 0x000F +#define TSO_PARAM_TCPHDRLEN_SHIFT 14 +#define TSO_PARAM_HDRFLAG_MASK 0x0001 +#define TSO_PARAM_HDRFLAG_SHIFT 18 +#define TSO_PARAM_MSS_MASK 0x1FFF +#define TSO_PARAM_MSS_SHIFT 19 + +/* csumpu defines */ +#define CSUM_PARAM_BUFLEN_MASK 0x3FFF +#define CSUM_PARAM_BUFLEN_SHIFT 0 +#define CSUM_PARAM_DMAINT_MASK 0x0001 +#define CSUM_PARAM_DMAINT_SHIFT 14 +#define CSUM_PARAM_PKTINT_MASK 0x0001 +#define CSUM_PARAM_PKTINT_SHIFT 15 +#define CSUM_PARAM_VALANTAG_MASK 0xFFFF +#define CSUM_PARAM_VALAN_SHIFT 16 + +/* csumpl defines*/ +#define CSUM_PARAM_EOP_MASK 0x0001 +#define CSUM_PARAM_EOP_SHIFT 0 +#define CSUM_PARAM_COALESCE_MASK 0x0001 +#define CSUM_PARAM_COALESCE_SHIFT 1 +#define CSUM_PARAM_INSVLAG_MASK 0x0001 +#define CSUM_PARAM_INSVLAG_SHIFT 2 +#define CSUM_PARAM_CUSTOMCKSUM_MASK 0x0001 +#define CSUM_PARAM_CUSTOMCKSUM_SHIFT 3 +#define CSUM_PARAM_SEGMENT_MASK 0x0001 +#define CSUM_PARAM_SEGMENT_SHIFT 4 +#define CSUM_PARAM_IPCKSUM_MASK 0x0001 +#define CSUM_PARAM_IPCKSUM_SHIFT 5 +#define CSUM_PARAM_TCPCKSUM_MASK 0x0001 +#define CSUM_PARAM_TCPCKSUM_SHIFT 6 +#define CSUM_PARAM_UDPCKSUM_MASK 0x0001 +#define CSUM_PARAM_UDPCKSUM_SHIFT 7 +#define CSUM_PARAM_VLANTAGGED_MASK 0x0001 +#define CSUM_PARAM_VLANTAGGED_SHIFT 8 +#define CSUM_PARAM_ETHTYPE_MASK 0x0001 +#define CSUM_PARAM_ETHTYPE_SHIFT 9 +#define CSUM_PARAM_IPHL_MASK 0x000F +#define CSUM_PARAM_IPHL_SHIFT 10 +#define CSUM_PARAM_PLOADOFFSET_MASK 0x00FF +#define CSUM_PARAM_PLOADOFFSET_SHIFT 16 +#define CSUM_PARAM_XSUMOFFSET_MASK 0x00FF +#define CSUM_PARAM_XSUMOFFSET_SHIFT 24 + +/* TPD descriptor */ +struct tso_param { + /* The order of these declarations is important -- don't change it */ + u32 tsopu; /* tso_param upper word */ + u32 tsopl; /* tso_param lower word */ +}; + +struct csum_param { + /* The order of these declarations is important -- don't change it */ + u32 csumpu; /* csum_param upper word */ + u32 csumpl; /* csum_param lower word */ +}; + +union tpd_descr { + u64 data; + struct csum_param csum; + struct tso_param tso; +}; + +struct tx_packet_desc { + __le64 buffer_addr; + union tpd_descr desc; +}; + +/* DMA Order Settings */ +enum atl1_dma_order { + atl1_dma_ord_in = 1, + atl1_dma_ord_enh = 2, + atl1_dma_ord_out = 4 +}; + +enum atl1_dma_rcb { + atl1_rcb_64 = 0, + atl1_rcb_128 = 1 +}; + +enum atl1_dma_req_block { + atl1_dma_req_128 = 0, + atl1_dma_req_256 = 1, + atl1_dma_req_512 = 2, + atl1_dam_req_1024 = 3, + atl1_dam_req_2048 = 4, + atl1_dma_req_4096 = 5 +}; + +struct atl1_spi_flash_dev { + const char *manu_name; /* manufacturer id */ + /* op-code */ + u8 cmd_wrsr; + u8 cmd_read; + u8 cmd_program; + u8 cmd_wren; + u8 cmd_wrdi; + u8 cmd_rdsr; + u8 cmd_rdid; + u8 cmd_sector_erase; + u8 cmd_chip_erase; +}; + +#endif /* _ATL1_HW_H_ */ diff -puN /dev/null drivers/net/atl1/atl1_main.c --- /dev/null +++ a/drivers/net/atl1/atl1_main.c @@ -0,0 +1,2490 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + * + * Contact Information: + * Xiong Huang + * Attansic Technology Corp. 3F 147, Xianzheng 9th Road, Zhubei, + * Xinzhu 302, TAIWAN, REPUBLIC OF CHINA + * + * Chris Snook + * Jay Cliburn + * + * This version is adapted from the Attansic reference driver for + * inclusion in the Linux kernel. It is currently under heavy development. + * A very incomplete list of things that need to be dealt with: + * + * TODO: + * Fix TSO; tx performance is horrible with TSO enabled. + * Wake on LAN. + * Add more ethtool functions, including set ring parameters. + * Fix abstruse irq enable/disable condition described here: + * http://marc.theaimsgroup.com/?l=linux-netdev&m=116398508500553&w=2 + * + * NEEDS TESTING: + * VLAN + * multicast + * promiscuous mode + * interrupt coalescing + * SMP torture testing + **/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "atl1.h" + +#define RUN_REALTIME 0 +#define DRIVER_VERSION "2.0.5" + +char atl1_driver_name[] = "atl1"; +static const char atl1_driver_string[] = "Attansic L1 Ethernet Network Driver"; +static const char atl1_copyright[] = "Copyright(c) 2005-2006 Attansic Corporation."; +char atl1_driver_version[] = DRIVER_VERSION; + +MODULE_AUTHOR + ("Attansic Corporation , Chris Snook , Jay Cliburn "); +MODULE_DESCRIPTION("Attansic 1000M Ethernet Network Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +extern struct ethtool_ops atl1_ethtool_ops; + +/** + * atl1_pci_tbl - PCI Device ID Table + **/ +static const struct pci_device_id atl1_pci_tbl[] = { + {PCI_DEVICE(0x1969, 0x1048)}, + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, atl1_pci_tbl); + +/** + * atl1_sw_init - Initialize general software structures (struct atl1_adapter) + * @adapter: board private structure to initialize + * + * atl1_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ +static int __devinit atl1_sw_init(struct atl1_adapter *adapter) +{ + struct atl1_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + + /* PCI config space info */ + pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word); + + hw->max_frame_size = netdev->mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; + hw->min_frame_size = MINIMUM_ETHERNET_FRAME_SIZE; + + adapter->wol = 0; + adapter->rx_buffer_len = (hw->max_frame_size + 7) & ~7; + adapter->ict = 50000; /* 100ms */ + adapter->link_speed = SPEED_0; /* hardware init */ + adapter->link_duplex = FULL_DUPLEX; + + hw->phy_configured = false; + hw->preamble_len = 7; + hw->ipgt = 0x60; + hw->min_ifg = 0x50; + hw->ipgr1 = 0x40; + hw->ipgr2 = 0x60; + hw->max_retry = 0xf; + hw->lcol = 0x37; + hw->jam_ipg = 7; + hw->rfd_burst = 8; + hw->rrd_burst = 8; + hw->rfd_fetch_gap = 1; + hw->rx_jumbo_th = adapter->rx_buffer_len / 8; + hw->rx_jumbo_lkah = 1; + hw->rrd_ret_timer = 16; + hw->tpd_burst = 4; + hw->tpd_fetch_th = 16; + hw->txf_burst = 0x100; + hw->tx_jumbo_task_th = (hw->max_frame_size + 7) >> 3; + hw->tpd_fetch_gap = 1; + hw->rcb_value = atl1_rcb_64; + hw->dma_ord = atl1_dma_ord_enh; + hw->dmar_block = atl1_dma_req_256; + hw->dmaw_block = atl1_dma_req_256; + hw->cmb_rrd = 4; + hw->cmb_tpd = 4; + hw->cmb_rx_timer = 1; /* about 2us */ + hw->cmb_tx_timer = 1; /* about 2us */ + hw->smb_timer = 100000; /* about 200ms */ + + atomic_set(&adapter->irq_sem, 0); + spin_lock_init(&adapter->lock); + spin_lock_init(&adapter->mb_lock); + + return 0; +} + +/** + * atl1_setup_mem_resources - allocate Tx / RX descriptor resources + * @adapter: board private structure + * + * Return 0 on success, negative on failure + **/ +s32 atl1_setup_ring_resources(struct atl1_adapter * adapter) +{ + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; + struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; + struct atl1_ring_header *ring_header = &adapter->ring_header; + struct pci_dev *pdev = adapter->pdev; + int size; + u8 offset = 0; + + size = sizeof(struct atl1_buffer) * (tpd_ring->count + rfd_ring->count); + tpd_ring->buffer_info = kzalloc(size, GFP_KERNEL); + if (unlikely(!tpd_ring->buffer_info)) { + printk(KERN_WARNING "%s: kzalloc failed , size = D%d\n", + atl1_driver_name, size); + goto err_nomem; + } + rfd_ring->buffer_info = + (struct atl1_buffer *)(tpd_ring->buffer_info + tpd_ring->count); + + /* real ring DMA buffer */ + ring_header->size = size = sizeof(struct tx_packet_desc) * + tpd_ring->count + + sizeof(struct rx_free_desc) * rfd_ring->count + + sizeof(struct rx_return_desc) * rrd_ring->count + + sizeof(struct coals_msg_block) + + sizeof(struct stats_msg_block) + + 40; /* "40: for 8 bytes align" huh? -- CHS */ + + ring_header->desc = pci_alloc_consistent(pdev, ring_header->size, + &ring_header->dma); + if (unlikely(!ring_header->desc)) { + printk(KERN_WARNING + "%s: pci_alloc_consistent failed, size = D%d\n", + atl1_driver_name, size); + goto err_nomem; + } + + if (adapter->pci_using_64) { + /* test whether HIDWORD dma buffer is not cross boundary */ + if (unlikely(((ring_header->dma & 0xffffffff00000000ULL) >> 32) + != (((ring_header->dma + size) & 0xffffffff00000000ULL) >> + 32))) { + pci_free_consistent(pdev, ring_header->size, + ring_header->desc, + ring_header->dma); + printk(KERN_DEBUG + "%s: memory allocated across 32bit boundary\n", + atl1_driver_name); + goto err_nomem; + } + } + + memset(ring_header->desc, 0, ring_header->size); + + /* init TPD ring */ + tpd_ring->dma = ring_header->dma; + offset = (tpd_ring->dma & 0x7) ? (8 - (ring_header->dma & 0x7)) : 0; + tpd_ring->dma += offset; + tpd_ring->desc = (u8 *) ring_header->desc + offset; + tpd_ring->size = sizeof(struct tx_packet_desc) * tpd_ring->count; + atomic_set(&tpd_ring->next_to_use, 0); + atomic_set(&tpd_ring->next_to_clean, 0); + + /* init RFD ring */ + rfd_ring->dma = tpd_ring->dma + tpd_ring->size; + offset = (rfd_ring->dma & 0x7) ? (8 - (rfd_ring->dma & 0x7)) : 0; + rfd_ring->dma += offset; + rfd_ring->desc = (u8 *) tpd_ring->desc + (tpd_ring->size + offset); + rfd_ring->size = sizeof(struct rx_free_desc) * rfd_ring->count; + rfd_ring->next_to_clean = 0; + /* rfd_ring->next_to_use = rfd_ring->count - 1; */ + atomic_set(&rfd_ring->next_to_use, 0); + + /* init RRD ring */ + rrd_ring->dma = rfd_ring->dma + rfd_ring->size; + offset = (rrd_ring->dma & 0x7) ? (8 - (rrd_ring->dma & 0x7)) : 0; + rrd_ring->dma += offset; + rrd_ring->desc = (u8 *) rfd_ring->desc + (rfd_ring->size + offset); + rrd_ring->size = sizeof(struct rx_return_desc) * rrd_ring->count; + rrd_ring->next_to_use = 0; + atomic_set(&rrd_ring->next_to_clean, 0); + + /* init CMB */ + adapter->cmb.dma = rrd_ring->dma + rrd_ring->size; + offset = (adapter->cmb.dma & 0x7) ? (8 - (adapter->cmb.dma & 0x7)) : 0; + adapter->cmb.dma += offset; + adapter->cmb.cmb = + (struct coals_msg_block *) ((u8 *) rrd_ring->desc + + (rrd_ring->size + offset)); + + /* init SMB */ + adapter->smb.dma = adapter->cmb.dma + sizeof(struct coals_msg_block); + offset = (adapter->smb.dma & 0x7) ? (8 - (adapter->smb.dma & 0x7)) : 0; + adapter->smb.dma += offset; + adapter->smb.smb = (struct stats_msg_block *) + ((u8 *) adapter->cmb.cmb + (sizeof(struct coals_msg_block) + offset)); + + return ATL1_SUCCESS; + +err_nomem: + if (tpd_ring->buffer_info) + kfree(tpd_ring->buffer_info); + return -ENOMEM; +} + +/** + * atl1_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ +static void atl1_irq_enable(struct atl1_adapter *adapter) +{ + if (likely(!atomic_dec_and_test(&adapter->irq_sem))) + iowrite32(IMR_NORMAL_MASK, adapter->hw.hw_addr + REG_IMR); +} + +static void atl1_clear_phy_int(struct atl1_adapter *adapter) +{ + u16 phy_data; + spin_lock(&adapter->lock); + atl1_read_phy_reg(&adapter->hw, 19, &phy_data); + spin_unlock(&adapter->lock); +} + +static void atl1_inc_smb(struct atl1_adapter *adapter) +{ + struct stats_msg_block *smb = adapter->smb.smb; + + /* Fill out the OS statistics structure */ + adapter->soft_stats.rx_packets += smb->rx_ok; + adapter->soft_stats.tx_packets += smb->tx_ok; + adapter->soft_stats.rx_bytes += smb->rx_byte_cnt; + adapter->soft_stats.tx_bytes += smb->tx_byte_cnt; + adapter->soft_stats.multicast += smb->rx_mcast; + adapter->soft_stats.collisions += (smb->tx_1_col + + smb->tx_2_col * 2 + + smb->tx_late_col + + smb->tx_abort_col * + adapter->hw.max_retry); + + /* Rx Errors */ + adapter->soft_stats.rx_errors += (smb->rx_frag + + smb->rx_fcs_err + + smb->rx_len_err + + smb->rx_sz_ov + + smb->rx_rxf_ov + + smb->rx_rrd_ov + smb->rx_align_err); + adapter->soft_stats.rx_fifo_errors += smb->rx_rxf_ov; + adapter->soft_stats.rx_length_errors += smb->rx_len_err; + adapter->soft_stats.rx_crc_errors += smb->rx_fcs_err; + adapter->soft_stats.rx_frame_errors += smb->rx_align_err; + adapter->soft_stats.rx_missed_errors += (smb->rx_rrd_ov + + smb->rx_rxf_ov); + + adapter->soft_stats.rx_pause += smb->rx_pause; + adapter->soft_stats.rx_rrd_ov += smb->rx_rrd_ov; + adapter->soft_stats.rx_trunc += smb->rx_sz_ov; + + /* Tx Errors */ + adapter->soft_stats.tx_errors += (smb->tx_late_col + + smb->tx_abort_col + + smb->tx_underrun + smb->tx_trunc); + adapter->soft_stats.tx_fifo_errors += smb->tx_underrun; + adapter->soft_stats.tx_aborted_errors += smb->tx_abort_col; + adapter->soft_stats.tx_window_errors += smb->tx_late_col; + + adapter->soft_stats.excecol += smb->tx_abort_col; + adapter->soft_stats.deffer += smb->tx_defer; + adapter->soft_stats.scc += smb->tx_1_col; + adapter->soft_stats.mcc += smb->tx_2_col; + adapter->soft_stats.latecol += smb->tx_late_col; + adapter->soft_stats.tx_underun += smb->tx_underrun; + adapter->soft_stats.tx_trunc += smb->tx_trunc; + adapter->soft_stats.tx_pause += smb->tx_pause; + + adapter->net_stats.rx_packets = adapter->soft_stats.rx_packets; + adapter->net_stats.tx_packets = adapter->soft_stats.tx_packets; + adapter->net_stats.rx_bytes = adapter->soft_stats.rx_bytes; + adapter->net_stats.tx_bytes = adapter->soft_stats.tx_bytes; + adapter->net_stats.multicast = adapter->soft_stats.multicast; + adapter->net_stats.collisions = adapter->soft_stats.collisions; + adapter->net_stats.rx_errors = adapter->soft_stats.rx_errors; + adapter->net_stats.rx_over_errors = + adapter->soft_stats.rx_missed_errors; + adapter->net_stats.rx_length_errors = + adapter->soft_stats.rx_length_errors; + adapter->net_stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors; + adapter->net_stats.rx_frame_errors = + adapter->soft_stats.rx_frame_errors; + adapter->net_stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors; + adapter->net_stats.rx_missed_errors = + adapter->soft_stats.rx_missed_errors; + adapter->net_stats.tx_errors = adapter->soft_stats.tx_errors; + adapter->net_stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors; + adapter->net_stats.tx_aborted_errors = + adapter->soft_stats.tx_aborted_errors; + adapter->net_stats.tx_window_errors = + adapter->soft_stats.tx_window_errors; + adapter->net_stats.tx_carrier_errors = + adapter->soft_stats.tx_carrier_errors; +} + +static void atl1_rx_checksum(struct atl1_adapter *adapter, + struct rx_return_desc * rrd, + struct sk_buff *skb) +{ + skb->ip_summed = CHECKSUM_NONE; + + if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { + if (rrd->err_flg & (ERR_FLAG_CRC | ERR_FLAG_TRUNC | + ERR_FLAG_CODE | ERR_FLAG_OV)) { + adapter->hw_csum_err++; + printk(KERN_DEBUG "%s: rx checksum error\n", + atl1_driver_name); + return; + } + } + + /* not IPv4 */ + if (!(rrd->pkt_flg & PACKET_FLAG_IPV4)) + /* checksum is invalid, but it's not an IPv4 pkt, so ok */ + return; + + /* IPv4 packet */ + if (likely(!(rrd->err_flg & + (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM)))) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + adapter->hw_csum_good++; + return; + } + + /* IPv4, but hardware thinks its checksum is wrong */ + printk(KERN_DEBUG "%s: hw csum wrong pkt_flag:%x, err_flag:%x\n", + atl1_driver_name, rrd->pkt_flg, rrd->err_flg); + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = htons(rrd->xsz.xsum_sz.rx_chksum); + adapter->hw_csum_err++; + return; +} + +/** + * atl1_alloc_rx_buffers - Replace used receive buffers + * @adapter: address of board private structure + **/ +static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) +{ + struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + struct page *page; + unsigned long offset; + struct atl1_buffer *buffer_info, *next_info; + struct sk_buff *skb; + u16 num_alloc = 0; + u16 rfd_next_to_use, next_next; + struct rx_free_desc *rfd_desc; + + next_next = rfd_next_to_use = atomic_read(&rfd_ring->next_to_use); + if (++next_next == rfd_ring->count) + next_next = 0; + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + + while (!buffer_info->alloced && !next_info->alloced) { + if (buffer_info->skb) { + buffer_info->alloced = 1; + goto next; + } + + rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use); + + skb = dev_alloc_skb(adapter->rx_buffer_len + NET_IP_ALIGN); + if (unlikely(!skb)) { /* Better luck next round */ + adapter->net_stats.rx_dropped++; + break; + } + + /** + * Make buffer alignment 2 beyond a 16 byte boundary + * this will result in a 16 byte aligned IP header after + * the 14 byte MAC header is removed + **/ + skb_reserve(skb, NET_IP_ALIGN); + skb->dev = netdev; + + buffer_info->alloced = 1; + buffer_info->skb = skb; + buffer_info->length = (u16) adapter->rx_buffer_len; + page = virt_to_page(skb->data); + offset = (unsigned long)skb->data & ~PAGE_MASK; + buffer_info->dma = pci_map_page(pdev, page, offset, + adapter->rx_buffer_len, + PCI_DMA_FROMDEVICE); + rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); + rfd_desc->buf_len = cpu_to_le16(adapter->rx_buffer_len); + rfd_desc->coalese = 0; + +next: + rfd_next_to_use = next_next; + if (unlikely(++next_next == rfd_ring->count)) + next_next = 0; + + buffer_info = &rfd_ring->buffer_info[rfd_next_to_use]; + next_info = &rfd_ring->buffer_info[next_next]; + num_alloc++; + } + + if (num_alloc) { + /** + * Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + **/ + wmb(); + atomic_set(&rfd_ring->next_to_use, (int)rfd_next_to_use); + } + return num_alloc; +} + +static void atl1_intr_rx(struct atl1_adapter *adapter) +{ + int i, count; + u16 length; + u16 rrd_next_to_clean; + u32 value; + struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; + struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; + struct atl1_buffer *buffer_info; + struct rx_return_desc *rrd; + struct sk_buff *skb; + + count = 0; + + rrd_next_to_clean = atomic_read(&rrd_ring->next_to_clean); + + while (1) { + rrd = ATL1_RRD_DESC(rrd_ring, rrd_next_to_clean); + i = 1; + if (likely(rrd->xsz.valid)) { /* packet valid */ +chk_rrd: + /* check rrd status */ + if (likely(rrd->num_buf == 1)) + goto rrd_ok; + + /* rrd seems to be bad */ + if (unlikely(i-- > 0)) { + /* rrd may not be DMAed completely */ + printk(KERN_DEBUG + "%s: RRD may not be DMAed completely\n", + atl1_driver_name); + udelay(1); + goto chk_rrd; + } + /* bad rrd */ + printk(KERN_DEBUG "%s: bad RRD\n", atl1_driver_name); + /* see if update RFD index */ + if (rrd->num_buf > 1) { + u16 num_buf; + num_buf = + (rrd->xsz.xsum_sz.pkt_size + + adapter->rx_buffer_len - + 1) / adapter->rx_buffer_len; + if (rrd->num_buf == num_buf) { + /* clean alloc flag for bad rrd */ + while (rfd_ring->next_to_clean != + (rrd->buf_indx + num_buf)) { + rfd_ring->buffer_info[rfd_ring-> + next_to_clean].alloced = 0; + if (++rfd_ring->next_to_clean == + rfd_ring->count) { + rfd_ring-> + next_to_clean = 0; + } + } + } + } + + /* update rrd */ + rrd->xsz.valid = 0; + if (++rrd_next_to_clean == rrd_ring->count) + rrd_next_to_clean = 0; + count++; + continue; + } else { /* current rrd still not be updated */ + + break; + } +rrd_ok: + /* clean alloc flag for bad rrd */ + while (rfd_ring->next_to_clean != rrd->buf_indx) { + rfd_ring->buffer_info[rfd_ring->next_to_clean].alloced = + 0; + if (++rfd_ring->next_to_clean == rfd_ring->count) + rfd_ring->next_to_clean = 0; + } + + buffer_info = &rfd_ring->buffer_info[rrd->buf_indx]; + if (++rfd_ring->next_to_clean == rfd_ring->count) + rfd_ring->next_to_clean = 0; + + /* update rrd next to clean */ + if (++rrd_next_to_clean == rrd_ring->count) + rrd_next_to_clean = 0; + count++; + + if (unlikely(rrd->pkt_flg & PACKET_FLAG_ERR)) { + if (!(rrd->err_flg & + (ERR_FLAG_IP_CHKSUM | ERR_FLAG_L4_CHKSUM + | ERR_FLAG_LEN))) { + /* packet error, don't need upstream */ + buffer_info->alloced = 0; + rrd->xsz.valid = 0; + continue; + } + } + + /* Good Receive */ + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_FROMDEVICE); + skb = buffer_info->skb; + length = le16_to_cpu(rrd->xsz.xsum_sz.pkt_size); + + skb_put(skb, length - ETHERNET_FCS_SIZE); + + /* Receive Checksum Offload */ + atl1_rx_checksum(adapter, rrd, skb); + skb->protocol = eth_type_trans(skb, adapter->netdev); + + if (adapter->vlgrp && (rrd->pkt_flg & PACKET_FLAG_VLAN_INS)) { + u16 vlan_tag = (rrd->vlan_tag >> 4) | + ((rrd->vlan_tag & 7) << 13) | + ((rrd->vlan_tag & 8) << 9); + vlan_hwaccel_rx(skb, adapter->vlgrp, vlan_tag); + } else + netif_rx(skb); + + /* let protocol layer free skb */ + buffer_info->skb = NULL; + buffer_info->alloced = 0; + rrd->xsz.valid = 0; + + adapter->netdev->last_rx = jiffies; + } + + atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean); + + atl1_alloc_rx_buffers(adapter); + + /* update mailbox ? */ + if (count) { + u32 tpd_next_to_use; + u32 rfd_next_to_use; + u32 rrd_next_to_clean; + + spin_lock(&adapter->mb_lock); + + tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); + rfd_next_to_use = + atomic_read(&adapter->rfd_ring.next_to_use); + rrd_next_to_clean = + atomic_read(&adapter->rrd_ring.next_to_clean); + value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << + MB_RFD_PROD_INDX_SHIFT) | + ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << + MB_RRD_CONS_INDX_SHIFT) | + ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << + MB_TPD_PROD_INDX_SHIFT); + iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); + spin_unlock(&adapter->mb_lock); + } +} + +static void atl1_intr_tx(struct atl1_adapter *adapter) +{ + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_buffer *buffer_info; + u16 sw_tpd_next_to_clean; + u16 cmb_tpd_next_to_clean; + u8 update = 0; + + sw_tpd_next_to_clean = atomic_read(&tpd_ring->next_to_clean); + cmb_tpd_next_to_clean = le16_to_cpu(adapter->cmb.cmb->tpd_cons_idx); + + while (cmb_tpd_next_to_clean != sw_tpd_next_to_clean) { + struct tx_packet_desc *tpd; + update = 1; + tpd = ATL1_TPD_DESC(tpd_ring, sw_tpd_next_to_clean); + buffer_info = &tpd_ring->buffer_info[sw_tpd_next_to_clean]; + if (buffer_info->dma) { + pci_unmap_page(adapter->pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } + + if (buffer_info->skb) { + dev_kfree_skb_irq(buffer_info->skb); + buffer_info->skb = NULL; + } + tpd->buffer_addr = 0; + tpd->desc.data = 0; + + if (++sw_tpd_next_to_clean == tpd_ring->count) + sw_tpd_next_to_clean = 0; + } + atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean); + + if (netif_queue_stopped(adapter->netdev) + && netif_carrier_ok(adapter->netdev)) + netif_wake_queue(adapter->netdev); +} + +static void atl1_check_for_link(struct atl1_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + u16 phy_data = 0; + + spin_lock(&adapter->lock); + adapter->phy_timer_pending = false; + atl1_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + atl1_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + spin_unlock(&adapter->lock); + + /* notify upper layer link down ASAP */ + if (!(phy_data & BMSR_LSTATUS)) { /* Link Down */ + if (netif_carrier_ok(netdev)) { /* old link state: Up */ + printk(KERN_INFO "%s: %s link is down\n", + atl1_driver_name, netdev->name); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } + schedule_work(&adapter->link_chg_task); +} + +/** + * atl1_intr - Interrupt Handler + * @irq: interrupt number + * @data: pointer to a network interface device structure + * @pt_regs: CPU registers structure + **/ +static irqreturn_t atl1_intr(int irq, void *data) +{ + /*struct atl1_adapter *adapter = ((struct net_device *)data)->priv;*/ + struct atl1_adapter *adapter = netdev_priv(data); + u32 status; + u8 update_rx; + int max_ints = 10; + + status = adapter->cmb.cmb->int_stats; + if (!status) + return IRQ_NONE; + + update_rx = 0; + + do { + /* clear CMB interrupt status at once */ + adapter->cmb.cmb->int_stats = 0; + + if (status & ISR_GPHY) { /* clear phy status */ + atl1_clear_phy_int(adapter); + } + /* clear ISR status, and Enable CMB DMA/Disable Interrupt */ + iowrite32(status | ISR_DIS_INT, adapter->hw.hw_addr + REG_ISR); + + /* check if SMB intr */ + if (status & ISR_SMB) + atl1_inc_smb(adapter); + + /* check if PCIE PHY Link down */ + if (status & ISR_PHY_LINKDOWN) { + printk(KERN_DEBUG "%s: pcie phy link down %x\n", + atl1_driver_name, status); + if (netif_running(adapter->netdev)) { /* reset MAC */ + iowrite32(0, adapter->hw.hw_addr + REG_IMR); + schedule_work(&adapter->pcie_dma_to_rst_task); + return IRQ_HANDLED; + } + } + + /* check if DMA read/write error ? */ + if (status & (ISR_DMAR_TO_RST | ISR_DMAW_TO_RST)) { + printk(KERN_DEBUG + "%s: pcie DMA r/w error (status = 0x%x)\n", + atl1_driver_name, status); + iowrite32(0, adapter->hw.hw_addr + REG_IMR); + schedule_work(&adapter->pcie_dma_to_rst_task); + return IRQ_HANDLED; + } + + /* link event */ + if (status & ISR_GPHY) { + adapter->soft_stats.tx_carrier_errors++; + atl1_check_for_link(adapter); + } + + /* transmit event */ + if (status & ISR_CMB_TX) + atl1_intr_tx(adapter); + + /* rx exception */ + if (unlikely(status & (ISR_RXF_OV | ISR_RFD_UNRUN | + ISR_RRD_OV | ISR_HOST_RFD_UNRUN | + ISR_HOST_RRD_OV | ISR_CMB_RX))) { + if (status & + (ISR_RXF_OV | ISR_RFD_UNRUN | ISR_RRD_OV | + ISR_HOST_RFD_UNRUN | ISR_HOST_RRD_OV)) { + printk(KERN_INFO + "%s: rx exception: status = 0x%x\n", + atl1_driver_name, status); + } + atl1_intr_rx(adapter); + } + + if (--max_ints < 0) + break; + + } while ((status = adapter->cmb.cmb->int_stats)); + + /* re-enable Interrupt */ + iowrite32(ISR_DIS_SMB | ISR_DIS_DMA, adapter->hw.hw_addr + REG_ISR); + return IRQ_HANDLED; +} + +/** + * atl1_set_multi - Multicast and Promiscuous mode set + * @netdev: network interface device structure + * + * The set_multi entry point is called whenever the multicast address + * list or the network interface flags are updated. This routine is + * responsible for configuring the hardware for proper multicast, + * promiscuous mode, and all-multi behavior. + **/ +static void atl1_set_multi(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + struct dev_mc_list *mc_ptr; + u32 rctl; + u32 hash_value; + + /* Check for Promiscuous and All Multicast modes */ + rctl = ioread32(hw->hw_addr + REG_MAC_CTRL); + if (netdev->flags & IFF_PROMISC) { + rctl |= MAC_CTRL_PROMIS_EN; + } else if (netdev->flags & IFF_ALLMULTI) { + rctl |= MAC_CTRL_MC_ALL_EN; + rctl &= ~MAC_CTRL_PROMIS_EN; + } else { + rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); + } + + iowrite32(rctl, hw->hw_addr + REG_MAC_CTRL); + + /* clear the old settings from the multicast hash table */ + iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); + iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); + + /* compute mc addresses' hash value ,and put it into hash table */ + for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { + hash_value = atl1_hash_mc_addr(hw, mc_ptr->dmi_addr); + atl1_hash_set(hw, hash_value); + } +} + +static void atl1_setup_mac_ctrl(struct atl1_adapter *adapter) +{ + u32 value; + struct atl1_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + /* Config MAC CTRL Register */ + value = MAC_CTRL_TX_EN | MAC_CTRL_RX_EN; + /* duplex */ + if (FULL_DUPLEX == adapter->link_duplex) + value |= MAC_CTRL_DUPLX; + /* speed */ + value |= ((u32) ((SPEED_1000 == adapter->link_speed) ? + MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100) << + MAC_CTRL_SPEED_SHIFT); + /* flow control */ + value |= (MAC_CTRL_TX_FLOW | MAC_CTRL_RX_FLOW); + /* PAD & CRC */ + value |= (MAC_CTRL_ADD_CRC | MAC_CTRL_PAD); + /* preamble length */ + value |= (((u32) adapter->hw.preamble_len + & MAC_CTRL_PRMLEN_MASK) << MAC_CTRL_PRMLEN_SHIFT); + /* vlan */ + if (adapter->vlgrp) + value |= MAC_CTRL_RMV_VLAN; + /* rx checksum + if (adapter->rx_csum) + value |= MAC_CTRL_RX_CHKSUM_EN; + */ + /* filter mode */ + value |= MAC_CTRL_BC_EN; + if (netdev->flags & IFF_PROMISC) + value |= MAC_CTRL_PROMIS_EN; + else if (netdev->flags & IFF_ALLMULTI) + value |= MAC_CTRL_MC_ALL_EN; + /* value |= MAC_CTRL_LOOPBACK; */ + iowrite32(value, hw->hw_addr + REG_MAC_CTRL); +} + +static u32 atl1_check_link(struct atl1_adapter *adapter) +{ + struct atl1_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + u32 ret_val; + u16 speed, duplex, phy_data; + int reconfig = 0; + + /* MII_BMSR must read twice */ + atl1_read_phy_reg(hw, MII_BMSR, &phy_data); + atl1_read_phy_reg(hw, MII_BMSR, &phy_data); + if (!(phy_data & BMSR_LSTATUS)) { /* link down */ + if (netif_carrier_ok(netdev)) { /* old link state: Up */ + printk(KERN_INFO "%s: link is down\n", + atl1_driver_name); + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + return ATL1_SUCCESS; + } + + /* Link Up */ + ret_val = atl1_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) + return ret_val; + + switch (hw->media_type) { + case MEDIA_TYPE_1000M_FULL: + if (speed != SPEED_1000 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_100M_FULL: + if (speed != SPEED_100 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_100M_HALF: + if (speed != SPEED_100 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_FULL: + if (speed != SPEED_10 || duplex != FULL_DUPLEX) + reconfig = 1; + break; + case MEDIA_TYPE_10M_HALF: + if (speed != SPEED_10 || duplex != HALF_DUPLEX) + reconfig = 1; + break; + } + + /* link result is our setting */ + if (!reconfig) { + if (adapter->link_speed != speed + || adapter->link_duplex != duplex) { + adapter->link_speed = speed; + adapter->link_duplex = duplex; + atl1_setup_mac_ctrl(adapter); + printk(KERN_INFO "%s: %s link is up %d Mbps %s\n", + atl1_driver_name, netdev->name, + adapter->link_speed, + adapter->link_duplex == + FULL_DUPLEX ? "full duplex" : "half duplex"); + } + if (!netif_carrier_ok(netdev)) { /* Link down -> Up */ + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + return ATL1_SUCCESS; + } + + /* change orignal link status */ + if (netif_carrier_ok(netdev)) { + adapter->link_speed = SPEED_0; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + + if (hw->media_type != MEDIA_TYPE_AUTO_SENSOR && + hw->media_type != MEDIA_TYPE_1000M_FULL) { + switch (hw->media_type) { + case MEDIA_TYPE_100M_FULL: + phy_data = MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 | + MII_CR_RESET; + break; + case MEDIA_TYPE_100M_HALF: + phy_data = MII_CR_SPEED_100 | MII_CR_RESET; + break; + case MEDIA_TYPE_10M_FULL: + phy_data = + MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET; + break; + default: /* MEDIA_TYPE_10M_HALF: */ + phy_data = MII_CR_SPEED_10 | MII_CR_RESET; + break; + } + atl1_write_phy_reg(hw, MII_BMCR, phy_data); + return ATL1_SUCCESS; + } + + /* auto-neg, insert timer to re-config phy */ + if (!adapter->phy_timer_pending) { + adapter->phy_timer_pending = true; + mod_timer(&adapter->phy_config_timer, jiffies + 3 * HZ); + } + + return ATL1_SUCCESS; +} + +static void set_flow_ctrl_old(struct atl1_adapter *adapter) +{ + u32 hi, lo, value; + + /* RFD Flow Control */ + value = adapter->rfd_ring.count; + hi = value / 16; + if (hi < 2) + hi = 2; + lo = value * 7 / 8; + + value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | + ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); + iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RXF_PAUSE_THRESH); + + /* RRD Flow Control */ + value = adapter->rrd_ring.count; + lo = value / 16; + hi = value * 7 / 8; + if (lo < 2) + lo = 2; + value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | + ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); + iowrite32(value, adapter->hw.hw_addr + REG_RXQ_RRD_PAUSE_THRESH); +} + +static void set_flow_ctrl_new(struct atl1_hw *hw) +{ + u32 hi, lo, value; + + /* RXF Flow Control */ + value = ioread32(hw->hw_addr + REG_SRAM_RXF_LEN); + lo = value / 16; + if (lo < 192) + lo = 192; + hi = value * 7 / 8; + if (hi < lo) + hi = lo + 16; + value = ((hi & RXQ_RXF_PAUSE_TH_HI_MASK) << RXQ_RXF_PAUSE_TH_HI_SHIFT) | + ((lo & RXQ_RXF_PAUSE_TH_LO_MASK) << RXQ_RXF_PAUSE_TH_LO_SHIFT); + iowrite32(value, hw->hw_addr + REG_RXQ_RXF_PAUSE_THRESH); + + /* RRD Flow Control */ + value = ioread32(hw->hw_addr + REG_SRAM_RRD_LEN); + lo = value / 8; + hi = value * 7 / 8; + if (lo < 2) + lo = 2; + if (hi < lo) + hi = lo + 3; + value = ((hi & RXQ_RRD_PAUSE_TH_HI_MASK) << RXQ_RRD_PAUSE_TH_HI_SHIFT) | + ((lo & RXQ_RRD_PAUSE_TH_LO_MASK) << RXQ_RRD_PAUSE_TH_LO_SHIFT); + iowrite32(value, hw->hw_addr + REG_RXQ_RRD_PAUSE_THRESH); +} + +/** + * atl1_configure - Configure Transmit&Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Tx /Rx unit of the MAC after a reset. + **/ +static u32 atl1_configure(struct atl1_adapter *adapter) +{ + struct atl1_hw *hw = &adapter->hw; + u32 value; + + /* clear interrupt status */ + iowrite32(0xffffffff, adapter->hw.hw_addr + REG_ISR); + + /* set MAC Address */ + value = (((u32) hw->mac_addr[2]) << 24) | + (((u32) hw->mac_addr[3]) << 16) | + (((u32) hw->mac_addr[4]) << 8) | + (((u32) hw->mac_addr[5])); + iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR); + value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1])); + iowrite32(value, hw->hw_addr + (REG_MAC_STA_ADDR + 4)); + + /* tx / rx ring */ + + /* HI base address */ + iowrite32((u32) ((adapter->tpd_ring.dma & 0xffffffff00000000ULL) >> 32), + hw->hw_addr + REG_DESC_BASE_ADDR_HI); + /* LO base address */ + iowrite32((u32) (adapter->rfd_ring.dma & 0x00000000ffffffffULL), + hw->hw_addr + REG_DESC_RFD_ADDR_LO); + iowrite32((u32) (adapter->rrd_ring.dma & 0x00000000ffffffffULL), + hw->hw_addr + REG_DESC_RRD_ADDR_LO); + iowrite32((u32) (adapter->tpd_ring.dma & 0x00000000ffffffffULL), + hw->hw_addr + REG_DESC_TPD_ADDR_LO); + iowrite32((u32) (adapter->cmb.dma & 0x00000000ffffffffULL), + hw->hw_addr + REG_DESC_CMB_ADDR_LO); + iowrite32((u32) (adapter->smb.dma & 0x00000000ffffffffULL), + hw->hw_addr + REG_DESC_SMB_ADDR_LO); + + /* element count */ + value = adapter->rrd_ring.count; + value <<= 16; + value += adapter->rfd_ring.count; + iowrite32(value, hw->hw_addr + REG_DESC_RFD_RRD_RING_SIZE); + iowrite32(adapter->tpd_ring.count, hw->hw_addr + REG_DESC_TPD_RING_SIZE); + + /* Load Ptr */ + iowrite32(1, hw->hw_addr + REG_LOAD_PTR); + + /* config Mailbox */ + value = ((atomic_read(&adapter->tpd_ring.next_to_use) + & MB_TPD_PROD_INDX_MASK) << MB_TPD_PROD_INDX_SHIFT) | + ((atomic_read(&adapter->rrd_ring.next_to_clean) + & MB_RRD_CONS_INDX_MASK) << MB_RRD_CONS_INDX_SHIFT) | + ((atomic_read(&adapter->rfd_ring.next_to_use) + & MB_RFD_PROD_INDX_MASK) << MB_RFD_PROD_INDX_SHIFT); + iowrite32(value, hw->hw_addr + REG_MAILBOX); + + /* config IPG/IFG */ + value = (((u32) hw->ipgt & MAC_IPG_IFG_IPGT_MASK) + << MAC_IPG_IFG_IPGT_SHIFT) | + (((u32) hw->min_ifg & MAC_IPG_IFG_MIFG_MASK) + << MAC_IPG_IFG_MIFG_SHIFT) | + (((u32) hw->ipgr1 & MAC_IPG_IFG_IPGR1_MASK) + << MAC_IPG_IFG_IPGR1_SHIFT) | + (((u32) hw->ipgr2 & MAC_IPG_IFG_IPGR2_MASK) + << MAC_IPG_IFG_IPGR2_SHIFT); + iowrite32(value, hw->hw_addr + REG_MAC_IPG_IFG); + + /* config Half-Duplex Control */ + value = ((u32) hw->lcol & MAC_HALF_DUPLX_CTRL_LCOL_MASK) | + (((u32) hw->max_retry & MAC_HALF_DUPLX_CTRL_RETRY_MASK) + << MAC_HALF_DUPLX_CTRL_RETRY_SHIFT) | + MAC_HALF_DUPLX_CTRL_EXC_DEF_EN | + (0xa << MAC_HALF_DUPLX_CTRL_ABEBT_SHIFT) | + (((u32) hw->jam_ipg & MAC_HALF_DUPLX_CTRL_JAMIPG_MASK) + << MAC_HALF_DUPLX_CTRL_JAMIPG_SHIFT); + iowrite32(value, hw->hw_addr + REG_MAC_HALF_DUPLX_CTRL); + + /* set Interrupt Moderator Timer */ + iowrite16(adapter->imt, hw->hw_addr + REG_IRQ_MODU_TIMER_INIT); + iowrite32(MASTER_CTRL_ITIMER_EN, hw->hw_addr + REG_MASTER_CTRL); + + /* set Interrupt Clear Timer */ + iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER); + + /* set MTU, 4 : VLAN */ + iowrite32(hw->max_frame_size + 4, hw->hw_addr + REG_MTU); + + /* jumbo size & rrd retirement timer */ + value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) + << RXQ_JMBOSZ_TH_SHIFT) | + (((u32) hw->rx_jumbo_lkah & RXQ_JMBO_LKAH_MASK) + << RXQ_JMBO_LKAH_SHIFT) | + (((u32) hw->rrd_ret_timer & RXQ_RRD_TIMER_MASK) + << RXQ_RRD_TIMER_SHIFT); + iowrite32(value, hw->hw_addr + REG_RXQ_JMBOSZ_RRDTIM); + + /* Flow Control */ + switch (hw->dev_rev) { + case 0x8001: + case 0x9001: + case 0x9002: + case 0x9003: + set_flow_ctrl_old(adapter); + break; + default: + set_flow_ctrl_new(hw); + break; + } + + /* config TXQ */ + value = (((u32) hw->tpd_burst & TXQ_CTRL_TPD_BURST_NUM_MASK) + << TXQ_CTRL_TPD_BURST_NUM_SHIFT) | + (((u32) hw->txf_burst & TXQ_CTRL_TXF_BURST_NUM_MASK) + << TXQ_CTRL_TXF_BURST_NUM_SHIFT) | + (((u32) hw->tpd_fetch_th & TXQ_CTRL_TPD_FETCH_TH_MASK) + << TXQ_CTRL_TPD_FETCH_TH_SHIFT) | TXQ_CTRL_ENH_MODE | TXQ_CTRL_EN; + iowrite32(value, hw->hw_addr + REG_TXQ_CTRL); + + /* min tpd fetch gap & tx jumbo packet size threshold for taskoffload */ + value = (((u32) hw->tx_jumbo_task_th & TX_JUMBO_TASK_TH_MASK) + << TX_JUMBO_TASK_TH_SHIFT) | + (((u32) hw->tpd_fetch_gap & TX_TPD_MIN_IPG_MASK) + << TX_TPD_MIN_IPG_SHIFT); + iowrite32(value, hw->hw_addr + REG_TX_JUMBO_TASK_TH_TPD_IPG); + + /* config RXQ */ + value = (((u32) hw->rfd_burst & RXQ_CTRL_RFD_BURST_NUM_MASK) + << RXQ_CTRL_RFD_BURST_NUM_SHIFT) | + (((u32) hw->rrd_burst & RXQ_CTRL_RRD_BURST_THRESH_MASK) + << RXQ_CTRL_RRD_BURST_THRESH_SHIFT) | + (((u32) hw->rfd_fetch_gap & RXQ_CTRL_RFD_PREF_MIN_IPG_MASK) + << RXQ_CTRL_RFD_PREF_MIN_IPG_SHIFT) | + RXQ_CTRL_CUT_THRU_EN | RXQ_CTRL_EN; + iowrite32(value, hw->hw_addr + REG_RXQ_CTRL); + + /* config DMA Engine */ + value = ((((u32) hw->dmar_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) + << DMA_CTRL_DMAR_BURST_LEN_SHIFT) | + ((((u32) hw->dmaw_block) & DMA_CTRL_DMAR_BURST_LEN_MASK) + << DMA_CTRL_DMAR_BURST_LEN_SHIFT) | + DMA_CTRL_DMAR_EN | DMA_CTRL_DMAW_EN; + value |= (u32) hw->dma_ord; + if (atl1_rcb_128 == hw->rcb_value) + value |= DMA_CTRL_RCB_VALUE; + iowrite32(value, hw->hw_addr + REG_DMA_CTRL); + + /* config CMB / SMB */ + value = hw->cmb_rrd | ((u32) hw->cmb_tpd << 16); + iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TH); + value = hw->cmb_rx_timer | ((u32) hw->cmb_tx_timer << 16); + iowrite32(value, hw->hw_addr + REG_CMB_WRITE_TIMER); + iowrite32(hw->smb_timer, hw->hw_addr + REG_SMB_TIMER); + + /* --- enable CMB / SMB */ + value = CSMB_CTRL_CMB_EN | CSMB_CTRL_SMB_EN; + iowrite32(value, hw->hw_addr + REG_CSMB_CTRL); + + value = ioread32(adapter->hw.hw_addr + REG_ISR); + if (unlikely((value & ISR_PHY_LINKDOWN) != 0)) { + value = 1; /* config failed */ + } else { + value = 0; + } + /* clear all interrupt status */ + iowrite32(0x3fffffff, adapter->hw.hw_addr + REG_ISR); + iowrite32(0, adapter->hw.hw_addr + REG_ISR); + return value; +} + +/** + * atl1_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ +void atl1_irq_disable(struct atl1_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + iowrite32(0, adapter->hw.hw_addr + REG_IMR); + synchronize_irq(adapter->pdev->irq); +} + +static void atl1_vlan_rx_register(struct net_device *netdev, + struct vlan_group *grp) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + u32 ctrl; + + spin_lock_irqsave(&adapter->lock, flags); + /* atl1_irq_disable(adapter); */ + adapter->vlgrp = grp; + + if (grp) { + /* enable VLAN tag insert/strip */ + ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL); + ctrl |= MAC_CTRL_RMV_VLAN; + iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL); + } else { + /* disable VLAN tag insert/strip */ + ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL); + ctrl &= ~MAC_CTRL_RMV_VLAN; + iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL); + } + + /* atl1_irq_enable(adapter); */ + spin_unlock_irqrestore(&adapter->lock, flags); +} + +/** FIXME: justify or remove -- CHS */ +static void atl1_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +{ + /* We don't do Vlan filtering */ + return; +} + +/** FIXME: this looks wrong too -- CHS */ +static void atl1_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + + spin_lock_irqsave(&adapter->lock, flags); + /* atl1_irq_disable(adapter); */ + if (adapter->vlgrp) + adapter->vlgrp->vlan_devices[vid] = NULL; + /* atl1_irq_enable(adapter); */ + spin_unlock_irqrestore(&adapter->lock, flags); + /* We don't do Vlan filtering */ + return; +} + +static void atl1_restore_vlan(struct atl1_adapter *adapter) +{ + atl1_vlan_rx_register(adapter->netdev, adapter->vlgrp); + if (adapter->vlgrp) { + u16 vid; + for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) { + if (!adapter->vlgrp->vlan_devices[vid]) + continue; + atl1_vlan_rx_add_vid(adapter->netdev, vid); + } + } +} + +static u16 tpd_avail(struct atl1_tpd_ring *tpd_ring) +{ + u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); + u16 next_to_use = atomic_read(&tpd_ring->next_to_use); + return ((next_to_clean > + next_to_use) ? next_to_clean - next_to_use - + 1 : tpd_ring->count + next_to_clean - next_to_use - 1); +} + +static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb, + struct tso_param * tso) +{ + /* We enter this function holding a spinlock. */ + u8 ipofst; + int err; + + if (skb_shinfo(skb)->gso_size) { + if (skb_header_cloned(skb)) { + err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (unlikely(err)) + return err; + } + + if (skb->protocol == ntohs(ETH_P_IP)) { + skb->nh.iph->tot_len = 0; + skb->nh.iph->check = 0; + skb->h.th->check = + ~csum_tcpudp_magic(skb->nh.iph->saddr, + skb->nh.iph->daddr, 0, + IPPROTO_TCP, 0); + ipofst = skb->nh.raw - skb->data; + if (ipofst != ENET_HEADER_SIZE) /* 802.3 frame */ + tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT; + + tso->tsopl |= (skb->nh.iph->ihl & + CSUM_PARAM_IPHL_MASK) << CSUM_PARAM_IPHL_SHIFT; + tso->tsopl |= ((skb->h.th->doff << 2) & + TSO_PARAM_TCPHDRLEN_MASK) << TSO_PARAM_TCPHDRLEN_SHIFT; + tso->tsopl |= (skb_shinfo(skb)->gso_size & + TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT; + tso->tsopl |= 1 << TSO_PARAM_IPCKSUM_SHIFT; + tso->tsopl |= 1 << TSO_PARAM_TCPCKSUM_SHIFT; + tso->tsopl |= 1 << TSO_PARAM_SEGMENT_SHIFT; + return true; + } + } + return false; +} + +static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb, + struct csum_param * csum) +{ + u8 css, cso; + + if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + cso = skb->h.raw - skb->data; + css = (skb->h.raw + skb->csum) - skb->data; + if (unlikely(cso & 0x1)) { + printk(KERN_DEBUG "%s: payload offset != even number\n", + atl1_driver_name); + return -1; + } + csum->csumpl |= (cso & CSUM_PARAM_PLOADOFFSET_MASK) << + CSUM_PARAM_PLOADOFFSET_SHIFT; + csum->csumpl |= (css & CSUM_PARAM_XSUMOFFSET_MASK) << + CSUM_PARAM_XSUMOFFSET_SHIFT; + csum->csumpl |= 1 << CSUM_PARAM_CUSTOMCKSUM_SHIFT; + return true; + } + + return true; +} + +static void atl1_tx_map(struct atl1_adapter *adapter, + struct sk_buff *skb, bool tcp_seg) +{ + /* We enter this function holding a spinlock. */ + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_buffer *buffer_info; + struct page *page; + int first_buf_len = skb->len; + unsigned long offset; + unsigned int nr_frags; + unsigned int f; + u16 tpd_next_to_use; + u16 proto_hdr_len; + u16 i, m, len12; + + first_buf_len -= skb->data_len; + nr_frags = skb_shinfo(skb)->nr_frags; + tpd_next_to_use = atomic_read(&tpd_ring->next_to_use); + buffer_info = &tpd_ring->buffer_info[tpd_next_to_use]; + if (unlikely(buffer_info->skb)) + BUG(); + buffer_info->skb = NULL; /* put skb in last TPD */ + + if (tcp_seg) { + /* TSO/GSO */ + proto_hdr_len = + ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + buffer_info->length = proto_hdr_len; + page = virt_to_page(skb->data); + offset = (unsigned long)skb->data & ~PAGE_MASK; + buffer_info->dma = pci_map_page(adapter->pdev, page, + offset, proto_hdr_len, + PCI_DMA_TODEVICE); + + if (++tpd_next_to_use == tpd_ring->count) + tpd_next_to_use = 0; + + if (first_buf_len > proto_hdr_len) { + len12 = first_buf_len - proto_hdr_len; + m = (len12 + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; + for (i = 0; i < m; i++) { + buffer_info = + &tpd_ring->buffer_info[tpd_next_to_use]; + buffer_info->skb = NULL; + buffer_info->length = + (MAX_TX_BUF_LEN >= + len12) ? MAX_TX_BUF_LEN : len12; + len12 -= buffer_info->length; + page = virt_to_page(skb->data + + (proto_hdr_len + + i * MAX_TX_BUF_LEN)); + offset = (unsigned long)(skb->data + + (proto_hdr_len + + i * MAX_TX_BUF_LEN)) & + ~PAGE_MASK; + buffer_info->dma = + pci_map_page(adapter->pdev, page, offset, + buffer_info->length, + PCI_DMA_TODEVICE); + if (++tpd_next_to_use == tpd_ring->count) + tpd_next_to_use = 0; + } + } + } else { + /* not TSO/GSO */ + buffer_info->length = first_buf_len; + page = virt_to_page(skb->data); + offset = (unsigned long)skb->data & ~PAGE_MASK; + buffer_info->dma = pci_map_page(adapter->pdev, page, + offset, first_buf_len, + PCI_DMA_TODEVICE); + if (++tpd_next_to_use == tpd_ring->count) + tpd_next_to_use = 0; + } + + for (f = 0; f < nr_frags; f++) { + struct skb_frag_struct *frag; + u16 lenf, i, m; + + frag = &skb_shinfo(skb)->frags[f]; + lenf = frag->size; + + m = (lenf + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; + for (i = 0; i < m; i++) { + buffer_info = &tpd_ring->buffer_info[tpd_next_to_use]; + if (unlikely(buffer_info->skb)) + BUG(); + buffer_info->skb = NULL; + buffer_info->length = + (lenf > MAX_TX_BUF_LEN) ? MAX_TX_BUF_LEN : lenf; + lenf -= buffer_info->length; + buffer_info->dma = + pci_map_page(adapter->pdev, frag->page, + frag->page_offset + i * MAX_TX_BUF_LEN, + buffer_info->length, PCI_DMA_TODEVICE); + + if (++tpd_next_to_use == tpd_ring->count) + tpd_next_to_use = 0; + } + } + + /* last tpd's buffer-info */ + buffer_info->skb = skb; +} + +static void atl1_tx_queue(struct atl1_adapter *adapter, int count, + union tpd_descr * descr) +{ + /* We enter this function holding a spinlock. */ + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + int j; + u32 val; + struct atl1_buffer *buffer_info; + struct tx_packet_desc *tpd; + u16 tpd_next_to_use = atomic_read(&tpd_ring->next_to_use); + + for (j = 0; j < count; j++) { + buffer_info = &tpd_ring->buffer_info[tpd_next_to_use]; + tpd = ATL1_TPD_DESC(&adapter->tpd_ring, tpd_next_to_use); + tpd->desc.csum.csumpu = descr->csum.csumpu; + tpd->desc.csum.csumpl = descr->csum.csumpl; + tpd->desc.tso.tsopu = descr->tso.tsopu; + tpd->desc.tso.tsopl = descr->tso.tsopl; + tpd->buffer_addr = cpu_to_le64(buffer_info->dma); + tpd->desc.data = descr->data; + tpd->desc.csum.csumpu |= (cpu_to_le16(buffer_info->length) & + CSUM_PARAM_BUFLEN_MASK) << CSUM_PARAM_BUFLEN_SHIFT; + + val = (descr->tso.tsopl >> TSO_PARAM_SEGMENT_SHIFT) & + TSO_PARAM_SEGMENT_MASK; + if (val && !j) + tpd->desc.tso.tsopl |= 1 << TSO_PARAM_HDRFLAG_SHIFT; + + if (j == (count - 1)) + tpd->desc.csum.csumpl |= 1 << CSUM_PARAM_EOP_SHIFT; + + if (++tpd_next_to_use == tpd_ring->count) + tpd_next_to_use = 0; + } + /** + * Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + **/ + wmb(); + + atomic_set(&tpd_ring->next_to_use, (int)tpd_next_to_use); +} + +static void atl1_update_mailbox(struct atl1_adapter *adapter) +{ + unsigned long flags; + u32 tpd_next_to_use; + u32 rfd_next_to_use; + u32 rrd_next_to_clean; + u32 value; + + spin_lock_irqsave(&adapter->mb_lock, flags); + + tpd_next_to_use = atomic_read(&adapter->tpd_ring.next_to_use); + rfd_next_to_use = atomic_read(&adapter->rfd_ring.next_to_use); + rrd_next_to_clean = atomic_read(&adapter->rrd_ring.next_to_clean); + + value = ((rfd_next_to_use & MB_RFD_PROD_INDX_MASK) << + MB_RFD_PROD_INDX_SHIFT) | + ((rrd_next_to_clean & MB_RRD_CONS_INDX_MASK) << + MB_RRD_CONS_INDX_SHIFT) | + ((tpd_next_to_use & MB_TPD_PROD_INDX_MASK) << + MB_TPD_PROD_INDX_SHIFT); + iowrite32(value, adapter->hw.hw_addr + REG_MAILBOX); + + spin_unlock_irqrestore(&adapter->mb_lock, flags); +} + +static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + int len = skb->len; + int tso; + int count = 1; + int ret_val; + u32 val; + union tpd_descr param; + u16 frag_size; + u16 vlan_tag; + unsigned long flags; + unsigned int nr_frags = 0; + unsigned int mss = 0; + unsigned int f; + unsigned int proto_hdr_len; + + len -= skb->data_len; + + if (unlikely(skb->len == 0)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + param.data = 0; + param.tso.tsopu = 0; + param.tso.tsopl = 0; + param.csum.csumpu = 0; + param.csum.csumpl = 0; + + /* nr_frags will be nonzero if we're doing scatter/gather (SG) */ + nr_frags = skb_shinfo(skb)->nr_frags; + for (f = 0; f < nr_frags; f++) { + frag_size = skb_shinfo(skb)->frags[f].size; + if (frag_size) + count += + (frag_size + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; + } + + /* mss will be nonzero if we're doing segment offload (TSO/GSO) */ + mss = skb_shinfo(skb)->gso_size; + if (mss) { + if (skb->protocol == ntohs(ETH_P_IP)) { + proto_hdr_len = ((skb->h.raw - skb->data) + + (skb->h.th->doff << 2)); + if (unlikely(proto_hdr_len > len)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + /* need additional TPD ? */ + if (proto_hdr_len != len) + count += (len - proto_hdr_len + + MAX_TX_BUF_LEN - 1) / MAX_TX_BUF_LEN; + } + } + + local_irq_save(flags); + if (!spin_trylock(&adapter->lock)) { + /* Can't get lock - tell upper layer to requeue */ + local_irq_restore(flags); + printk(KERN_DEBUG "%s: TX locked\n", atl1_driver_name); + return NETDEV_TX_LOCKED; + } + + if (tpd_avail(&adapter->tpd_ring) < count) { + /* not enough descriptors */ + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->lock, flags); + printk(KERN_DEBUG "%s: TX busy\n", atl1_driver_name); + return NETDEV_TX_BUSY; + } + + param.data = 0; + + if (adapter->vlgrp && vlan_tx_tag_present(skb)) { + vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | + ((vlan_tag >> 9) & 0x8); + param.csum.csumpl |= 1 << CSUM_PARAM_INSVLAG_SHIFT; + param.csum.csumpu |= (vlan_tag & CSUM_PARAM_VALANTAG_MASK) << + CSUM_PARAM_VALAN_SHIFT; + } + + tso = atl1_tso(adapter, skb, ¶m.tso); + if (tso < 0) { + spin_unlock_irqrestore(&adapter->lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if (!tso) { + ret_val = atl1_tx_csum(adapter, skb, ¶m.csum); + if (ret_val < 0) { + spin_unlock_irqrestore(&adapter->lock, flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + } + + val = (param.csum.csumpl >> CSUM_PARAM_SEGMENT_SHIFT) & + CSUM_PARAM_SEGMENT_MASK; + atl1_tx_map(adapter, skb, 1 == val); + atl1_tx_queue(adapter, count, ¶m); + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&adapter->lock, flags); + atl1_update_mailbox(adapter); + return NETDEV_TX_OK; +} + +/** + * atl1_get_stats - Get System Network Statistics + * @netdev: network interface device structure + * + * Returns the address of the device statistics structure. + * The statistics are actually updated from the timer callback. + **/ +static struct net_device_stats *atl1_get_stats(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + return &adapter->net_stats; +} + +/** + * atl1_clean_rx_ring - Free RFD Buffers + * @adapter: board private structure + **/ +static void atl1_clean_rx_ring(struct atl1_adapter *adapter) +{ + struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; + struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; + struct atl1_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + unsigned long size; + unsigned int i; + + /* Free all the Rx ring sk_buffs */ + for (i = 0; i < rfd_ring->count; i++) { + buffer_info = &rfd_ring->buffer_info[i]; + if (buffer_info->dma) { + pci_unmap_page(pdev, + buffer_info->dma, + buffer_info->length, + PCI_DMA_FROMDEVICE); + buffer_info->dma = 0; + } + if (buffer_info->skb) { + dev_kfree_skb(buffer_info->skb); + buffer_info->skb = NULL; + } + } + + size = sizeof(struct atl1_buffer) * rfd_ring->count; + memset(rfd_ring->buffer_info, 0, size); + + /* Zero out the descriptor ring */ + memset(rfd_ring->desc, 0, rfd_ring->size); + + rfd_ring->next_to_clean = 0; + atomic_set(&rfd_ring->next_to_use, 0); + + rrd_ring->next_to_use = 0; + atomic_set(&rrd_ring->next_to_clean, 0); +} + +/** + * atl1_clean_tx_ring - Free Tx Buffers + * @adapter: board private structure + **/ +static void atl1_clean_tx_ring(struct atl1_adapter *adapter) +{ + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_buffer *buffer_info; + struct pci_dev *pdev = adapter->pdev; + unsigned long size; + unsigned int i; + + /* Free all the Tx ring sk_buffs */ + for (i = 0; i < tpd_ring->count; i++) { + buffer_info = &tpd_ring->buffer_info[i]; + if (buffer_info->dma) { + pci_unmap_page(pdev, buffer_info->dma, + buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->dma = 0; + } + } + + for (i = 0; i < tpd_ring->count; i++) { + buffer_info = &tpd_ring->buffer_info[i]; + if (buffer_info->skb) { + dev_kfree_skb_any(buffer_info->skb); + buffer_info->skb = NULL; + } + } + + size = sizeof(struct atl1_buffer) * tpd_ring->count; + memset(tpd_ring->buffer_info, 0, size); + + /* Zero out the descriptor ring */ + memset(tpd_ring->desc, 0, tpd_ring->size); + + atomic_set(&tpd_ring->next_to_use, 0); + atomic_set(&tpd_ring->next_to_clean, 0); +} + +/** + * atl1_free_ring_resources - Free Tx / RX descriptor Resources + * @adapter: board private structure + * + * Free all transmit software resources + **/ +void atl1_free_ring_resources(struct atl1_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct atl1_tpd_ring *tpd_ring = &adapter->tpd_ring; + struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; + struct atl1_rrd_ring *rrd_ring = &adapter->rrd_ring; + struct atl1_ring_header *ring_header = &adapter->ring_header; + + atl1_clean_tx_ring(adapter); + atl1_clean_rx_ring(adapter); + + kfree(tpd_ring->buffer_info); + pci_free_consistent(pdev, ring_header->size, ring_header->desc, + ring_header->dma); + + tpd_ring->buffer_info = NULL; + tpd_ring->desc = NULL; + tpd_ring->dma = 0; + + rfd_ring->buffer_info = NULL; + rfd_ring->desc = NULL; + rfd_ring->dma = 0; + + rrd_ring->desc = NULL; + rrd_ring->dma = 0; +} + +s32 atl1_up(struct atl1_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int err; + int irq_flags = IRQF_SAMPLE_RANDOM; + + /* hardware has been reset, we need to reload some things */ + atl1_set_multi(netdev); + atl1_restore_vlan(adapter); + err = atl1_alloc_rx_buffers(adapter); + if (unlikely(!err)) /* no RX BUFFER allocated */ + return -ENOMEM; + + if (unlikely(atl1_configure(adapter))) { + err = -EIO; + goto err_up; + } + +#ifdef CONFIG_PCI_MSI + if (adapter->enable_msi) { + err = pci_enable_msi(adapter->pdev); + if (err) { + dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err); + adapter->enable_msi = 0; + } + } +#endif + if (!adapter->enable_msi) + irq_flags |= IRQF_SHARED; + + err = request_irq(adapter->pdev->irq, &atl1_intr, irq_flags, + netdev->name, netdev); + if (unlikely(err)) + goto err_up; + + mod_timer(&adapter->watchdog_timer, jiffies); + atl1_irq_enable(adapter); + atl1_check_link(adapter); + return 0; + + /* FIXME: unreachable code! -- CHS */ + /* free irq disable any interrupt */ + iowrite32(0, adapter->hw.hw_addr + REG_IMR); + free_irq(adapter->pdev->irq, netdev); + +err_up: + /* free rx_buffers */ + atl1_clean_rx_ring(adapter); + return err; +} + +void atl1_down(struct atl1_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + del_timer_sync(&adapter->watchdog_timer); + del_timer_sync(&adapter->phy_config_timer); + adapter->phy_timer_pending = false; + + atl1_irq_disable(adapter); + free_irq(adapter->pdev->irq, netdev); +#ifdef CONFIG_PCI_MSI + if (adapter->enable_msi) + pci_disable_msi(adapter->pdev); +#endif + atl1_reset_hw(&adapter->hw); + adapter->cmb.cmb->int_stats = 0; + + adapter->link_speed = SPEED_0; + adapter->link_duplex = -1; + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + atl1_clean_tx_ring(adapter); + atl1_clean_rx_ring(adapter); +} + +/** + * atl1_change_mtu - Change the Maximum Transfer Unit + * @netdev: network interface device structure + * @new_mtu: new value for maximum frame size + * + * Returns 0 on success, negative on failure + **/ +static int atl1_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + int old_mtu = netdev->mtu; + int max_frame = new_mtu + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; + + if ((max_frame < MINIMUM_ETHERNET_FRAME_SIZE) || + (max_frame > MAX_JUMBO_FRAME_SIZE)) { + printk(KERN_WARNING "%s: invalid MTU setting\n", atl1_driver_name); + return -EINVAL; + } + + adapter->hw.max_frame_size = max_frame; + adapter->hw.tx_jumbo_task_th = (max_frame + 7) >> 3; + adapter->rx_buffer_len = (max_frame + 7) & ~7; + adapter->hw.rx_jumbo_th = adapter->rx_buffer_len / 8; + + netdev->mtu = new_mtu; + if ((old_mtu != new_mtu) && netif_running(netdev)) { + atl1_down(adapter); + atl1_up(adapter); + } + + return 0; +} + +/** + * atl1_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int atl1_set_mac(struct net_device *netdev, void *p) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (netif_running(netdev)) + return -EBUSY; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); + + atl1_set_mac_addr(&adapter->hw); + return 0; +} + +/** + * atl1_watchdog - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + **/ +static void atl1_watchdog(unsigned long data) +{ + struct atl1_adapter *adapter = (struct atl1_adapter *)data; + + /* Reset the timer */ + mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); +} + +static int mdio_read(struct net_device *netdev, int phy_id, int reg_num) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + u16 result; + + atl1_read_phy_reg(&adapter->hw, reg_num & 0x1f, &result); + + return result; +} + +static void mdio_write(struct net_device *netdev, int phy_id, int reg_num, int val) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + + atl1_write_phy_reg(&adapter->hw, reg_num, val); +} + +/** + * atl1_mii_ioctl - + * @netdev: + * @ifreq: + * @cmd: + **/ +static int atl1_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + unsigned long flags; + int retval; + + if (!netif_running(netdev)) + return -EINVAL; + + spin_lock_irqsave(&adapter->lock, flags); + retval = generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL); + spin_unlock_irqrestore(&adapter->lock, flags); + + return retval; +} + +/** + * atl1_ioctl - + * @netdev: + * @ifreq: + * @cmd: + **/ +static int atl1_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + return atl1_mii_ioctl(netdev, ifr, cmd); + default: + return -EOPNOTSUPP; + } +} + +/** + * atl1_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + **/ +static void atl1_tx_timeout(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->tx_timeout_task); +} + +/** + * atl1_phy_config - Timer Call-back + * @data: pointer to netdev cast into an unsigned long + **/ +static void atl1_phy_config(unsigned long data) +{ + struct atl1_adapter *adapter = (struct atl1_adapter *)data; + struct atl1_hw *hw = &adapter->hw; + unsigned long flags; + + spin_lock_irqsave(&adapter->lock, flags); + adapter->phy_timer_pending = false; + atl1_write_phy_reg(hw, MII_ADVERTISE, hw->mii_autoneg_adv_reg); + atl1_write_phy_reg(hw, MII_AT001_CR, hw->mii_1000t_ctrl_reg); + atl1_write_phy_reg(hw, MII_BMCR, MII_CR_RESET | MII_CR_AUTO_NEG_EN); + spin_unlock_irqrestore(&adapter->lock, flags); +} + +int atl1_reset(struct atl1_adapter *adapter) +{ + int ret; + + ret = atl1_reset_hw(&adapter->hw); + if (ret != ATL1_SUCCESS) + return ret; + return atl1_init_hw(&adapter->hw); +} + +/** + * atl1_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * Returns 0 on success, negative value on failure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). At this point all resources needed + * for transmit and receive operations are allocated, the interrupt + * handler is registered with the OS, the watchdog timer is started, + * and the stack is notified that the interface is ready. + **/ +static int atl1_open(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + int err; + + /* allocate transmit descriptors */ + err = atl1_setup_ring_resources(adapter); + if (err) + return err; + + err = atl1_up(adapter); + if (err) + goto err_up; + + return 0; + +err_up: + atl1_reset(adapter); + return err; +} + +/** + * atl1_close - Disables a network interface + * @netdev: network interface device structure + * + * Returns 0, this is not allowed to fail + * + * The close entry point is called when an interface is de-activated + * by the OS. The hardware is still under the drivers control, but + * needs to be disabled. A global MAC reset is issued to stop the + * hardware, and all transmit and receive resources are freed. + **/ +static int atl1_close(struct net_device *netdev) +{ + struct atl1_adapter *adapter = netdev_priv(netdev); + atl1_down(adapter); + atl1_free_ring_resources(adapter); + return 0; +} + +/** + * If TPD Buffer size equal to 0, PCIE DMAR_TO_INT + * will assert. We do soft reset <0x1400=1> according + * with the SPEC. BUT, it seemes that PCIE or DMA + * state-machine will not be reset. DMAR_TO_INT will + * assert again and again. + */ +static void atl1_tx_timeout_task(struct work_struct *work) +{ + struct atl1_adapter *adapter = + container_of(work, struct atl1_adapter, tx_timeout_task); + struct net_device *netdev = adapter->netdev; + + netif_device_detach(netdev); + atl1_down(adapter); + atl1_up(adapter); + netif_device_attach(netdev); +} + +/** + * atl1_link_chg_task - deal with link change event Out of interrupt context + **/ +static void atl1_link_chg_task(struct work_struct *work) +{ + struct atl1_adapter *adapter = + container_of(work, struct atl1_adapter, link_chg_task); + unsigned long flags; + + spin_lock_irqsave(&adapter->lock, flags); + atl1_check_link(adapter); + spin_unlock_irqrestore(&adapter->lock, flags); +} + +/** + * atl1_pcie_patch - Patch for PCIE module + **/ +static void atl1_pcie_patch(struct atl1_adapter *adapter) +{ + u32 value; + value = 0x6500; + iowrite32(value, adapter->hw.hw_addr + 0x12FC); + /* pcie flow control mode change */ + value = ioread32(adapter->hw.hw_addr + 0x1008); + value |= 0x8000; + iowrite32(value, adapter->hw.hw_addr + 0x1008); +} + +/** + * When ACPI resume on some VIA MotherBoard, the Interrupt Disable bit/0x400 + * on PCI Command register is disable. + * The function enable this bit. + * Brackett, 2006/03/15 + */ +static void atl1_via_workaround(struct atl1_adapter *adapter) +{ + unsigned long value; + + value = ioread16(adapter->hw.hw_addr + PCI_COMMAND); + if (value & PCI_COMMAND_INTX_DISABLE) + value &= ~PCI_COMMAND_INTX_DISABLE; + iowrite32(value, adapter->hw.hw_addr + PCI_COMMAND); +} + +/** + * atl1_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in atl1_pci_tbl + * + * Returns 0 on success, negative on failure + * + * atl1_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int __devinit atl1_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *netdev; + struct atl1_adapter *adapter; + static int cards_found = 0; + bool pci_using_64 = true; + int err; + + err = pci_enable_device(pdev); + if (err) + return err; + + err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + if (err) { + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (err) { + printk(KERN_DEBUG + "%s: no usable DMA configuration, aborting\n", + atl1_driver_name); + goto err_dma; + } + pci_using_64 = false; + } + /* Mark all PCI regions associated with PCI device + * pdev as being reserved by owner atl1_driver_name */ + err = pci_request_regions(pdev, atl1_driver_name); + if (err) + goto err_request_regions; + + /* Enables bus-mastering on the device and calls + * pcibios_set_master to do the needed arch specific settings */ + pci_set_master(pdev); + + netdev = alloc_etherdev(sizeof(struct atl1_adapter)); + if (!netdev) { + err = -ENOMEM; + goto err_alloc_etherdev; + } + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + + pci_set_drvdata(pdev, netdev); + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.back = adapter; + + adapter->hw.hw_addr = pci_iomap(pdev, 0, 0); + if (!adapter->hw.hw_addr) { + err = -EIO; + goto err_pci_iomap; + } + /* get device revision number */ + adapter->hw.dev_rev = ioread16(adapter->hw.hw_addr + (REG_MASTER_CTRL + 2)); + + /* set default ring resource counts */ + adapter->rfd_ring.count = adapter->rrd_ring.count = ATL1_DEFAULT_RFD; + adapter->tpd_ring.count = ATL1_DEFAULT_TPD; + + adapter->mii.dev = netdev; + adapter->mii.mdio_read = mdio_read; + adapter->mii.mdio_write = mdio_write; + adapter->mii.phy_id_mask = 0x1f; + adapter->mii.reg_num_mask = 0x1f; + + netdev->open = &atl1_open; + netdev->stop = &atl1_close; + netdev->hard_start_xmit = &atl1_xmit_frame; + netdev->get_stats = &atl1_get_stats; + netdev->set_multicast_list = &atl1_set_multi; + netdev->set_mac_address = &atl1_set_mac; + netdev->change_mtu = &atl1_change_mtu; + netdev->do_ioctl = &atl1_ioctl; + netdev->tx_timeout = &atl1_tx_timeout; + netdev->watchdog_timeo = 5 * HZ; + netdev->vlan_rx_register = atl1_vlan_rx_register; + netdev->vlan_rx_add_vid = atl1_vlan_rx_add_vid; + netdev->vlan_rx_kill_vid = atl1_vlan_rx_kill_vid; + netdev->ethtool_ops = &atl1_ethtool_ops; + adapter->bd_number = cards_found; + adapter->pci_using_64 = pci_using_64; + + /* setup the private structure */ + err = atl1_sw_init(adapter); + if (err) { + goto err_common; + } + + netdev->features = NETIF_F_HW_CSUM; + netdev->features |= NETIF_F_SG; + netdev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); + + /** + * FIXME - Until tso performance gets fixed, disable the feature. + * Enable it with ethtool -K if desired. + **/ + /* netdev->features |= NETIF_F_TSO; */ + + if (pci_using_64) { + netdev->features |= NETIF_F_HIGHDMA; + } + netdev->features |= NETIF_F_LLTX; + + /** + * patch for some L1 of old version, + * the final version of L1 may not need these + * patches + **/ + /* atl1_pcie_patch(adapter); */ + + /* really reset GPHY core */ + iowrite16(0, adapter->hw.hw_addr + REG_GPHY_ENABLE); + + /** + * reset the controller to + * put the device in a known good starting state + **/ + if (atl1_reset_hw(&adapter->hw)) { + err = -EIO; + goto err_common; + } + + /* copy the MAC address out of the EEPROM */ + atl1_read_mac_addr(&adapter->hw); + memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len); + + if (!is_valid_ether_addr(netdev->dev_addr)) { + err = -EIO; + goto err_common; + } + + atl1_check_options(adapter); + + /* pre-init the MAC, and setup link */ + err = atl1_init_hw(&adapter->hw); + if (err) { + err = -EIO; + goto err_common; + } + + atl1_pcie_patch(adapter); + /* assume we have no link for now */ + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + init_timer(&adapter->watchdog_timer); + adapter->watchdog_timer.function = &atl1_watchdog; + adapter->watchdog_timer.data = (unsigned long)adapter; + + init_timer(&adapter->phy_config_timer); + adapter->phy_config_timer.function = &atl1_phy_config; + adapter->phy_config_timer.data = (unsigned long)adapter; + adapter->phy_timer_pending = false; + + INIT_WORK(&adapter->tx_timeout_task, atl1_tx_timeout_task); + + INIT_WORK(&adapter->link_chg_task, atl1_link_chg_task); + + INIT_WORK(&adapter->pcie_dma_to_rst_task, atl1_tx_timeout_task); + + err = register_netdev(netdev); + if (err) { + goto err_common; + } + + cards_found++; + atl1_via_workaround(adapter); + return 0; + +err_common: + pci_iounmap(pdev, adapter->hw.hw_addr); +err_pci_iomap: + free_netdev(netdev); +err_alloc_etherdev: + pci_release_regions(pdev); +err_dma: +err_request_regions: + pci_disable_device(pdev); + return err; +} + +/** + * atl1_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * atl1_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void __devexit atl1_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1_adapter *adapter; + /* Device not available. Return. */ + if (!netdev) + return; + + adapter = netdev_priv(netdev); + iowrite16(0, adapter->hw.hw_addr + REG_GPHY_ENABLE); + unregister_netdev(netdev); + pci_iounmap(pdev, adapter->hw.hw_addr); + pci_release_regions(pdev); + free_netdev(netdev); + pci_disable_device(pdev); +} + +static int atl1_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1_adapter *adapter = netdev_priv(netdev); + struct atl1_hw *hw = &adapter->hw; + u32 ctrl = 0; + u32 wufc = adapter->wol; + + netif_device_detach(netdev); + if (netif_running(netdev)) + atl1_down(adapter); + + atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); + atl1_read_phy_reg(hw, MII_BMSR, (u16 *) & ctrl); + if (ctrl & BMSR_LSTATUS) + wufc &= ~ATL1_WUFC_LNKC; + + /* reduce speed to 10/100M */ + if (wufc) { + atl1_phy_enter_power_saving(hw); + /* if resume, let driver to re- setup link */ + hw->phy_configured = false; + atl1_set_mac_addr(hw); + atl1_set_multi(netdev); + + ctrl = 0; + /* turn on magic packet wol */ + if (wufc & ATL1_WUFC_MAG) + ctrl = WOL_MAGIC_EN | WOL_MAGIC_PME_EN; + + /* turn on Link change WOL */ + if (wufc & ATL1_WUFC_LNKC) + ctrl |= (WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN); + iowrite32(ctrl, hw->hw_addr + REG_WOL_CTRL); + + /* turn on all-multi mode if wake on multicast is enabled */ + ctrl = ioread32(hw->hw_addr + REG_MAC_CTRL); + ctrl &= ~MAC_CTRL_DBG; + ctrl &= ~MAC_CTRL_PROMIS_EN; + if (wufc & ATL1_WUFC_MC) + ctrl |= MAC_CTRL_MC_ALL_EN; + else + ctrl &= ~MAC_CTRL_MC_ALL_EN; + + /* turn on broadcast mode if wake on-BC is enabled */ + if (wufc & ATL1_WUFC_BC) + ctrl |= MAC_CTRL_BC_EN; + else + ctrl &= ~MAC_CTRL_BC_EN; + + /* enable RX */ + ctrl |= MAC_CTRL_RX_EN; + iowrite32(ctrl, hw->hw_addr + REG_MAC_CTRL); + pci_enable_wake(pdev, PCI_D3hot, 1); + pci_enable_wake(pdev, PCI_D3cold, 1); /* 4 == D3 cold */ + } else { + iowrite32(0, hw->hw_addr + REG_WOL_CTRL); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); /* 4 == D3 cold */ + } + + pci_save_state(pdev); + pci_disable_device(pdev); + + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int atl1_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct atl1_adapter *adapter = netdev_priv(netdev); + u32 ret_val; + + pci_set_power_state(pdev, 0); + pci_restore_state(pdev); + + ret_val = pci_enable_device(pdev); + pci_enable_wake(pdev, PCI_D3hot, 0); + pci_enable_wake(pdev, PCI_D3cold, 0); + + iowrite32(0, adapter->hw.hw_addr + REG_WOL_CTRL); + atl1_reset(adapter); + + if (netif_running(netdev)) + atl1_up(adapter); + netif_device_attach(netdev); + + atl1_via_workaround(adapter); + + return 0; +} + +static struct pci_driver atl1_driver = { + .name = atl1_driver_name, + .id_table = atl1_pci_tbl, + .probe = atl1_probe, + .remove = __devexit_p(atl1_remove), + /* Power Managment Hooks */ + /* probably broken right now -- CHS */ +#ifdef CONFIG_PM + .suspend = atl1_suspend, + .resume = atl1_resume +#endif +}; + +/** + * atl1_exit_module - Driver Exit Cleanup Routine + * + * atl1_exit_module is called just before the driver is removed + * from memory. + **/ +static void __exit atl1_exit_module(void) +{ + pci_unregister_driver(&atl1_driver); +} + +/** + * atl1_init_module - Driver Registration Routine + * + * atl1_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ +static int __init atl1_init_module(void) +{ + int ret; + printk(KERN_INFO "%s - version %s\n", atl1_driver_string, DRIVER_VERSION); + printk(KERN_INFO "%s\n", atl1_copyright); + ret = pci_module_init(&atl1_driver); + + return ret; +} + +module_init(atl1_init_module); +module_exit(atl1_exit_module); diff -puN /dev/null drivers/net/atl1/atl1_param.c --- /dev/null +++ a/drivers/net/atl1/atl1_param.c @@ -0,0 +1,223 @@ +/** + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. + * Copyright(c) 2006 Chris Snook + * Copyright(c) 2006 Jay Cliburn + * + * Derived from Intel e1000 driver + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + **/ + +#include +#include +#include +#include "atl1.h" + +extern char atl1_driver_name[]; + +/** + * This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + **/ +#define ATL1_MAX_NIC 4 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET } + +/** + * Interrupt Moderate Timer in units of 2 us + * + * Valid Range: 10-65535 + * + * Default Value: 100 (200us) + **/ +static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; +static int num_int_mod_timer = 0; +module_param_array_named(int_mod_timer, int_mod_timer, int, &num_int_mod_timer, 0); +MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer"); + +/** + * flash_vendor + * + * Valid Range: 0-2 + * + * 0 - Atmel + * 1 - SST + * 2 - ST + * + * Default Value: 0 + **/ +static int __devinitdata flash_vendor[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; +static int num_flash_vendor = 0; +module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0); +MODULE_PARM_DESC(flash_vendor, "SPI flash vendor"); + +int enable_msi; +module_param(enable_msi, int, 0444); +MODULE_PARM_DESC(enable_msi, "Enable PCI MSI"); + +#define DEFAULT_INT_MOD_CNT 100 /* 200us */ +#define MAX_INT_MOD_CNT 65000 +#define MIN_INT_MOD_CNT 50 + +#define FLASH_VENDOR_DEFAULT 0 +#define FLASH_VENDOR_MIN 0 +#define FLASH_VENDOR_MAX 2 + +struct atl1_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct atl1_opt_list { + int i; + char *str; + } *p; + } l; + } arg; +}; + +static int __devinit atl1_validate_option(int *value, struct atl1_option *opt) +{ + if (*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + printk(KERN_INFO "%s: %s Enabled\n", atl1_driver_name, + opt->name); + return 0; + case OPTION_DISABLED: + printk(KERN_INFO "%s: %s Disabled\n", atl1_driver_name, + opt->name); + return 0; + } + break; + case range_option: + if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + printk(KERN_INFO "%s: %s set to %i\n", + atl1_driver_name, opt->name, *value); + return 0; + } + break; + case list_option:{ + int i; + struct atl1_opt_list *ent; + + for (i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if (*value == ent->i) { + if (ent->str[0] != '\0') + printk(KERN_INFO "%s: %s\n", + atl1_driver_name, ent->str); + return 0; + } + } + } + break; + + default: + break; + } + + printk(KERN_INFO "%s: invalid %s specified (%i) %s\n", + atl1_driver_name, opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +/** + * atl1_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line parameters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + **/ +void __devinit atl1_check_options(struct atl1_adapter *adapter) +{ + int bd = adapter->bd_number; + if (bd >= ATL1_MAX_NIC) { + printk(KERN_NOTICE "%s: warning: no configuration for board #%i\n", + atl1_driver_name, bd); + printk(KERN_NOTICE "%s: using defaults for all values\n", + atl1_driver_name); + } + { /* Interrupt Moderate Timer */ + struct atl1_option opt = { + .type = range_option, + .name = "Interrupt Moderator Timer", + .err = "using default of " + __MODULE_STRING(DEFAULT_INT_MOD_CNT), + .def = DEFAULT_INT_MOD_CNT, + .arg = {.r = + {.min = MIN_INT_MOD_CNT,.max = MAX_INT_MOD_CNT}} + }; + int val; + if (num_int_mod_timer > bd) { + val = int_mod_timer[bd]; + atl1_validate_option(&val, &opt); + adapter->imt = (u16) val; + } else { + adapter->imt = (u16) (opt.def); + } + } + + { /* Flash Vendor */ + struct atl1_option opt = { + .type = range_option, + .name = "SPI Flash Vendor", + .err = "using default of " + __MODULE_STRING(FLASH_VENDOR_DEFAULT), + .def = DEFAULT_INT_MOD_CNT, + .arg = {.r = + {.min = FLASH_VENDOR_MIN,.max = + FLASH_VENDOR_MAX}} + }; + int val; + if (num_flash_vendor > bd) { + val = flash_vendor[bd]; + atl1_validate_option(&val, &opt); + adapter->hw.flash_vendor = (u8) val; + } else { + adapter->hw.flash_vendor = (u8) (opt.def); + } + } + + { /* PCI MSI usage */ + +#ifdef CONFIG_PCI_MSI + adapter->enable_msi = enable_msi; +#else + adapter->enable_msi = 0; +#endif + } +} diff -puN drivers/net/bmac.c~git-netdev-all drivers/net/bmac.c --- a/drivers/net/bmac.c~git-netdev-all +++ a/drivers/net/bmac.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -140,7 +141,6 @@ static unsigned char *bmac_emergency_rxb + (N_RX_RING + N_TX_RING + 4) * sizeof(struct dbdma_cmd) \ + sizeof(struct sk_buff_head)) -static unsigned char bitrev(unsigned char b); static int bmac_open(struct net_device *dev); static int bmac_close(struct net_device *dev); static int bmac_transmit_packet(struct sk_buff *skb, struct net_device *dev); @@ -586,18 +586,6 @@ bmac_construct_rxbuff(struct sk_buff *sk virt_to_bus(addr), 0); } -/* Bit-reverse one byte of an ethernet hardware address. */ -static unsigned char -bitrev(unsigned char b) -{ - int d = 0, i; - - for (i = 0; i < 8; ++i, b >>= 1) - d = (d << 1) | (b & 1); - return d; -} - - static void bmac_init_tx_ring(struct bmac_data *bp) { @@ -1224,8 +1212,8 @@ bmac_get_station_address(struct net_devi { reset_and_select_srom(dev); data = read_srom(dev, i + EnetAddressOffset/2, SROMAddressBits); - ea[2*i] = bitrev(data & 0x0ff); - ea[2*i+1] = bitrev((data >> 8) & 0x0ff); + ea[2*i] = bitrev8(data & 0x0ff); + ea[2*i+1] = bitrev8((data >> 8) & 0x0ff); } } @@ -1315,7 +1303,7 @@ static int __devinit bmac_probe(struct m rev = addr[0] == 0 && addr[1] == 0xA0; for (j = 0; j < 6; ++j) - dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j]; + dev->dev_addr[j] = rev ? bitrev8(addr[j]): addr[j]; /* Enable chip without interrupts for now */ bmac_enable_and_reset_chip(dev); diff -puN drivers/net/bnx2.c~git-netdev-all drivers/net/bnx2.c --- a/drivers/net/bnx2.c~git-netdev-all +++ a/drivers/net/bnx2.c @@ -39,12 +39,10 @@ #include #define BCM_VLAN 1 #endif -#ifdef NETIF_F_TSO #include #include #include #define BCM_TSO 1 -#endif #include #include #include @@ -1720,7 +1718,7 @@ bnx2_tx_int(struct bnx2 *bp) tx_buf = &bp->tx_buf_ring[sw_ring_cons]; skb = tx_buf->skb; -#ifdef BCM_TSO + /* partial BD completions possible with TSO packets */ if (skb_is_gso(skb)) { u16 last_idx, last_ring_idx; @@ -1736,7 +1734,7 @@ bnx2_tx_int(struct bnx2 *bp) break; } } -#endif + pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping), skb_headlen(skb), PCI_DMA_TODEVICE); @@ -4506,7 +4504,6 @@ bnx2_start_xmit(struct sk_buff *skb, str vlan_tag_flags |= (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); } -#ifdef BCM_TSO if ((mss = skb_shinfo(skb)->gso_size) && (skb->len > (bp->dev->mtu + ETH_HLEN))) { u32 tcp_opt_len, ip_tcp_len; @@ -4539,7 +4536,6 @@ bnx2_start_xmit(struct sk_buff *skb, str } } else -#endif { mss = 0; } @@ -5536,10 +5532,8 @@ static const struct ethtool_ops bnx2_eth .set_tx_csum = ethtool_op_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, -#ifdef BCM_TSO .get_tso = ethtool_op_get_tso, .set_tso = bnx2_set_tso, -#endif .self_test_count = bnx2_self_test_count, .self_test = bnx2_self_test, .get_strings = bnx2_get_strings, @@ -6094,9 +6088,7 @@ bnx2_init_one(struct pci_dev *pdev, cons #ifdef BCM_VLAN dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; #endif -#ifdef BCM_TSO dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; -#endif netif_carrier_off(bp->dev); diff -puN drivers/net/bonding/bond_main.c~git-netdev-all drivers/net/bonding/bond_main.c --- a/drivers/net/bonding/bond_main.c~git-netdev-all +++ a/drivers/net/bonding/bond_main.c @@ -4704,6 +4704,7 @@ static int bond_check_params(struct bond static struct lock_class_key bonding_netdev_xmit_lock_key; /* Create a new bond based on the specified name and bonding parameters. + * If name is NULL, obtain a suitable "bond%d" name for us. * Caller must NOT hold rtnl_lock; we need to release it here before we * set up our sysfs entries. */ @@ -4713,7 +4714,8 @@ int bond_create(char *name, struct bond_ int res; rtnl_lock(); - bond_dev = alloc_netdev(sizeof(struct bonding), name, ether_setup); + bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "", + ether_setup); if (!bond_dev) { printk(KERN_ERR DRV_NAME ": %s: eek! can't alloc netdev!\n", @@ -4722,6 +4724,12 @@ int bond_create(char *name, struct bond_ goto out_rtnl; } + if (!name) { + res = dev_alloc_name(bond_dev, "bond%d"); + if (res < 0) + goto out_netdev; + } + /* bond_init() must be called after dev_alloc_name() (for the * /proc files), but before register_netdevice(), because we * need to set function pointers. @@ -4748,14 +4756,19 @@ int bond_create(char *name, struct bond_ rtnl_unlock(); /* allows sysfs registration of net device */ res = bond_create_sysfs_entry(bond_dev->priv); - goto done; + if (res < 0) { + rtnl_lock(); + goto out_bond; + } + + return 0; + out_bond: bond_deinit(bond_dev); out_netdev: free_netdev(bond_dev); out_rtnl: rtnl_unlock(); -done: return res; } @@ -4763,7 +4776,6 @@ static int __init bonding_init(void) { int i; int res; - char new_bond_name[8]; /* Enough room for 999 bonds at init. */ printk(KERN_INFO "%s", version); @@ -4776,8 +4788,7 @@ static int __init bonding_init(void) bond_create_proc_dir(); #endif for (i = 0; i < max_bonds; i++) { - sprintf(new_bond_name, "bond%d",i); - res = bond_create(new_bond_name,&bonding_defaults, NULL); + res = bond_create(NULL, &bonding_defaults, NULL); if (res) goto err; } diff -puN drivers/net/bonding/bond_sysfs.c~git-netdev-all drivers/net/bonding/bond_sysfs.c --- a/drivers/net/bonding/bond_sysfs.c~git-netdev-all +++ a/drivers/net/bonding/bond_sysfs.c @@ -1433,6 +1433,21 @@ int bond_create_sysfs(void) return -ENODEV; ret = class_create_file(netdev_class, &class_attr_bonding_masters); + /* + * Permit multiple loads of the module by ignoring failures to + * create the bonding_masters sysfs file. Bonding devices + * created by second or subsequent loads of the module will + * not be listed in, or controllable by, bonding_masters, but + * will have the usual "bonding" sysfs directory. + * + * This is done to preserve backwards compatibility for + * initscripts/sysconfig, which load bonding multiple times to + * configure multiple bonding devices. + */ + if (ret == -EEXIST) { + netdev_class = NULL; + return 0; + } return ret; diff -puN drivers/net/bonding/bonding.h~git-netdev-all drivers/net/bonding/bonding.h --- a/drivers/net/bonding/bonding.h~git-netdev-all +++ a/drivers/net/bonding/bonding.h @@ -22,8 +22,8 @@ #include "bond_3ad.h" #include "bond_alb.h" -#define DRV_VERSION "3.1.1" -#define DRV_RELDATE "September 26, 2006" +#define DRV_VERSION "3.1.2" +#define DRV_RELDATE "January 20, 2007" #define DRV_NAME "bonding" #define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" diff -puN drivers/net/chelsio/common.h~git-netdev-all drivers/net/chelsio/common.h --- a/drivers/net/chelsio/common.h~git-netdev-all +++ a/drivers/net/chelsio/common.h @@ -324,7 +324,7 @@ struct board_info { unsigned char mdio_phybaseaddr; struct gmac *gmac; struct gphy *gphy; - struct mdio_ops *mdio_ops; + struct mdio_ops *mdio_ops; const char *desc; }; diff -puN drivers/net/chelsio/cpl5_cmd.h~git-netdev-all drivers/net/chelsio/cpl5_cmd.h --- a/drivers/net/chelsio/cpl5_cmd.h~git-netdev-all +++ a/drivers/net/chelsio/cpl5_cmd.h @@ -103,7 +103,7 @@ enum CPL_opcode { CPL_MIGRATE_C2T_RPL = 0xDD, CPL_ERROR = 0xD7, - /* internal: driver -> TOM */ + /* internal: driver -> TOM */ CPL_MSS_CHANGE = 0xE1 }; @@ -159,8 +159,8 @@ enum { // TX_PKT_LSO ethe }; union opcode_tid { - u32 opcode_tid; - u8 opcode; + u32 opcode_tid; + u8 opcode; }; #define S_OPCODE 24 @@ -234,7 +234,7 @@ struct cpl_pass_accept_req { u32 local_ip; u32 peer_ip; u32 tos_tid; - struct tcp_options tcp_options; + struct tcp_options tcp_options; u8 dst_mac[6]; u16 vlan_tag; u8 src_mac[6]; @@ -250,12 +250,12 @@ struct cpl_pass_accept_rpl { u32 peer_ip; u32 opt0h; union { - u32 opt0l; - struct { - u8 rsvd[3]; - u8 status; + u32 opt0l; + struct { + u8 rsvd[3]; + u8 status; + }; }; - }; }; struct cpl_act_open_req { diff -puN drivers/net/chelsio/cxgb2.c~git-netdev-all drivers/net/chelsio/cxgb2.c --- a/drivers/net/chelsio/cxgb2.c~git-netdev-all +++ a/drivers/net/chelsio/cxgb2.c @@ -69,14 +69,14 @@ static inline void cancel_mac_stats_upda cancel_delayed_work(&ap->stats_update_task); } -#define MAX_CMDQ_ENTRIES 16384 -#define MAX_CMDQ1_ENTRIES 1024 -#define MAX_RX_BUFFERS 16384 -#define MAX_RX_JUMBO_BUFFERS 16384 +#define MAX_CMDQ_ENTRIES 16384 +#define MAX_CMDQ1_ENTRIES 1024 +#define MAX_RX_BUFFERS 16384 +#define MAX_RX_JUMBO_BUFFERS 16384 #define MAX_TX_BUFFERS_HIGH 16384U #define MAX_TX_BUFFERS_LOW 1536U #define MAX_TX_BUFFERS 1460U -#define MIN_FL_ENTRIES 32 +#define MIN_FL_ENTRIES 32 #define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ @@ -143,7 +143,7 @@ static void link_report(struct port_info case SPEED_100: s = "100Mbps"; break; } - printk(KERN_INFO "%s: link up, %s, %s-duplex\n", + printk(KERN_INFO "%s: link up, %s, %s-duplex\n", p->dev->name, s, p->link_config.duplex == DUPLEX_FULL ? "full" : "half"); } @@ -233,7 +233,7 @@ static int cxgb_up(struct adapter *adapt t1_sge_start(adapter->sge); t1_interrupts_enable(adapter); - out_err: +out_err: return err; } @@ -454,51 +454,21 @@ static void get_stats(struct net_device const struct cmac_statistics *s; const struct sge_intr_counts *t; struct sge_port_stats ss; + unsigned int len; s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL); - *data++ = s->TxOctetsOK; - *data++ = s->TxOctetsBad; - *data++ = s->TxUnicastFramesOK; - *data++ = s->TxMulticastFramesOK; - *data++ = s->TxBroadcastFramesOK; - *data++ = s->TxPauseFrames; - *data++ = s->TxFramesWithDeferredXmissions; - *data++ = s->TxLateCollisions; - *data++ = s->TxTotalCollisions; - *data++ = s->TxFramesAbortedDueToXSCollisions; - *data++ = s->TxUnderrun; - *data++ = s->TxLengthErrors; - *data++ = s->TxInternalMACXmitError; - *data++ = s->TxFramesWithExcessiveDeferral; - *data++ = s->TxFCSErrors; - - *data++ = s->RxOctetsOK; - *data++ = s->RxOctetsBad; - *data++ = s->RxUnicastFramesOK; - *data++ = s->RxMulticastFramesOK; - *data++ = s->RxBroadcastFramesOK; - *data++ = s->RxPauseFrames; - *data++ = s->RxFCSErrors; - *data++ = s->RxAlignErrors; - *data++ = s->RxSymbolErrors; - *data++ = s->RxDataErrors; - *data++ = s->RxSequenceErrors; - *data++ = s->RxRuntErrors; - *data++ = s->RxJabberErrors; - *data++ = s->RxInternalMACRcvError; - *data++ = s->RxInRangeLengthErrors; - *data++ = s->RxOutOfRangeLengthField; - *data++ = s->RxFrameTooLongErrors; + len = sizeof(u64)*(&s->TxFCSErrors + 1 - &s->TxOctetsOK); + memcpy(data, &s->TxOctetsOK, len); + data += len; + + len = sizeof(u64)*(&s->RxFrameTooLongErrors + 1 - &s->RxOctetsOK); + memcpy(data, &s->RxOctetsOK, len); + data += len; t1_sge_get_port_stats(adapter->sge, dev->if_port, &ss); - *data++ = ss.rx_packets; - *data++ = ss.rx_cso_good; - *data++ = ss.tx_packets; - *data++ = ss.tx_cso; - *data++ = ss.tx_tso; - *data++ = ss.vlan_xtract; - *data++ = ss.vlan_insert; + memcpy(data, &ss, sizeof(ss)); + data += sizeof(ss); t = t1_sge_get_intr_counts(adapter->sge); *data++ = t->rx_drops; @@ -749,7 +719,7 @@ static int set_sge_param(struct net_devi return -EINVAL; if (adapter->flags & FULL_INIT_DONE) - return -EBUSY; + return -EBUSY; adapter->params.sge.freelQ_size[!jumbo_fl] = e->rx_pending; adapter->params.sge.freelQ_size[jumbo_fl] = e->rx_jumbo_pending; @@ -764,7 +734,7 @@ static int set_coalesce(struct net_devic struct adapter *adapter = dev->priv; adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs; - adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce; + adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce; adapter->params.sge.sample_interval_usecs = c->rate_sample_interval; t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge); return 0; @@ -782,9 +752,9 @@ static int get_coalesce(struct net_devic static int get_eeprom_len(struct net_device *dev) { - struct adapter *adapter = dev->priv; + struct adapter *adapter = dev->priv; - return t1_is_asic(adapter) ? EEPROM_SIZE : 0; + return t1_is_asic(adapter) ? EEPROM_SIZE : 0; } #define EEPROM_MAGIC(ap) \ @@ -848,7 +818,7 @@ static int t1_ioctl(struct net_device *d u32 val; if (!phy->mdio_read) - return -EOPNOTSUPP; + return -EOPNOTSUPP; phy->mdio_read(adapter, data->phy_id, 0, data->reg_num & 0x1f, &val); data->val_out = val; @@ -860,7 +830,7 @@ static int t1_ioctl(struct net_device *d if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!phy->mdio_write) - return -EOPNOTSUPP; + return -EOPNOTSUPP; phy->mdio_write(adapter, data->phy_id, 0, data->reg_num & 0x1f, data->val_in); break; @@ -879,9 +849,9 @@ static int t1_change_mtu(struct net_devi struct cmac *mac = adapter->port[dev->if_port].mac; if (!mac->ops->set_mtu) - return -EOPNOTSUPP; + return -EOPNOTSUPP; if (new_mtu < 68) - return -EINVAL; + return -EINVAL; if ((ret = mac->ops->set_mtu(mac, new_mtu))) return ret; dev->mtu = new_mtu; @@ -1211,9 +1181,9 @@ static int __devinit init_one(struct pci return 0; - out_release_adapter_res: +out_release_adapter_res: t1_free_sw_modules(adapter); - out_free_dev: +out_free_dev: if (adapter) { if (adapter->regs) iounmap(adapter->regs); @@ -1222,7 +1192,7 @@ static int __devinit init_one(struct pci free_netdev(adapter->port[i].dev); } pci_release_regions(pdev); - out_disable_pdev: +out_disable_pdev: pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); return err; @@ -1273,28 +1243,27 @@ static int t1_clock(struct adapter *adap int M_MEM_VAL; enum { - M_CORE_BITS = 9, - T_CORE_VAL = 0, - T_CORE_BITS = 2, - N_CORE_VAL = 0, - N_CORE_BITS = 2, - M_MEM_BITS = 9, - T_MEM_VAL = 0, - T_MEM_BITS = 2, - N_MEM_VAL = 0, - N_MEM_BITS = 2, - NP_LOAD = 1 << 17, - S_LOAD_MEM = 1 << 5, - S_LOAD_CORE = 1 << 6, - S_CLOCK = 1 << 3 + M_CORE_BITS = 9, + T_CORE_VAL = 0, + T_CORE_BITS = 2, + N_CORE_VAL = 0, + N_CORE_BITS = 2, + M_MEM_BITS = 9, + T_MEM_VAL = 0, + T_MEM_BITS = 2, + N_MEM_VAL = 0, + N_MEM_BITS = 2, + NP_LOAD = 1 << 17, + S_LOAD_MEM = 1 << 5, + S_LOAD_CORE = 1 << 6, + S_CLOCK = 1 << 3 }; if (!t1_is_T1B(adapter)) return -ENODEV; /* Can't re-clock this chip. */ - if (mode & 2) { + if (mode & 2) return 0; /* show current mode. */ - } if ((adapter->t1powersave & 1) == (mode & 1)) return -EALREADY; /* ASIC already running in mode. */ @@ -1386,26 +1355,26 @@ static inline void t1_sw_reset(struct pc static void __devexit remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); + struct adapter *adapter = dev->priv; + int i; - if (dev) { - int i; - struct adapter *adapter = dev->priv; - - for_each_port(adapter, i) - if (test_bit(i, &adapter->registered_device_map)) - unregister_netdev(adapter->port[i].dev); - - t1_free_sw_modules(adapter); - iounmap(adapter->regs); - while (--i >= 0) - if (adapter->port[i].dev) - free_netdev(adapter->port[i].dev); + for_each_port(adapter, i) { + if (test_bit(i, &adapter->registered_device_map)) + unregister_netdev(adapter->port[i].dev); + } + + t1_free_sw_modules(adapter); + iounmap(adapter->regs); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - t1_sw_reset(pdev); + while (--i >= 0) { + if (adapter->port[i].dev) + free_netdev(adapter->port[i].dev); } + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + t1_sw_reset(pdev); } static struct pci_driver driver = { diff -puN drivers/net/chelsio/elmer0.h~git-netdev-all drivers/net/chelsio/elmer0.h --- a/drivers/net/chelsio/elmer0.h~git-netdev-all +++ a/drivers/net/chelsio/elmer0.h @@ -46,14 +46,14 @@ enum { }; /* ELMER0 registers */ -#define A_ELMER0_VERSION 0x100000 -#define A_ELMER0_PHY_CFG 0x100004 -#define A_ELMER0_INT_ENABLE 0x100008 -#define A_ELMER0_INT_CAUSE 0x10000c -#define A_ELMER0_GPI_CFG 0x100010 -#define A_ELMER0_GPI_STAT 0x100014 -#define A_ELMER0_GPO 0x100018 -#define A_ELMER0_PORT0_MI1_CFG 0x400000 +#define A_ELMER0_VERSION 0x100000 +#define A_ELMER0_PHY_CFG 0x100004 +#define A_ELMER0_INT_ENABLE 0x100008 +#define A_ELMER0_INT_CAUSE 0x10000c +#define A_ELMER0_GPI_CFG 0x100010 +#define A_ELMER0_GPI_STAT 0x100014 +#define A_ELMER0_GPO 0x100018 +#define A_ELMER0_PORT0_MI1_CFG 0x400000 #define S_MI1_MDI_ENABLE 0 #define V_MI1_MDI_ENABLE(x) ((x) << S_MI1_MDI_ENABLE) @@ -111,18 +111,18 @@ enum { #define V_MI1_OP_BUSY(x) ((x) << S_MI1_OP_BUSY) #define F_MI1_OP_BUSY V_MI1_OP_BUSY(1U) -#define A_ELMER0_PORT1_MI1_CFG 0x500000 -#define A_ELMER0_PORT1_MI1_ADDR 0x500004 -#define A_ELMER0_PORT1_MI1_DATA 0x500008 -#define A_ELMER0_PORT1_MI1_OP 0x50000c -#define A_ELMER0_PORT2_MI1_CFG 0x600000 -#define A_ELMER0_PORT2_MI1_ADDR 0x600004 -#define A_ELMER0_PORT2_MI1_DATA 0x600008 -#define A_ELMER0_PORT2_MI1_OP 0x60000c -#define A_ELMER0_PORT3_MI1_CFG 0x700000 -#define A_ELMER0_PORT3_MI1_ADDR 0x700004 -#define A_ELMER0_PORT3_MI1_DATA 0x700008 -#define A_ELMER0_PORT3_MI1_OP 0x70000c +#define A_ELMER0_PORT1_MI1_CFG 0x500000 +#define A_ELMER0_PORT1_MI1_ADDR 0x500004 +#define A_ELMER0_PORT1_MI1_DATA 0x500008 +#define A_ELMER0_PORT1_MI1_OP 0x50000c +#define A_ELMER0_PORT2_MI1_CFG 0x600000 +#define A_ELMER0_PORT2_MI1_ADDR 0x600004 +#define A_ELMER0_PORT2_MI1_DATA 0x600008 +#define A_ELMER0_PORT2_MI1_OP 0x60000c +#define A_ELMER0_PORT3_MI1_CFG 0x700000 +#define A_ELMER0_PORT3_MI1_ADDR 0x700004 +#define A_ELMER0_PORT3_MI1_DATA 0x700008 +#define A_ELMER0_PORT3_MI1_OP 0x70000c /* Simple bit definition for GPI and GP0 registers. */ #define ELMER0_GP_BIT0 0x0001 diff -puN drivers/net/chelsio/espi.c~git-netdev-all drivers/net/chelsio/espi.c --- a/drivers/net/chelsio/espi.c~git-netdev-all +++ a/drivers/net/chelsio/espi.c @@ -202,9 +202,9 @@ static void espi_setup_for_pm3393(adapte static void espi_setup_for_vsc7321(adapter_t *adapter) { - writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0); - writel(0x1f401f4, adapter->regs + A_ESPI_SCH_TOKEN1); - writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2); + writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0); + writel(0x1f401f4, adapter->regs + A_ESPI_SCH_TOKEN1); + writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2); writel(0xa00, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); writel(0x1ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); writel(1, adapter->regs + A_ESPI_CALENDAR_LENGTH); @@ -247,10 +247,10 @@ int t1_espi_init(struct peespi *espi, in writel(V_OUT_OF_SYNC_COUNT(4) | V_DIP2_PARITY_ERR_THRES(3) | V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL); - writel(nports == 4 ? 0x200040 : 0x1000080, + writel(nports == 4 ? 0x200040 : 0x1000080, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); } else - writel(0x800100, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); + writel(0x800100, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); if (mac_type == CHBT_MAC_PM3393) espi_setup_for_pm3393(adapter); @@ -301,7 +301,8 @@ void t1_espi_set_misc_ctrl(adapter_t *ad { struct peespi *espi = adapter->espi; - if (!is_T2(adapter)) return; + if (!is_T2(adapter)) + return; spin_lock(&espi->lock); espi->misc_ctrl = (val & ~MON_MASK) | (espi->misc_ctrl & MON_MASK); @@ -340,32 +341,31 @@ u32 t1_espi_get_mon(adapter_t *adapter, * compare with t1_espi_get_mon(), it reads espiInTxSop[0 ~ 3] in * one shot, since there is no per port counter on the out side. */ -int -t1_espi_get_mon_t204(adapter_t *adapter, u32 *valp, u8 wait) +int t1_espi_get_mon_t204(adapter_t *adapter, u32 *valp, u8 wait) { - struct peespi *espi = adapter->espi; + struct peespi *espi = adapter->espi; u8 i, nport = (u8)adapter->params.nports; - if (!wait) { - if (!spin_trylock(&espi->lock)) - return -1; - } else - spin_lock(&espi->lock); + if (!wait) { + if (!spin_trylock(&espi->lock)) + return -1; + } else + spin_lock(&espi->lock); - if ( (espi->misc_ctrl & MON_MASK) != F_MONITORED_DIRECTION ) { + if ((espi->misc_ctrl & MON_MASK) != F_MONITORED_DIRECTION) { espi->misc_ctrl = (espi->misc_ctrl & ~MON_MASK) | F_MONITORED_DIRECTION; - writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); - } + writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); + } for (i = 0 ; i < nport; i++, valp++) { if (i) { writel(espi->misc_ctrl | V_MONITORED_PORT_NUM(i), adapter->regs + A_ESPI_MISC_CONTROL); } - *valp = readl(adapter->regs + A_ESPI_SCH_TOKEN3); - } + *valp = readl(adapter->regs + A_ESPI_SCH_TOKEN3); + } - writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); - spin_unlock(&espi->lock); - return 0; + writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); + spin_unlock(&espi->lock); + return 0; } diff -puN drivers/net/chelsio/fpga_defs.h~git-netdev-all drivers/net/chelsio/fpga_defs.h --- a/drivers/net/chelsio/fpga_defs.h~git-netdev-all +++ a/drivers/net/chelsio/fpga_defs.h @@ -98,9 +98,9 @@ #define A_MI0_DATA_INT 0xb10 /* GMAC registers */ -#define A_GMAC_MACID_LO 0x28 -#define A_GMAC_MACID_HI 0x2c -#define A_GMAC_CSR 0x30 +#define A_GMAC_MACID_LO 0x28 +#define A_GMAC_MACID_HI 0x2c +#define A_GMAC_CSR 0x30 #define S_INTERFACE 0 #define M_INTERFACE 0x3 diff -puN drivers/net/chelsio/gmac.h~git-netdev-all drivers/net/chelsio/gmac.h --- a/drivers/net/chelsio/gmac.h~git-netdev-all +++ a/drivers/net/chelsio/gmac.h @@ -42,8 +42,15 @@ #include "common.h" -enum { MAC_STATS_UPDATE_FAST, MAC_STATS_UPDATE_FULL }; -enum { MAC_DIRECTION_RX = 1, MAC_DIRECTION_TX = 2 }; +enum { + MAC_STATS_UPDATE_FAST, + MAC_STATS_UPDATE_FULL +}; + +enum { + MAC_DIRECTION_RX = 1, + MAC_DIRECTION_TX = 2 +}; struct cmac_statistics { /* Transmit */ diff -puN drivers/net/chelsio/ixf1010.c~git-netdev-all drivers/net/chelsio/ixf1010.c --- a/drivers/net/chelsio/ixf1010.c~git-netdev-all +++ a/drivers/net/chelsio/ixf1010.c @@ -145,48 +145,61 @@ static void disable_port(struct cmac *ma t1_tpi_write(mac->adapter, REG_PORT_ENABLE, val); } -#define RMON_UPDATE(mac, name, stat_name) \ - t1_tpi_read((mac)->adapter, MACREG(mac, REG_##name), &val); \ - (mac)->stats.stat_name += val; - /* * Read the current values of the RMON counters and add them to the cumulative * port statistics. The HW RMON counters are cleared by this operation. */ static void port_stats_update(struct cmac *mac) { - u32 val; + static struct { + unsigned int reg; + unsigned int offset; + } hw_stats[] = { + +#define HW_STAT(name, stat_name) \ + { REG_##name, \ + (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } + + /* Rx stats */ + HW_STAT(RxOctetsTotalOK, RxOctetsOK), + HW_STAT(RxOctetsBad, RxOctetsBad), + HW_STAT(RxUCPkts, RxUnicastFramesOK), + HW_STAT(RxMCPkts, RxMulticastFramesOK), + HW_STAT(RxBCPkts, RxBroadcastFramesOK), + HW_STAT(RxJumboPkts, RxJumboFramesOK), + HW_STAT(RxFCSErrors, RxFCSErrors), + HW_STAT(RxAlignErrors, RxAlignErrors), + HW_STAT(RxLongErrors, RxFrameTooLongErrors), + HW_STAT(RxVeryLongErrors, RxFrameTooLongErrors), + HW_STAT(RxPauseMacControlCounter, RxPauseFrames), + HW_STAT(RxDataErrors, RxDataErrors), + HW_STAT(RxJabberErrors, RxJabberErrors), + HW_STAT(RxRuntErrors, RxRuntErrors), + HW_STAT(RxShortErrors, RxRuntErrors), + HW_STAT(RxSequenceErrors, RxSequenceErrors), + HW_STAT(RxSymbolErrors, RxSymbolErrors), + + /* Tx stats (skip collision stats as we are full-duplex only) */ + HW_STAT(TxOctetsTotalOK, TxOctetsOK), + HW_STAT(TxOctetsBad, TxOctetsBad), + HW_STAT(TxUCPkts, TxUnicastFramesOK), + HW_STAT(TxMCPkts, TxMulticastFramesOK), + HW_STAT(TxBCPkts, TxBroadcastFramesOK), + HW_STAT(TxJumboPkts, TxJumboFramesOK), + HW_STAT(TxPauseFrames, TxPauseFrames), + HW_STAT(TxExcessiveLengthDrop, TxLengthErrors), + HW_STAT(TxUnderrun, TxUnderrun), + HW_STAT(TxCRCErrors, TxFCSErrors) + }, *p = hw_stats; + u64 *stats = (u64 *) &mac->stats; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hw_stats); i++) { + u32 val; - /* Rx stats */ - RMON_UPDATE(mac, RxOctetsTotalOK, RxOctetsOK); - RMON_UPDATE(mac, RxOctetsBad, RxOctetsBad); - RMON_UPDATE(mac, RxUCPkts, RxUnicastFramesOK); - RMON_UPDATE(mac, RxMCPkts, RxMulticastFramesOK); - RMON_UPDATE(mac, RxBCPkts, RxBroadcastFramesOK); - RMON_UPDATE(mac, RxJumboPkts, RxJumboFramesOK); - RMON_UPDATE(mac, RxFCSErrors, RxFCSErrors); - RMON_UPDATE(mac, RxAlignErrors, RxAlignErrors); - RMON_UPDATE(mac, RxLongErrors, RxFrameTooLongErrors); - RMON_UPDATE(mac, RxVeryLongErrors, RxFrameTooLongErrors); - RMON_UPDATE(mac, RxPauseMacControlCounter, RxPauseFrames); - RMON_UPDATE(mac, RxDataErrors, RxDataErrors); - RMON_UPDATE(mac, RxJabberErrors, RxJabberErrors); - RMON_UPDATE(mac, RxRuntErrors, RxRuntErrors); - RMON_UPDATE(mac, RxShortErrors, RxRuntErrors); - RMON_UPDATE(mac, RxSequenceErrors, RxSequenceErrors); - RMON_UPDATE(mac, RxSymbolErrors, RxSymbolErrors); - - /* Tx stats (skip collision stats as we are full-duplex only) */ - RMON_UPDATE(mac, TxOctetsTotalOK, TxOctetsOK); - RMON_UPDATE(mac, TxOctetsBad, TxOctetsBad); - RMON_UPDATE(mac, TxUCPkts, TxUnicastFramesOK); - RMON_UPDATE(mac, TxMCPkts, TxMulticastFramesOK); - RMON_UPDATE(mac, TxBCPkts, TxBroadcastFramesOK); - RMON_UPDATE(mac, TxJumboPkts, TxJumboFramesOK); - RMON_UPDATE(mac, TxPauseFrames, TxPauseFrames); - RMON_UPDATE(mac, TxExcessiveLengthDrop, TxLengthErrors); - RMON_UPDATE(mac, TxUnderrun, TxUnderrun); - RMON_UPDATE(mac, TxCRCErrors, TxFCSErrors); + t1_tpi_read(mac->adapter, MACREG(mac, p->reg), &val); + stats[p->offset] += val; + } } /* No-op interrupt operation as this MAC does not support interrupts */ @@ -273,7 +286,8 @@ static int mac_set_rx_mode(struct cmac * static int mac_set_mtu(struct cmac *mac, int mtu) { /* MAX_FRAME_SIZE inludes header + FCS, mtu doesn't */ - if (mtu > (MAX_FRAME_SIZE - 14 - 4)) return -EINVAL; + if (mtu > (MAX_FRAME_SIZE - 14 - 4)) + return -EINVAL; t1_tpi_write(mac->adapter, MACREG(mac, REG_MAX_FRAME_SIZE), mtu + 14 + 4); return 0; @@ -357,8 +371,8 @@ static void enable_port(struct cmac *mac val |= (1 << index); t1_tpi_write(adapter, REG_PORT_ENABLE, val); - index <<= 2; - if (is_T2(adapter)) { + index <<= 2; + if (is_T2(adapter)) { /* T204: set the Fifo water level & threshold */ t1_tpi_write(adapter, RX_FIFO_HIGH_WATERMARK_BASE + index, 0x740); t1_tpi_write(adapter, RX_FIFO_LOW_WATERMARK_BASE + index, 0x730); @@ -389,6 +403,10 @@ static int mac_disable(struct cmac *mac, return 0; } +#define RMON_UPDATE(mac, name, stat_name) \ + t1_tpi_read((mac)->adapter, MACREG(mac, REG_##name), &val); \ + (mac)->stats.stat_name += val; + /* * This function is called periodically to accumulate the current values of the * RMON counters into the port statistics. Since the counters are only 32 bits @@ -460,10 +478,12 @@ static struct cmac *ixf1010_mac_create(a struct cmac *mac; u32 val; - if (index > 9) return NULL; + if (index > 9) + return NULL; mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); - if (!mac) return NULL; + if (!mac) + return NULL; mac->ops = &ixf1010_ops; mac->instance = (cmac_instance *)(mac + 1); diff -puN drivers/net/chelsio/mv88e1xxx.c~git-netdev-all drivers/net/chelsio/mv88e1xxx.c --- a/drivers/net/chelsio/mv88e1xxx.c~git-netdev-all +++ a/drivers/net/chelsio/mv88e1xxx.c @@ -73,9 +73,8 @@ static int mv88e1xxx_interrupt_enable(st t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { - elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - } + if (is_T2(cphy->adapter)) + elmer |= ELMER0_GP_BIT2 | ELMER0_GP_BIT3 | ELMER0_GP_BIT4; t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); } return 0; @@ -92,9 +91,8 @@ static int mv88e1xxx_interrupt_disable(s t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); elmer &= ~ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { + if (is_T2(cphy->adapter)) elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4); - } t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); } return 0; @@ -112,9 +110,8 @@ static int mv88e1xxx_interrupt_clear(str if (t1_is_asic(cphy->adapter)) { t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { + if (is_T2(cphy->adapter)) elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - } t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); } return 0; @@ -300,7 +297,7 @@ static int mv88e1xxx_interrupt_handler(s /* * Loop until cause reads zero. Need to handle bouncing interrupts. - */ + */ while (1) { u32 cause; @@ -308,15 +305,16 @@ static int mv88e1xxx_interrupt_handler(s MV88E1XXX_INTERRUPT_STATUS_REGISTER, &cause); cause &= INTR_ENABLE_MASK; - if (!cause) break; + if (!cause) + break; if (cause & MV88E1XXX_INTR_LINK_CHNG) { (void) simple_mdio_read(cphy, MV88E1XXX_SPECIFIC_STATUS_REGISTER, &status); - if (status & MV88E1XXX_INTR_LINK_CHNG) { + if (status & MV88E1XXX_INTR_LINK_CHNG) cphy->state |= PHY_LINK_UP; - } else { + else { cphy->state &= ~PHY_LINK_UP; if (cphy->state & PHY_AUTONEG_EN) cphy->state &= ~PHY_AUTONEG_RDY; @@ -360,7 +358,8 @@ static struct cphy *mv88e1xxx_phy_create { struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); - if (!cphy) return NULL; + if (!cphy) + return NULL; cphy_init(cphy, adapter, phy_addr, &mv88e1xxx_ops, mdio_ops); @@ -377,11 +376,11 @@ static struct cphy *mv88e1xxx_phy_create } (void) mv88e1xxx_downshift_set(cphy, 1); /* Enable downshift */ - /* LED */ + /* LED */ if (is_T2(adapter)) { (void) simple_mdio_write(cphy, MV88E1XXX_LED_CONTROL_REGISTER, 0x1); - } + } return cphy; } diff -puN drivers/net/chelsio/my3126.c~git-netdev-all drivers/net/chelsio/my3126.c --- a/drivers/net/chelsio/my3126.c~git-netdev-all +++ a/drivers/net/chelsio/my3126.c @@ -10,25 +10,25 @@ static int my3126_reset(struct cphy *cph * This can be done through registers. It is not required since * a full chip reset is used. */ - return (0); + return 0; } static int my3126_interrupt_enable(struct cphy *cphy) { schedule_delayed_work(&cphy->phy_update, HZ/30); t1_tpi_read(cphy->adapter, A_ELMER0_GPO, &cphy->elmer_gpo); - return (0); + return 0; } static int my3126_interrupt_disable(struct cphy *cphy) { cancel_rearming_delayed_work(&cphy->phy_update); - return (0); + return 0; } static int my3126_interrupt_clear(struct cphy *cphy) { - return (0); + return 0; } #define OFFSET(REG_ADDR) (REG_ADDR << 2) @@ -102,7 +102,7 @@ static void my3216_poll(struct work_stru static int my3126_set_loopback(struct cphy *cphy, int on) { - return (0); + return 0; } /* To check the activity LED */ @@ -146,7 +146,7 @@ static int my3126_get_link_status(struct if (fc) *fc = PAUSE_RX | PAUSE_TX; - return (0); + return 0; } static void my3126_destroy(struct cphy *cphy) @@ -177,7 +177,7 @@ static struct cphy *my3126_phy_create(ad INIT_DELAYED_WORK(&cphy->phy_update, my3216_poll); cphy->bmsr = 0; - return (cphy); + return cphy; } /* Chip Reset */ @@ -198,7 +198,7 @@ static int my3126_phy_reset(adapter_t * val |= 0x8000; t1_tpi_write(adapter, A_ELMER0_GPO, val); udelay(100); - return (0); + return 0; } struct gphy t1_my3126_ops = { diff -puN drivers/net/chelsio/pm3393.c~git-netdev-all drivers/net/chelsio/pm3393.c --- a/drivers/net/chelsio/pm3393.c~git-netdev-all +++ a/drivers/net/chelsio/pm3393.c @@ -446,17 +446,51 @@ static void pm3393_rmon_update(struct ad *val += 1ull << 40; } -#define RMON_UPDATE(mac, name, stat_name) \ - pm3393_rmon_update((mac)->adapter, OFFSET(name), \ - &(mac)->stats.stat_name, \ - (ro &((name - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW) >> 2))) - - static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac, int flag) { - u64 ro; - u32 val0, val1, val2, val3; + static struct { + unsigned int reg; + unsigned int offset; + } hw_stats [] = { + +#define HW_STAT(name, stat_name) \ + { name, (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } + + /* Rx stats */ + HW_STAT(RxOctetsReceivedOK, RxOctetsOK), + HW_STAT(RxUnicastFramesReceivedOK, RxUnicastFramesOK), + HW_STAT(RxMulticastFramesReceivedOK, RxMulticastFramesOK), + HW_STAT(RxBroadcastFramesReceivedOK, RxBroadcastFramesOK), + HW_STAT(RxPAUSEMACCtrlFramesReceived, RxPauseFrames), + HW_STAT(RxFrameCheckSequenceErrors, RxFCSErrors), + HW_STAT(RxFramesLostDueToInternalMACErrors, + RxInternalMACRcvError), + HW_STAT(RxSymbolErrors, RxSymbolErrors), + HW_STAT(RxInRangeLengthErrors, RxInRangeLengthErrors), + HW_STAT(RxFramesTooLongErrors , RxFrameTooLongErrors), + HW_STAT(RxJabbers, RxJabberErrors), + HW_STAT(RxFragments, RxRuntErrors), + HW_STAT(RxUndersizedFrames, RxRuntErrors), + HW_STAT(RxJumboFramesReceivedOK, RxJumboFramesOK), + HW_STAT(RxJumboOctetsReceivedOK, RxJumboOctetsOK), + + /* Tx stats */ + HW_STAT(TxOctetsTransmittedOK, TxOctetsOK), + HW_STAT(TxFramesLostDueToInternalMACTransmissionError, + TxInternalMACXmitError), + HW_STAT(TxTransmitSystemError, TxFCSErrors), + HW_STAT(TxUnicastFramesTransmittedOK, TxUnicastFramesOK), + HW_STAT(TxMulticastFramesTransmittedOK, TxMulticastFramesOK), + HW_STAT(TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK), + HW_STAT(TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames), + HW_STAT(TxJumboFramesReceivedOK, TxJumboFramesOK), + HW_STAT(TxJumboOctetsReceivedOK, TxJumboOctetsOK) + }, *p = hw_stats; + u64 ro; + u32 val0, val1, val2, val3; + u64 *stats = (u64 *) &mac->stats; + unsigned int i; /* Snap the counters */ pmwrite(mac, SUNI1x10GEXP_REG_MSTAT_CONTROL, @@ -470,35 +504,14 @@ static const struct cmac_statistics *pm3 ro = ((u64)val0 & 0xffff) | (((u64)val1 & 0xffff) << 16) | (((u64)val2 & 0xffff) << 32) | (((u64)val3 & 0xffff) << 48); - /* Rx stats */ - RMON_UPDATE(mac, RxOctetsReceivedOK, RxOctetsOK); - RMON_UPDATE(mac, RxUnicastFramesReceivedOK, RxUnicastFramesOK); - RMON_UPDATE(mac, RxMulticastFramesReceivedOK, RxMulticastFramesOK); - RMON_UPDATE(mac, RxBroadcastFramesReceivedOK, RxBroadcastFramesOK); - RMON_UPDATE(mac, RxPAUSEMACCtrlFramesReceived, RxPauseFrames); - RMON_UPDATE(mac, RxFrameCheckSequenceErrors, RxFCSErrors); - RMON_UPDATE(mac, RxFramesLostDueToInternalMACErrors, - RxInternalMACRcvError); - RMON_UPDATE(mac, RxSymbolErrors, RxSymbolErrors); - RMON_UPDATE(mac, RxInRangeLengthErrors, RxInRangeLengthErrors); - RMON_UPDATE(mac, RxFramesTooLongErrors , RxFrameTooLongErrors); - RMON_UPDATE(mac, RxJabbers, RxJabberErrors); - RMON_UPDATE(mac, RxFragments, RxRuntErrors); - RMON_UPDATE(mac, RxUndersizedFrames, RxRuntErrors); - RMON_UPDATE(mac, RxJumboFramesReceivedOK, RxJumboFramesOK); - RMON_UPDATE(mac, RxJumboOctetsReceivedOK, RxJumboOctetsOK); - - /* Tx stats */ - RMON_UPDATE(mac, TxOctetsTransmittedOK, TxOctetsOK); - RMON_UPDATE(mac, TxFramesLostDueToInternalMACTransmissionError, - TxInternalMACXmitError); - RMON_UPDATE(mac, TxTransmitSystemError, TxFCSErrors); - RMON_UPDATE(mac, TxUnicastFramesTransmittedOK, TxUnicastFramesOK); - RMON_UPDATE(mac, TxMulticastFramesTransmittedOK, TxMulticastFramesOK); - RMON_UPDATE(mac, TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK); - RMON_UPDATE(mac, TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames); - RMON_UPDATE(mac, TxJumboFramesReceivedOK, TxJumboFramesOK); - RMON_UPDATE(mac, TxJumboOctetsReceivedOK, TxJumboOctetsOK); + for (i = 0; i < ARRAY_SIZE(hw_stats); i++) { + unsigned reg = p->reg - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW; + + pm3393_rmon_update((mac)->adapter, OFFSET(p->reg), + stats + p->offset, ro & (reg >> 2)); + } + + return &mac->stats; } @@ -534,9 +547,9 @@ static int pm3393_macaddress_set(struct /* Store local copy */ memcpy(cmac->instance->mac_addr, ma, 6); - lo = ((u32) ma[1] << 8) | (u32) ma[0]; + lo = ((u32) ma[1] << 8) | (u32) ma[0]; mid = ((u32) ma[3] << 8) | (u32) ma[2]; - hi = ((u32) ma[5] << 8) | (u32) ma[4]; + hi = ((u32) ma[5] << 8) | (u32) ma[4]; /* Disable Rx/Tx MAC before configuring it. */ if (enabled) diff -puN drivers/net/chelsio/sge.c~git-netdev-all drivers/net/chelsio/sge.c --- a/drivers/net/chelsio/sge.c~git-netdev-all +++ a/drivers/net/chelsio/sge.c @@ -71,12 +71,9 @@ #define SGE_FREEL_REFILL_THRESH 16 #define SGE_RESPQ_E_N 1024 #define SGE_INTRTIMER_NRES 1000 -#define SGE_RX_COPY_THRES 256 #define SGE_RX_SM_BUF_SIZE 1536 #define SGE_TX_DESC_MAX_PLEN 16384 -# define SGE_RX_DROP_THRES 2 - #define SGE_RESPQ_REPLENISH_THRES (SGE_RESPQ_E_N / 4) /* @@ -85,10 +82,6 @@ */ #define TX_RECLAIM_PERIOD (HZ / 4) -#ifndef NET_IP_ALIGN -# define NET_IP_ALIGN 2 -#endif - #define M_CMD_LEN 0x7fffffff #define V_CMD_LEN(v) (v) #define G_CMD_LEN(v) ((v) & M_CMD_LEN) @@ -195,7 +188,7 @@ struct cmdQ { struct cmdQ_e *entries; /* HW command descriptor Q */ struct cmdQ_ce *centries; /* SW command context descriptor Q */ dma_addr_t dma_addr; /* DMA addr HW command descriptor Q */ - spinlock_t lock; /* Lock to protect cmdQ enqueuing */ + spinlock_t lock; /* Lock to protect cmdQ enqueuing */ }; struct freelQ { @@ -241,9 +234,9 @@ struct sched_port { /* Per T204 device */ struct sched { ktime_t last_updated; /* last time quotas were computed */ - unsigned int max_avail; /* max bits to be sent to any port */ - unsigned int port; /* port index (round robin ports) */ - unsigned int num; /* num skbs in per port queues */ + unsigned int max_avail; /* max bits to be sent to any port */ + unsigned int port; /* port index (round robin ports) */ + unsigned int num; /* num skbs in per port queues */ struct sched_port p[MAX_NPORTS]; struct tasklet_struct sched_tsk;/* tasklet used to run scheduler */ }; @@ -259,10 +252,10 @@ static void restart_sched(unsigned long) * contention. */ struct sge { - struct adapter *adapter; /* adapter backpointer */ + struct adapter *adapter; /* adapter backpointer */ struct net_device *netdev; /* netdevice backpointer */ - struct freelQ freelQ[SGE_FREELQ_N]; /* buffer free lists */ - struct respQ respQ; /* response Q */ + struct freelQ freelQ[SGE_FREELQ_N]; /* buffer free lists */ + struct respQ respQ; /* response Q */ unsigned long stopped_tx_queues; /* bitmap of suspended Tx queues */ unsigned int rx_pkt_pad; /* RX padding for L2 packets */ unsigned int jumbo_fl; /* jumbo freelist Q index */ @@ -460,7 +453,7 @@ static struct sk_buff *sched_skb(struct if (credits < MAX_SKB_FRAGS + 1) goto out; - again: +again: for (i = 0; i < MAX_NPORTS; i++) { s->port = ++s->port & (MAX_NPORTS - 1); skbq = &s->p[s->port].skbq; @@ -483,8 +476,8 @@ static struct sk_buff *sched_skb(struct if (update-- && sched_update_avail(sge)) goto again; - out: - /* If there are more pending skbs, we use the hardware to schedule us +out: + /* If there are more pending skbs, we use the hardware to schedule us * again. */ if (s->num && !skb) { @@ -575,11 +568,10 @@ static int alloc_rx_resources(struct sge q->size = p->freelQ_size[i]; q->dma_offset = sge->rx_pkt_pad ? 0 : NET_IP_ALIGN; size = sizeof(struct freelQ_e) * q->size; - q->entries = (struct freelQ_e *) - pci_alloc_consistent(pdev, size, &q->dma_addr); + q->entries = pci_alloc_consistent(pdev, size, &q->dma_addr); if (!q->entries) goto err_no_mem; - memset(q->entries, 0, size); + size = sizeof(struct freelQ_ce) * q->size; q->centries = kzalloc(size, GFP_KERNEL); if (!q->centries) @@ -613,11 +605,10 @@ static int alloc_rx_resources(struct sge sge->respQ.size = SGE_RESPQ_E_N; sge->respQ.credits = 0; size = sizeof(struct respQ_e) * sge->respQ.size; - sge->respQ.entries = (struct respQ_e *) + sge->respQ.entries = pci_alloc_consistent(pdev, size, &sge->respQ.dma_addr); if (!sge->respQ.entries) goto err_no_mem; - memset(sge->respQ.entries, 0, size); return 0; err_no_mem: @@ -637,20 +628,12 @@ static void free_cmdQ_buffers(struct sge q->in_use -= n; ce = &q->centries[cidx]; while (n--) { - if (q->sop) { - if (likely(pci_unmap_len(ce, dma_len))) { - pci_unmap_single(pdev, - pci_unmap_addr(ce, dma_addr), - pci_unmap_len(ce, dma_len), - PCI_DMA_TODEVICE); + if (likely(pci_unmap_len(ce, dma_len))) { + pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr), + pci_unmap_len(ce, dma_len), + PCI_DMA_TODEVICE); + if (q->sop) q->sop = 0; - } - } else { - if (likely(pci_unmap_len(ce, dma_len))) { - pci_unmap_page(pdev, pci_unmap_addr(ce, dma_addr), - pci_unmap_len(ce, dma_len), - PCI_DMA_TODEVICE); - } } if (ce->skb) { dev_kfree_skb_any(ce->skb); @@ -711,11 +694,10 @@ static int alloc_tx_resources(struct sge q->stop_thres = 0; spin_lock_init(&q->lock); size = sizeof(struct cmdQ_e) * q->size; - q->entries = (struct cmdQ_e *) - pci_alloc_consistent(pdev, size, &q->dma_addr); + q->entries = pci_alloc_consistent(pdev, size, &q->dma_addr); if (!q->entries) goto err_no_mem; - memset(q->entries, 0, size); + size = sizeof(struct cmdQ_ce) * q->size; q->centries = kzalloc(size, GFP_KERNEL); if (!q->centries) @@ -770,7 +752,7 @@ void t1_set_vlan_accel(struct adapter *a static void configure_sge(struct sge *sge, struct sge_params *p) { struct adapter *ap = sge->adapter; - + writel(0, ap->regs + A_SG_CONTROL); setup_ring_params(ap, sge->cmdQ[0].dma_addr, sge->cmdQ[0].size, A_SG_CMD0BASELWR, A_SG_CMD0BASEUPR, A_SG_CMD0SIZE); @@ -850,7 +832,6 @@ static void refill_free_list(struct sge struct freelQ_e *e = &q->entries[q->pidx]; unsigned int dma_len = q->rx_buffer_size - q->dma_offset; - while (q->credits < q->size) { struct sk_buff *skb; dma_addr_t mapping; @@ -862,6 +843,8 @@ static void refill_free_list(struct sge skb_reserve(skb, q->dma_offset); mapping = pci_map_single(pdev, skb->data, dma_len, PCI_DMA_FROMDEVICE); + skb_reserve(skb, sge->rx_pkt_pad); + ce->skb = skb; pci_unmap_addr_set(ce, dma_addr, mapping); pci_unmap_len_set(ce, dma_len, dma_len); @@ -881,7 +864,6 @@ static void refill_free_list(struct sge } q->credits++; } - } /* @@ -1041,6 +1023,10 @@ static void recycle_fl_buf(struct freelQ } } +static int copybreak __read_mostly = 256; +module_param(copybreak, int, 0); +MODULE_PARM_DESC(copybreak, "Receive copy threshold"); + /** * get_packet - return the next ingress packet buffer * @pdev: the PCI device that received the packet @@ -1060,45 +1046,42 @@ static void recycle_fl_buf(struct freelQ * be copied but there is no memory for the copy. */ static inline struct sk_buff *get_packet(struct pci_dev *pdev, - struct freelQ *fl, unsigned int len, - int dma_pad, int skb_pad, - unsigned int copy_thres, - unsigned int drop_thres) + struct freelQ *fl, unsigned int len) { struct sk_buff *skb; - struct freelQ_ce *ce = &fl->centries[fl->cidx]; + const struct freelQ_ce *ce = &fl->centries[fl->cidx]; - if (len < copy_thres) { - skb = alloc_skb(len + skb_pad, GFP_ATOMIC); - if (likely(skb != NULL)) { - skb_reserve(skb, skb_pad); - skb_put(skb, len); - pci_dma_sync_single_for_cpu(pdev, - pci_unmap_addr(ce, dma_addr), - pci_unmap_len(ce, dma_len), - PCI_DMA_FROMDEVICE); - memcpy(skb->data, ce->skb->data + dma_pad, len); - pci_dma_sync_single_for_device(pdev, - pci_unmap_addr(ce, dma_addr), - pci_unmap_len(ce, dma_len), - PCI_DMA_FROMDEVICE); - } else if (!drop_thres) + if (len < copybreak) { + skb = alloc_skb(len + 2, GFP_ATOMIC); + if (!skb) goto use_orig_buf; + skb_reserve(skb, 2); /* align IP header */ + skb_put(skb, len); + pci_dma_sync_single_for_cpu(pdev, + pci_unmap_addr(ce, dma_addr), + pci_unmap_len(ce, dma_len), + PCI_DMA_FROMDEVICE); + memcpy(skb->data, ce->skb->data, len); + pci_dma_sync_single_for_device(pdev, + pci_unmap_addr(ce, dma_addr), + pci_unmap_len(ce, dma_len), + PCI_DMA_FROMDEVICE); recycle_fl_buf(fl, fl->cidx); return skb; } - if (fl->credits < drop_thres) { +use_orig_buf: + if (fl->credits < 2) { recycle_fl_buf(fl, fl->cidx); return NULL; } -use_orig_buf: pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE); skb = ce->skb; - skb_reserve(skb, dma_pad); + prefetch(skb->data); + skb_put(skb, len); return skb; } @@ -1137,6 +1120,7 @@ static void unexpected_offload(struct ad static inline unsigned int compute_large_page_tx_descs(struct sk_buff *skb) { unsigned int count = 0; + if (PAGE_SIZE > SGE_TX_DESC_MAX_PLEN) { unsigned int nfrags = skb_shinfo(skb)->nr_frags; unsigned int i, len = skb->len - skb->data_len; @@ -1343,7 +1327,7 @@ static void restart_sched(unsigned long while ((skb = sched_skb(sge, NULL, credits)) != NULL) { unsigned int genbit, pidx, count; count = 1 + skb_shinfo(skb)->nr_frags; - count += compute_large_page_tx_descs(skb); + count += compute_large_page_tx_descs(skb); q->in_use += count; genbit = q->genbit; pidx = q->pidx; @@ -1375,27 +1359,25 @@ static void restart_sched(unsigned long * * Process an ingress ethernet pakcet and deliver it to the stack. */ -static int sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len) +static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len) { struct sk_buff *skb; - struct cpl_rx_pkt *p; + const struct cpl_rx_pkt *p; struct adapter *adapter = sge->adapter; struct sge_port_stats *st; - skb = get_packet(adapter->pdev, fl, len - sge->rx_pkt_pad, - sge->rx_pkt_pad, 2, SGE_RX_COPY_THRES, - SGE_RX_DROP_THRES); + skb = get_packet(adapter->pdev, fl, len - sge->rx_pkt_pad); if (unlikely(!skb)) { sge->stats.rx_drops++; - return 0; + return; } - p = (struct cpl_rx_pkt *)skb->data; - skb_pull(skb, sizeof(*p)); + p = (const struct cpl_rx_pkt *) skb->data; if (p->iff >= adapter->params.nports) { kfree_skb(skb); - return 0; + return; } + __skb_pull(skb, sizeof(*p)); skb->dev = adapter->port[p->iff].dev; skb->dev->last_rx = jiffies; @@ -1427,7 +1409,6 @@ static int sge_rx(struct sge *sge, struc netif_rx(skb); #endif } - return 0; } /* @@ -1448,29 +1429,28 @@ static inline int enough_free_Tx_descs(c static void restart_tx_queues(struct sge *sge) { struct adapter *adap = sge->adapter; + int i; - if (enough_free_Tx_descs(&sge->cmdQ[0])) { - int i; + if (!enough_free_Tx_descs(&sge->cmdQ[0])) + return; - for_each_port(adap, i) { - struct net_device *nd = adap->port[i].dev; + for_each_port(adap, i) { + struct net_device *nd = adap->port[i].dev; - if (test_and_clear_bit(nd->if_port, - &sge->stopped_tx_queues) && - netif_running(nd)) { - sge->stats.cmdQ_restarted[2]++; - netif_wake_queue(nd); - } + if (test_and_clear_bit(nd->if_port, &sge->stopped_tx_queues) && + netif_running(nd)) { + sge->stats.cmdQ_restarted[2]++; + netif_wake_queue(nd); } } } /* - * update_tx_info is called from the interrupt handler/NAPI to return cmdQ0 + * update_tx_info is called from the interrupt handler/NAPI to return cmdQ0 * information. */ -static unsigned int update_tx_info(struct adapter *adapter, - unsigned int flags, +static unsigned int update_tx_info(struct adapter *adapter, + unsigned int flags, unsigned int pr0) { struct sge *sge = adapter->sge; @@ -1510,29 +1490,30 @@ static int process_responses(struct adap struct sge *sge = adapter->sge; struct respQ *q = &sge->respQ; struct respQ_e *e = &q->entries[q->cidx]; - int budget_left = budget; + int done = 0; unsigned int flags = 0; unsigned int cmdq_processed[SGE_CMDQ_N] = {0, 0}; - - while (likely(budget_left && e->GenerationBit == q->genbit)) { + while (done < budget && e->GenerationBit == q->genbit) { flags |= e->Qsleeping; - + cmdq_processed[0] += e->Cmdq0CreditReturn; cmdq_processed[1] += e->Cmdq1CreditReturn; - + /* We batch updates to the TX side to avoid cacheline * ping-pong of TX state information on MP where the sender * might run on a different CPU than this function... */ - if (unlikely(flags & F_CMDQ0_ENABLE || cmdq_processed[0] > 64)) { + if (unlikely((flags & F_CMDQ0_ENABLE) || cmdq_processed[0] > 64)) { flags = update_tx_info(adapter, flags, cmdq_processed[0]); cmdq_processed[0] = 0; } + if (unlikely(cmdq_processed[1] > 16)) { sge->cmdQ[1].processed += cmdq_processed[1]; cmdq_processed[1] = 0; } + if (likely(e->DataValid)) { struct freelQ *fl = &sge->freelQ[e->FreelistQid]; @@ -1542,12 +1523,16 @@ static int process_responses(struct adap else sge_rx(sge, fl, e->BufferLength); + ++done; + /* * Note: this depends on each packet consuming a * single free-list buffer; cf. the BUG above. */ if (++fl->cidx == fl->size) fl->cidx = 0; + prefetch(fl->centries[fl->cidx].skb); + if (unlikely(--fl->credits < fl->size - SGE_FREEL_REFILL_THRESH)) refill_free_list(sge, fl); @@ -1566,14 +1551,20 @@ static int process_responses(struct adap writel(q->credits, adapter->regs + A_SG_RSPQUEUECREDIT); q->credits = 0; } - --budget_left; } - flags = update_tx_info(adapter, flags, cmdq_processed[0]); + flags = update_tx_info(adapter, flags, cmdq_processed[0]); sge->cmdQ[1].processed += cmdq_processed[1]; - budget -= budget_left; - return budget; + return done; +} + +static inline int responses_pending(const struct adapter *adapter) +{ + const struct respQ *Q = &adapter->sge->respQ; + const struct respQ_e *e = &Q->entries[Q->cidx]; + + return (e->GenerationBit == Q->genbit); } #ifdef CONFIG_CHELSIO_T1_NAPI @@ -1585,19 +1576,25 @@ static int process_responses(struct adap * which the caller must ensure is a valid pure response. Returns 1 if it * encounters a valid data-carrying response, 0 otherwise. */ -static int process_pure_responses(struct adapter *adapter, struct respQ_e *e) +static int process_pure_responses(struct adapter *adapter) { struct sge *sge = adapter->sge; struct respQ *q = &sge->respQ; + struct respQ_e *e = &q->entries[q->cidx]; + const struct freelQ *fl = &sge->freelQ[e->FreelistQid]; unsigned int flags = 0; unsigned int cmdq_processed[SGE_CMDQ_N] = {0, 0}; + prefetch(fl->centries[fl->cidx].skb); + if (e->DataValid) + return 1; + do { flags |= e->Qsleeping; cmdq_processed[0] += e->Cmdq0CreditReturn; cmdq_processed[1] += e->Cmdq1CreditReturn; - + e++; if (unlikely(++q->cidx == q->size)) { q->cidx = 0; @@ -1613,7 +1610,7 @@ static int process_pure_responses(struct sge->stats.pure_rsps++; } while (e->GenerationBit == q->genbit && !e->DataValid); - flags = update_tx_info(adapter, flags, cmdq_processed[0]); + flags = update_tx_info(adapter, flags, cmdq_processed[0]); sge->cmdQ[1].processed += cmdq_processed[1]; return e->GenerationBit == q->genbit; @@ -1627,23 +1624,20 @@ static int process_pure_responses(struct int t1_poll(struct net_device *dev, int *budget) { struct adapter *adapter = dev->priv; - int effective_budget = min(*budget, dev->quota); - int work_done = process_responses(adapter, effective_budget); + int work_done; + work_done = process_responses(adapter, min(*budget, dev->quota)); *budget -= work_done; dev->quota -= work_done; - if (work_done >= effective_budget) + if (unlikely(responses_pending(adapter))) return 1; - spin_lock_irq(&adapter->async_lock); - __netif_rx_complete(dev); + netif_rx_complete(dev); writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); - writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA, - adapter->regs + A_PL_ENABLE); - spin_unlock_irq(&adapter->async_lock); return 0; + } /* @@ -1652,44 +1646,33 @@ int t1_poll(struct net_device *dev, int irqreturn_t t1_interrupt(int irq, void *data) { struct adapter *adapter = data; - struct net_device *dev = adapter->sge->netdev; struct sge *sge = adapter->sge; - u32 cause; - int handled = 0; + int handled; - cause = readl(adapter->regs + A_PL_CAUSE); - if (cause == 0 || cause == ~0) - return IRQ_NONE; + if (likely(responses_pending(adapter))) { + struct net_device *dev = sge->netdev; - spin_lock(&adapter->async_lock); - if (cause & F_PL_INTR_SGE_DATA) { - struct respQ *q = &adapter->sge->respQ; - struct respQ_e *e = &q->entries[q->cidx]; - - handled = 1; - writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE); - - if (e->GenerationBit == q->genbit && - __netif_rx_schedule_prep(dev)) { - if (e->DataValid || process_pure_responses(adapter, e)) { - /* mask off data IRQ */ - writel(adapter->slow_intr_mask, - adapter->regs + A_PL_ENABLE); - __netif_rx_schedule(sge->netdev); - goto unlock; - } - /* no data, no NAPI needed */ - netif_poll_enable(dev); + writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE); + if (__netif_rx_schedule_prep(dev)) { + if (process_pure_responses(adapter)) + __netif_rx_schedule(dev); + else { + /* no data, no NAPI needed */ + writel(sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); + netif_poll_enable(dev); /* undo schedule_prep */ + } } - writel(q->cidx, adapter->regs + A_SG_SLEEPING); - } else - handled = t1_slow_intr_handler(adapter); + return IRQ_HANDLED; + } + + spin_lock(&adapter->async_lock); + handled = t1_slow_intr_handler(adapter); + spin_unlock(&adapter->async_lock); if (!handled) sge->stats.unhandled_irqs++; -unlock: - spin_unlock(&adapter->async_lock); + return IRQ_RETVAL(handled != 0); } @@ -1712,17 +1695,13 @@ unlock: irqreturn_t t1_interrupt(int irq, void *cookie) { int work_done; - struct respQ_e *e; struct adapter *adapter = cookie; - struct respQ *Q = &adapter->sge->respQ; spin_lock(&adapter->async_lock); - e = &Q->entries[Q->cidx]; - prefetch(e); writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE); - if (likely(e->GenerationBit == Q->genbit)) + if (likely(responses_pending(adapter)) work_done = process_responses(adapter, -1); else work_done = t1_slow_intr_handler(adapter); @@ -1796,7 +1775,7 @@ static int t1_sge_tx(struct sk_buff *skb * through the scheduler. */ if (sge->tx_sched && !qid && skb->dev) { - use_sched: +use_sched: use_sched_skb = 1; /* Note that the scheduler might return a different skb than * the one passed in. @@ -1900,7 +1879,7 @@ int t1_start_xmit(struct sk_buff *skb, s cpl = (struct cpl_tx_pkt *)hdr; } else { /* - * Packets shorter than ETH_HLEN can break the MAC, drop them + * Packets shorter than ETH_HLEN can break the MAC, drop them * early. Also, we may get oversized packets because some * parts of the kernel don't handle our unusual hard_header_len * right, drop those too. @@ -1984,9 +1963,9 @@ send: * then silently discard to avoid leak. */ if (unlikely(ret != NETDEV_TX_OK && skb != orig_skb)) { - dev_kfree_skb_any(skb); + dev_kfree_skb_any(skb); ret = NETDEV_TX_OK; - } + } return ret; } @@ -2099,31 +2078,35 @@ static void espibug_workaround_t204(unsi if (adapter->open_device_map & PORT_MASK) { int i; - if (t1_espi_get_mon_t204(adapter, &(seop[0]), 0) < 0) { + + if (t1_espi_get_mon_t204(adapter, &(seop[0]), 0) < 0) return; - } + for (i = 0; i < nports; i++) { - struct sk_buff *skb = sge->espibug_skb[i]; - if ( (netif_running(adapter->port[i].dev)) && - !(netif_queue_stopped(adapter->port[i].dev)) && - (seop[i] && ((seop[i] & 0xfff) == 0)) && - skb ) { - if (!skb->cb[0]) { - u8 ch_mac_addr[ETH_ALEN] = - {0x0, 0x7, 0x43, 0x0, 0x0, 0x0}; - memcpy(skb->data + sizeof(struct cpl_tx_pkt), - ch_mac_addr, ETH_ALEN); - memcpy(skb->data + skb->len - 10, - ch_mac_addr, ETH_ALEN); - skb->cb[0] = 0xff; - } - - /* bump the reference count to avoid freeing of - * the skb once the DMA has completed. - */ - skb = skb_get(skb); - t1_sge_tx(skb, adapter, 0, adapter->port[i].dev); + struct sk_buff *skb = sge->espibug_skb[i]; + + if (!netif_running(adapter->port[i].dev) || + netif_queue_stopped(adapter->port[i].dev) || + !seop[i] || ((seop[i] & 0xfff) != 0) || !skb) + continue; + + if (!skb->cb[0]) { + u8 ch_mac_addr[ETH_ALEN] = { + 0x0, 0x7, 0x43, 0x0, 0x0, 0x0 + }; + + memcpy(skb->data + sizeof(struct cpl_tx_pkt), + ch_mac_addr, ETH_ALEN); + memcpy(skb->data + skb->len - 10, + ch_mac_addr, ETH_ALEN); + skb->cb[0] = 0xff; } + + /* bump the reference count to avoid freeing of + * the skb once the DMA has completed. + */ + skb = skb_get(skb); + t1_sge_tx(skb, adapter, 0, adapter->port[i].dev); } } mod_timer(&sge->espibug_timer, jiffies + sge->espibug_timeout); @@ -2192,9 +2175,8 @@ struct sge * __devinit t1_sge_create(str if (adapter->params.nports > 1) { tx_sched_init(sge); sge->espibug_timer.function = espibug_workaround_t204; - } else { + } else sge->espibug_timer.function = espibug_workaround; - } sge->espibug_timer.data = (unsigned long)sge->adapter; sge->espibug_timeout = 1; @@ -2202,7 +2184,7 @@ struct sge * __devinit t1_sge_create(str if (adapter->params.nports > 1) sge->espibug_timeout = HZ/100; } - + p->cmdQ_size[0] = SGE_CMDQ0_E_N; p->cmdQ_size[1] = SGE_CMDQ1_E_N; diff -puN drivers/net/chelsio/subr.c~git-netdev-all drivers/net/chelsio/subr.c --- a/drivers/net/chelsio/subr.c~git-netdev-all +++ a/drivers/net/chelsio/subr.c @@ -223,13 +223,13 @@ static int fpga_slow_intr(adapter_t *ada t1_sge_intr_error_handler(adapter->sge); if (cause & FPGA_PCIX_INTERRUPT_GMAC) - fpga_phy_intr_handler(adapter); + fpga_phy_intr_handler(adapter); if (cause & FPGA_PCIX_INTERRUPT_TP) { - /* + /* * FPGA doesn't support MC4 interrupts and it requires * this odd layer of indirection for MC5. - */ + */ u32 tp_cause = readl(adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE); /* Clear TP interrupt */ @@ -262,8 +262,7 @@ static int mi1_wait_until_ready(adapter_ udelay(10); } while (busy && --attempts); if (busy) - CH_ALERT("%s: MDIO operation timed out\n", - adapter->name); + CH_ALERT("%s: MDIO operation timed out\n", adapter->name); return busy; } @@ -605,22 +604,23 @@ int t1_elmer0_ext_intr_handler(adapter_t switch (board_info(adapter)->board) { #ifdef CONFIG_CHELSIO_T1_1G - case CHBT_BOARD_CHT204: - case CHBT_BOARD_CHT204E: - case CHBT_BOARD_CHN204: - case CHBT_BOARD_CHT204V: { - int i, port_bit; + case CHBT_BOARD_CHT204: + case CHBT_BOARD_CHT204E: + case CHBT_BOARD_CHN204: + case CHBT_BOARD_CHT204V: { + int i, port_bit; for_each_port(adapter, i) { port_bit = i + 1; - if (!(cause & (1 << port_bit))) continue; + if (!(cause & (1 << port_bit))) + continue; - phy = adapter->port[i].phy; + phy = adapter->port[i].phy; phy_cause = phy->ops->interrupt_handler(phy); if (phy_cause & cphy_cause_link_change) t1_link_changed(adapter, i); } - break; - } + break; + } case CHBT_BOARD_CHT101: if (cause & ELMER0_GP_BIT1) { /* Marvell 88E1111 interrupt */ phy = adapter->port[0].phy; @@ -631,13 +631,13 @@ int t1_elmer0_ext_intr_handler(adapter_t break; case CHBT_BOARD_7500: { int p; - /* + /* * Elmer0's interrupt cause isn't useful here because there is * only one bit that can be set for all 4 ports. This means * we are forced to check every PHY's interrupt status * register to see who initiated the interrupt. - */ - for_each_port(adapter, p) { + */ + for_each_port(adapter, p) { phy = adapter->port[p].phy; phy_cause = phy->ops->interrupt_handler(phy); if (phy_cause & cphy_cause_link_change) @@ -658,7 +658,7 @@ int t1_elmer0_ext_intr_handler(adapter_t break; case CHBT_BOARD_8000: case CHBT_BOARD_CHT110: - CH_DBG(adapter, INTR, "External interrupt cause 0x%x\n", + CH_DBG(adapter, INTR, "External interrupt cause 0x%x\n", cause); if (cause & ELMER0_GP_BIT1) { /* PMC3393 INTB */ struct cmac *mac = adapter->port[0].mac; @@ -670,9 +670,9 @@ int t1_elmer0_ext_intr_handler(adapter_t t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect); - CH_MSG(adapter, INFO, LINK, "XPAK %s\n", + CH_MSG(adapter, INFO, LINK, "XPAK %s\n", mod_detect ? "removed" : "inserted"); - } + } break; #ifdef CONFIG_CHELSIO_T1_COUGAR case CHBT_BOARD_COUGAR: @@ -688,7 +688,8 @@ int t1_elmer0_ext_intr_handler(adapter_t for_each_port(adapter, i) { port_bit = i ? i + 1 : 0; - if (!(cause & (1 << port_bit))) continue; + if (!(cause & (1 << port_bit))) + continue; phy = adapter->port[i].phy; phy_cause = phy->ops->interrupt_handler(phy); @@ -755,7 +756,7 @@ void t1_interrupts_disable(adapter_t* ad /* Disable PCIX & external chip interrupts. */ if (t1_is_asic(adapter)) - writel(0, adapter->regs + A_PL_ENABLE); + writel(0, adapter->regs + A_PL_ENABLE); /* PCI-X interrupts */ pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE, 0); @@ -830,11 +831,11 @@ int t1_slow_intr_handler(adapter_t *adap /* Power sequencing is a work-around for Intel's XPAKs. */ static void power_sequence_xpak(adapter_t* adapter) { - u32 mod_detect; - u32 gpo; + u32 mod_detect; + u32 gpo; - /* Check for XPAK */ - t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect); + /* Check for XPAK */ + t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect); if (!(ELMER0_GP_BIT5 & mod_detect)) { /* XPAK is present */ t1_tpi_read(adapter, A_ELMER0_GPO, &gpo); @@ -877,31 +878,31 @@ static int board_init(adapter_t *adapter case CHBT_BOARD_N210: case CHBT_BOARD_CHT210: case CHBT_BOARD_COUGAR: - t1_tpi_par(adapter, 0xf); - t1_tpi_write(adapter, A_ELMER0_GPO, 0x800); + t1_tpi_par(adapter, 0xf); + t1_tpi_write(adapter, A_ELMER0_GPO, 0x800); break; case CHBT_BOARD_CHT110: - t1_tpi_par(adapter, 0xf); - t1_tpi_write(adapter, A_ELMER0_GPO, 0x1800); + t1_tpi_par(adapter, 0xf); + t1_tpi_write(adapter, A_ELMER0_GPO, 0x1800); - /* TBD XXX Might not need. This fixes a problem - * described in the Intel SR XPAK errata. - */ - power_sequence_xpak(adapter); + /* TBD XXX Might not need. This fixes a problem + * described in the Intel SR XPAK errata. + */ + power_sequence_xpak(adapter); break; #ifdef CONFIG_CHELSIO_T1_1G - case CHBT_BOARD_CHT204E: - /* add config space write here */ + case CHBT_BOARD_CHT204E: + /* add config space write here */ case CHBT_BOARD_CHT204: case CHBT_BOARD_CHT204V: case CHBT_BOARD_CHN204: - t1_tpi_par(adapter, 0xf); - t1_tpi_write(adapter, A_ELMER0_GPO, 0x804); - break; + t1_tpi_par(adapter, 0xf); + t1_tpi_write(adapter, A_ELMER0_GPO, 0x804); + break; case CHBT_BOARD_CHT101: case CHBT_BOARD_7500: - t1_tpi_par(adapter, 0xf); - t1_tpi_write(adapter, A_ELMER0_GPO, 0x1804); + t1_tpi_par(adapter, 0xf); + t1_tpi_write(adapter, A_ELMER0_GPO, 0x1804); break; #endif } @@ -941,7 +942,7 @@ int t1_init_hw_modules(adapter_t *adapte goto out_err; err = 0; - out_err: +out_err: return err; } @@ -983,7 +984,7 @@ void t1_free_sw_modules(adapter_t *adapt if (adapter->espi) t1_espi_destroy(adapter->espi); #ifdef CONFIG_CHELSIO_T1_COUGAR - if (adapter->cspi) + if (adapter->cspi) t1_cspi_destroy(adapter->cspi); #endif } @@ -1010,7 +1011,7 @@ static void __devinit init_link_config(s CH_ERR("%s: CSPI initialization failed\n", adapter->name); goto error; - } + } #endif /* diff -puN drivers/net/chelsio/tp.c~git-netdev-all drivers/net/chelsio/tp.c --- a/drivers/net/chelsio/tp.c~git-netdev-all +++ a/drivers/net/chelsio/tp.c @@ -17,39 +17,36 @@ struct petp { static void tp_init(adapter_t * ap, const struct tp_params *p, unsigned int tp_clk) { - if (t1_is_asic(ap)) { - u32 val; + u32 val; - val = F_TP_IN_CSPI_CPL | F_TP_IN_CSPI_CHECK_IP_CSUM | - F_TP_IN_CSPI_CHECK_TCP_CSUM | F_TP_IN_ESPI_ETHERNET; - if (!p->pm_size) - val |= F_OFFLOAD_DISABLE; - else - val |= F_TP_IN_ESPI_CHECK_IP_CSUM | - F_TP_IN_ESPI_CHECK_TCP_CSUM; - writel(val, ap->regs + A_TP_IN_CONFIG); - writel(F_TP_OUT_CSPI_CPL | - F_TP_OUT_ESPI_ETHERNET | - F_TP_OUT_ESPI_GENERATE_IP_CSUM | - F_TP_OUT_ESPI_GENERATE_TCP_CSUM, - ap->regs + A_TP_OUT_CONFIG); - writel(V_IP_TTL(64) | - F_PATH_MTU /* IP DF bit */ | - V_5TUPLE_LOOKUP(p->use_5tuple_mode) | - V_SYN_COOKIE_PARAMETER(29), - ap->regs + A_TP_GLOBAL_CONFIG); - /* - * Enable pause frame deadlock prevention. - */ - if (is_T2(ap) && ap->params.nports > 1) { - u32 drop_ticks = DROP_MSEC * (tp_clk / 1000); - - writel(F_ENABLE_TX_DROP | F_ENABLE_TX_ERROR | - V_DROP_TICKS_CNT(drop_ticks) | - V_NUM_PKTS_DROPPED(DROP_PKTS_CNT), - ap->regs + A_TP_TX_DROP_CONFIG); - } + if (!t1_is_asic(ap)) + return; + val = F_TP_IN_CSPI_CPL | F_TP_IN_CSPI_CHECK_IP_CSUM | + F_TP_IN_CSPI_CHECK_TCP_CSUM | F_TP_IN_ESPI_ETHERNET; + if (!p->pm_size) + val |= F_OFFLOAD_DISABLE; + else + val |= F_TP_IN_ESPI_CHECK_IP_CSUM | F_TP_IN_ESPI_CHECK_TCP_CSUM; + writel(val, ap->regs + A_TP_IN_CONFIG); + writel(F_TP_OUT_CSPI_CPL | + F_TP_OUT_ESPI_ETHERNET | + F_TP_OUT_ESPI_GENERATE_IP_CSUM | + F_TP_OUT_ESPI_GENERATE_TCP_CSUM, ap->regs + A_TP_OUT_CONFIG); + writel(V_IP_TTL(64) | + F_PATH_MTU /* IP DF bit */ | + V_5TUPLE_LOOKUP(p->use_5tuple_mode) | + V_SYN_COOKIE_PARAMETER(29), ap->regs + A_TP_GLOBAL_CONFIG); + /* + * Enable pause frame deadlock prevention. + */ + if (is_T2(ap) && ap->params.nports > 1) { + u32 drop_ticks = DROP_MSEC * (tp_clk / 1000); + + writel(F_ENABLE_TX_DROP | F_ENABLE_TX_ERROR | + V_DROP_TICKS_CNT(drop_ticks) | + V_NUM_PKTS_DROPPED(DROP_PKTS_CNT), + ap->regs + A_TP_TX_DROP_CONFIG); } } @@ -61,6 +58,7 @@ void t1_tp_destroy(struct petp *tp) struct petp *__devinit t1_tp_create(adapter_t * adapter, struct tp_params *p) { struct petp *tp = kzalloc(sizeof(*tp), GFP_KERNEL); + if (!tp) return NULL; diff -puN drivers/net/chelsio/vsc7326.c~git-netdev-all drivers/net/chelsio/vsc7326.c --- a/drivers/net/chelsio/vsc7326.c~git-netdev-all +++ a/drivers/net/chelsio/vsc7326.c @@ -226,22 +226,21 @@ static void run_table(adapter_t *adapter if (ib[i].addr == INITBLOCK_SLEEP) { udelay( ib[i].data ); CH_ERR("sleep %d us\n",ib[i].data); - } else { + } else vsc_write( adapter, ib[i].addr, ib[i].data ); - } } } static int bist_rd(adapter_t *adapter, int moduleid, int address) { - int data=0; - u32 result=0; + int data = 0; + u32 result = 0; - if( (address != 0x0) && - (address != 0x1) && - (address != 0x2) && - (address != 0xd) && - (address != 0xe)) + if ((address != 0x0) && + (address != 0x1) && + (address != 0x2) && + (address != 0xd) && + (address != 0xe)) CH_ERR("No bist address: 0x%x\n", address); data = ((0x00 << 24) | ((address & 0xff) << 16) | (0x00 << 8) | @@ -251,27 +250,27 @@ static int bist_rd(adapter_t *adapter, i udelay(10); vsc_read(adapter, REG_RAM_BIST_RESULT, &result); - if((result & (1<<9)) != 0x0) + if ((result & (1 << 9)) != 0x0) CH_ERR("Still in bist read: 0x%x\n", result); - else if((result & (1<<8)) != 0x0) + else if ((result & (1 << 8)) != 0x0) CH_ERR("bist read error: 0x%x\n", result); - return(result & 0xff); + return (result & 0xff); } static int bist_wr(adapter_t *adapter, int moduleid, int address, int value) { - int data=0; - u32 result=0; + int data = 0; + u32 result = 0; - if( (address != 0x0) && - (address != 0x1) && - (address != 0x2) && - (address != 0xd) && - (address != 0xe)) + if ((address != 0x0) && + (address != 0x1) && + (address != 0x2) && + (address != 0xd) && + (address != 0xe)) CH_ERR("No bist address: 0x%x\n", address); - if( value>255 ) + if (value > 255) CH_ERR("Suspicious write out of range value: 0x%x\n", value); data = ((0x01 << 24) | ((address & 0xff) << 16) | (value << 8) | @@ -281,12 +280,12 @@ static int bist_wr(adapter_t *adapter, i udelay(5); vsc_read(adapter, REG_RAM_BIST_CMD, &result); - if((result & (1<<27)) != 0x0) + if ((result & (1 << 27)) != 0x0) CH_ERR("Still in bist write: 0x%x\n", result); - else if((result & (1<<26)) != 0x0) + else if ((result & (1 << 26)) != 0x0) CH_ERR("bist write error: 0x%x\n", result); - return(0); + return 0; } static int run_bist(adapter_t *adapter, int moduleid) @@ -295,7 +294,7 @@ static int run_bist(adapter_t *adapter, (void) bist_wr(adapter,moduleid, 0x00, 0x02); (void) bist_wr(adapter,moduleid, 0x01, 0x01); - return(0); + return 0; } static int check_bist(adapter_t *adapter, int moduleid) @@ -309,27 +308,26 @@ static int check_bist(adapter_t *adapter if ((result & 3) != 0x3) CH_ERR("Result: 0x%x BIST error in ram %d, column: 0x%04x\n", result, moduleid, column); - return(0); + return 0; } static int enable_mem(adapter_t *adapter, int moduleid) { /*enable mem*/ (void) bist_wr(adapter,moduleid, 0x00, 0x00); - return(0); + return 0; } static int run_bist_all(adapter_t *adapter) { - int port=0; - u32 val=0; + int port = 0; + u32 val = 0; vsc_write(adapter, REG_MEM_BIST, 0x5); vsc_read(adapter, REG_MEM_BIST, &val); - for(port=0; port<12; port++){ + for (port = 0; port < 12; port++) vsc_write(adapter, REG_DEV_SETUP(port), 0x0); - } udelay(300); vsc_write(adapter, REG_SPI4_MISC, 0x00040409); @@ -352,13 +350,13 @@ static int run_bist_all(adapter_t *adapt udelay(300); vsc_write(adapter, REG_SPI4_MISC, 0x60040400); udelay(300); - for(port=0; port<12; port++){ + for (port = 0; port < 12; port++) vsc_write(adapter, REG_DEV_SETUP(port), 0x1); - } + udelay(300); vsc_write(adapter, REG_MEM_BIST, 0x0); mdelay(10); - return(0); + return 0; } static int mac_intr_handler(struct cmac *mac) @@ -591,40 +589,46 @@ static void rmon_update(struct cmac *mac static void port_stats_update(struct cmac *mac) { - int port = mac->instance->index; + struct { + unsigned int reg; + unsigned int offset; + } hw_stats[] = { + +#define HW_STAT(reg, stat_name) \ + { reg, (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } + + /* Rx stats */ + HW_STAT(RxUnicast, RxUnicastFramesOK), + HW_STAT(RxMulticast, RxMulticastFramesOK), + HW_STAT(RxBroadcast, RxBroadcastFramesOK), + HW_STAT(Crc, RxFCSErrors), + HW_STAT(RxAlignment, RxAlignErrors), + HW_STAT(RxOversize, RxFrameTooLongErrors), + HW_STAT(RxPause, RxPauseFrames), + HW_STAT(RxJabbers, RxJabberErrors), + HW_STAT(RxFragments, RxRuntErrors), + HW_STAT(RxUndersize, RxRuntErrors), + HW_STAT(RxSymbolCarrier, RxSymbolErrors), + HW_STAT(RxSize1519ToMax, RxJumboFramesOK), + + /* Tx stats (skip collision stats as we are full-duplex only) */ + HW_STAT(TxUnicast, TxUnicastFramesOK), + HW_STAT(TxMulticast, TxMulticastFramesOK), + HW_STAT(TxBroadcast, TxBroadcastFramesOK), + HW_STAT(TxPause, TxPauseFrames), + HW_STAT(TxUnderrun, TxUnderrun), + HW_STAT(TxSize1519ToMax, TxJumboFramesOK), + }, *p = hw_stats; + unsigned int port = mac->instance->index; + u64 *stats = (u64 *)&mac->stats; + unsigned int i; - /* Rx stats */ - rmon_update(mac, REG_RX_OK_BYTES(port), &mac->stats.RxOctetsOK); - rmon_update(mac, REG_RX_BAD_BYTES(port), &mac->stats.RxOctetsBad); - rmon_update(mac, REG_RX_UNICAST(port), &mac->stats.RxUnicastFramesOK); - rmon_update(mac, REG_RX_MULTICAST(port), - &mac->stats.RxMulticastFramesOK); - rmon_update(mac, REG_RX_BROADCAST(port), - &mac->stats.RxBroadcastFramesOK); - rmon_update(mac, REG_CRC(port), &mac->stats.RxFCSErrors); - rmon_update(mac, REG_RX_ALIGNMENT(port), &mac->stats.RxAlignErrors); - rmon_update(mac, REG_RX_OVERSIZE(port), - &mac->stats.RxFrameTooLongErrors); - rmon_update(mac, REG_RX_PAUSE(port), &mac->stats.RxPauseFrames); - rmon_update(mac, REG_RX_JABBERS(port), &mac->stats.RxJabberErrors); - rmon_update(mac, REG_RX_FRAGMENTS(port), &mac->stats.RxRuntErrors); - rmon_update(mac, REG_RX_UNDERSIZE(port), &mac->stats.RxRuntErrors); - rmon_update(mac, REG_RX_SYMBOL_CARRIER(port), - &mac->stats.RxSymbolErrors); - rmon_update(mac, REG_RX_SIZE_1519_TO_MAX(port), - &mac->stats.RxJumboFramesOK); + for (i = 0; i < ARRAY_SIZE(hw_stats); i++) + rmon_update(mac, CRA(0x4, port, p->reg), stats + p->offset); - /* Tx stats (skip collision stats as we are full-duplex only) */ rmon_update(mac, REG_TX_OK_BYTES(port), &mac->stats.TxOctetsOK); - rmon_update(mac, REG_TX_UNICAST(port), &mac->stats.TxUnicastFramesOK); - rmon_update(mac, REG_TX_MULTICAST(port), - &mac->stats.TxMulticastFramesOK); - rmon_update(mac, REG_TX_BROADCAST(port), - &mac->stats.TxBroadcastFramesOK); - rmon_update(mac, REG_TX_PAUSE(port), &mac->stats.TxPauseFrames); - rmon_update(mac, REG_TX_UNDERRUN(port), &mac->stats.TxUnderrun); - rmon_update(mac, REG_TX_SIZE_1519_TO_MAX(port), - &mac->stats.TxJumboFramesOK); + rmon_update(mac, REG_RX_OK_BYTES(port), &mac->stats.RxOctetsOK); + rmon_update(mac, REG_RX_BAD_BYTES(port), &mac->stats.RxOctetsBad); } /* @@ -686,7 +690,8 @@ static struct cmac *vsc7326_mac_create(a int i; mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); - if (!mac) return NULL; + if (!mac) + return NULL; mac->ops = &vsc7326_ops; mac->instance = (cmac_instance *)(mac + 1); diff -puN drivers/net/chelsio/vsc7326_reg.h~git-netdev-all drivers/net/chelsio/vsc7326_reg.h --- a/drivers/net/chelsio/vsc7326_reg.h~git-netdev-all +++ a/drivers/net/chelsio/vsc7326_reg.h @@ -192,73 +192,84 @@ #define REG_HDX(pn) CRA(0x1,pn,0x19) /* Half-duplex config */ /* Statistics */ +/* CRA(0x4,pn,reg) */ +/* reg below */ /* pn = port number, 0-a, a = 10GbE */ -#define REG_RX_IN_BYTES(pn) CRA(0x4,pn,0x00) /* # Rx in octets */ -#define REG_RX_SYMBOL_CARRIER(pn) CRA(0x4,pn,0x01) /* Frames w/ symbol errors */ -#define REG_RX_PAUSE(pn) CRA(0x4,pn,0x02) /* # pause frames received */ -#define REG_RX_UNSUP_OPCODE(pn) CRA(0x4,pn,0x03) /* # control frames with unsupported opcode */ -#define REG_RX_OK_BYTES(pn) CRA(0x4,pn,0x04) /* # octets in good frames */ -#define REG_RX_BAD_BYTES(pn) CRA(0x4,pn,0x05) /* # octets in bad frames */ -#define REG_RX_UNICAST(pn) CRA(0x4,pn,0x06) /* # good unicast frames */ -#define REG_RX_MULTICAST(pn) CRA(0x4,pn,0x07) /* # good multicast frames */ -#define REG_RX_BROADCAST(pn) CRA(0x4,pn,0x08) /* # good broadcast frames */ -#define REG_CRC(pn) CRA(0x4,pn,0x09) /* # frames w/ bad CRC only */ -#define REG_RX_ALIGNMENT(pn) CRA(0x4,pn,0x0a) /* # frames w/ alignment err */ -#define REG_RX_UNDERSIZE(pn) CRA(0x4,pn,0x0b) /* # frames undersize */ -#define REG_RX_FRAGMENTS(pn) CRA(0x4,pn,0x0c) /* # frames undersize w/ crc err */ -#define REG_RX_IN_RANGE_LENGTH_ERROR(pn) CRA(0x4,pn,0x0d) /* # frames with length error */ -#define REG_RX_OUT_OF_RANGE_ERROR(pn) CRA(0x4,pn,0x0e) /* # frames with illegal length field */ -#define REG_RX_OVERSIZE(pn) CRA(0x4,pn,0x0f) /* # frames oversize */ -#define REG_RX_JABBERS(pn) CRA(0x4,pn,0x10) /* # frames oversize w/ crc err */ -#define REG_RX_SIZE_64(pn) CRA(0x4,pn,0x11) /* # frames 64 octets long */ -#define REG_RX_SIZE_65_TO_127(pn) CRA(0x4,pn,0x12) /* # frames 65-127 octets */ -#define REG_RX_SIZE_128_TO_255(pn) CRA(0x4,pn,0x13) /* # frames 128-255 */ -#define REG_RX_SIZE_256_TO_511(pn) CRA(0x4,pn,0x14) /* # frames 256-511 */ -#define REG_RX_SIZE_512_TO_1023(pn) CRA(0x4,pn,0x15) /* # frames 512-1023 */ -#define REG_RX_SIZE_1024_TO_1518(pn) CRA(0x4,pn,0x16) /* # frames 1024-1518 */ -#define REG_RX_SIZE_1519_TO_MAX(pn) CRA(0x4,pn,0x17) /* # frames 1519-max */ - -#define REG_TX_OUT_BYTES(pn) CRA(0x4,pn,0x18) /* # octets tx */ -#define REG_TX_PAUSE(pn) CRA(0x4,pn,0x19) /* # pause frames sent */ -#define REG_TX_OK_BYTES(pn) CRA(0x4,pn,0x1a) /* # octets tx OK */ -#define REG_TX_UNICAST(pn) CRA(0x4,pn,0x1b) /* # frames unicast */ -#define REG_TX_MULTICAST(pn) CRA(0x4,pn,0x1c) /* # frames multicast */ -#define REG_TX_BROADCAST(pn) CRA(0x4,pn,0x1d) /* # frames broadcast */ -#define REG_TX_MULTIPLE_COLL(pn) CRA(0x4,pn,0x1e) /* # frames tx after multiple collisions */ -#define REG_TX_LATE_COLL(pn) CRA(0x4,pn,0x1f) /* # late collisions detected */ -#define REG_TX_XCOLL(pn) CRA(0x4,pn,0x20) /* # frames lost, excessive collisions */ -#define REG_TX_DEFER(pn) CRA(0x4,pn,0x21) /* # frames deferred on first tx attempt */ -#define REG_TX_XDEFER(pn) CRA(0x4,pn,0x22) /* # frames excessively deferred */ -#define REG_TX_CSENSE(pn) CRA(0x4,pn,0x23) /* carrier sense errors at frame end */ -#define REG_TX_SIZE_64(pn) CRA(0x4,pn,0x24) /* # frames 64 octets long */ -#define REG_TX_SIZE_65_TO_127(pn) CRA(0x4,pn,0x25) /* # frames 65-127 octets */ -#define REG_TX_SIZE_128_TO_255(pn) CRA(0x4,pn,0x26) /* # frames 128-255 */ -#define REG_TX_SIZE_256_TO_511(pn) CRA(0x4,pn,0x27) /* # frames 256-511 */ -#define REG_TX_SIZE_512_TO_1023(pn) CRA(0x4,pn,0x28) /* # frames 512-1023 */ -#define REG_TX_SIZE_1024_TO_1518(pn) CRA(0x4,pn,0x29) /* # frames 1024-1518 */ -#define REG_TX_SIZE_1519_TO_MAX(pn) CRA(0x4,pn,0x2a) /* # frames 1519-max */ -#define REG_TX_SINGLE_COLL(pn) CRA(0x4,pn,0x2b) /* # frames tx after single collision */ -#define REG_TX_BACKOFF2(pn) CRA(0x4,pn,0x2c) /* # frames tx ok after 2 backoffs/collisions */ -#define REG_TX_BACKOFF3(pn) CRA(0x4,pn,0x2d) /* after 3 backoffs/collisions */ -#define REG_TX_BACKOFF4(pn) CRA(0x4,pn,0x2e) /* after 4 */ -#define REG_TX_BACKOFF5(pn) CRA(0x4,pn,0x2f) /* after 5 */ -#define REG_TX_BACKOFF6(pn) CRA(0x4,pn,0x30) /* after 6 */ -#define REG_TX_BACKOFF7(pn) CRA(0x4,pn,0x31) /* after 7 */ -#define REG_TX_BACKOFF8(pn) CRA(0x4,pn,0x32) /* after 8 */ -#define REG_TX_BACKOFF9(pn) CRA(0x4,pn,0x33) /* after 9 */ -#define REG_TX_BACKOFF10(pn) CRA(0x4,pn,0x34) /* after 10 */ -#define REG_TX_BACKOFF11(pn) CRA(0x4,pn,0x35) /* after 11 */ -#define REG_TX_BACKOFF12(pn) CRA(0x4,pn,0x36) /* after 12 */ -#define REG_TX_BACKOFF13(pn) CRA(0x4,pn,0x37) /* after 13 */ -#define REG_TX_BACKOFF14(pn) CRA(0x4,pn,0x38) /* after 14 */ -#define REG_TX_BACKOFF15(pn) CRA(0x4,pn,0x39) /* after 15 */ -#define REG_TX_UNDERRUN(pn) CRA(0x4,pn,0x3a) /* # frames dropped from underrun */ -#define REG_RX_XGMII_PROT_ERR CRA(0x4,0xa,0x3b) /* # protocol errors detected on XGMII interface */ -#define REG_RX_IPG_SHRINK(pn) CRA(0x4,pn,0x3c) /* # of IPG shrinks detected */ - -#define REG_STAT_STICKY1G(pn) CRA(0x4,pn,0x3e) /* tri-speed sticky bits */ -#define REG_STAT_STICKY10G CRA(0x4,0xa,0x3e) /* 10GbE sticky bits */ -#define REG_STAT_INIT(pn) CRA(0x4,pn,0x3f) /* Clear all statistics */ + +enum { + RxInBytes = 0x00, // # Rx in octets + RxSymbolCarrier = 0x01, // Frames w/ symbol errors + RxPause = 0x02, // # pause frames received + RxUnsupOpcode = 0x03, // # control frames with unsupported opcode + RxOkBytes = 0x04, // # octets in good frames + RxBadBytes = 0x05, // # octets in bad frames + RxUnicast = 0x06, // # good unicast frames + RxMulticast = 0x07, // # good multicast frames + RxBroadcast = 0x08, // # good broadcast frames + Crc = 0x09, // # frames w/ bad CRC only + RxAlignment = 0x0a, // # frames w/ alignment err + RxUndersize = 0x0b, // # frames undersize + RxFragments = 0x0c, // # frames undersize w/ crc err + RxInRangeLengthError = 0x0d, // # frames with length error + RxOutOfRangeError = 0x0e, // # frames with illegal length field + RxOversize = 0x0f, // # frames oversize + RxJabbers = 0x10, // # frames oversize w/ crc err + RxSize64 = 0x11, // # frames 64 octets long + RxSize65To127 = 0x12, // # frames 65-127 octets + RxSize128To255 = 0x13, // # frames 128-255 + RxSize256To511 = 0x14, // # frames 256-511 + RxSize512To1023 = 0x15, // # frames 512-1023 + RxSize1024To1518 = 0x16, // # frames 1024-1518 + RxSize1519ToMax = 0x17, // # frames 1519-max + + TxOutBytes = 0x18, // # octets tx + TxPause = 0x19, // # pause frames sent + TxOkBytes = 0x1a, // # octets tx OK + TxUnicast = 0x1b, // # frames unicast + TxMulticast = 0x1c, // # frames multicast + TxBroadcast = 0x1d, // # frames broadcast + TxMultipleColl = 0x1e, // # frames tx after multiple collisions + TxLateColl = 0x1f, // # late collisions detected + TxXcoll = 0x20, // # frames lost, excessive collisions + TxDefer = 0x21, // # frames deferred on first tx attempt + TxXdefer = 0x22, // # frames excessively deferred + TxCsense = 0x23, // carrier sense errors at frame end + TxSize64 = 0x24, // # frames 64 octets long + TxSize65To127 = 0x25, // # frames 65-127 octets + TxSize128To255 = 0x26, // # frames 128-255 + TxSize256To511 = 0x27, // # frames 256-511 + TxSize512To1023 = 0x28, // # frames 512-1023 + TxSize1024To1518 = 0x29, // # frames 1024-1518 + TxSize1519ToMax = 0x2a, // # frames 1519-max + TxSingleColl = 0x2b, // # frames tx after single collision + TxBackoff2 = 0x2c, // # frames tx ok after 2 backoffs/collisions + TxBackoff3 = 0x2d, // after 3 backoffs/collisions + TxBackoff4 = 0x2e, // after 4 + TxBackoff5 = 0x2f, // after 5 + TxBackoff6 = 0x30, // after 6 + TxBackoff7 = 0x31, // after 7 + TxBackoff8 = 0x32, // after 8 + TxBackoff9 = 0x33, // after 9 + TxBackoff10 = 0x34, // after 10 + TxBackoff11 = 0x35, // after 11 + TxBackoff12 = 0x36, // after 12 + TxBackoff13 = 0x37, // after 13 + TxBackoff14 = 0x38, // after 14 + TxBackoff15 = 0x39, // after 15 + TxUnderrun = 0x3a, // # frames dropped from underrun + // Hole. See REG_RX_XGMII_PROT_ERR below. + RxIpgShrink = 0x3c, // # of IPG shrinks detected + // Duplicate. See REG_STAT_STICKY10G below. + StatSticky1G = 0x3e, // tri-speed sticky bits + StatInit = 0x3f // Clear all statistics +}; + +#define REG_RX_XGMII_PROT_ERR CRA(0x4,0xa,0x3b) /* # protocol errors detected on XGMII interface */ +#define REG_STAT_STICKY10G CRA(0x4,0xa,StatSticky1G) /* 10GbE sticky bits */ + +#define REG_RX_OK_BYTES(pn) CRA(0x4,pn,RxOkBytes) +#define REG_RX_BAD_BYTES(pn) CRA(0x4,pn,RxBadBytes) +#define REG_TX_OK_BYTES(pn) CRA(0x4,pn,TxOkBytes) /* MII-Management Block registers */ /* These are for MII-M interface 0, which is the bidirectional LVTTL one. If diff -puN drivers/net/chelsio/vsc8244.c~git-netdev-all drivers/net/chelsio/vsc8244.c --- a/drivers/net/chelsio/vsc8244.c~git-netdev-all +++ a/drivers/net/chelsio/vsc8244.c @@ -54,7 +54,7 @@ enum { }; #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ - VSC_INTR_NEG_DONE) + VSC_INTR_NEG_DONE) #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ VSC_INTR_ENABLE) @@ -94,19 +94,18 @@ static int vsc8244_intr_enable(struct cp { simple_mdio_write(cphy, VSC8244_INTR_ENABLE, INTR_MASK); - /* Enable interrupts through Elmer */ + /* Enable interrupts through Elmer */ if (t1_is_asic(cphy->adapter)) { u32 elmer; t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { + if (is_T2(cphy->adapter)) elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - } t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); } - return 0; + return 0; } static int vsc8244_intr_disable(struct cphy *cphy) @@ -118,19 +117,18 @@ static int vsc8244_intr_disable(struct c t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); elmer &= ~ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { + if (is_T2(cphy->adapter)) elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4); - } t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); } - return 0; + return 0; } static int vsc8244_intr_clear(struct cphy *cphy) { u32 val; - u32 elmer; + u32 elmer; /* Clear PHY interrupts by reading the register. */ simple_mdio_read(cphy, VSC8244_INTR_ENABLE, &val); @@ -138,13 +136,12 @@ static int vsc8244_intr_clear(struct cph if (t1_is_asic(cphy->adapter)) { t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) { + if (is_T2(cphy->adapter)) elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - } t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); } - return 0; + return 0; } /* @@ -179,13 +176,13 @@ static int vsc8244_set_speed_duplex(stru int t1_mdio_set_bits(struct cphy *phy, int mmd, int reg, unsigned int bits) { - int ret; - unsigned int val; + int ret; + unsigned int val; - ret = mdio_read(phy, mmd, reg, &val); - if (!ret) - ret = mdio_write(phy, mmd, reg, val | bits); - return ret; + ret = mdio_read(phy, mmd, reg, &val); + if (!ret) + ret = mdio_write(phy, mmd, reg, val | bits); + return ret; } static int vsc8244_autoneg_enable(struct cphy *cphy) @@ -235,7 +232,7 @@ static int vsc8244_advertise(struct cphy } static int vsc8244_get_link_status(struct cphy *cphy, int *link_ok, - int *speed, int *duplex, int *fc) + int *speed, int *duplex, int *fc) { unsigned int bmcr, status, lpa, adv; int err, sp = -1, dplx = -1, pause = 0; @@ -343,11 +340,13 @@ static struct cphy_ops vsc8244_ops = { .get_link_status = vsc8244_get_link_status }; -static struct cphy* vsc8244_phy_create(adapter_t *adapter, int phy_addr, struct mdio_ops *mdio_ops) +static struct cphy* vsc8244_phy_create(adapter_t *adapter, int phy_addr, + struct mdio_ops *mdio_ops) { struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); - if (!cphy) return NULL; + if (!cphy) + return NULL; cphy_init(cphy, adapter, phy_addr, &vsc8244_ops, mdio_ops); diff -puN /dev/null drivers/net/cxgb3/Makefile --- /dev/null +++ a/drivers/net/cxgb3/Makefile @@ -0,0 +1,8 @@ +# +# Chelsio T3 driver +# + +obj-$(CONFIG_CHELSIO_T3) += cxgb3.o + +cxgb3-objs := cxgb3_main.o ael1002.o vsc8211.o t3_hw.o mc5.o \ + xgmac.o sge.o l2t.o cxgb3_offload.o diff -puN /dev/null drivers/net/cxgb3/adapter.h --- /dev/null +++ a/drivers/net/cxgb3/adapter.h @@ -0,0 +1,255 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver for Linux. + * + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +/* This file should not be included directly. Include common.h instead. */ + +#ifndef __T3_ADAPTER_H__ +#define __T3_ADAPTER_H__ + +#include +#include +#include +#include +#include +#include "t3cdev.h" +#include +#include +#include + +typedef irqreturn_t(*intr_handler_t) (int, void *); + +struct vlan_group; + +struct port_info { + struct vlan_group *vlan_grp; + const struct port_type_info *port_type; + u8 port_id; + u8 rx_csum_offload; + u8 nqsets; + u8 first_qset; + struct cphy phy; + struct cmac mac; + struct link_config link_config; + struct net_device_stats netstats; + int activity; +}; + +enum { /* adapter flags */ + FULL_INIT_DONE = (1 << 0), + USING_MSI = (1 << 1), + USING_MSIX = (1 << 2), +}; + +struct rx_desc; +struct rx_sw_desc; + +struct sge_fl { /* SGE per free-buffer list state */ + unsigned int buf_size; /* size of each Rx buffer */ + unsigned int credits; /* # of available Rx buffers */ + unsigned int size; /* capacity of free list */ + unsigned int cidx; /* consumer index */ + unsigned int pidx; /* producer index */ + unsigned int gen; /* free list generation */ + struct rx_desc *desc; /* address of HW Rx descriptor ring */ + struct rx_sw_desc *sdesc; /* address of SW Rx descriptor ring */ + dma_addr_t phys_addr; /* physical address of HW ring start */ + unsigned int cntxt_id; /* SGE context id for the free list */ + unsigned long empty; /* # of times queue ran out of buffers */ +}; + +/* + * Bundle size for grouping offload RX packets for delivery to the stack. + * Don't make this too big as we do prefetch on each packet in a bundle. + */ +# define RX_BUNDLE_SIZE 8 + +struct rsp_desc; + +struct sge_rspq { /* state for an SGE response queue */ + unsigned int credits; /* # of pending response credits */ + unsigned int size; /* capacity of response queue */ + unsigned int cidx; /* consumer index */ + unsigned int gen; /* current generation bit */ + unsigned int polling; /* is the queue serviced through NAPI? */ + unsigned int holdoff_tmr; /* interrupt holdoff timer in 100ns */ + unsigned int next_holdoff; /* holdoff time for next interrupt */ + struct rsp_desc *desc; /* address of HW response ring */ + dma_addr_t phys_addr; /* physical address of the ring */ + unsigned int cntxt_id; /* SGE context id for the response q */ + spinlock_t lock; /* guards response processing */ + struct sk_buff *rx_head; /* offload packet receive queue head */ + struct sk_buff *rx_tail; /* offload packet receive queue tail */ + + unsigned long offload_pkts; + unsigned long offload_bundles; + unsigned long eth_pkts; /* # of ethernet packets */ + unsigned long pure_rsps; /* # of pure (non-data) responses */ + unsigned long imm_data; /* responses with immediate data */ + unsigned long rx_drops; /* # of packets dropped due to no mem */ + unsigned long async_notif; /* # of asynchronous notification events */ + unsigned long empty; /* # of times queue ran out of credits */ + unsigned long nomem; /* # of responses deferred due to no mem */ + unsigned long unhandled_irqs; /* # of spurious intrs */ +}; + +struct tx_desc; +struct tx_sw_desc; + +struct sge_txq { /* state for an SGE Tx queue */ + unsigned long flags; /* HW DMA fetch status */ + unsigned int in_use; /* # of in-use Tx descriptors */ + unsigned int size; /* # of descriptors */ + unsigned int processed; /* total # of descs HW has processed */ + unsigned int cleaned; /* total # of descs SW has reclaimed */ + unsigned int stop_thres; /* SW TX queue suspend threshold */ + unsigned int cidx; /* consumer index */ + unsigned int pidx; /* producer index */ + unsigned int gen; /* current value of generation bit */ + unsigned int unacked; /* Tx descriptors used since last COMPL */ + struct tx_desc *desc; /* address of HW Tx descriptor ring */ + struct tx_sw_desc *sdesc; /* address of SW Tx descriptor ring */ + spinlock_t lock; /* guards enqueueing of new packets */ + unsigned int token; /* WR token */ + dma_addr_t phys_addr; /* physical address of the ring */ + struct sk_buff_head sendq; /* List of backpressured offload packets */ + struct tasklet_struct qresume_tsk; /* restarts the queue */ + unsigned int cntxt_id; /* SGE context id for the Tx q */ + unsigned long stops; /* # of times q has been stopped */ + unsigned long restarts; /* # of queue restarts */ +}; + +enum { /* per port SGE statistics */ + SGE_PSTAT_TSO, /* # of TSO requests */ + SGE_PSTAT_RX_CSUM_GOOD, /* # of successful RX csum offloads */ + SGE_PSTAT_TX_CSUM, /* # of TX checksum offloads */ + SGE_PSTAT_VLANEX, /* # of VLAN tag extractions */ + SGE_PSTAT_VLANINS, /* # of VLAN tag insertions */ + + SGE_PSTAT_MAX /* must be last */ +}; + +struct sge_qset { /* an SGE queue set */ + struct sge_rspq rspq; + struct sge_fl fl[SGE_RXQ_PER_SET]; + struct sge_txq txq[SGE_TXQ_PER_SET]; + struct net_device *netdev; /* associated net device */ + unsigned long txq_stopped; /* which Tx queues are stopped */ + struct timer_list tx_reclaim_timer; /* reclaims TX buffers */ + unsigned long port_stats[SGE_PSTAT_MAX]; +} ____cacheline_aligned; + +struct sge { + struct sge_qset qs[SGE_QSETS]; + spinlock_t reg_lock; /* guards non-atomic SGE registers (eg context) */ +}; + +struct adapter { + struct t3cdev tdev; + struct list_head adapter_list; + void __iomem *regs; + struct pci_dev *pdev; + unsigned long registered_device_map; + unsigned long open_device_map; + unsigned long flags; + + const char *name; + int msg_enable; + unsigned int mmio_len; + + struct adapter_params params; + unsigned int slow_intr_mask; + unsigned long irq_stats[IRQ_NUM_STATS]; + + struct { + unsigned short vec; + char desc[22]; + } msix_info[SGE_QSETS + 1]; + + /* T3 modules */ + struct sge sge; + struct mc7 pmrx; + struct mc7 pmtx; + struct mc7 cm; + struct mc5 mc5; + + struct net_device *port[MAX_NPORTS]; + unsigned int check_task_cnt; + struct delayed_work adap_check_task; + struct work_struct ext_intr_handler_task; + + /* + * Dummy netdevices are needed when using multiple receive queues with + * NAPI as each netdevice can service only one queue. + */ + struct net_device *dummy_netdev[SGE_QSETS - 1]; + + struct dentry *debugfs_root; + + struct mutex mdio_lock; + spinlock_t stats_lock; + spinlock_t work_lock; +}; + +static inline u32 t3_read_reg(struct adapter *adapter, u32 reg_addr) +{ + u32 val = readl(adapter->regs + reg_addr); + + CH_DBG(adapter, MMIO, "read register 0x%x value 0x%x\n", reg_addr, val); + return val; +} + +static inline void t3_write_reg(struct adapter *adapter, u32 reg_addr, u32 val) +{ + CH_DBG(adapter, MMIO, "setting register 0x%x to 0x%x\n", reg_addr, val); + writel(val, adapter->regs + reg_addr); +} + +static inline struct port_info *adap2pinfo(struct adapter *adap, int idx) +{ + return netdev_priv(adap->port[idx]); +} + +/* + * We use the spare atalk_ptr to map a net device to its SGE queue set. + * This is a macro so it can be used as l-value. + */ +#define dev2qset(netdev) ((netdev)->atalk_ptr) + +#define OFFLOAD_DEVMAP_BIT 15 + +#define tdev2adap(d) container_of(d, struct adapter, tdev) + +static inline int offload_running(struct adapter *adapter) +{ + return test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); +} + +int t3_offload_tx(struct t3cdev *tdev, struct sk_buff *skb); + +void t3_os_ext_intr_handler(struct adapter *adapter); +void t3_os_link_changed(struct adapter *adapter, int port_id, int link_status, + int speed, int duplex, int fc); + +void t3_sge_start(struct adapter *adap); +void t3_sge_stop(struct adapter *adap); +void t3_free_sge_resources(struct adapter *adap); +void t3_sge_err_intr_handler(struct adapter *adapter); +intr_handler_t t3_intr_handler(struct adapter *adap, int polling); +int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev); +void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p); +int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, + int irq_vec_idx, const struct qset_params *p, + int ntxq, struct net_device *netdev); +int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx, + unsigned char *data); +irqreturn_t t3_sge_intr_msix(int irq, void *cookie); + +#endif /* __T3_ADAPTER_H__ */ diff -puN /dev/null drivers/net/cxgb3/ael1002.c --- /dev/null +++ a/drivers/net/cxgb3/ael1002.c @@ -0,0 +1,231 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include "common.h" +#include "regs.h" + +enum { + AEL100X_TX_DISABLE = 9, + AEL100X_TX_CONFIG1 = 0xc002, + AEL1002_PWR_DOWN_HI = 0xc011, + AEL1002_PWR_DOWN_LO = 0xc012, + AEL1002_XFI_EQL = 0xc015, + AEL1002_LB_EN = 0xc017, + + LASI_CTRL = 0x9002, + LASI_STAT = 0x9005 +}; + +static void ael100x_txon(struct cphy *phy) +{ + int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; + + msleep(100); + t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); + msleep(30); +} + +static int ael1002_power_down(struct cphy *phy, int enable) +{ + int err; + + err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable); + if (!err) + err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, + BMCR_PDOWN, enable ? BMCR_PDOWN : 0); + return err; +} + +static int ael1002_reset(struct cphy *phy, int wait) +{ + int err; + + if ((err = ael1002_power_down(phy, 0)) || + (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) || + (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) || + (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) || + (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) || + (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN, + 0, 1 << 5))) + return err; + return 0; +} + +static int ael1002_intr_noop(struct cphy *phy) +{ + return 0; +} + +static int ael100x_get_link_status(struct cphy *phy, int *link_ok, + int *speed, int *duplex, int *fc) +{ + if (link_ok) { + unsigned int status; + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status); + + /* + * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it + * once more to get the current link state. + */ + if (!err && !(status & BMSR_LSTATUS)) + err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, + &status); + if (err) + return err; + *link_ok = !!(status & BMSR_LSTATUS); + } + if (speed) + *speed = SPEED_10000; + if (duplex) + *duplex = DUPLEX_FULL; + return 0; +} + +static struct cphy_ops ael1002_ops = { + .reset = ael1002_reset, + .intr_enable = ael1002_intr_noop, + .intr_disable = ael1002_intr_noop, + .intr_clear = ael1002_intr_noop, + .intr_handler = ael1002_intr_noop, + .get_link_status = ael100x_get_link_status, + .power_down = ael1002_power_down, +}; + +void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) +{ + cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops); + ael100x_txon(phy); +} + +static int ael1006_reset(struct cphy *phy, int wait) +{ + return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); +} + +static int ael1006_intr_enable(struct cphy *phy) +{ + return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); +} + +static int ael1006_intr_disable(struct cphy *phy) +{ + return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); +} + +static int ael1006_intr_clear(struct cphy *phy) +{ + u32 val; + + return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); +} + +static int ael1006_intr_handler(struct cphy *phy) +{ + unsigned int status; + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); + + if (err) + return err; + return (status & 1) ? cphy_cause_link_change : 0; +} + +static int ael1006_power_down(struct cphy *phy, int enable) +{ + return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, + BMCR_PDOWN, enable ? BMCR_PDOWN : 0); +} + +static struct cphy_ops ael1006_ops = { + .reset = ael1006_reset, + .intr_enable = ael1006_intr_enable, + .intr_disable = ael1006_intr_disable, + .intr_clear = ael1006_intr_clear, + .intr_handler = ael1006_intr_handler, + .get_link_status = ael100x_get_link_status, + .power_down = ael1006_power_down, +}; + +void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) +{ + cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops); + ael100x_txon(phy); +} + +static struct cphy_ops qt2045_ops = { + .reset = ael1006_reset, + .intr_enable = ael1006_intr_enable, + .intr_disable = ael1006_intr_disable, + .intr_clear = ael1006_intr_clear, + .intr_handler = ael1006_intr_handler, + .get_link_status = ael100x_get_link_status, + .power_down = ael1006_power_down, +}; + +void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) +{ + unsigned int stat; + + cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops); + + /* + * Some cards where the PHY is supposed to be at address 0 actually + * have it at 1. + */ + if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) && + stat == 0xffff) + phy->addr = 1; +} + +static int xaui_direct_reset(struct cphy *phy, int wait) +{ + return 0; +} + +static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, + int *speed, int *duplex, int *fc) +{ + if (link_ok) { + unsigned int status; + + status = t3_read_reg(phy->adapter, + XGM_REG(A_XGM_SERDES_STAT0, phy->addr)); + *link_ok = !(status & F_LOWSIG0); + } + if (speed) + *speed = SPEED_10000; + if (duplex) + *duplex = DUPLEX_FULL; + return 0; +} + +static int xaui_direct_power_down(struct cphy *phy, int enable) +{ + return 0; +} + +static struct cphy_ops xaui_direct_ops = { + .reset = xaui_direct_reset, + .intr_enable = ael1002_intr_noop, + .intr_disable = ael1002_intr_noop, + .intr_clear = ael1002_intr_noop, + .intr_handler = ael1002_intr_noop, + .get_link_status = xaui_direct_get_link_status, + .power_down = xaui_direct_power_down, +}; + +void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) +{ + cphy_init(phy, adapter, 1, &xaui_direct_ops, mdio_ops); +} diff -puN /dev/null drivers/net/cxgb3/common.h --- /dev/null +++ a/drivers/net/cxgb3/common.h @@ -0,0 +1,709 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#ifndef __CHELSIO_COMMON_H +#define __CHELSIO_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "version.h" + +#define CH_ERR(adap, fmt, ...) dev_err(&adap->pdev->dev, fmt, ## __VA_ARGS__) +#define CH_WARN(adap, fmt, ...) dev_warn(&adap->pdev->dev, fmt, ## __VA_ARGS__) +#define CH_ALERT(adap, fmt, ...) \ + dev_printk(KERN_ALERT, &adap->pdev->dev, fmt, ## __VA_ARGS__) + +/* + * More powerful macro that selectively prints messages based on msg_enable. + * For info and debugging messages. + */ +#define CH_MSG(adapter, level, category, fmt, ...) do { \ + if ((adapter)->msg_enable & NETIF_MSG_##category) \ + dev_printk(KERN_##level, &adapter->pdev->dev, fmt, \ + ## __VA_ARGS__); \ +} while (0) + +#ifdef DEBUG +# define CH_DBG(adapter, category, fmt, ...) \ + CH_MSG(adapter, DEBUG, category, fmt, ## __VA_ARGS__) +#else +# define CH_DBG(adapter, category, fmt, ...) +#endif + +/* Additional NETIF_MSG_* categories */ +#define NETIF_MSG_MMIO 0x8000000 + +struct t3_rx_mode { + struct net_device *dev; + struct dev_mc_list *mclist; + unsigned int idx; +}; + +static inline void init_rx_mode(struct t3_rx_mode *p, struct net_device *dev, + struct dev_mc_list *mclist) +{ + p->dev = dev; + p->mclist = mclist; + p->idx = 0; +} + +static inline u8 *t3_get_next_mcaddr(struct t3_rx_mode *rm) +{ + u8 *addr = NULL; + + if (rm->mclist && rm->idx < rm->dev->mc_count) { + addr = rm->mclist->dmi_addr; + rm->mclist = rm->mclist->next; + rm->idx++; + } + return addr; +} + +enum { + MAX_NPORTS = 2, /* max # of ports */ + MAX_FRAME_SIZE = 10240, /* max MAC frame size, including header + FCS */ + EEPROMSIZE = 8192, /* Serial EEPROM size */ + RSS_TABLE_SIZE = 64, /* size of RSS lookup and mapping tables */ + TCB_SIZE = 128, /* TCB size */ + NMTUS = 16, /* size of MTU table */ + NCCTRL_WIN = 32, /* # of congestion control windows */ +}; + +#define MAX_RX_COALESCING_LEN 16224U + +enum { + PAUSE_RX = 1 << 0, + PAUSE_TX = 1 << 1, + PAUSE_AUTONEG = 1 << 2 +}; + +enum { + SUPPORTED_OFFLOAD = 1 << 24, + SUPPORTED_IRQ = 1 << 25 +}; + +enum { /* adapter interrupt-maintained statistics */ + STAT_ULP_CH0_PBL_OOB, + STAT_ULP_CH1_PBL_OOB, + STAT_PCI_CORR_ECC, + + IRQ_NUM_STATS /* keep last */ +}; + +enum { + SGE_QSETS = 8, /* # of SGE Tx/Rx/RspQ sets */ + SGE_RXQ_PER_SET = 2, /* # of Rx queues per set */ + SGE_TXQ_PER_SET = 3 /* # of Tx queues per set */ +}; + +enum sge_context_type { /* SGE egress context types */ + SGE_CNTXT_RDMA = 0, + SGE_CNTXT_ETH = 2, + SGE_CNTXT_OFLD = 4, + SGE_CNTXT_CTRL = 5 +}; + +enum { + AN_PKT_SIZE = 32, /* async notification packet size */ + IMMED_PKT_SIZE = 48 /* packet size for immediate data */ +}; + +struct sg_ent { /* SGE scatter/gather entry */ + u32 len[2]; + u64 addr[2]; +}; + +#ifndef SGE_NUM_GENBITS +/* Must be 1 or 2 */ +# define SGE_NUM_GENBITS 2 +#endif + +#define TX_DESC_FLITS 16U +#define WR_FLITS (TX_DESC_FLITS + 1 - SGE_NUM_GENBITS) + +struct cphy; +struct adapter; + +struct mdio_ops { + int (*read)(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int *val); + int (*write)(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int val); +}; + +struct adapter_info { + unsigned char nports; /* # of ports */ + unsigned char phy_base_addr; /* MDIO PHY base address */ + unsigned char mdien; + unsigned char mdiinv; + unsigned int gpio_out; /* GPIO output settings */ + unsigned int gpio_intr; /* GPIO IRQ enable mask */ + unsigned long caps; /* adapter capabilities */ + const struct mdio_ops *mdio_ops; /* MDIO operations */ + const char *desc; /* product description */ +}; + +struct port_type_info { + void (*phy_prep)(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *ops); + unsigned int caps; + const char *desc; +}; + +struct mc5_stats { + unsigned long parity_err; + unsigned long active_rgn_full; + unsigned long nfa_srch_err; + unsigned long unknown_cmd; + unsigned long reqq_parity_err; + unsigned long dispq_parity_err; + unsigned long del_act_empty; +}; + +struct mc7_stats { + unsigned long corr_err; + unsigned long uncorr_err; + unsigned long parity_err; + unsigned long addr_err; +}; + +struct mac_stats { + u64 tx_octets; /* total # of octets in good frames */ + u64 tx_octets_bad; /* total # of octets in error frames */ + u64 tx_frames; /* all good frames */ + u64 tx_mcast_frames; /* good multicast frames */ + u64 tx_bcast_frames; /* good broadcast frames */ + u64 tx_pause; /* # of transmitted pause frames */ + u64 tx_deferred; /* frames with deferred transmissions */ + u64 tx_late_collisions; /* # of late collisions */ + u64 tx_total_collisions; /* # of total collisions */ + u64 tx_excess_collisions; /* frame errors from excessive collissions */ + u64 tx_underrun; /* # of Tx FIFO underruns */ + u64 tx_len_errs; /* # of Tx length errors */ + u64 tx_mac_internal_errs; /* # of internal MAC errors on Tx */ + u64 tx_excess_deferral; /* # of frames with excessive deferral */ + u64 tx_fcs_errs; /* # of frames with bad FCS */ + + u64 tx_frames_64; /* # of Tx frames in a particular range */ + u64 tx_frames_65_127; + u64 tx_frames_128_255; + u64 tx_frames_256_511; + u64 tx_frames_512_1023; + u64 tx_frames_1024_1518; + u64 tx_frames_1519_max; + + u64 rx_octets; /* total # of octets in good frames */ + u64 rx_octets_bad; /* total # of octets in error frames */ + u64 rx_frames; /* all good frames */ + u64 rx_mcast_frames; /* good multicast frames */ + u64 rx_bcast_frames; /* good broadcast frames */ + u64 rx_pause; /* # of received pause frames */ + u64 rx_fcs_errs; /* # of received frames with bad FCS */ + u64 rx_align_errs; /* alignment errors */ + u64 rx_symbol_errs; /* symbol errors */ + u64 rx_data_errs; /* data errors */ + u64 rx_sequence_errs; /* sequence errors */ + u64 rx_runt; /* # of runt frames */ + u64 rx_jabber; /* # of jabber frames */ + u64 rx_short; /* # of short frames */ + u64 rx_too_long; /* # of oversized frames */ + u64 rx_mac_internal_errs; /* # of internal MAC errors on Rx */ + + u64 rx_frames_64; /* # of Rx frames in a particular range */ + u64 rx_frames_65_127; + u64 rx_frames_128_255; + u64 rx_frames_256_511; + u64 rx_frames_512_1023; + u64 rx_frames_1024_1518; + u64 rx_frames_1519_max; + + u64 rx_cong_drops; /* # of Rx drops due to SGE congestion */ + + unsigned long tx_fifo_parity_err; + unsigned long rx_fifo_parity_err; + unsigned long tx_fifo_urun; + unsigned long rx_fifo_ovfl; + unsigned long serdes_signal_loss; + unsigned long xaui_pcs_ctc_err; + unsigned long xaui_pcs_align_change; +}; + +struct tp_mib_stats { + u32 ipInReceive_hi; + u32 ipInReceive_lo; + u32 ipInHdrErrors_hi; + u32 ipInHdrErrors_lo; + u32 ipInAddrErrors_hi; + u32 ipInAddrErrors_lo; + u32 ipInUnknownProtos_hi; + u32 ipInUnknownProtos_lo; + u32 ipInDiscards_hi; + u32 ipInDiscards_lo; + u32 ipInDelivers_hi; + u32 ipInDelivers_lo; + u32 ipOutRequests_hi; + u32 ipOutRequests_lo; + u32 ipOutDiscards_hi; + u32 ipOutDiscards_lo; + u32 ipOutNoRoutes_hi; + u32 ipOutNoRoutes_lo; + u32 ipReasmTimeout; + u32 ipReasmReqds; + u32 ipReasmOKs; + u32 ipReasmFails; + + u32 reserved[8]; + + u32 tcpActiveOpens; + u32 tcpPassiveOpens; + u32 tcpAttemptFails; + u32 tcpEstabResets; + u32 tcpOutRsts; + u32 tcpCurrEstab; + u32 tcpInSegs_hi; + u32 tcpInSegs_lo; + u32 tcpOutSegs_hi; + u32 tcpOutSegs_lo; + u32 tcpRetransSeg_hi; + u32 tcpRetransSeg_lo; + u32 tcpInErrs_hi; + u32 tcpInErrs_lo; + u32 tcpRtoMin; + u32 tcpRtoMax; +}; + +struct tp_params { + unsigned int nchan; /* # of channels */ + unsigned int pmrx_size; /* total PMRX capacity */ + unsigned int pmtx_size; /* total PMTX capacity */ + unsigned int cm_size; /* total CM capacity */ + unsigned int chan_rx_size; /* per channel Rx size */ + unsigned int chan_tx_size; /* per channel Tx size */ + unsigned int rx_pg_size; /* Rx page size */ + unsigned int tx_pg_size; /* Tx page size */ + unsigned int rx_num_pgs; /* # of Rx pages */ + unsigned int tx_num_pgs; /* # of Tx pages */ + unsigned int ntimer_qs; /* # of timer queues */ +}; + +struct qset_params { /* SGE queue set parameters */ + unsigned int polling; /* polling/interrupt service for rspq */ + unsigned int coalesce_usecs; /* irq coalescing timer */ + unsigned int rspq_size; /* # of entries in response queue */ + unsigned int fl_size; /* # of entries in regular free list */ + unsigned int jumbo_size; /* # of entries in jumbo free list */ + unsigned int txq_size[SGE_TXQ_PER_SET]; /* Tx queue sizes */ + unsigned int cong_thres; /* FL congestion threshold */ +}; + +struct sge_params { + unsigned int max_pkt_size; /* max offload pkt size */ + struct qset_params qset[SGE_QSETS]; +}; + +struct mc5_params { + unsigned int mode; /* selects MC5 width */ + unsigned int nservers; /* size of server region */ + unsigned int nfilters; /* size of filter region */ + unsigned int nroutes; /* size of routing region */ +}; + +/* Default MC5 region sizes */ +enum { + DEFAULT_NSERVERS = 512, + DEFAULT_NFILTERS = 128 +}; + +/* MC5 modes, these must be non-0 */ +enum { + MC5_MODE_144_BIT = 1, + MC5_MODE_72_BIT = 2 +}; + +struct vpd_params { + unsigned int cclk; + unsigned int mclk; + unsigned int uclk; + unsigned int mdc; + unsigned int mem_timing; + u8 eth_base[6]; + u8 port_type[MAX_NPORTS]; + unsigned short xauicfg[2]; +}; + +struct pci_params { + unsigned int vpd_cap_addr; + unsigned int pcie_cap_addr; + unsigned short speed; + unsigned char width; + unsigned char variant; +}; + +enum { + PCI_VARIANT_PCI, + PCI_VARIANT_PCIX_MODE1_PARITY, + PCI_VARIANT_PCIX_MODE1_ECC, + PCI_VARIANT_PCIX_266_MODE2, + PCI_VARIANT_PCIE +}; + +struct adapter_params { + struct sge_params sge; + struct mc5_params mc5; + struct tp_params tp; + struct vpd_params vpd; + struct pci_params pci; + + const struct adapter_info *info; + + unsigned short mtus[NMTUS]; + unsigned short a_wnd[NCCTRL_WIN]; + unsigned short b_wnd[NCCTRL_WIN]; + + unsigned int nports; /* # of ethernet ports */ + unsigned int stats_update_period; /* MAC stats accumulation period */ + unsigned int linkpoll_period; /* link poll period in 0.1s */ + unsigned int rev; /* chip revision */ +}; + +struct trace_params { + u32 sip; + u32 sip_mask; + u32 dip; + u32 dip_mask; + u16 sport; + u16 sport_mask; + u16 dport; + u16 dport_mask; + u32 vlan:12; + u32 vlan_mask:12; + u32 intf:4; + u32 intf_mask:4; + u8 proto; + u8 proto_mask; +}; + +struct link_config { + unsigned int supported; /* link capabilities */ + unsigned int advertising; /* advertised capabilities */ + unsigned short requested_speed; /* speed user has requested */ + unsigned short speed; /* actual link speed */ + unsigned char requested_duplex; /* duplex user has requested */ + unsigned char duplex; /* actual link duplex */ + unsigned char requested_fc; /* flow control user has requested */ + unsigned char fc; /* actual link flow control */ + unsigned char autoneg; /* autonegotiating? */ + unsigned int link_ok; /* link up? */ +}; + +#define SPEED_INVALID 0xffff +#define DUPLEX_INVALID 0xff + +struct mc5 { + struct adapter *adapter; + unsigned int tcam_size; + unsigned char part_type; + unsigned char parity_enabled; + unsigned char mode; + struct mc5_stats stats; +}; + +static inline unsigned int t3_mc5_size(const struct mc5 *p) +{ + return p->tcam_size; +} + +struct mc7 { + struct adapter *adapter; /* backpointer to adapter */ + unsigned int size; /* memory size in bytes */ + unsigned int width; /* MC7 interface width */ + unsigned int offset; /* register address offset for MC7 instance */ + const char *name; /* name of MC7 instance */ + struct mc7_stats stats; /* MC7 statistics */ +}; + +static inline unsigned int t3_mc7_size(const struct mc7 *p) +{ + return p->size; +} + +struct cmac { + struct adapter *adapter; + unsigned int offset; + unsigned int nucast; /* # of address filters for unicast MACs */ + struct mac_stats stats; +}; + +enum { + MAC_DIRECTION_RX = 1, + MAC_DIRECTION_TX = 2, + MAC_RXFIFO_SIZE = 32768 +}; + +/* IEEE 802.3ae specified MDIO devices */ +enum { + MDIO_DEV_PMA_PMD = 1, + MDIO_DEV_WIS = 2, + MDIO_DEV_PCS = 3, + MDIO_DEV_XGXS = 4 +}; + +/* PHY loopback direction */ +enum { + PHY_LOOPBACK_TX = 1, + PHY_LOOPBACK_RX = 2 +}; + +/* PHY interrupt types */ +enum { + cphy_cause_link_change = 1, + cphy_cause_fifo_error = 2 +}; + +/* PHY operations */ +struct cphy_ops { + void (*destroy)(struct cphy *phy); + int (*reset)(struct cphy *phy, int wait); + + int (*intr_enable)(struct cphy *phy); + int (*intr_disable)(struct cphy *phy); + int (*intr_clear)(struct cphy *phy); + int (*intr_handler)(struct cphy *phy); + + int (*autoneg_enable)(struct cphy *phy); + int (*autoneg_restart)(struct cphy *phy); + + int (*advertise)(struct cphy *phy, unsigned int advertise_map); + int (*set_loopback)(struct cphy *phy, int mmd, int dir, int enable); + int (*set_speed_duplex)(struct cphy *phy, int speed, int duplex); + int (*get_link_status)(struct cphy *phy, int *link_ok, int *speed, + int *duplex, int *fc); + int (*power_down)(struct cphy *phy, int enable); +}; + +/* A PHY instance */ +struct cphy { + int addr; /* PHY address */ + struct adapter *adapter; /* associated adapter */ + unsigned long fifo_errors; /* FIFO over/under-flows */ + const struct cphy_ops *ops; /* PHY operations */ + int (*mdio_read)(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int *val); + int (*mdio_write)(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int val); +}; + +/* Convenience MDIO read/write wrappers */ +static inline int mdio_read(struct cphy *phy, int mmd, int reg, + unsigned int *valp) +{ + return phy->mdio_read(phy->adapter, phy->addr, mmd, reg, valp); +} + +static inline int mdio_write(struct cphy *phy, int mmd, int reg, + unsigned int val) +{ + return phy->mdio_write(phy->adapter, phy->addr, mmd, reg, val); +} + +/* Convenience initializer */ +static inline void cphy_init(struct cphy *phy, struct adapter *adapter, + int phy_addr, struct cphy_ops *phy_ops, + const struct mdio_ops *mdio_ops) +{ + phy->adapter = adapter; + phy->addr = phy_addr; + phy->ops = phy_ops; + if (mdio_ops) { + phy->mdio_read = mdio_ops->read; + phy->mdio_write = mdio_ops->write; + } +} + +/* Accumulate MAC statistics every 180 seconds. For 1G we multiply by 10. */ +#define MAC_STATS_ACCUM_SECS 180 + +#define XGM_REG(reg_addr, idx) \ + ((reg_addr) + (idx) * (XGMAC0_1_BASE_ADDR - XGMAC0_0_BASE_ADDR)) + +struct addr_val_pair { + unsigned int reg_addr; + unsigned int val; +}; + +#include "adapter.h" + +#ifndef PCI_VENDOR_ID_CHELSIO +# define PCI_VENDOR_ID_CHELSIO 0x1425 +#endif + +#define for_each_port(adapter, iter) \ + for (iter = 0; iter < (adapter)->params.nports; ++iter) + +#define adapter_info(adap) ((adap)->params.info) + +static inline int uses_xaui(const struct adapter *adap) +{ + return adapter_info(adap)->caps & SUPPORTED_AUI; +} + +static inline int is_10G(const struct adapter *adap) +{ + return adapter_info(adap)->caps & SUPPORTED_10000baseT_Full; +} + +static inline int is_offload(const struct adapter *adap) +{ + return adapter_info(adap)->caps & SUPPORTED_OFFLOAD; +} + +static inline unsigned int core_ticks_per_usec(const struct adapter *adap) +{ + return adap->params.vpd.cclk / 1000; +} + +static inline unsigned int is_pcie(const struct adapter *adap) +{ + return adap->params.pci.variant == PCI_VARIANT_PCIE; +} + +void t3_set_reg_field(struct adapter *adap, unsigned int addr, u32 mask, + u32 val); +void t3_write_regs(struct adapter *adapter, const struct addr_val_pair *p, + int n, unsigned int offset); +int t3_wait_op_done_val(struct adapter *adapter, int reg, u32 mask, + int polarity, int attempts, int delay, u32 *valp); +static inline int t3_wait_op_done(struct adapter *adapter, int reg, u32 mask, + int polarity, int attempts, int delay) +{ + return t3_wait_op_done_val(adapter, reg, mask, polarity, attempts, + delay, NULL); +} +int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear, + unsigned int set); +int t3_phy_reset(struct cphy *phy, int mmd, int wait); +int t3_phy_advertise(struct cphy *phy, unsigned int advert); +int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex); + +void t3_intr_enable(struct adapter *adapter); +void t3_intr_disable(struct adapter *adapter); +void t3_intr_clear(struct adapter *adapter); +void t3_port_intr_enable(struct adapter *adapter, int idx); +void t3_port_intr_disable(struct adapter *adapter, int idx); +void t3_port_intr_clear(struct adapter *adapter, int idx); +int t3_slow_intr_handler(struct adapter *adapter); +int t3_phy_intr_handler(struct adapter *adapter); + +void t3_link_changed(struct adapter *adapter, int port_id); +int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc); +const struct adapter_info *t3_get_adapter_info(unsigned int board_id); +int t3_seeprom_read(struct adapter *adapter, u32 addr, u32 *data); +int t3_seeprom_write(struct adapter *adapter, u32 addr, u32 data); +int t3_seeprom_wp(struct adapter *adapter, int enable); +int t3_read_flash(struct adapter *adapter, unsigned int addr, + unsigned int nwords, u32 *data, int byte_oriented); +int t3_load_fw(struct adapter *adapter, const u8 * fw_data, unsigned int size); +int t3_get_fw_version(struct adapter *adapter, u32 *vers); +int t3_check_fw_version(struct adapter *adapter); +int t3_init_hw(struct adapter *adapter, u32 fw_params); +void mac_prep(struct cmac *mac, struct adapter *adapter, int index); +void early_hw_init(struct adapter *adapter, const struct adapter_info *ai); +int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, + int reset); +void t3_led_ready(struct adapter *adapter); +void t3_fatal_err(struct adapter *adapter); +void t3_set_vlan_accel(struct adapter *adapter, unsigned int ports, int on); +void t3_config_rss(struct adapter *adapter, unsigned int rss_config, + const u8 * cpus, const u16 *rspq); +int t3_read_rss(struct adapter *adapter, u8 * lkup, u16 *map); +int t3_mps_set_active_ports(struct adapter *adap, unsigned int port_mask); +int t3_cim_ctl_blk_read(struct adapter *adap, unsigned int addr, + unsigned int n, unsigned int *valp); +int t3_mc7_bd_read(struct mc7 *mc7, unsigned int start, unsigned int n, + u64 *buf); + +int t3_mac_reset(struct cmac *mac); +void t3b_pcs_reset(struct cmac *mac); +int t3_mac_enable(struct cmac *mac, int which); +int t3_mac_disable(struct cmac *mac, int which); +int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu); +int t3_mac_set_rx_mode(struct cmac *mac, struct t3_rx_mode *rm); +int t3_mac_set_address(struct cmac *mac, unsigned int idx, u8 addr[6]); +int t3_mac_set_num_ucast(struct cmac *mac, int n); +const struct mac_stats *t3_mac_update_stats(struct cmac *mac); +int t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc); + +void t3_mc5_prep(struct adapter *adapter, struct mc5 *mc5, int mode); +int t3_mc5_init(struct mc5 *mc5, unsigned int nservers, unsigned int nfilters, + unsigned int nroutes); +void t3_mc5_intr_handler(struct mc5 *mc5); +int t3_read_mc5_range(const struct mc5 *mc5, unsigned int start, unsigned int n, + u32 *buf); + +int t3_tp_set_coalescing_size(struct adapter *adap, unsigned int size, int psh); +void t3_tp_set_max_rxsize(struct adapter *adap, unsigned int size); +void t3_tp_set_offload_mode(struct adapter *adap, int enable); +void t3_tp_get_mib_stats(struct adapter *adap, struct tp_mib_stats *tps); +void t3_load_mtus(struct adapter *adap, unsigned short mtus[NMTUS], + unsigned short alpha[NCCTRL_WIN], + unsigned short beta[NCCTRL_WIN], unsigned short mtu_cap); +void t3_read_hw_mtus(struct adapter *adap, unsigned short mtus[NMTUS]); +void t3_get_cong_cntl_tab(struct adapter *adap, + unsigned short incr[NMTUS][NCCTRL_WIN]); +void t3_config_trace_filter(struct adapter *adapter, + const struct trace_params *tp, int filter_index, + int invert, int enable); +int t3_config_sched(struct adapter *adap, unsigned int kbps, int sched); + +void t3_sge_prep(struct adapter *adap, struct sge_params *p); +void t3_sge_init(struct adapter *adap, struct sge_params *p); +int t3_sge_init_ecntxt(struct adapter *adapter, unsigned int id, int gts_enable, + enum sge_context_type type, int respq, u64 base_addr, + unsigned int size, unsigned int token, int gen, + unsigned int cidx); +int t3_sge_init_flcntxt(struct adapter *adapter, unsigned int id, + int gts_enable, u64 base_addr, unsigned int size, + unsigned int esize, unsigned int cong_thres, int gen, + unsigned int cidx); +int t3_sge_init_rspcntxt(struct adapter *adapter, unsigned int id, + int irq_vec_idx, u64 base_addr, unsigned int size, + unsigned int fl_thres, int gen, unsigned int cidx); +int t3_sge_init_cqcntxt(struct adapter *adapter, unsigned int id, u64 base_addr, + unsigned int size, int rspq, int ovfl_mode, + unsigned int credits, unsigned int credit_thres); +int t3_sge_enable_ecntxt(struct adapter *adapter, unsigned int id, int enable); +int t3_sge_disable_fl(struct adapter *adapter, unsigned int id); +int t3_sge_disable_rspcntxt(struct adapter *adapter, unsigned int id); +int t3_sge_disable_cqcntxt(struct adapter *adapter, unsigned int id); +int t3_sge_read_ecntxt(struct adapter *adapter, unsigned int id, u32 data[4]); +int t3_sge_read_fl(struct adapter *adapter, unsigned int id, u32 data[4]); +int t3_sge_read_cq(struct adapter *adapter, unsigned int id, u32 data[4]); +int t3_sge_read_rspq(struct adapter *adapter, unsigned int id, u32 data[4]); +int t3_sge_cqcntxt_op(struct adapter *adapter, unsigned int id, unsigned int op, + unsigned int credits); + +void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, + const struct mdio_ops *mdio_ops); +void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +#endif /* __CHELSIO_COMMON_H */ diff -puN /dev/null drivers/net/cxgb3/cxgb3_ctl_defs.h --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_ctl_defs.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#ifndef _CXGB3_OFFLOAD_CTL_DEFS_H +#define _CXGB3_OFFLOAD_CTL_DEFS_H + +enum { + GET_MAX_OUTSTANDING_WR, + GET_TX_MAX_CHUNK, + GET_TID_RANGE, + GET_STID_RANGE, + GET_RTBL_RANGE, + GET_L2T_CAPACITY, + GET_MTUS, + GET_WR_LEN, + GET_IFF_FROM_MAC, + GET_DDP_PARAMS, + GET_PORTS, + + ULP_ISCSI_GET_PARAMS, + ULP_ISCSI_SET_PARAMS, + + RDMA_GET_PARAMS, + RDMA_CQ_OP, + RDMA_CQ_SETUP, + RDMA_CQ_DISABLE, + RDMA_CTRL_QP_SETUP, + RDMA_GET_MEM, +}; + +/* + * Structure used to describe a TID range. Valid TIDs are [base, base+num). + */ +struct tid_range { + unsigned int base; /* first TID */ + unsigned int num; /* number of TIDs in range */ +}; + +/* + * Structure used to request the size and contents of the MTU table. + */ +struct mtutab { + unsigned int size; /* # of entries in the MTU table */ + const unsigned short *mtus; /* the MTU table values */ +}; + +struct net_device; + +/* + * Structure used to request the adapter net_device owning a given MAC address. + */ +struct iff_mac { + struct net_device *dev; /* the net_device */ + const unsigned char *mac_addr; /* MAC address to lookup */ + u16 vlan_tag; +}; + +struct pci_dev; + +/* + * Structure used to request the TCP DDP parameters. + */ +struct ddp_params { + unsigned int llimit; /* TDDP region start address */ + unsigned int ulimit; /* TDDP region end address */ + unsigned int tag_mask; /* TDDP tag mask */ + struct pci_dev *pdev; +}; + +struct adap_ports { + unsigned int nports; /* number of ports on this adapter */ + struct net_device *lldevs[2]; +}; + +/* + * Structure used to return information to the iscsi layer. + */ +struct ulp_iscsi_info { + unsigned int offset; + unsigned int llimit; + unsigned int ulimit; + unsigned int tagmask; + unsigned int pgsz3; + unsigned int pgsz2; + unsigned int pgsz1; + unsigned int pgsz0; + unsigned int max_rxsz; + unsigned int max_txsz; + struct pci_dev *pdev; +}; + +/* + * Structure used to return information to the RDMA layer. + */ +struct rdma_info { + unsigned int tpt_base; /* TPT base address */ + unsigned int tpt_top; /* TPT last entry address */ + unsigned int pbl_base; /* PBL base address */ + unsigned int pbl_top; /* PBL last entry address */ + unsigned int rqt_base; /* RQT base address */ + unsigned int rqt_top; /* RQT last entry address */ + unsigned int udbell_len; /* user doorbell region length */ + unsigned long udbell_physbase; /* user doorbell physical start addr */ + void __iomem *kdb_addr; /* kernel doorbell register address */ + struct pci_dev *pdev; /* associated PCI device */ +}; + +/* + * Structure used to request an operation on an RDMA completion queue. + */ +struct rdma_cq_op { + unsigned int id; + unsigned int op; + unsigned int credits; +}; + +/* + * Structure used to setup RDMA completion queues. + */ +struct rdma_cq_setup { + unsigned int id; + unsigned long long base_addr; + unsigned int size; + unsigned int credits; + unsigned int credit_thres; + unsigned int ovfl_mode; +}; + +/* + * Structure used to setup the RDMA control egress context. + */ +struct rdma_ctrlqp_setup { + unsigned long long base_addr; + unsigned int size; +}; +#endif /* _CXGB3_OFFLOAD_CTL_DEFS_H */ diff -puN /dev/null drivers/net/cxgb3/cxgb3_defs.h --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_defs.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2006 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _CHELSIO_DEFS_H +#define _CHELSIO_DEFS_H + +#include +#include + +#include "t3cdev.h" + +#include "cxgb3_offload.h" + +#define VALIDATE_TID 1 + +void *cxgb_alloc_mem(unsigned long size); +void cxgb_free_mem(void *addr); +void cxgb_neigh_update(struct neighbour *neigh); +void cxgb_redirect(struct dst_entry *old, struct dst_entry *new); + +/* + * Map an ATID or STID to their entries in the corresponding TID tables. + */ +static inline union active_open_entry *atid2entry(const struct tid_info *t, + unsigned int atid) +{ + return &t->atid_tab[atid - t->atid_base]; +} + +static inline union listen_entry *stid2entry(const struct tid_info *t, + unsigned int stid) +{ + return &t->stid_tab[stid - t->stid_base]; +} + +/* + * Find the connection corresponding to a TID. + */ +static inline struct t3c_tid_entry *lookup_tid(const struct tid_info *t, + unsigned int tid) +{ + return tid < t->ntids ? &(t->tid_tab[tid]) : NULL; +} + +/* + * Find the connection corresponding to a server TID. + */ +static inline struct t3c_tid_entry *lookup_stid(const struct tid_info *t, + unsigned int tid) +{ + if (tid < t->stid_base || tid >= t->stid_base + t->nstids) + return NULL; + return &(stid2entry(t, tid)->t3c_tid); +} + +/* + * Find the connection corresponding to an active-open TID. + */ +static inline struct t3c_tid_entry *lookup_atid(const struct tid_info *t, + unsigned int tid) +{ + if (tid < t->atid_base || tid >= t->atid_base + t->natids) + return NULL; + return &(atid2entry(t, tid)->t3c_tid); +} + +int process_rx(struct t3cdev *dev, struct sk_buff **skbs, int n); +int attach_t3cdev(struct t3cdev *dev); +void detach_t3cdev(struct t3cdev *dev); +#endif diff -puN /dev/null drivers/net/cxgb3/cxgb3_ioctl.h --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_ioctl.h @@ -0,0 +1,165 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver for Linux. + * + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#ifndef __CHIOCTL_H__ +#define __CHIOCTL_H__ + +/* + * Ioctl commands specific to this driver. + */ +enum { + CHELSIO_SETREG = 1024, + CHELSIO_GETREG, + CHELSIO_SETTPI, + CHELSIO_GETTPI, + CHELSIO_GETMTUTAB, + CHELSIO_SETMTUTAB, + CHELSIO_GETMTU, + CHELSIO_SET_PM, + CHELSIO_GET_PM, + CHELSIO_GET_TCAM, + CHELSIO_SET_TCAM, + CHELSIO_GET_TCB, + CHELSIO_GET_MEM, + CHELSIO_LOAD_FW, + CHELSIO_GET_PROTO, + CHELSIO_SET_PROTO, + CHELSIO_SET_TRACE_FILTER, + CHELSIO_SET_QSET_PARAMS, + CHELSIO_GET_QSET_PARAMS, + CHELSIO_SET_QSET_NUM, + CHELSIO_GET_QSET_NUM, + CHELSIO_SET_PKTSCHED, +}; + +struct ch_reg { + uint32_t cmd; + uint32_t addr; + uint32_t val; +}; + +struct ch_cntxt { + uint32_t cmd; + uint32_t cntxt_type; + uint32_t cntxt_id; + uint32_t data[4]; +}; + +/* context types */ +enum { CNTXT_TYPE_EGRESS, CNTXT_TYPE_FL, CNTXT_TYPE_RSP, CNTXT_TYPE_CQ }; + +struct ch_desc { + uint32_t cmd; + uint32_t queue_num; + uint32_t idx; + uint32_t size; + uint8_t data[128]; +}; + +struct ch_mem_range { + uint32_t cmd; + uint32_t mem_id; + uint32_t addr; + uint32_t len; + uint32_t version; + uint8_t buf[0]; +}; + +struct ch_qset_params { + uint32_t cmd; + uint32_t qset_idx; + int32_t txq_size[3]; + int32_t rspq_size; + int32_t fl_size[2]; + int32_t intr_lat; + int32_t polling; + int32_t cong_thres; +}; + +struct ch_pktsched_params { + uint32_t cmd; + uint8_t sched; + uint8_t idx; + uint8_t min; + uint8_t max; + uint8_t binding; +}; + +#ifndef TCB_SIZE +# define TCB_SIZE 128 +#endif + +/* TCB size in 32-bit words */ +#define TCB_WORDS (TCB_SIZE / 4) + +enum { MEM_CM, MEM_PMRX, MEM_PMTX }; /* ch_mem_range.mem_id values */ + +struct ch_mtus { + uint32_t cmd; + uint32_t nmtus; + uint16_t mtus[NMTUS]; +}; + +struct ch_pm { + uint32_t cmd; + uint32_t tx_pg_sz; + uint32_t tx_num_pg; + uint32_t rx_pg_sz; + uint32_t rx_num_pg; + uint32_t pm_total; +}; + +struct ch_tcam { + uint32_t cmd; + uint32_t tcam_size; + uint32_t nservers; + uint32_t nroutes; + uint32_t nfilters; +}; + +struct ch_tcb { + uint32_t cmd; + uint32_t tcb_index; + uint32_t tcb_data[TCB_WORDS]; +}; + +struct ch_tcam_word { + uint32_t cmd; + uint32_t addr; + uint32_t buf[3]; +}; + +struct ch_trace { + uint32_t cmd; + uint32_t sip; + uint32_t sip_mask; + uint32_t dip; + uint32_t dip_mask; + uint16_t sport; + uint16_t sport_mask; + uint16_t dport; + uint16_t dport_mask; + uint32_t vlan:12; + uint32_t vlan_mask:12; + uint32_t intf:4; + uint32_t intf_mask:4; + uint8_t proto; + uint8_t proto_mask; + uint8_t invert_match:1; + uint8_t config_tx:1; + uint8_t config_rx:1; + uint8_t trace_tx:1; + uint8_t trace_rx:1; +}; + +#define SIOCCHIOCTL SIOCDEVPRIVATE + +#endif diff -puN /dev/null drivers/net/cxgb3/cxgb3_main.c --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_main.c @@ -0,0 +1,2474 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver for Linux. + * + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "cxgb3_ioctl.h" +#include "regs.h" +#include "cxgb3_offload.h" +#include "version.h" + +#include "cxgb3_ctl_defs.h" +#include "t3_cpl.h" +#include "firmware_exports.h" + +enum { + MAX_TXQ_ENTRIES = 16384, + MAX_CTRL_TXQ_ENTRIES = 1024, + MAX_RSPQ_ENTRIES = 16384, + MAX_RX_BUFFERS = 16384, + MAX_RX_JUMBO_BUFFERS = 16384, + MIN_TXQ_ENTRIES = 4, + MIN_CTRL_TXQ_ENTRIES = 4, + MIN_RSPQ_ENTRIES = 32, + MIN_FL_ENTRIES = 32 +}; + +#define PORT_MASK ((1 << MAX_NPORTS) - 1) + +#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ + NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ + NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) + +#define EEPROM_MAGIC 0x38E2F10C + +#define to_net_dev(class) container_of(class, struct net_device, class_dev) + +#define CH_DEVICE(devid, ssid, idx) \ + { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, ssid, 0, 0, idx } + +static const struct pci_device_id cxgb3_pci_tbl[] = { + CH_DEVICE(0x20, 1, 0), /* PE9000 */ + CH_DEVICE(0x21, 1, 1), /* T302E */ + CH_DEVICE(0x22, 1, 2), /* T310E */ + CH_DEVICE(0x23, 1, 3), /* T320X */ + CH_DEVICE(0x24, 1, 1), /* T302X */ + CH_DEVICE(0x25, 1, 3), /* T320E */ + CH_DEVICE(0x26, 1, 2), /* T310X */ + CH_DEVICE(0x30, 1, 2), /* T3B10 */ + CH_DEVICE(0x31, 1, 3), /* T3B20 */ + CH_DEVICE(0x32, 1, 1), /* T3B02 */ + {0,} +}; + +MODULE_DESCRIPTION(DRV_DESC); +MODULE_AUTHOR("Chelsio Communications"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, cxgb3_pci_tbl); + +static int dflt_msg_enable = DFLT_MSG_ENABLE; + +module_param(dflt_msg_enable, int, 0644); +MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T3 default message enable bitmap"); + +/* + * The driver uses the best interrupt scheme available on a platform in the + * order MSI-X, MSI, legacy pin interrupts. This parameter determines which + * of these schemes the driver may consider as follows: + * + * msi = 2: choose from among all three options + * msi = 1: only consider MSI and pin interrupts + * msi = 0: force pin interrupts + */ +static int msi = 2; + +module_param(msi, int, 0644); +MODULE_PARM_DESC(msi, "whether to use MSI or MSI-X"); + +/* + * The driver enables offload as a default. + * To disable it, use ofld_disable = 1. + */ + +static int ofld_disable = 0; + +module_param(ofld_disable, int, 0644); +MODULE_PARM_DESC(ofld_disable, "whether to enable offload at init time or not"); + +/* + * We have work elements that we need to cancel when an interface is taken + * down. Normally the work elements would be executed by keventd but that + * can deadlock because of linkwatch. If our close method takes the rtnl + * lock and linkwatch is ahead of our work elements in keventd, linkwatch + * will block keventd as it needs the rtnl lock, and we'll deadlock waiting + * for our work to complete. Get our own work queue to solve this. + */ +static struct workqueue_struct *cxgb3_wq; + +/** + * link_report - show link status and link speed/duplex + * @p: the port whose settings are to be reported + * + * Shows the link status, speed, and duplex of a port. + */ +static void link_report(struct net_device *dev) +{ + if (!netif_carrier_ok(dev)) + printk(KERN_INFO "%s: link down\n", dev->name); + else { + const char *s = "10Mbps"; + const struct port_info *p = netdev_priv(dev); + + switch (p->link_config.speed) { + case SPEED_10000: + s = "10Gbps"; + break; + case SPEED_1000: + s = "1000Mbps"; + break; + case SPEED_100: + s = "100Mbps"; + break; + } + + printk(KERN_INFO "%s: link up, %s, %s-duplex\n", dev->name, s, + p->link_config.duplex == DUPLEX_FULL ? "full" : "half"); + } +} + +/** + * t3_os_link_changed - handle link status changes + * @adapter: the adapter associated with the link change + * @port_id: the port index whose limk status has changed + * @link_stat: the new status of the link + * @speed: the new speed setting + * @duplex: the new duplex setting + * @pause: the new flow-control setting + * + * This is the OS-dependent handler for link status changes. The OS + * neutral handler takes care of most of the processing for these events, + * then calls this handler for any OS-specific processing. + */ +void t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, + int speed, int duplex, int pause) +{ + struct net_device *dev = adapter->port[port_id]; + + /* Skip changes from disabled ports. */ + if (!netif_running(dev)) + return; + + if (link_stat != netif_carrier_ok(dev)) { + if (link_stat) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + link_report(dev); + } +} + +static void cxgb_set_rxmode(struct net_device *dev) +{ + struct t3_rx_mode rm; + struct port_info *pi = netdev_priv(dev); + + init_rx_mode(&rm, dev, dev->mc_list); + t3_mac_set_rx_mode(&pi->mac, &rm); +} + +/** + * link_start - enable a port + * @dev: the device to enable + * + * Performs the MAC and PHY actions needed to enable a port. + */ +static void link_start(struct net_device *dev) +{ + struct t3_rx_mode rm; + struct port_info *pi = netdev_priv(dev); + struct cmac *mac = &pi->mac; + + init_rx_mode(&rm, dev, dev->mc_list); + t3_mac_reset(mac); + t3_mac_set_mtu(mac, dev->mtu); + t3_mac_set_address(mac, 0, dev->dev_addr); + t3_mac_set_rx_mode(mac, &rm); + t3_link_start(&pi->phy, mac, &pi->link_config); + t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); +} + +static inline void cxgb_disable_msi(struct adapter *adapter) +{ + if (adapter->flags & USING_MSIX) { + pci_disable_msix(adapter->pdev); + adapter->flags &= ~USING_MSIX; + } else if (adapter->flags & USING_MSI) { + pci_disable_msi(adapter->pdev); + adapter->flags &= ~USING_MSI; + } +} + +/* + * Interrupt handler for asynchronous events used with MSI-X. + */ +static irqreturn_t t3_async_intr_handler(int irq, void *cookie) +{ + t3_slow_intr_handler(cookie); + return IRQ_HANDLED; +} + +/* + * Name the MSI-X interrupts. + */ +static void name_msix_vecs(struct adapter *adap) +{ + int i, j, msi_idx = 1, n = sizeof(adap->msix_info[0].desc) - 1; + + snprintf(adap->msix_info[0].desc, n, "%s", adap->name); + adap->msix_info[0].desc[n] = 0; + + for_each_port(adap, j) { + struct net_device *d = adap->port[j]; + const struct port_info *pi = netdev_priv(d); + + for (i = 0; i < pi->nqsets; i++, msi_idx++) { + snprintf(adap->msix_info[msi_idx].desc, n, + "%s (queue %d)", d->name, i); + adap->msix_info[msi_idx].desc[n] = 0; + } + } +} + +static int request_msix_data_irqs(struct adapter *adap) +{ + int i, j, err, qidx = 0; + + for_each_port(adap, i) { + int nqsets = adap2pinfo(adap, i)->nqsets; + + for (j = 0; j < nqsets; ++j) { + err = request_irq(adap->msix_info[qidx + 1].vec, + t3_intr_handler(adap, + adap->sge.qs[qidx]. + rspq.polling), 0, + adap->msix_info[qidx + 1].desc, + &adap->sge.qs[qidx]); + if (err) { + while (--qidx >= 0) + free_irq(adap->msix_info[qidx + 1].vec, + &adap->sge.qs[qidx]); + return err; + } + qidx++; + } + } + return 0; +} + +/** + * setup_rss - configure RSS + * @adap: the adapter + * + * Sets up RSS to distribute packets to multiple receive queues. We + * configure the RSS CPU lookup table to distribute to the number of HW + * receive queues, and the response queue lookup table to narrow that + * down to the response queues actually configured for each port. + * We always configure the RSS mapping for two ports since the mapping + * table has plenty of entries. + */ +static void setup_rss(struct adapter *adap) +{ + int i; + unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; + unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; + u8 cpus[SGE_QSETS + 1]; + u16 rspq_map[RSS_TABLE_SIZE]; + + for (i = 0; i < SGE_QSETS; ++i) + cpus[i] = i; + cpus[SGE_QSETS] = 0xff; /* terminator */ + + for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) { + rspq_map[i] = i % nq0; + rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; + } + + t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | + F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | + V_RRCPLCPUSIZE(6), cpus, rspq_map); +} + +/* + * If we have multiple receive queues per port serviced by NAPI we need one + * netdevice per queue as NAPI operates on netdevices. We already have one + * netdevice, namely the one associated with the interface, so we use dummy + * ones for any additional queues. Note that these netdevices exist purely + * so that NAPI has something to work with, they do not represent network + * ports and are not registered. + */ +static int init_dummy_netdevs(struct adapter *adap) +{ + int i, j, dummy_idx = 0; + struct net_device *nd; + + for_each_port(adap, i) { + struct net_device *dev = adap->port[i]; + const struct port_info *pi = netdev_priv(dev); + + for (j = 0; j < pi->nqsets - 1; j++) { + if (!adap->dummy_netdev[dummy_idx]) { + nd = alloc_netdev(0, "", ether_setup); + if (!nd) + goto free_all; + + nd->priv = adap; + nd->weight = 64; + set_bit(__LINK_STATE_START, &nd->state); + adap->dummy_netdev[dummy_idx] = nd; + } + strcpy(adap->dummy_netdev[dummy_idx]->name, dev->name); + dummy_idx++; + } + } + return 0; + +free_all: + while (--dummy_idx >= 0) { + free_netdev(adap->dummy_netdev[dummy_idx]); + adap->dummy_netdev[dummy_idx] = NULL; + } + return -ENOMEM; +} + +/* + * Wait until all NAPI handlers are descheduled. This includes the handlers of + * both netdevices representing interfaces and the dummy ones for the extra + * queues. + */ +static void quiesce_rx(struct adapter *adap) +{ + int i; + struct net_device *dev; + + for_each_port(adap, i) { + dev = adap->port[i]; + while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) + msleep(1); + } + + for (i = 0; i < ARRAY_SIZE(adap->dummy_netdev); i++) { + dev = adap->dummy_netdev[i]; + if (dev) + while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) + msleep(1); + } +} + +/** + * setup_sge_qsets - configure SGE Tx/Rx/response queues + * @adap: the adapter + * + * Determines how many sets of SGE queues to use and initializes them. + * We support multiple queue sets per port if we have MSI-X, otherwise + * just one queue set per port. + */ +static int setup_sge_qsets(struct adapter *adap) +{ + int i, j, err, irq_idx = 0, qset_idx = 0, dummy_dev_idx = 0; + unsigned int ntxq = is_offload(adap) ? SGE_TXQ_PER_SET : 1; + + if (adap->params.rev > 0 && !(adap->flags & USING_MSI)) + irq_idx = -1; + + for_each_port(adap, i) { + struct net_device *dev = adap->port[i]; + const struct port_info *pi = netdev_priv(dev); + + for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { + err = t3_sge_alloc_qset(adap, qset_idx, 1, + (adap->flags & USING_MSIX) ? qset_idx + 1 : + irq_idx, + &adap->params.sge.qset[qset_idx], ntxq, + j == 0 ? dev : + adap-> dummy_netdev[dummy_dev_idx++]); + if (err) { + t3_free_sge_resources(adap); + return err; + } + } + } + + return 0; +} + +static ssize_t attr_show(struct class_device *cd, char *buf, + ssize_t(*format) (struct adapter *, char *)) +{ + ssize_t len; + struct adapter *adap = to_net_dev(cd)->priv; + + /* Synchronize with ioctls that may shut down the device */ + rtnl_lock(); + len = (*format) (adap, buf); + rtnl_unlock(); + return len; +} + +static ssize_t attr_store(struct class_device *cd, const char *buf, size_t len, + ssize_t(*set) (struct adapter *, unsigned int), + unsigned int min_val, unsigned int max_val) +{ + char *endp; + ssize_t ret; + unsigned int val; + struct adapter *adap = to_net_dev(cd)->priv; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + val = simple_strtoul(buf, &endp, 0); + if (endp == buf || val < min_val || val > max_val) + return -EINVAL; + + rtnl_lock(); + ret = (*set) (adap, val); + if (!ret) + ret = len; + rtnl_unlock(); + return ret; +} + +#define CXGB3_SHOW(name, val_expr) \ +static ssize_t format_##name(struct adapter *adap, char *buf) \ +{ \ + return sprintf(buf, "%u\n", val_expr); \ +} \ +static ssize_t show_##name(struct class_device *cd, char *buf) \ +{ \ + return attr_show(cd, buf, format_##name); \ +} + +static ssize_t set_nfilters(struct adapter *adap, unsigned int val) +{ + if (adap->flags & FULL_INIT_DONE) + return -EBUSY; + if (val && adap->params.rev == 0) + return -EINVAL; + if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers) + return -EINVAL; + adap->params.mc5.nfilters = val; + return 0; +} + +static ssize_t store_nfilters(struct class_device *cd, const char *buf, + size_t len) +{ + return attr_store(cd, buf, len, set_nfilters, 0, ~0); +} + +static ssize_t set_nservers(struct adapter *adap, unsigned int val) +{ + if (adap->flags & FULL_INIT_DONE) + return -EBUSY; + if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters) + return -EINVAL; + adap->params.mc5.nservers = val; + return 0; +} + +static ssize_t store_nservers(struct class_device *cd, const char *buf, + size_t len) +{ + return attr_store(cd, buf, len, set_nservers, 0, ~0); +} + +#define CXGB3_ATTR_R(name, val_expr) \ +CXGB3_SHOW(name, val_expr) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +#define CXGB3_ATTR_RW(name, val_expr, store_method) \ +CXGB3_SHOW(name, val_expr) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_method) + +CXGB3_ATTR_R(cam_size, t3_mc5_size(&adap->mc5)); +CXGB3_ATTR_RW(nfilters, adap->params.mc5.nfilters, store_nfilters); +CXGB3_ATTR_RW(nservers, adap->params.mc5.nservers, store_nservers); + +static struct attribute *cxgb3_attrs[] = { + &class_device_attr_cam_size.attr, + &class_device_attr_nfilters.attr, + &class_device_attr_nservers.attr, + NULL +}; + +static struct attribute_group cxgb3_attr_group = {.attrs = cxgb3_attrs }; + +static ssize_t tm_attr_show(struct class_device *cd, char *buf, int sched) +{ + ssize_t len; + unsigned int v, addr, bpt, cpt; + struct adapter *adap = to_net_dev(cd)->priv; + + addr = A_TP_TX_MOD_Q1_Q0_RATE_LIMIT - sched / 2; + rtnl_lock(); + t3_write_reg(adap, A_TP_TM_PIO_ADDR, addr); + v = t3_read_reg(adap, A_TP_TM_PIO_DATA); + if (sched & 1) + v >>= 16; + bpt = (v >> 8) & 0xff; + cpt = v & 0xff; + if (!cpt) + len = sprintf(buf, "disabled\n"); + else { + v = (adap->params.vpd.cclk * 1000) / cpt; + len = sprintf(buf, "%u Kbps\n", (v * bpt) / 125); + } + rtnl_unlock(); + return len; +} + +static ssize_t tm_attr_store(struct class_device *cd, const char *buf, + size_t len, int sched) +{ + char *endp; + ssize_t ret; + unsigned int val; + struct adapter *adap = to_net_dev(cd)->priv; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + val = simple_strtoul(buf, &endp, 0); + if (endp == buf || val > 10000000) + return -EINVAL; + + rtnl_lock(); + ret = t3_config_sched(adap, val, sched); + if (!ret) + ret = len; + rtnl_unlock(); + return ret; +} + +#define TM_ATTR(name, sched) \ +static ssize_t show_##name(struct class_device *cd, char *buf) \ +{ \ + return tm_attr_show(cd, buf, sched); \ +} \ +static ssize_t store_##name(struct class_device *cd, const char *buf, size_t len) \ +{ \ + return tm_attr_store(cd, buf, len, sched); \ +} \ +static CLASS_DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name) + +TM_ATTR(sched0, 0); +TM_ATTR(sched1, 1); +TM_ATTR(sched2, 2); +TM_ATTR(sched3, 3); +TM_ATTR(sched4, 4); +TM_ATTR(sched5, 5); +TM_ATTR(sched6, 6); +TM_ATTR(sched7, 7); + +static struct attribute *offload_attrs[] = { + &class_device_attr_sched0.attr, + &class_device_attr_sched1.attr, + &class_device_attr_sched2.attr, + &class_device_attr_sched3.attr, + &class_device_attr_sched4.attr, + &class_device_attr_sched5.attr, + &class_device_attr_sched6.attr, + &class_device_attr_sched7.attr, + NULL +}; + +static struct attribute_group offload_attr_group = {.attrs = offload_attrs }; + +/* + * Sends an sk_buff to an offload queue driver + * after dealing with any active network taps. + */ +static inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb) +{ + int ret; + + local_bh_disable(); + ret = t3_offload_tx(tdev, skb); + local_bh_enable(); + return ret; +} + +static int write_smt_entry(struct adapter *adapter, int idx) +{ + struct cpl_smt_write_req *req; + struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL); + + if (!skb) + return -ENOMEM; + + req = (struct cpl_smt_write_req *)__skb_put(skb, sizeof(*req)); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx)); + req->mtu_idx = NMTUS - 1; /* should be 0 but there's a T3 bug */ + req->iff = idx; + memset(req->src_mac1, 0, sizeof(req->src_mac1)); + memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN); + skb->priority = 1; + offload_tx(&adapter->tdev, skb); + return 0; +} + +static int init_smt(struct adapter *adapter) +{ + int i; + + for_each_port(adapter, i) + write_smt_entry(adapter, i); + return 0; +} + +static void init_port_mtus(struct adapter *adapter) +{ + unsigned int mtus = adapter->port[0]->mtu; + + if (adapter->port[1]) + mtus |= adapter->port[1]->mtu << 16; + t3_write_reg(adapter, A_TP_MTU_PORT_TABLE, mtus); +} + +/** + * cxgb_up - enable the adapter + * @adapter: adapter being enabled + * + * Called when the first port is enabled, this function performs the + * actions necessary to make an adapter operational, such as completing + * the initialization of HW modules, and enabling interrupts. + * + * Must be called with the rtnl lock held. + */ +static int cxgb_up(struct adapter *adap) +{ + int err = 0; + + if (!(adap->flags & FULL_INIT_DONE)) { + err = t3_check_fw_version(adap); + if (err) { + dev_err(&adap->pdev->dev, + "adapter FW is not compatible with driver\n"); + goto out; + } + + err = init_dummy_netdevs(adap); + if (err) + goto out; + + err = t3_init_hw(adap, 0); + if (err) + goto out; + + err = setup_sge_qsets(adap); + if (err) + goto out; + + setup_rss(adap); + adap->flags |= FULL_INIT_DONE; + } + + t3_intr_clear(adap); + + if (adap->flags & USING_MSIX) { + name_msix_vecs(adap); + err = request_irq(adap->msix_info[0].vec, + t3_async_intr_handler, 0, + adap->msix_info[0].desc, adap); + if (err) + goto irq_err; + + if (request_msix_data_irqs(adap)) { + free_irq(adap->msix_info[0].vec, adap); + goto irq_err; + } + } else if ((err = request_irq(adap->pdev->irq, + t3_intr_handler(adap, + adap->sge.qs[0].rspq. + polling), + (adap->flags & USING_MSI) ? 0 : SA_SHIRQ, + adap->name, adap))) + goto irq_err; + + t3_sge_start(adap); + t3_intr_enable(adap); +out: + return err; +irq_err: + CH_ERR(adap, "request_irq failed, err %d\n", err); + goto out; +} + +/* + * Release resources when all the ports and offloading have been stopped. + */ +static void cxgb_down(struct adapter *adapter) +{ + t3_sge_stop(adapter); + spin_lock_irq(&adapter->work_lock); /* sync with PHY intr task */ + t3_intr_disable(adapter); + spin_unlock_irq(&adapter->work_lock); + + if (adapter->flags & USING_MSIX) { + int i, n = 0; + + free_irq(adapter->msix_info[0].vec, adapter); + for_each_port(adapter, i) + n += adap2pinfo(adapter, i)->nqsets; + + for (i = 0; i < n; ++i) + free_irq(adapter->msix_info[i + 1].vec, + &adapter->sge.qs[i]); + } else + free_irq(adapter->pdev->irq, adapter); + + flush_workqueue(cxgb3_wq); /* wait for external IRQ handler */ + quiesce_rx(adapter); +} + +static void schedule_chk_task(struct adapter *adap) +{ + unsigned int timeo; + + timeo = adap->params.linkpoll_period ? + (HZ * adap->params.linkpoll_period) / 10 : + adap->params.stats_update_period * HZ; + if (timeo) + queue_delayed_work(cxgb3_wq, &adap->adap_check_task, timeo); +} + +static int offload_open(struct net_device *dev) +{ + struct adapter *adapter = dev->priv; + struct t3cdev *tdev = T3CDEV(dev); + int adap_up = adapter->open_device_map & PORT_MASK; + int err = 0; + + if (test_and_set_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) + return 0; + + if (!adap_up && (err = cxgb_up(adapter)) < 0) + return err; + + t3_tp_set_offload_mode(adapter, 1); + tdev->lldev = adapter->port[0]; + err = cxgb3_offload_activate(adapter); + if (err) + goto out; + + init_port_mtus(adapter); + t3_load_mtus(adapter, adapter->params.mtus, adapter->params.a_wnd, + adapter->params.b_wnd, + adapter->params.rev == 0 ? + adapter->port[0]->mtu : 0xffff); + init_smt(adapter); + + /* Never mind if the next step fails */ + sysfs_create_group(&tdev->lldev->class_dev.kobj, &offload_attr_group); + + /* Call back all registered clients */ + cxgb3_add_clients(tdev); + +out: + /* restore them in case the offload module has changed them */ + if (err) { + t3_tp_set_offload_mode(adapter, 0); + clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); + cxgb3_set_dummy_ops(tdev); + } + return err; +} + +static int offload_close(struct t3cdev *tdev) +{ + struct adapter *adapter = tdev2adap(tdev); + + if (!test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) + return 0; + + /* Call back all registered clients */ + cxgb3_remove_clients(tdev); + + sysfs_remove_group(&tdev->lldev->class_dev.kobj, &offload_attr_group); + + tdev->lldev = NULL; + cxgb3_set_dummy_ops(tdev); + t3_tp_set_offload_mode(adapter, 0); + clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); + + if (!adapter->open_device_map) + cxgb_down(adapter); + + cxgb3_offload_deactivate(adapter); + return 0; +} + +static int cxgb_open(struct net_device *dev) +{ + int err; + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + int other_ports = adapter->open_device_map & PORT_MASK; + + if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) + return err; + + set_bit(pi->port_id, &adapter->open_device_map); + if (!ofld_disable) { + err = offload_open(dev); + if (err) + printk(KERN_WARNING + "Could not initialize offload capabilities\n"); + } + + link_start(dev); + t3_port_intr_enable(adapter, pi->port_id); + netif_start_queue(dev); + if (!other_ports) + schedule_chk_task(adapter); + + return 0; +} + +static int cxgb_close(struct net_device *dev) +{ + struct adapter *adapter = dev->priv; + struct port_info *p = netdev_priv(dev); + + t3_port_intr_disable(adapter, p->port_id); + netif_stop_queue(dev); + p->phy.ops->power_down(&p->phy, 1); + netif_carrier_off(dev); + t3_mac_disable(&p->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); + + spin_lock(&adapter->work_lock); /* sync with update task */ + clear_bit(p->port_id, &adapter->open_device_map); + spin_unlock(&adapter->work_lock); + + if (!(adapter->open_device_map & PORT_MASK)) + cancel_rearming_delayed_workqueue(cxgb3_wq, + &adapter->adap_check_task); + + if (!adapter->open_device_map) + cxgb_down(adapter); + + return 0; +} + +static struct net_device_stats *cxgb_get_stats(struct net_device *dev) +{ + struct adapter *adapter = dev->priv; + struct port_info *p = netdev_priv(dev); + struct net_device_stats *ns = &p->netstats; + const struct mac_stats *pstats; + + spin_lock(&adapter->stats_lock); + pstats = t3_mac_update_stats(&p->mac); + spin_unlock(&adapter->stats_lock); + + ns->tx_bytes = pstats->tx_octets; + ns->tx_packets = pstats->tx_frames; + ns->rx_bytes = pstats->rx_octets; + ns->rx_packets = pstats->rx_frames; + ns->multicast = pstats->rx_mcast_frames; + + ns->tx_errors = pstats->tx_underrun; + ns->rx_errors = pstats->rx_symbol_errs + pstats->rx_fcs_errs + + pstats->rx_too_long + pstats->rx_jabber + pstats->rx_short + + pstats->rx_fifo_ovfl; + + /* detailed rx_errors */ + ns->rx_length_errors = pstats->rx_jabber + pstats->rx_too_long; + ns->rx_over_errors = 0; + ns->rx_crc_errors = pstats->rx_fcs_errs; + ns->rx_frame_errors = pstats->rx_symbol_errs; + ns->rx_fifo_errors = pstats->rx_fifo_ovfl; + ns->rx_missed_errors = pstats->rx_cong_drops; + + /* detailed tx_errors */ + ns->tx_aborted_errors = 0; + ns->tx_carrier_errors = 0; + ns->tx_fifo_errors = pstats->tx_underrun; + ns->tx_heartbeat_errors = 0; + ns->tx_window_errors = 0; + return ns; +} + +static u32 get_msglevel(struct net_device *dev) +{ + struct adapter *adapter = dev->priv; + + return adapter->msg_enable; +} + +static void set_msglevel(struct net_device *dev, u32 val) +{ + struct adapter *adapter = dev->priv; + + adapter->msg_enable = val; +} + +static char stats_strings[][ETH_GSTRING_LEN] = { + "TxOctetsOK ", + "TxFramesOK ", + "TxMulticastFramesOK", + "TxBroadcastFramesOK", + "TxPauseFrames ", + "TxUnderrun ", + "TxExtUnderrun ", + + "TxFrames64 ", + "TxFrames65To127 ", + "TxFrames128To255 ", + "TxFrames256To511 ", + "TxFrames512To1023 ", + "TxFrames1024To1518 ", + "TxFrames1519ToMax ", + + "RxOctetsOK ", + "RxFramesOK ", + "RxMulticastFramesOK", + "RxBroadcastFramesOK", + "RxPauseFrames ", + "RxFCSErrors ", + "RxSymbolErrors ", + "RxShortErrors ", + "RxJabberErrors ", + "RxLengthErrors ", + "RxFIFOoverflow ", + + "RxFrames64 ", + "RxFrames65To127 ", + "RxFrames128To255 ", + "RxFrames256To511 ", + "RxFrames512To1023 ", + "RxFrames1024To1518 ", + "RxFrames1519ToMax ", + + "PhyFIFOErrors ", + "TSO ", + "VLANextractions ", + "VLANinsertions ", + "TxCsumOffload ", + "RxCsumGood ", + "RxDrops " +}; + +static int get_stats_count(struct net_device *dev) +{ + return ARRAY_SIZE(stats_strings); +} + +#define T3_REGMAP_SIZE (3 * 1024) + +static int get_regs_len(struct net_device *dev) +{ + return T3_REGMAP_SIZE; +} + +static int get_eeprom_len(struct net_device *dev) +{ + return EEPROMSIZE; +} + +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + u32 fw_vers = 0; + struct adapter *adapter = dev->priv; + + t3_get_fw_version(adapter, &fw_vers); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(adapter->pdev)); + if (!fw_vers) + strcpy(info->fw_version, "N/A"); + else + snprintf(info->fw_version, sizeof(info->fw_version), + "%s %u.%u", (fw_vers >> 24) ? "T" : "N", + (fw_vers >> 12) & 0xfff, fw_vers & 0xfff); +} + +static void get_strings(struct net_device *dev, u32 stringset, u8 * data) +{ + if (stringset == ETH_SS_STATS) + memcpy(data, stats_strings, sizeof(stats_strings)); +} + +static unsigned long collect_sge_port_stats(struct adapter *adapter, + struct port_info *p, int idx) +{ + int i; + unsigned long tot = 0; + + for (i = 0; i < p->nqsets; ++i) + tot += adapter->sge.qs[i + p->first_qset].port_stats[idx]; + return tot; +} + +static void get_stats(struct net_device *dev, struct ethtool_stats *stats, + u64 *data) +{ + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + const struct mac_stats *s; + + spin_lock(&adapter->stats_lock); + s = t3_mac_update_stats(&pi->mac); + spin_unlock(&adapter->stats_lock); + + *data++ = s->tx_octets; + *data++ = s->tx_frames; + *data++ = s->tx_mcast_frames; + *data++ = s->tx_bcast_frames; + *data++ = s->tx_pause; + *data++ = s->tx_underrun; + *data++ = s->tx_fifo_urun; + + *data++ = s->tx_frames_64; + *data++ = s->tx_frames_65_127; + *data++ = s->tx_frames_128_255; + *data++ = s->tx_frames_256_511; + *data++ = s->tx_frames_512_1023; + *data++ = s->tx_frames_1024_1518; + *data++ = s->tx_frames_1519_max; + + *data++ = s->rx_octets; + *data++ = s->rx_frames; + *data++ = s->rx_mcast_frames; + *data++ = s->rx_bcast_frames; + *data++ = s->rx_pause; + *data++ = s->rx_fcs_errs; + *data++ = s->rx_symbol_errs; + *data++ = s->rx_short; + *data++ = s->rx_jabber; + *data++ = s->rx_too_long; + *data++ = s->rx_fifo_ovfl; + + *data++ = s->rx_frames_64; + *data++ = s->rx_frames_65_127; + *data++ = s->rx_frames_128_255; + *data++ = s->rx_frames_256_511; + *data++ = s->rx_frames_512_1023; + *data++ = s->rx_frames_1024_1518; + *data++ = s->rx_frames_1519_max; + + *data++ = pi->phy.fifo_errors; + + *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TSO); + *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANEX); + *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); + *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); + *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); + *data++ = s->rx_cong_drops; +} + +static inline void reg_block_dump(struct adapter *ap, void *buf, + unsigned int start, unsigned int end) +{ + u32 *p = buf + start; + + for (; start <= end; start += sizeof(u32)) + *p++ = t3_read_reg(ap, start); +} + +static void get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *buf) +{ + struct adapter *ap = dev->priv; + + /* + * Version scheme: + * bits 0..9: chip version + * bits 10..15: chip revision + * bit 31: set for PCIe cards + */ + regs->version = 3 | (ap->params.rev << 10) | (is_pcie(ap) << 31); + + /* + * We skip the MAC statistics registers because they are clear-on-read. + * Also reading multi-register stats would need to synchronize with the + * periodic mac stats accumulation. Hard to justify the complexity. + */ + memset(buf, 0, T3_REGMAP_SIZE); + reg_block_dump(ap, buf, 0, A_SG_RSPQ_CREDIT_RETURN); + reg_block_dump(ap, buf, A_SG_HI_DRB_HI_THRSH, A_ULPRX_PBL_ULIMIT); + reg_block_dump(ap, buf, A_ULPTX_CONFIG, A_MPS_INT_CAUSE); + reg_block_dump(ap, buf, A_CPL_SWITCH_CNTRL, A_CPL_MAP_TBL_DATA); + reg_block_dump(ap, buf, A_SMB_GLOBAL_TIME_CFG, A_XGM_SERDES_STAT3); + reg_block_dump(ap, buf, A_XGM_SERDES_STATUS0, + XGM_REG(A_XGM_SERDES_STAT3, 1)); + reg_block_dump(ap, buf, XGM_REG(A_XGM_SERDES_STATUS0, 1), + XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1)); +} + +static int restart_autoneg(struct net_device *dev) +{ + struct port_info *p = netdev_priv(dev); + + if (!netif_running(dev)) + return -EAGAIN; + if (p->link_config.autoneg != AUTONEG_ENABLE) + return -EINVAL; + p->phy.ops->autoneg_restart(&p->phy); + return 0; +} + +static int cxgb3_phys_id(struct net_device *dev, u32 data) +{ + int i; + struct adapter *adapter = dev->priv; + + if (data == 0) + data = 2; + + for (i = 0; i < data * 2; i++) { + t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, + (i & 1) ? F_GPIO0_OUT_VAL : 0); + if (msleep_interruptible(500)) + break; + } + t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, + F_GPIO0_OUT_VAL); + return 0; +} + +static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct port_info *p = netdev_priv(dev); + + cmd->supported = p->link_config.supported; + cmd->advertising = p->link_config.advertising; + + if (netif_carrier_ok(dev)) { + cmd->speed = p->link_config.speed; + cmd->duplex = p->link_config.duplex; + } else { + cmd->speed = -1; + cmd->duplex = -1; + } + + cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; + cmd->phy_address = p->phy.addr; + cmd->transceiver = XCVR_EXTERNAL; + cmd->autoneg = p->link_config.autoneg; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + return 0; +} + +static int speed_duplex_to_caps(int speed, int duplex) +{ + int cap = 0; + + switch (speed) { + case SPEED_10: + if (duplex == DUPLEX_FULL) + cap = SUPPORTED_10baseT_Full; + else + cap = SUPPORTED_10baseT_Half; + break; + case SPEED_100: + if (duplex == DUPLEX_FULL) + cap = SUPPORTED_100baseT_Full; + else + cap = SUPPORTED_100baseT_Half; + break; + case SPEED_1000: + if (duplex == DUPLEX_FULL) + cap = SUPPORTED_1000baseT_Full; + else + cap = SUPPORTED_1000baseT_Half; + break; + case SPEED_10000: + if (duplex == DUPLEX_FULL) + cap = SUPPORTED_10000baseT_Full; + } + return cap; +} + +#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \ + ADVERTISED_10000baseT_Full) + +static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct port_info *p = netdev_priv(dev); + struct link_config *lc = &p->link_config; + + if (!(lc->supported & SUPPORTED_Autoneg)) + return -EOPNOTSUPP; /* can't change speed/duplex */ + + if (cmd->autoneg == AUTONEG_DISABLE) { + int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex); + + if (!(lc->supported & cap) || cmd->speed == SPEED_1000) + return -EINVAL; + lc->requested_speed = cmd->speed; + lc->requested_duplex = cmd->duplex; + lc->advertising = 0; + } else { + cmd->advertising &= ADVERTISED_MASK; + cmd->advertising &= lc->supported; + if (!cmd->advertising) + return -EINVAL; + lc->requested_speed = SPEED_INVALID; + lc->requested_duplex = DUPLEX_INVALID; + lc->advertising = cmd->advertising | ADVERTISED_Autoneg; + } + lc->autoneg = cmd->autoneg; + if (netif_running(dev)) + t3_link_start(&p->phy, &p->mac, lc); + return 0; +} + +static void get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct port_info *p = netdev_priv(dev); + + epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0; + epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0; + epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0; +} + +static int set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct port_info *p = netdev_priv(dev); + struct link_config *lc = &p->link_config; + + if (epause->autoneg == AUTONEG_DISABLE) + lc->requested_fc = 0; + else if (lc->supported & SUPPORTED_Autoneg) + lc->requested_fc = PAUSE_AUTONEG; + else + return -EINVAL; + + if (epause->rx_pause) + lc->requested_fc |= PAUSE_RX; + if (epause->tx_pause) + lc->requested_fc |= PAUSE_TX; + if (lc->autoneg == AUTONEG_ENABLE) { + if (netif_running(dev)) + t3_link_start(&p->phy, &p->mac, lc); + } else { + lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + if (netif_running(dev)) + t3_mac_set_speed_duplex_fc(&p->mac, -1, -1, lc->fc); + } + return 0; +} + +static u32 get_rx_csum(struct net_device *dev) +{ + struct port_info *p = netdev_priv(dev); + + return p->rx_csum_offload; +} + +static int set_rx_csum(struct net_device *dev, u32 data) +{ + struct port_info *p = netdev_priv(dev); + + p->rx_csum_offload = data; + return 0; +} + +static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ + struct adapter *adapter = dev->priv; + + e->rx_max_pending = MAX_RX_BUFFERS; + e->rx_mini_max_pending = 0; + e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS; + e->tx_max_pending = MAX_TXQ_ENTRIES; + + e->rx_pending = adapter->params.sge.qset[0].fl_size; + e->rx_mini_pending = adapter->params.sge.qset[0].rspq_size; + e->rx_jumbo_pending = adapter->params.sge.qset[0].jumbo_size; + e->tx_pending = adapter->params.sge.qset[0].txq_size[0]; +} + +static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ + int i; + struct adapter *adapter = dev->priv; + + if (e->rx_pending > MAX_RX_BUFFERS || + e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS || + e->tx_pending > MAX_TXQ_ENTRIES || + e->rx_mini_pending > MAX_RSPQ_ENTRIES || + e->rx_mini_pending < MIN_RSPQ_ENTRIES || + e->rx_pending < MIN_FL_ENTRIES || + e->rx_jumbo_pending < MIN_FL_ENTRIES || + e->tx_pending < adapter->params.nports * MIN_TXQ_ENTRIES) + return -EINVAL; + + if (adapter->flags & FULL_INIT_DONE) + return -EBUSY; + + for (i = 0; i < SGE_QSETS; ++i) { + struct qset_params *q = &adapter->params.sge.qset[i]; + + q->rspq_size = e->rx_mini_pending; + q->fl_size = e->rx_pending; + q->jumbo_size = e->rx_jumbo_pending; + q->txq_size[0] = e->tx_pending; + q->txq_size[1] = e->tx_pending; + q->txq_size[2] = e->tx_pending; + } + return 0; +} + +static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ + struct adapter *adapter = dev->priv; + struct qset_params *qsp = &adapter->params.sge.qset[0]; + struct sge_qset *qs = &adapter->sge.qs[0]; + + if (c->rx_coalesce_usecs * 10 > M_NEWTIMER) + return -EINVAL; + + qsp->coalesce_usecs = c->rx_coalesce_usecs; + t3_update_qset_coalesce(qs, qsp); + return 0; +} + +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ + struct adapter *adapter = dev->priv; + struct qset_params *q = adapter->params.sge.qset; + + c->rx_coalesce_usecs = q->coalesce_usecs; + return 0; +} + +static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, + u8 * data) +{ + int i, err = 0; + struct adapter *adapter = dev->priv; + + u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + e->magic = EEPROM_MAGIC; + for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) + err = t3_seeprom_read(adapter, i, (u32 *) & buf[i]); + + if (!err) + memcpy(data, buf + e->offset, e->len); + kfree(buf); + return err; +} + +static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, + u8 * data) +{ + u8 *buf; + int err = 0; + u32 aligned_offset, aligned_len, *p; + struct adapter *adapter = dev->priv; + + if (eeprom->magic != EEPROM_MAGIC) + return -EINVAL; + + aligned_offset = eeprom->offset & ~3; + aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; + + if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { + buf = kmalloc(aligned_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + err = t3_seeprom_read(adapter, aligned_offset, (u32 *) buf); + if (!err && aligned_len > 4) + err = t3_seeprom_read(adapter, + aligned_offset + aligned_len - 4, + (u32 *) & buf[aligned_len - 4]); + if (err) + goto out; + memcpy(buf + (eeprom->offset & 3), data, eeprom->len); + } else + buf = data; + + err = t3_seeprom_wp(adapter, 0); + if (err) + goto out; + + for (p = (u32 *) buf; !err && aligned_len; aligned_len -= 4, p++) { + err = t3_seeprom_write(adapter, aligned_offset, *p); + aligned_offset += 4; + } + + if (!err) + err = t3_seeprom_wp(adapter, 1); +out: + if (buf != data) + kfree(buf); + return err; +} + +static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static const struct ethtool_ops cxgb_ethtool_ops = { + .get_settings = get_settings, + .set_settings = set_settings, + .get_drvinfo = get_drvinfo, + .get_msglevel = get_msglevel, + .set_msglevel = set_msglevel, + .get_ringparam = get_sge_param, + .set_ringparam = set_sge_param, + .get_coalesce = get_coalesce, + .set_coalesce = set_coalesce, + .get_eeprom_len = get_eeprom_len, + .get_eeprom = get_eeprom, + .set_eeprom = set_eeprom, + .get_pauseparam = get_pauseparam, + .set_pauseparam = set_pauseparam, + .get_rx_csum = get_rx_csum, + .set_rx_csum = set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_link = ethtool_op_get_link, + .get_strings = get_strings, + .phys_id = cxgb3_phys_id, + .nway_reset = restart_autoneg, + .get_stats_count = get_stats_count, + .get_ethtool_stats = get_stats, + .get_regs_len = get_regs_len, + .get_regs = get_regs, + .get_wol = get_wol, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, + .get_perm_addr = ethtool_op_get_perm_addr +}; + +static int in_range(int val, int lo, int hi) +{ + return val < 0 || (val <= hi && val >= lo); +} + +static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) +{ + int ret; + u32 cmd; + struct adapter *adapter = dev->priv; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + + switch (cmd) { + case CHELSIO_SETREG:{ + struct ch_reg edata; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if ((edata.addr & 3) != 0 + || edata.addr >= adapter->mmio_len) + return -EINVAL; + writel(edata.val, adapter->regs + edata.addr); + break; + } + case CHELSIO_GETREG:{ + struct ch_reg edata; + + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if ((edata.addr & 3) != 0 + || edata.addr >= adapter->mmio_len) + return -EINVAL; + edata.val = readl(adapter->regs + edata.addr); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + break; + } + case CHELSIO_SET_QSET_PARAMS:{ + int i; + struct qset_params *q; + struct ch_qset_params t; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&t, useraddr, sizeof(t))) + return -EFAULT; + if (t.qset_idx >= SGE_QSETS) + return -EINVAL; + if (!in_range(t.intr_lat, 0, M_NEWTIMER) || + !in_range(t.cong_thres, 0, 255) || + !in_range(t.txq_size[0], MIN_TXQ_ENTRIES, + MAX_TXQ_ENTRIES) || + !in_range(t.txq_size[1], MIN_TXQ_ENTRIES, + MAX_TXQ_ENTRIES) || + !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES, + MAX_CTRL_TXQ_ENTRIES) || + !in_range(t.fl_size[0], MIN_FL_ENTRIES, + MAX_RX_BUFFERS) + || !in_range(t.fl_size[1], MIN_FL_ENTRIES, + MAX_RX_JUMBO_BUFFERS) + || !in_range(t.rspq_size, MIN_RSPQ_ENTRIES, + MAX_RSPQ_ENTRIES)) + return -EINVAL; + if ((adapter->flags & FULL_INIT_DONE) && + (t.rspq_size >= 0 || t.fl_size[0] >= 0 || + t.fl_size[1] >= 0 || t.txq_size[0] >= 0 || + t.txq_size[1] >= 0 || t.txq_size[2] >= 0 || + t.polling >= 0 || t.cong_thres >= 0)) + return -EBUSY; + + q = &adapter->params.sge.qset[t.qset_idx]; + + if (t.rspq_size >= 0) + q->rspq_size = t.rspq_size; + if (t.fl_size[0] >= 0) + q->fl_size = t.fl_size[0]; + if (t.fl_size[1] >= 0) + q->jumbo_size = t.fl_size[1]; + if (t.txq_size[0] >= 0) + q->txq_size[0] = t.txq_size[0]; + if (t.txq_size[1] >= 0) + q->txq_size[1] = t.txq_size[1]; + if (t.txq_size[2] >= 0) + q->txq_size[2] = t.txq_size[2]; + if (t.cong_thres >= 0) + q->cong_thres = t.cong_thres; + if (t.intr_lat >= 0) { + struct sge_qset *qs = + &adapter->sge.qs[t.qset_idx]; + + q->coalesce_usecs = t.intr_lat; + t3_update_qset_coalesce(qs, q); + } + if (t.polling >= 0) { + if (adapter->flags & USING_MSIX) + q->polling = t.polling; + else { + /* No polling with INTx for T3A */ + if (adapter->params.rev == 0 && + !(adapter->flags & USING_MSI)) + t.polling = 0; + + for (i = 0; i < SGE_QSETS; i++) { + q = &adapter->params.sge. + qset[i]; + q->polling = t.polling; + } + } + } + break; + } + case CHELSIO_GET_QSET_PARAMS:{ + struct qset_params *q; + struct ch_qset_params t; + + if (copy_from_user(&t, useraddr, sizeof(t))) + return -EFAULT; + if (t.qset_idx >= SGE_QSETS) + return -EINVAL; + + q = &adapter->params.sge.qset[t.qset_idx]; + t.rspq_size = q->rspq_size; + t.txq_size[0] = q->txq_size[0]; + t.txq_size[1] = q->txq_size[1]; + t.txq_size[2] = q->txq_size[2]; + t.fl_size[0] = q->fl_size; + t.fl_size[1] = q->jumbo_size; + t.polling = q->polling; + t.intr_lat = q->coalesce_usecs; + t.cong_thres = q->cong_thres; + + if (copy_to_user(useraddr, &t, sizeof(t))) + return -EFAULT; + break; + } + case CHELSIO_SET_QSET_NUM:{ + struct ch_reg edata; + struct port_info *pi = netdev_priv(dev); + unsigned int i, first_qset = 0, other_qsets = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (adapter->flags & FULL_INIT_DONE) + return -EBUSY; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.val < 1 || + (edata.val > 1 && !(adapter->flags & USING_MSIX))) + return -EINVAL; + + for_each_port(adapter, i) + if (adapter->port[i] && adapter->port[i] != dev) + other_qsets += adap2pinfo(adapter, i)->nqsets; + + if (edata.val + other_qsets > SGE_QSETS) + return -EINVAL; + + pi->nqsets = edata.val; + + for_each_port(adapter, i) + if (adapter->port[i]) { + pi = adap2pinfo(adapter, i); + pi->first_qset = first_qset; + first_qset += pi->nqsets; + } + break; + } + case CHELSIO_GET_QSET_NUM:{ + struct ch_reg edata; + struct port_info *pi = netdev_priv(dev); + + edata.cmd = CHELSIO_GET_QSET_NUM; + edata.val = pi->nqsets; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + break; + } + case CHELSIO_LOAD_FW:{ + u8 *fw_data; + struct ch_mem_range t; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&t, useraddr, sizeof(t))) + return -EFAULT; + + fw_data = kmalloc(t.len, GFP_KERNEL); + if (!fw_data) + return -ENOMEM; + + if (copy_from_user + (fw_data, useraddr + sizeof(t), t.len)) { + kfree(fw_data); + return -EFAULT; + } + + ret = t3_load_fw(adapter, fw_data, t.len); + kfree(fw_data); + if (ret) + return ret; + break; + } + case CHELSIO_SETMTUTAB:{ + struct ch_mtus m; + int i; + + if (!is_offload(adapter)) + return -EOPNOTSUPP; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (offload_running(adapter)) + return -EBUSY; + if (copy_from_user(&m, useraddr, sizeof(m))) + return -EFAULT; + if (m.nmtus != NMTUS) + return -EINVAL; + if (m.mtus[0] < 81) /* accommodate SACK */ + return -EINVAL; + + /* MTUs must be in ascending order */ + for (i = 1; i < NMTUS; ++i) + if (m.mtus[i] < m.mtus[i - 1]) + return -EINVAL; + + memcpy(adapter->params.mtus, m.mtus, + sizeof(adapter->params.mtus)); + break; + } + case CHELSIO_GET_PM:{ + struct tp_params *p = &adapter->params.tp; + struct ch_pm m = {.cmd = CHELSIO_GET_PM }; + + if (!is_offload(adapter)) + return -EOPNOTSUPP; + m.tx_pg_sz = p->tx_pg_size; + m.tx_num_pg = p->tx_num_pgs; + m.rx_pg_sz = p->rx_pg_size; + m.rx_num_pg = p->rx_num_pgs; + m.pm_total = p->pmtx_size + p->chan_rx_size * p->nchan; + if (copy_to_user(useraddr, &m, sizeof(m))) + return -EFAULT; + break; + } + case CHELSIO_SET_PM:{ + struct ch_pm m; + struct tp_params *p = &adapter->params.tp; + + if (!is_offload(adapter)) + return -EOPNOTSUPP; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (adapter->flags & FULL_INIT_DONE) + return -EBUSY; + if (copy_from_user(&m, useraddr, sizeof(m))) + return -EFAULT; + if (!m.rx_pg_sz || (m.rx_pg_sz & (m.rx_pg_sz - 1)) || + !m.tx_pg_sz || (m.tx_pg_sz & (m.tx_pg_sz - 1))) + return -EINVAL; /* not power of 2 */ + if (!(m.rx_pg_sz & 0x14000)) + return -EINVAL; /* not 16KB or 64KB */ + if (!(m.tx_pg_sz & 0x1554000)) + return -EINVAL; + if (m.tx_num_pg == -1) + m.tx_num_pg = p->tx_num_pgs; + if (m.rx_num_pg == -1) + m.rx_num_pg = p->rx_num_pgs; + if (m.tx_num_pg % 24 || m.rx_num_pg % 24) + return -EINVAL; + if (m.rx_num_pg * m.rx_pg_sz > p->chan_rx_size || + m.tx_num_pg * m.tx_pg_sz > p->chan_tx_size) + return -EINVAL; + p->rx_pg_size = m.rx_pg_sz; + p->tx_pg_size = m.tx_pg_sz; + p->rx_num_pgs = m.rx_num_pg; + p->tx_num_pgs = m.tx_num_pg; + break; + } + case CHELSIO_GET_MEM:{ + struct ch_mem_range t; + struct mc7 *mem; + u64 buf[32]; + + if (!is_offload(adapter)) + return -EOPNOTSUPP; + if (!(adapter->flags & FULL_INIT_DONE)) + return -EIO; /* need the memory controllers */ + if (copy_from_user(&t, useraddr, sizeof(t))) + return -EFAULT; + if ((t.addr & 7) || (t.len & 7)) + return -EINVAL; + if (t.mem_id == MEM_CM) + mem = &adapter->cm; + else if (t.mem_id == MEM_PMRX) + mem = &adapter->pmrx; + else if (t.mem_id == MEM_PMTX) + mem = &adapter->pmtx; + else + return -EINVAL; + + /* + * Version scheme: + * bits 0..9: chip version + * bits 10..15: chip revision + */ + t.version = 3 | (adapter->params.rev << 10); + if (copy_to_user(useraddr, &t, sizeof(t))) + return -EFAULT; + + /* + * Read 256 bytes at a time as len can be large and we don't + * want to use huge intermediate buffers. + */ + useraddr += sizeof(t); /* advance to start of buffer */ + while (t.len) { + unsigned int chunk = + min_t(unsigned int, t.len, sizeof(buf)); + + ret = + t3_mc7_bd_read(mem, t.addr / 8, chunk / 8, + buf); + if (ret) + return ret; + if (copy_to_user(useraddr, buf, chunk)) + return -EFAULT; + useraddr += chunk; + t.addr += chunk; + t.len -= chunk; + } + break; + } + case CHELSIO_SET_TRACE_FILTER:{ + struct ch_trace t; + const struct trace_params *tp; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (!offload_running(adapter)) + return -EAGAIN; + if (copy_from_user(&t, useraddr, sizeof(t))) + return -EFAULT; + + tp = (const struct trace_params *)&t.sip; + if (t.config_tx) + t3_config_trace_filter(adapter, tp, 0, + t.invert_match, + t.trace_tx); + if (t.config_rx) + t3_config_trace_filter(adapter, tp, 1, + t.invert_match, + t.trace_rx); + break; + } + case CHELSIO_SET_PKTSCHED:{ + struct sk_buff *skb; + struct ch_pktsched_params p; + struct mngt_pktsched_wr *req; + + if (!(adapter->flags & FULL_INIT_DONE)) + return -EIO; /* uP must be up and running */ + if (copy_from_user(&p, useraddr, sizeof(p))) + return -EFAULT; + skb = alloc_skb(sizeof(*req), GFP_KERNEL); + if (!skb) + return -ENOMEM; + req = + (struct mngt_pktsched_wr *)skb_put(skb, + sizeof(*req)); + req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT)); + req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET; + req->sched = p.sched; + req->idx = p.idx; + req->min = p.min; + req->max = p.max; + req->binding = p.binding; + printk(KERN_INFO + "pktsched: sched %u idx %u min %u max %u binding %u\n", + req->sched, req->idx, req->min, req->max, + req->binding); + skb->priority = 1; + offload_tx(&adapter->tdev, skb); + break; + } + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret, mmd; + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(req); + + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = pi->phy.addr; + /* FALLTHRU */ + case SIOCGMIIREG:{ + u32 val; + struct cphy *phy = &pi->phy; + + if (!phy->mdio_read) + return -EOPNOTSUPP; + if (is_10G(adapter)) { + mmd = data->phy_id >> 8; + if (!mmd) + mmd = MDIO_DEV_PCS; + else if (mmd > MDIO_DEV_XGXS) + return -EINVAL; + + ret = + phy->mdio_read(adapter, data->phy_id & 0x1f, + mmd, data->reg_num, &val); + } else + ret = + phy->mdio_read(adapter, data->phy_id & 0x1f, + 0, data->reg_num & 0x1f, + &val); + if (!ret) + data->val_out = val; + break; + } + case SIOCSMIIREG:{ + struct cphy *phy = &pi->phy; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (!phy->mdio_write) + return -EOPNOTSUPP; + if (is_10G(adapter)) { + mmd = data->phy_id >> 8; + if (!mmd) + mmd = MDIO_DEV_PCS; + else if (mmd > MDIO_DEV_XGXS) + return -EINVAL; + + ret = + phy->mdio_write(adapter, + data->phy_id & 0x1f, mmd, + data->reg_num, + data->val_in); + } else + ret = + phy->mdio_write(adapter, + data->phy_id & 0x1f, 0, + data->reg_num & 0x1f, + data->val_in); + break; + } + case SIOCCHIOCTL: + return cxgb_extension_ioctl(dev, req->ifr_data); + default: + return -EOPNOTSUPP; + } + return ret; +} + +static int cxgb_change_mtu(struct net_device *dev, int new_mtu) +{ + int ret; + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + + if (new_mtu < 81) /* accommodate SACK */ + return -EINVAL; + if ((ret = t3_mac_set_mtu(&pi->mac, new_mtu))) + return ret; + dev->mtu = new_mtu; + init_port_mtus(adapter); + if (adapter->params.rev == 0 && offload_running(adapter)) + t3_load_mtus(adapter, adapter->params.mtus, + adapter->params.a_wnd, adapter->params.b_wnd, + adapter->port[0]->mtu); + return 0; +} + +static int cxgb_set_mac_addr(struct net_device *dev, void *p) +{ + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + t3_mac_set_address(&pi->mac, 0, dev->dev_addr); + if (offload_running(adapter)) + write_smt_entry(adapter, pi->port_id); + return 0; +} + +/** + * t3_synchronize_rx - wait for current Rx processing on a port to complete + * @adap: the adapter + * @p: the port + * + * Ensures that current Rx processing on any of the queues associated with + * the given port completes before returning. We do this by acquiring and + * releasing the locks of the response queues associated with the port. + */ +static void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) +{ + int i; + + for (i = 0; i < p->nqsets; i++) { + struct sge_rspq *q = &adap->sge.qs[i + p->first_qset].rspq; + + spin_lock_irq(&q->lock); + spin_unlock_irq(&q->lock); + } +} + +static void vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ + struct adapter *adapter = dev->priv; + struct port_info *pi = netdev_priv(dev); + + pi->vlan_grp = grp; + if (adapter->params.rev > 0) + t3_set_vlan_accel(adapter, 1 << pi->port_id, grp != NULL); + else { + /* single control for all ports */ + unsigned int i, have_vlans = 0; + for_each_port(adapter, i) + have_vlans |= adap2pinfo(adapter, i)->vlan_grp != NULL; + + t3_set_vlan_accel(adapter, 1, have_vlans); + } + t3_synchronize_rx(adapter, pi); +} + +static void vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + /* nothing */ +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void cxgb_netpoll(struct net_device *dev) +{ + struct adapter *adapter = dev->priv; + struct sge_qset *qs = dev2qset(dev); + + t3_intr_handler(adapter, qs->rspq.polling) (adapter->pdev->irq, + adapter); +} +#endif + +/* + * Periodic accumulation of MAC statistics. + */ +static void mac_stats_update(struct adapter *adapter) +{ + int i; + + for_each_port(adapter, i) { + struct net_device *dev = adapter->port[i]; + struct port_info *p = netdev_priv(dev); + + if (netif_running(dev)) { + spin_lock(&adapter->stats_lock); + t3_mac_update_stats(&p->mac); + spin_unlock(&adapter->stats_lock); + } + } +} + +static void check_link_status(struct adapter *adapter) +{ + int i; + + for_each_port(adapter, i) { + struct net_device *dev = adapter->port[i]; + struct port_info *p = netdev_priv(dev); + + if (!(p->port_type->caps & SUPPORTED_IRQ) && netif_running(dev)) + t3_link_changed(adapter, i); + } +} + +static void t3_adap_check_task(struct work_struct *work) +{ + struct adapter *adapter = container_of(work, struct adapter, + adap_check_task.work); + const struct adapter_params *p = &adapter->params; + + adapter->check_task_cnt++; + + /* Check link status for PHYs without interrupts */ + if (p->linkpoll_period) + check_link_status(adapter); + + /* Accumulate MAC stats if needed */ + if (!p->linkpoll_period || + (adapter->check_task_cnt * p->linkpoll_period) / 10 >= + p->stats_update_period) { + mac_stats_update(adapter); + adapter->check_task_cnt = 0; + } + + /* Schedule the next check update if any port is active. */ + spin_lock(&adapter->work_lock); + if (adapter->open_device_map & PORT_MASK) + schedule_chk_task(adapter); + spin_unlock(&adapter->work_lock); +} + +/* + * Processes external (PHY) interrupts in process context. + */ +static void ext_intr_task(struct work_struct *work) +{ + struct adapter *adapter = container_of(work, struct adapter, + ext_intr_handler_task); + + t3_phy_intr_handler(adapter); + + /* Now reenable external interrupts */ + spin_lock_irq(&adapter->work_lock); + if (adapter->slow_intr_mask) { + adapter->slow_intr_mask |= F_T3DBG; + t3_write_reg(adapter, A_PL_INT_CAUSE0, F_T3DBG); + t3_write_reg(adapter, A_PL_INT_ENABLE0, + adapter->slow_intr_mask); + } + spin_unlock_irq(&adapter->work_lock); +} + +/* + * Interrupt-context handler for external (PHY) interrupts. + */ +void t3_os_ext_intr_handler(struct adapter *adapter) +{ + /* + * Schedule a task to handle external interrupts as they may be slow + * and we use a mutex to protect MDIO registers. We disable PHY + * interrupts in the meantime and let the task reenable them when + * it's done. + */ + spin_lock(&adapter->work_lock); + if (adapter->slow_intr_mask) { + adapter->slow_intr_mask &= ~F_T3DBG; + t3_write_reg(adapter, A_PL_INT_ENABLE0, + adapter->slow_intr_mask); + queue_work(cxgb3_wq, &adapter->ext_intr_handler_task); + } + spin_unlock(&adapter->work_lock); +} + +void t3_fatal_err(struct adapter *adapter) +{ + unsigned int fw_status[4]; + + if (adapter->flags & FULL_INIT_DONE) { + t3_sge_stop(adapter); + t3_intr_disable(adapter); + } + CH_ALERT(adapter, "encountered fatal error, operation suspended\n"); + if (!t3_cim_ctl_blk_read(adapter, 0xa0, 4, fw_status)) + CH_ALERT(adapter, "FW status: 0x%x, 0x%x, 0x%x, 0x%x\n", + fw_status[0], fw_status[1], + fw_status[2], fw_status[3]); + +} + +static int __devinit cxgb_enable_msix(struct adapter *adap) +{ + struct msix_entry entries[SGE_QSETS + 1]; + int i, err; + + for (i = 0; i < ARRAY_SIZE(entries); ++i) + entries[i].entry = i; + + err = pci_enable_msix(adap->pdev, entries, ARRAY_SIZE(entries)); + if (!err) { + for (i = 0; i < ARRAY_SIZE(entries); ++i) + adap->msix_info[i].vec = entries[i].vector; + } else if (err > 0) + dev_info(&adap->pdev->dev, + "only %d MSI-X vectors left, not using MSI-X\n", err); + return err; +} + +static void __devinit print_port_info(struct adapter *adap, + const struct adapter_info *ai) +{ + static const char *pci_variant[] = { + "PCI", "PCI-X", "PCI-X ECC", "PCI-X 266", "PCI Express" + }; + + int i; + char buf[80]; + + if (is_pcie(adap)) + snprintf(buf, sizeof(buf), "%s x%d", + pci_variant[adap->params.pci.variant], + adap->params.pci.width); + else + snprintf(buf, sizeof(buf), "%s %dMHz/%d-bit", + pci_variant[adap->params.pci.variant], + adap->params.pci.speed, adap->params.pci.width); + + for_each_port(adap, i) { + struct net_device *dev = adap->port[i]; + const struct port_info *pi = netdev_priv(dev); + + if (!test_bit(i, &adap->registered_device_map)) + continue; + printk(KERN_INFO "%s: %s %s RNIC (rev %d) %s%s\n", + dev->name, ai->desc, pi->port_type->desc, + adap->params.rev, buf, + (adap->flags & USING_MSIX) ? " MSI-X" : + (adap->flags & USING_MSI) ? " MSI" : ""); + if (adap->name == dev->name && adap->params.vpd.mclk) + printk(KERN_INFO "%s: %uMB CM, %uMB PMTX, %uMB PMRX\n", + adap->name, t3_mc7_size(&adap->cm) >> 20, + t3_mc7_size(&adap->pmtx) >> 20, + t3_mc7_size(&adap->pmrx) >> 20); + } +} + +static int __devinit init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int version_printed; + + int i, err, pci_using_dac = 0; + unsigned long mmio_start, mmio_len; + const struct adapter_info *ai; + struct adapter *adapter = NULL; + struct port_info *pi; + + if (!version_printed) { + printk(KERN_INFO "%s - version %s\n", DRV_DESC, DRV_VERSION); + ++version_printed; + } + + if (!cxgb3_wq) { + cxgb3_wq = create_singlethread_workqueue(DRV_NAME); + if (!cxgb3_wq) { + printk(KERN_ERR DRV_NAME + ": cannot initialize work queue\n"); + return -ENOMEM; + } + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + /* Just info, some other driver may have claimed the device. */ + dev_info(&pdev->dev, "cannot obtain PCI resources\n"); + return err; + } + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "cannot enable PCI device\n"); + goto out_release_regions; + } + + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + pci_using_dac = 1; + err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (err) { + dev_err(&pdev->dev, "unable to obtain 64-bit DMA for " + "coherent allocations\n"); + goto out_disable_device; + } + } else if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) != 0) { + dev_err(&pdev->dev, "no usable DMA configuration\n"); + goto out_disable_device; + } + + pci_set_master(pdev); + + mmio_start = pci_resource_start(pdev, 0); + mmio_len = pci_resource_len(pdev, 0); + ai = t3_get_adapter_info(ent->driver_data); + + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) { + err = -ENOMEM; + goto out_disable_device; + } + + adapter->regs = ioremap_nocache(mmio_start, mmio_len); + if (!adapter->regs) { + dev_err(&pdev->dev, "cannot map device registers\n"); + err = -ENOMEM; + goto out_free_adapter; + } + + adapter->pdev = pdev; + adapter->name = pci_name(pdev); + adapter->msg_enable = dflt_msg_enable; + adapter->mmio_len = mmio_len; + + mutex_init(&adapter->mdio_lock); + spin_lock_init(&adapter->work_lock); + spin_lock_init(&adapter->stats_lock); + + INIT_LIST_HEAD(&adapter->adapter_list); + INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task); + INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task); + + for (i = 0; i < ai->nports; ++i) { + struct net_device *netdev; + + netdev = alloc_etherdev(sizeof(struct port_info)); + if (!netdev) { + err = -ENOMEM; + goto out_free_dev; + } + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + + adapter->port[i] = netdev; + pi = netdev_priv(netdev); + pi->rx_csum_offload = 1; + pi->nqsets = 1; + pi->first_qset = i; + pi->activity = 0; + pi->port_id = i; + netif_carrier_off(netdev); + netdev->irq = pdev->irq; + netdev->mem_start = mmio_start; + netdev->mem_end = mmio_start + mmio_len - 1; + netdev->priv = adapter; + netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + netdev->features |= NETIF_F_LLTX; + if (pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + + netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + netdev->vlan_rx_register = vlan_rx_register; + netdev->vlan_rx_kill_vid = vlan_rx_kill_vid; + + netdev->open = cxgb_open; + netdev->stop = cxgb_close; + netdev->hard_start_xmit = t3_eth_xmit; + netdev->get_stats = cxgb_get_stats; + netdev->set_multicast_list = cxgb_set_rxmode; + netdev->do_ioctl = cxgb_ioctl; + netdev->change_mtu = cxgb_change_mtu; + netdev->set_mac_address = cxgb_set_mac_addr; +#ifdef CONFIG_NET_POLL_CONTROLLER + netdev->poll_controller = cxgb_netpoll; +#endif + netdev->weight = 64; + + SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops); + } + + pci_set_drvdata(pdev, adapter->port[0]); + if (t3_prep_adapter(adapter, ai, 1) < 0) { + err = -ENODEV; + goto out_free_dev; + } + + /* + * The card is now ready to go. If any errors occur during device + * registration we do not fail the whole card but rather proceed only + * with the ports we manage to register successfully. However we must + * register at least one net device. + */ + for_each_port(adapter, i) { + err = register_netdev(adapter->port[i]); + if (err) + dev_warn(&pdev->dev, + "cannot register net device %s, skipping\n", + adapter->port[i]->name); + else { + /* + * Change the name we use for messages to the name of + * the first successfully registered interface. + */ + if (!adapter->registered_device_map) + adapter->name = adapter->port[i]->name; + + __set_bit(i, &adapter->registered_device_map); + } + } + if (!adapter->registered_device_map) { + dev_err(&pdev->dev, "could not register any net devices\n"); + goto out_free_dev; + } + + /* Driver's ready. Reflect it on LEDs */ + t3_led_ready(adapter); + + if (is_offload(adapter)) { + __set_bit(OFFLOAD_DEVMAP_BIT, &adapter->registered_device_map); + cxgb3_adapter_ofld(adapter); + } + + /* See what interrupts we'll be using */ + if (msi > 1 && cxgb_enable_msix(adapter) == 0) + adapter->flags |= USING_MSIX; + else if (msi > 0 && pci_enable_msi(pdev) == 0) + adapter->flags |= USING_MSI; + + err = sysfs_create_group(&adapter->port[0]->class_dev.kobj, + &cxgb3_attr_group); + + print_port_info(adapter, ai); + return 0; + +out_free_dev: + iounmap(adapter->regs); + for (i = ai->nports - 1; i >= 0; --i) + if (adapter->port[i]) + free_netdev(adapter->port[i]); + +out_free_adapter: + kfree(adapter); + +out_disable_device: + pci_disable_device(pdev); +out_release_regions: + pci_release_regions(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void __devexit remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + int i; + struct adapter *adapter = dev->priv; + + t3_sge_stop(adapter); + sysfs_remove_group(&adapter->port[0]->class_dev.kobj, + &cxgb3_attr_group); + + for_each_port(adapter, i) + if (test_bit(i, &adapter->registered_device_map)) + unregister_netdev(adapter->port[i]); + + if (is_offload(adapter)) { + cxgb3_adapter_unofld(adapter); + if (test_bit(OFFLOAD_DEVMAP_BIT, + &adapter->open_device_map)) + offload_close(&adapter->tdev); + } + + t3_free_sge_resources(adapter); + cxgb_disable_msi(adapter); + + for (i = 0; i < ARRAY_SIZE(adapter->dummy_netdev); i++) + if (adapter->dummy_netdev[i]) { + free_netdev(adapter->dummy_netdev[i]); + adapter->dummy_netdev[i] = NULL; + } + + for_each_port(adapter, i) + if (adapter->port[i]) + free_netdev(adapter->port[i]); + + iounmap(adapter->regs); + kfree(adapter); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + } +} + +static struct pci_driver driver = { + .name = DRV_NAME, + .id_table = cxgb3_pci_tbl, + .probe = init_one, + .remove = __devexit_p(remove_one), +}; + +static int __init cxgb3_init_module(void) +{ + int ret; + + cxgb3_offload_init(); + + ret = pci_register_driver(&driver); + return ret; +} + +static void __exit cxgb3_cleanup_module(void) +{ + pci_unregister_driver(&driver); + if (cxgb3_wq) + destroy_workqueue(cxgb3_wq); +} + +module_init(cxgb3_init_module); +module_exit(cxgb3_cleanup_module); diff -puN /dev/null drivers/net/cxgb3/cxgb3_offload.c --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_offload.c @@ -0,0 +1,1222 @@ +/* + * Copyright (c) 2006 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "regs.h" +#include "cxgb3_ioctl.h" +#include "cxgb3_ctl_defs.h" +#include "cxgb3_defs.h" +#include "l2t.h" +#include "firmware_exports.h" +#include "cxgb3_offload.h" + +static LIST_HEAD(client_list); +static LIST_HEAD(ofld_dev_list); +static DEFINE_MUTEX(cxgb3_db_lock); + +static DEFINE_RWLOCK(adapter_list_lock); +static LIST_HEAD(adapter_list); + +static const unsigned int MAX_ATIDS = 64 * 1024; +static const unsigned int ATID_BASE = 0x100000; + +static inline int offload_activated(struct t3cdev *tdev) +{ + const struct adapter *adapter = tdev2adap(tdev); + + return (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)); +} + +/** + * cxgb3_register_client - register an offload client + * @client: the client + * + * Add the client to the client list, + * and call backs the client for each activated offload device + */ +void cxgb3_register_client(struct cxgb3_client *client) +{ + struct t3cdev *tdev; + + mutex_lock(&cxgb3_db_lock); + list_add_tail(&client->client_list, &client_list); + + if (client->add) { + list_for_each_entry(tdev, &ofld_dev_list, ofld_dev_list) { + if (offload_activated(tdev)) + client->add(tdev); + } + } + mutex_unlock(&cxgb3_db_lock); +} + +EXPORT_SYMBOL(cxgb3_register_client); + +/** + * cxgb3_unregister_client - unregister an offload client + * @client: the client + * + * Remove the client to the client list, + * and call backs the client for each activated offload device. + */ +void cxgb3_unregister_client(struct cxgb3_client *client) +{ + struct t3cdev *tdev; + + mutex_lock(&cxgb3_db_lock); + list_del(&client->client_list); + + if (client->remove) { + list_for_each_entry(tdev, &ofld_dev_list, ofld_dev_list) { + if (offload_activated(tdev)) + client->remove(tdev); + } + } + mutex_unlock(&cxgb3_db_lock); +} + +EXPORT_SYMBOL(cxgb3_unregister_client); + +/** + * cxgb3_add_clients - activate registered clients for an offload device + * @tdev: the offload device + * + * Call backs all registered clients once a offload device is activated + */ +void cxgb3_add_clients(struct t3cdev *tdev) +{ + struct cxgb3_client *client; + + mutex_lock(&cxgb3_db_lock); + list_for_each_entry(client, &client_list, client_list) { + if (client->add) + client->add(tdev); + } + mutex_unlock(&cxgb3_db_lock); +} + +/** + * cxgb3_remove_clients - deactivates registered clients + * for an offload device + * @tdev: the offload device + * + * Call backs all registered clients once a offload device is deactivated + */ +void cxgb3_remove_clients(struct t3cdev *tdev) +{ + struct cxgb3_client *client; + + mutex_lock(&cxgb3_db_lock); + list_for_each_entry(client, &client_list, client_list) { + if (client->remove) + client->remove(tdev); + } + mutex_unlock(&cxgb3_db_lock); +} + +static struct net_device *get_iff_from_mac(struct adapter *adapter, + const unsigned char *mac, + unsigned int vlan) +{ + int i; + + for_each_port(adapter, i) { + const struct vlan_group *grp; + struct net_device *dev = adapter->port[i]; + const struct port_info *p = netdev_priv(dev); + + if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) { + if (vlan && vlan != VLAN_VID_MASK) { + grp = p->vlan_grp; + dev = grp ? grp->vlan_devices[vlan] : NULL; + } else + while (dev->master) + dev = dev->master; + return dev; + } + } + return NULL; +} + +static int cxgb_ulp_iscsi_ctl(struct adapter *adapter, unsigned int req, + void *data) +{ + int ret = 0; + struct ulp_iscsi_info *uiip = data; + + switch (req) { + case ULP_ISCSI_GET_PARAMS: + uiip->pdev = adapter->pdev; + uiip->llimit = t3_read_reg(adapter, A_ULPRX_ISCSI_LLIMIT); + uiip->ulimit = t3_read_reg(adapter, A_ULPRX_ISCSI_ULIMIT); + uiip->tagmask = t3_read_reg(adapter, A_ULPRX_ISCSI_TAGMASK); + /* + * On tx, the iscsi pdu has to be <= tx page size and has to + * fit into the Tx PM FIFO. + */ + uiip->max_txsz = min(adapter->params.tp.tx_pg_size, + t3_read_reg(adapter, A_PM1_TX_CFG) >> 17); + /* on rx, the iscsi pdu has to be < rx page size and the + whole pdu + cpl headers has to fit into one sge buffer */ + uiip->max_rxsz = min_t(unsigned int, + adapter->params.tp.rx_pg_size, + (adapter->sge.qs[0].fl[1].buf_size - + sizeof(struct cpl_rx_data) * 2 - + sizeof(struct cpl_rx_data_ddp))); + break; + case ULP_ISCSI_SET_PARAMS: + t3_write_reg(adapter, A_ULPRX_ISCSI_TAGMASK, uiip->tagmask); + break; + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +/* Response queue used for RDMA events. */ +#define ASYNC_NOTIF_RSPQ 0 + +static int cxgb_rdma_ctl(struct adapter *adapter, unsigned int req, void *data) +{ + int ret = 0; + + switch (req) { + case RDMA_GET_PARAMS:{ + struct rdma_info *req = data; + struct pci_dev *pdev = adapter->pdev; + + req->udbell_physbase = pci_resource_start(pdev, 2); + req->udbell_len = pci_resource_len(pdev, 2); + req->tpt_base = + t3_read_reg(adapter, A_ULPTX_TPT_LLIMIT); + req->tpt_top = t3_read_reg(adapter, A_ULPTX_TPT_ULIMIT); + req->pbl_base = + t3_read_reg(adapter, A_ULPTX_PBL_LLIMIT); + req->pbl_top = t3_read_reg(adapter, A_ULPTX_PBL_ULIMIT); + req->rqt_base = t3_read_reg(adapter, A_ULPRX_RQ_LLIMIT); + req->rqt_top = t3_read_reg(adapter, A_ULPRX_RQ_ULIMIT); + req->kdb_addr = adapter->regs + A_SG_KDOORBELL; + req->pdev = pdev; + break; + } + case RDMA_CQ_OP:{ + unsigned long flags; + struct rdma_cq_op *req = data; + + /* may be called in any context */ + spin_lock_irqsave(&adapter->sge.reg_lock, flags); + ret = t3_sge_cqcntxt_op(adapter, req->id, req->op, + req->credits); + spin_unlock_irqrestore(&adapter->sge.reg_lock, flags); + break; + } + case RDMA_GET_MEM:{ + struct ch_mem_range *t = data; + struct mc7 *mem; + + if ((t->addr & 7) || (t->len & 7)) + return -EINVAL; + if (t->mem_id == MEM_CM) + mem = &adapter->cm; + else if (t->mem_id == MEM_PMRX) + mem = &adapter->pmrx; + else if (t->mem_id == MEM_PMTX) + mem = &adapter->pmtx; + else + return -EINVAL; + + ret = + t3_mc7_bd_read(mem, t->addr / 8, t->len / 8, + (u64 *) t->buf); + if (ret) + return ret; + break; + } + case RDMA_CQ_SETUP:{ + struct rdma_cq_setup *req = data; + + spin_lock_irq(&adapter->sge.reg_lock); + ret = + t3_sge_init_cqcntxt(adapter, req->id, + req->base_addr, req->size, + ASYNC_NOTIF_RSPQ, + req->ovfl_mode, req->credits, + req->credit_thres); + spin_unlock_irq(&adapter->sge.reg_lock); + break; + } + case RDMA_CQ_DISABLE: + spin_lock_irq(&adapter->sge.reg_lock); + ret = t3_sge_disable_cqcntxt(adapter, *(unsigned int *)data); + spin_unlock_irq(&adapter->sge.reg_lock); + break; + case RDMA_CTRL_QP_SETUP:{ + struct rdma_ctrlqp_setup *req = data; + + spin_lock_irq(&adapter->sge.reg_lock); + ret = t3_sge_init_ecntxt(adapter, FW_RI_SGEEC_START, 0, + SGE_CNTXT_RDMA, + ASYNC_NOTIF_RSPQ, + req->base_addr, req->size, + FW_RI_TID_START, 1, 0); + spin_unlock_irq(&adapter->sge.reg_lock); + break; + } + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +static int cxgb_offload_ctl(struct t3cdev *tdev, unsigned int req, void *data) +{ + struct adapter *adapter = tdev2adap(tdev); + struct tid_range *tid; + struct mtutab *mtup; + struct iff_mac *iffmacp; + struct ddp_params *ddpp; + struct adap_ports *ports; + int i; + + switch (req) { + case GET_MAX_OUTSTANDING_WR: + *(unsigned int *)data = FW_WR_NUM; + break; + case GET_WR_LEN: + *(unsigned int *)data = WR_FLITS; + break; + case GET_TX_MAX_CHUNK: + *(unsigned int *)data = 1 << 20; /* 1MB */ + break; + case GET_TID_RANGE: + tid = data; + tid->num = t3_mc5_size(&adapter->mc5) - + adapter->params.mc5.nroutes - + adapter->params.mc5.nfilters - adapter->params.mc5.nservers; + tid->base = 0; + break; + case GET_STID_RANGE: + tid = data; + tid->num = adapter->params.mc5.nservers; + tid->base = t3_mc5_size(&adapter->mc5) - tid->num - + adapter->params.mc5.nfilters - adapter->params.mc5.nroutes; + break; + case GET_L2T_CAPACITY: + *(unsigned int *)data = 2048; + break; + case GET_MTUS: + mtup = data; + mtup->size = NMTUS; + mtup->mtus = adapter->params.mtus; + break; + case GET_IFF_FROM_MAC: + iffmacp = data; + iffmacp->dev = get_iff_from_mac(adapter, iffmacp->mac_addr, + iffmacp->vlan_tag & + VLAN_VID_MASK); + break; + case GET_DDP_PARAMS: + ddpp = data; + ddpp->llimit = t3_read_reg(adapter, A_ULPRX_TDDP_LLIMIT); + ddpp->ulimit = t3_read_reg(adapter, A_ULPRX_TDDP_ULIMIT); + ddpp->tag_mask = t3_read_reg(adapter, A_ULPRX_TDDP_TAGMASK); + break; + case GET_PORTS: + ports = data; + ports->nports = adapter->params.nports; + for_each_port(adapter, i) + ports->lldevs[i] = adapter->port[i]; + break; + case ULP_ISCSI_GET_PARAMS: + case ULP_ISCSI_SET_PARAMS: + if (!offload_running(adapter)) + return -EAGAIN; + return cxgb_ulp_iscsi_ctl(adapter, req, data); + case RDMA_GET_PARAMS: + case RDMA_CQ_OP: + case RDMA_CQ_SETUP: + case RDMA_CQ_DISABLE: + case RDMA_CTRL_QP_SETUP: + case RDMA_GET_MEM: + if (!offload_running(adapter)) + return -EAGAIN; + return cxgb_rdma_ctl(adapter, req, data); + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Dummy handler for Rx offload packets in case we get an offload packet before + * proper processing is setup. This complains and drops the packet as it isn't + * normal to get offload packets at this stage. + */ +static int rx_offload_blackhole(struct t3cdev *dev, struct sk_buff **skbs, + int n) +{ + CH_ERR(tdev2adap(dev), "%d unexpected offload packets, first data %u\n", + n, ntohl(*(u32 *)skbs[0]->data)); + while (n--) + dev_kfree_skb_any(skbs[n]); + return 0; +} + +static void dummy_neigh_update(struct t3cdev *dev, struct neighbour *neigh) +{ +} + +void cxgb3_set_dummy_ops(struct t3cdev *dev) +{ + dev->recv = rx_offload_blackhole; + dev->neigh_update = dummy_neigh_update; +} + +/* + * Free an active-open TID. + */ +void *cxgb3_free_atid(struct t3cdev *tdev, int atid) +{ + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + union active_open_entry *p = atid2entry(t, atid); + void *ctx = p->t3c_tid.ctx; + + spin_lock_bh(&t->atid_lock); + p->next = t->afree; + t->afree = p; + t->atids_in_use--; + spin_unlock_bh(&t->atid_lock); + + return ctx; +} + +EXPORT_SYMBOL(cxgb3_free_atid); + +/* + * Free a server TID and return it to the free pool. + */ +void cxgb3_free_stid(struct t3cdev *tdev, int stid) +{ + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + union listen_entry *p = stid2entry(t, stid); + + spin_lock_bh(&t->stid_lock); + p->next = t->sfree; + t->sfree = p; + t->stids_in_use--; + spin_unlock_bh(&t->stid_lock); +} + +EXPORT_SYMBOL(cxgb3_free_stid); + +void cxgb3_insert_tid(struct t3cdev *tdev, struct cxgb3_client *client, + void *ctx, unsigned int tid) +{ + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + + t->tid_tab[tid].client = client; + t->tid_tab[tid].ctx = ctx; + atomic_inc(&t->tids_in_use); +} + +EXPORT_SYMBOL(cxgb3_insert_tid); + +/* + * Populate a TID_RELEASE WR. The skb must be already propely sized. + */ +static inline void mk_tid_release(struct sk_buff *skb, unsigned int tid) +{ + struct cpl_tid_release *req; + + skb->priority = CPL_PRIORITY_SETUP; + req = (struct cpl_tid_release *)__skb_put(skb, sizeof(*req)); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_TID_RELEASE, tid)); +} + +static void t3_process_tid_release_list(struct work_struct *work) +{ + struct t3c_data *td = container_of(work, struct t3c_data, + tid_release_task); + struct sk_buff *skb; + struct t3cdev *tdev = td->dev; + + + spin_lock_bh(&td->tid_release_lock); + while (td->tid_release_list) { + struct t3c_tid_entry *p = td->tid_release_list; + + td->tid_release_list = (struct t3c_tid_entry *)p->ctx; + spin_unlock_bh(&td->tid_release_lock); + + skb = alloc_skb(sizeof(struct cpl_tid_release), + GFP_KERNEL | __GFP_NOFAIL); + mk_tid_release(skb, p - td->tid_maps.tid_tab); + cxgb3_ofld_send(tdev, skb); + p->ctx = NULL; + spin_lock_bh(&td->tid_release_lock); + } + spin_unlock_bh(&td->tid_release_lock); +} + +/* use ctx as a next pointer in the tid release list */ +void cxgb3_queue_tid_release(struct t3cdev *tdev, unsigned int tid) +{ + struct t3c_data *td = T3C_DATA(tdev); + struct t3c_tid_entry *p = &td->tid_maps.tid_tab[tid]; + + spin_lock_bh(&td->tid_release_lock); + p->ctx = (void *)td->tid_release_list; + td->tid_release_list = p; + if (!p->ctx) + schedule_work(&td->tid_release_task); + spin_unlock_bh(&td->tid_release_lock); +} + +EXPORT_SYMBOL(cxgb3_queue_tid_release); + +/* + * Remove a tid from the TID table. A client may defer processing its last + * CPL message if it is locked at the time it arrives, and while the message + * sits in the client's backlog the TID may be reused for another connection. + * To handle this we atomically switch the TID association if it still points + * to the original client context. + */ +void cxgb3_remove_tid(struct t3cdev *tdev, void *ctx, unsigned int tid) +{ + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + + BUG_ON(tid >= t->ntids); + if (tdev->type == T3A) + (void)cmpxchg(&t->tid_tab[tid].ctx, ctx, NULL); + else { + struct sk_buff *skb; + + skb = alloc_skb(sizeof(struct cpl_tid_release), GFP_ATOMIC); + if (likely(skb)) { + mk_tid_release(skb, tid); + cxgb3_ofld_send(tdev, skb); + t->tid_tab[tid].ctx = NULL; + } else + cxgb3_queue_tid_release(tdev, tid); + } + atomic_dec(&t->tids_in_use); +} + +EXPORT_SYMBOL(cxgb3_remove_tid); + +int cxgb3_alloc_atid(struct t3cdev *tdev, struct cxgb3_client *client, + void *ctx) +{ + int atid = -1; + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + + spin_lock_bh(&t->atid_lock); + if (t->afree) { + union active_open_entry *p = t->afree; + + atid = (p - t->atid_tab) + t->atid_base; + t->afree = p->next; + p->t3c_tid.ctx = ctx; + p->t3c_tid.client = client; + t->atids_in_use++; + } + spin_unlock_bh(&t->atid_lock); + return atid; +} + +EXPORT_SYMBOL(cxgb3_alloc_atid); + +int cxgb3_alloc_stid(struct t3cdev *tdev, struct cxgb3_client *client, + void *ctx) +{ + int stid = -1; + struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; + + spin_lock_bh(&t->stid_lock); + if (t->sfree) { + union listen_entry *p = t->sfree; + + stid = (p - t->stid_tab) + t->stid_base; + t->sfree = p->next; + p->t3c_tid.ctx = ctx; + p->t3c_tid.client = client; + t->stids_in_use++; + } + spin_unlock_bh(&t->stid_lock); + return stid; +} + +EXPORT_SYMBOL(cxgb3_alloc_stid); + +static int do_smt_write_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_smt_write_rpl *rpl = cplhdr(skb); + + if (rpl->status != CPL_ERR_NONE) + printk(KERN_ERR + "Unexpected SMT_WRITE_RPL status %u for entry %u\n", + rpl->status, GET_TID(rpl)); + + return CPL_RET_BUF_DONE; +} + +static int do_l2t_write_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_l2t_write_rpl *rpl = cplhdr(skb); + + if (rpl->status != CPL_ERR_NONE) + printk(KERN_ERR + "Unexpected L2T_WRITE_RPL status %u for entry %u\n", + rpl->status, GET_TID(rpl)); + + return CPL_RET_BUF_DONE; +} + +static int do_act_open_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_act_open_rpl *rpl = cplhdr(skb); + unsigned int atid = G_TID(ntohl(rpl->atid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid); + if (t3c_tid->ctx && t3c_tid->client && t3c_tid->client->handlers && + t3c_tid->client->handlers[CPL_ACT_OPEN_RPL]) { + return t3c_tid->client->handlers[CPL_ACT_OPEN_RPL] (dev, skb, + t3c_tid-> + ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_ACT_OPEN_RPL); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int do_stid_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + union opcode_tid *p = cplhdr(skb); + unsigned int stid = G_TID(ntohl(p->opcode_tid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_stid(&(T3C_DATA(dev))->tid_maps, stid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[p->opcode]) { + return t3c_tid->client->handlers[p->opcode] (dev, skb, + t3c_tid->ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, p->opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int do_hwtid_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + union opcode_tid *p = cplhdr(skb); + unsigned int hwtid = G_TID(ntohl(p->opcode_tid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[p->opcode]) { + return t3c_tid->client->handlers[p->opcode] + (dev, skb, t3c_tid->ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, p->opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int do_cr(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_pass_accept_req *req = cplhdr(skb); + unsigned int stid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_stid(&(T3C_DATA(dev))->tid_maps, stid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[CPL_PASS_ACCEPT_REQ]) { + return t3c_tid->client->handlers[CPL_PASS_ACCEPT_REQ] + (dev, skb, t3c_tid->ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_PASS_ACCEPT_REQ); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int do_abort_req_rss(struct t3cdev *dev, struct sk_buff *skb) +{ + union opcode_tid *p = cplhdr(skb); + unsigned int hwtid = G_TID(ntohl(p->opcode_tid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[p->opcode]) { + return t3c_tid->client->handlers[p->opcode] + (dev, skb, t3c_tid->ctx); + } else { + struct cpl_abort_req_rss *req = cplhdr(skb); + struct cpl_abort_rpl *rpl; + + struct sk_buff *skb = + alloc_skb(sizeof(struct cpl_abort_rpl), GFP_ATOMIC); + if (!skb) { + printk("do_abort_req_rss: couldn't get skb!\n"); + goto out; + } + skb->priority = CPL_PRIORITY_DATA; + __skb_put(skb, sizeof(struct cpl_abort_rpl)); + rpl = cplhdr(skb); + rpl->wr.wr_hi = + htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL)); + rpl->wr.wr_lo = htonl(V_WR_TID(GET_TID(req))); + OPCODE_TID(rpl) = + htonl(MK_OPCODE_TID(CPL_ABORT_RPL, GET_TID(req))); + rpl->cmd = req->status; + cxgb3_ofld_send(dev, skb); +out: + return CPL_RET_BUF_DONE; + } +} + +static int do_act_establish(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_act_establish *req = cplhdr(skb); + unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[CPL_ACT_ESTABLISH]) { + return t3c_tid->client->handlers[CPL_ACT_ESTABLISH] + (dev, skb, t3c_tid->ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, CPL_PASS_ACCEPT_REQ); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int do_set_tcb_rpl(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_set_tcb_rpl *rpl = cplhdr(skb); + + if (rpl->status != CPL_ERR_NONE) + printk(KERN_ERR + "Unexpected SET_TCB_RPL status %u for tid %u\n", + rpl->status, GET_TID(rpl)); + return CPL_RET_BUF_DONE; +} + +static int do_trace(struct t3cdev *dev, struct sk_buff *skb) +{ + struct cpl_trace_pkt *p = cplhdr(skb); + + skb->protocol = 0xffff; + skb->dev = dev->lldev; + skb_pull(skb, sizeof(*p)); + skb->mac.raw = skb->data; + netif_receive_skb(skb); + return 0; +} + +static int do_term(struct t3cdev *dev, struct sk_buff *skb) +{ + unsigned int hwtid = ntohl(skb->priority) >> 8 & 0xfffff; + unsigned int opcode = G_OPCODE(ntohl(skb->csum)); + struct t3c_tid_entry *t3c_tid; + + t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); + if (t3c_tid->ctx && t3c_tid->client->handlers && + t3c_tid->client->handlers[opcode]) { + return t3c_tid->client->handlers[opcode] (dev, skb, + t3c_tid->ctx); + } else { + printk(KERN_ERR "%s: received clientless CPL command 0x%x\n", + dev->name, opcode); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; + } +} + +static int nb_callback(struct notifier_block *self, unsigned long event, + void *ctx) +{ + switch (event) { + case (NETEVENT_NEIGH_UPDATE):{ + cxgb_neigh_update((struct neighbour *)ctx); + break; + } + case (NETEVENT_PMTU_UPDATE): + break; + case (NETEVENT_REDIRECT):{ + struct netevent_redirect *nr = ctx; + cxgb_redirect(nr->old, nr->new); + cxgb_neigh_update(nr->new->neighbour); + break; + } + default: + break; + } + return 0; +} + +static struct notifier_block nb = { + .notifier_call = nb_callback +}; + +/* + * Process a received packet with an unknown/unexpected CPL opcode. + */ +static int do_bad_cpl(struct t3cdev *dev, struct sk_buff *skb) +{ + printk(KERN_ERR "%s: received bad CPL command 0x%x\n", dev->name, + *skb->data); + return CPL_RET_BUF_DONE | CPL_RET_BAD_MSG; +} + +/* + * Handlers for each CPL opcode + */ +static cpl_handler_func cpl_handlers[NUM_CPL_CMDS]; + +/* + * Add a new handler to the CPL dispatch table. A NULL handler may be supplied + * to unregister an existing handler. + */ +void t3_register_cpl_handler(unsigned int opcode, cpl_handler_func h) +{ + if (opcode < NUM_CPL_CMDS) + cpl_handlers[opcode] = h ? h : do_bad_cpl; + else + printk(KERN_ERR "T3C: handler registration for " + "opcode %x failed\n", opcode); +} + +EXPORT_SYMBOL(t3_register_cpl_handler); + +/* + * T3CDEV's receive method. + */ +int process_rx(struct t3cdev *dev, struct sk_buff **skbs, int n) +{ + while (n--) { + struct sk_buff *skb = *skbs++; + unsigned int opcode = G_OPCODE(ntohl(skb->csum)); + int ret = cpl_handlers[opcode] (dev, skb); + +#if VALIDATE_TID + if (ret & CPL_RET_UNKNOWN_TID) { + union opcode_tid *p = cplhdr(skb); + + printk(KERN_ERR "%s: CPL message (opcode %u) had " + "unknown TID %u\n", dev->name, opcode, + G_TID(ntohl(p->opcode_tid))); + } +#endif + if (ret & CPL_RET_BUF_DONE) + kfree_skb(skb); + } + return 0; +} + +/* + * Sends an sk_buff to a T3C driver after dealing with any active network taps. + */ +int cxgb3_ofld_send(struct t3cdev *dev, struct sk_buff *skb) +{ + int r; + + local_bh_disable(); + r = dev->send(dev, skb); + local_bh_enable(); + return r; +} + +EXPORT_SYMBOL(cxgb3_ofld_send); + +static int is_offloading(struct net_device *dev) +{ + struct adapter *adapter; + int i; + + read_lock_bh(&adapter_list_lock); + list_for_each_entry(adapter, &adapter_list, adapter_list) { + for_each_port(adapter, i) { + if (dev == adapter->port[i]) { + read_unlock_bh(&adapter_list_lock); + return 1; + } + } + } + read_unlock_bh(&adapter_list_lock); + return 0; +} + +void cxgb_neigh_update(struct neighbour *neigh) +{ + struct net_device *dev = neigh->dev; + + if (dev && (is_offloading(dev))) { + struct t3cdev *tdev = T3CDEV(dev); + + BUG_ON(!tdev); + t3_l2t_update(tdev, neigh); + } +} + +static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) +{ + struct sk_buff *skb; + struct cpl_set_tcb_field *req; + + skb = alloc_skb(sizeof(*req), GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: cannot allocate skb!\n", __FUNCTION__); + return; + } + skb->priority = CPL_PRIORITY_CONTROL; + req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req)); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); + req->reply = 0; + req->cpu_idx = 0; + req->word = htons(W_TCB_L2T_IX); + req->mask = cpu_to_be64(V_TCB_L2T_IX(M_TCB_L2T_IX)); + req->val = cpu_to_be64(V_TCB_L2T_IX(e->idx)); + tdev->send(tdev, skb); +} + +void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) +{ + struct net_device *olddev, *newdev; + struct tid_info *ti; + struct t3cdev *tdev; + u32 tid; + int update_tcb; + struct l2t_entry *e; + struct t3c_tid_entry *te; + + olddev = old->neighbour->dev; + newdev = new->neighbour->dev; + if (!is_offloading(olddev)) + return; + if (!is_offloading(newdev)) { + printk(KERN_WARNING "%s: Redirect to non-offload" + "device ignored.\n", __FUNCTION__); + return; + } + tdev = T3CDEV(olddev); + BUG_ON(!tdev); + if (tdev != T3CDEV(newdev)) { + printk(KERN_WARNING "%s: Redirect to different " + "offload device ignored.\n", __FUNCTION__); + return; + } + + /* Add new L2T entry */ + e = t3_l2t_get(tdev, new->neighbour, newdev); + if (!e) { + printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n", + __FUNCTION__); + return; + } + + /* Walk tid table and notify clients of dst change. */ + ti = &(T3C_DATA(tdev))->tid_maps; + for (tid = 0; tid < ti->ntids; tid++) { + te = lookup_tid(ti, tid); + BUG_ON(!te); + if (te->ctx && te->client && te->client->redirect) { + update_tcb = te->client->redirect(te->ctx, old, new, e); + if (update_tcb) { + l2t_hold(L2DATA(tdev), e); + set_l2t_ix(tdev, tid, e); + } + } + } + l2t_release(L2DATA(tdev), e); +} + +/* + * Allocate a chunk of memory using kmalloc or, if that fails, vmalloc. + * The allocated memory is cleared. + */ +void *cxgb_alloc_mem(unsigned long size) +{ + void *p = kmalloc(size, GFP_KERNEL); + + if (!p) + p = vmalloc(size); + if (p) + memset(p, 0, size); + return p; +} + +/* + * Free memory allocated through t3_alloc_mem(). + */ +void cxgb_free_mem(void *addr) +{ + unsigned long p = (unsigned long)addr; + + if (p >= VMALLOC_START && p < VMALLOC_END) + vfree(addr); + else + kfree(addr); +} + +/* + * Allocate and initialize the TID tables. Returns 0 on success. + */ +static int init_tid_tabs(struct tid_info *t, unsigned int ntids, + unsigned int natids, unsigned int nstids, + unsigned int atid_base, unsigned int stid_base) +{ + unsigned long size = ntids * sizeof(*t->tid_tab) + + natids * sizeof(*t->atid_tab) + nstids * sizeof(*t->stid_tab); + + t->tid_tab = cxgb_alloc_mem(size); + if (!t->tid_tab) + return -ENOMEM; + + t->stid_tab = (union listen_entry *)&t->tid_tab[ntids]; + t->atid_tab = (union active_open_entry *)&t->stid_tab[nstids]; + t->ntids = ntids; + t->nstids = nstids; + t->stid_base = stid_base; + t->sfree = NULL; + t->natids = natids; + t->atid_base = atid_base; + t->afree = NULL; + t->stids_in_use = t->atids_in_use = 0; + atomic_set(&t->tids_in_use, 0); + spin_lock_init(&t->stid_lock); + spin_lock_init(&t->atid_lock); + + /* + * Setup the free lists for stid_tab and atid_tab. + */ + if (nstids) { + while (--nstids) + t->stid_tab[nstids - 1].next = &t->stid_tab[nstids]; + t->sfree = t->stid_tab; + } + if (natids) { + while (--natids) + t->atid_tab[natids - 1].next = &t->atid_tab[natids]; + t->afree = t->atid_tab; + } + return 0; +} + +static void free_tid_maps(struct tid_info *t) +{ + cxgb_free_mem(t->tid_tab); +} + +static inline void add_adapter(struct adapter *adap) +{ + write_lock_bh(&adapter_list_lock); + list_add_tail(&adap->adapter_list, &adapter_list); + write_unlock_bh(&adapter_list_lock); +} + +static inline void remove_adapter(struct adapter *adap) +{ + write_lock_bh(&adapter_list_lock); + list_del(&adap->adapter_list); + write_unlock_bh(&adapter_list_lock); +} + +int cxgb3_offload_activate(struct adapter *adapter) +{ + struct t3cdev *dev = &adapter->tdev; + int natids, err; + struct t3c_data *t; + struct tid_range stid_range, tid_range; + struct mtutab mtutab; + unsigned int l2t_capacity; + + t = kcalloc(1, sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + + err = -EOPNOTSUPP; + if (dev->ctl(dev, GET_TX_MAX_CHUNK, &t->tx_max_chunk) < 0 || + dev->ctl(dev, GET_MAX_OUTSTANDING_WR, &t->max_wrs) < 0 || + dev->ctl(dev, GET_L2T_CAPACITY, &l2t_capacity) < 0 || + dev->ctl(dev, GET_MTUS, &mtutab) < 0 || + dev->ctl(dev, GET_TID_RANGE, &tid_range) < 0 || + dev->ctl(dev, GET_STID_RANGE, &stid_range) < 0) + goto out_free; + + err = -ENOMEM; + L2DATA(dev) = t3_init_l2t(l2t_capacity); + if (!L2DATA(dev)) + goto out_free; + + natids = min(tid_range.num / 2, MAX_ATIDS); + err = init_tid_tabs(&t->tid_maps, tid_range.num, natids, + stid_range.num, ATID_BASE, stid_range.base); + if (err) + goto out_free_l2t; + + t->mtus = mtutab.mtus; + t->nmtus = mtutab.size; + + INIT_WORK(&t->tid_release_task, t3_process_tid_release_list); + spin_lock_init(&t->tid_release_lock); + INIT_LIST_HEAD(&t->list_node); + t->dev = dev; + + T3C_DATA(dev) = t; + dev->recv = process_rx; + dev->neigh_update = t3_l2t_update; + + /* Register netevent handler once */ + if (list_empty(&adapter_list)) + register_netevent_notifier(&nb); + + add_adapter(adapter); + return 0; + +out_free_l2t: + t3_free_l2t(L2DATA(dev)); + L2DATA(dev) = NULL; +out_free: + kfree(t); + return err; +} + +void cxgb3_offload_deactivate(struct adapter *adapter) +{ + struct t3cdev *tdev = &adapter->tdev; + struct t3c_data *t = T3C_DATA(tdev); + + remove_adapter(adapter); + if (list_empty(&adapter_list)) + unregister_netevent_notifier(&nb); + + free_tid_maps(&t->tid_maps); + T3C_DATA(tdev) = NULL; + t3_free_l2t(L2DATA(tdev)); + L2DATA(tdev) = NULL; + kfree(t); +} + +static inline void register_tdev(struct t3cdev *tdev) +{ + static int unit; + + mutex_lock(&cxgb3_db_lock); + snprintf(tdev->name, sizeof(tdev->name), "ofld_dev%d", unit++); + list_add_tail(&tdev->ofld_dev_list, &ofld_dev_list); + mutex_unlock(&cxgb3_db_lock); +} + +static inline void unregister_tdev(struct t3cdev *tdev) +{ + mutex_lock(&cxgb3_db_lock); + list_del(&tdev->ofld_dev_list); + mutex_unlock(&cxgb3_db_lock); +} + +void __devinit cxgb3_adapter_ofld(struct adapter *adapter) +{ + struct t3cdev *tdev = &adapter->tdev; + + INIT_LIST_HEAD(&tdev->ofld_dev_list); + + cxgb3_set_dummy_ops(tdev); + tdev->send = t3_offload_tx; + tdev->ctl = cxgb_offload_ctl; + tdev->type = adapter->params.rev == 0 ? T3A : T3B; + + register_tdev(tdev); +} + +void __devexit cxgb3_adapter_unofld(struct adapter *adapter) +{ + struct t3cdev *tdev = &adapter->tdev; + + tdev->recv = NULL; + tdev->neigh_update = NULL; + + unregister_tdev(tdev); +} + +void __init cxgb3_offload_init(void) +{ + int i; + + for (i = 0; i < NUM_CPL_CMDS; ++i) + cpl_handlers[i] = do_bad_cpl; + + t3_register_cpl_handler(CPL_SMT_WRITE_RPL, do_smt_write_rpl); + t3_register_cpl_handler(CPL_L2T_WRITE_RPL, do_l2t_write_rpl); + t3_register_cpl_handler(CPL_PASS_OPEN_RPL, do_stid_rpl); + t3_register_cpl_handler(CPL_CLOSE_LISTSRV_RPL, do_stid_rpl); + t3_register_cpl_handler(CPL_PASS_ACCEPT_REQ, do_cr); + t3_register_cpl_handler(CPL_PASS_ESTABLISH, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_RPL_RSS, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_RPL, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_URG_NOTIFY, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_DATA, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TX_DATA_ACK, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TX_DMA_ACK, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ACT_OPEN_RPL, do_act_open_rpl); + t3_register_cpl_handler(CPL_PEER_CLOSE, do_hwtid_rpl); + t3_register_cpl_handler(CPL_CLOSE_CON_RPL, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ABORT_REQ_RSS, do_abort_req_rss); + t3_register_cpl_handler(CPL_ACT_ESTABLISH, do_act_establish); + t3_register_cpl_handler(CPL_SET_TCB_RPL, do_set_tcb_rpl); + t3_register_cpl_handler(CPL_RDMA_TERMINATE, do_term); + t3_register_cpl_handler(CPL_RDMA_EC_STATUS, do_hwtid_rpl); + t3_register_cpl_handler(CPL_TRACE_PKT, do_trace); + t3_register_cpl_handler(CPL_RX_DATA_DDP, do_hwtid_rpl); + t3_register_cpl_handler(CPL_RX_DDP_COMPLETE, do_hwtid_rpl); + t3_register_cpl_handler(CPL_ISCSI_HDR, do_hwtid_rpl); +} diff -puN /dev/null drivers/net/cxgb3/cxgb3_offload.h --- /dev/null +++ a/drivers/net/cxgb3/cxgb3_offload.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2006 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _CXGB3_OFFLOAD_H +#define _CXGB3_OFFLOAD_H + +#include +#include + +#include "l2t.h" + +#include "t3cdev.h" +#include "t3_cpl.h" + +struct adapter; + +void cxgb3_offload_init(void); + +void cxgb3_adapter_ofld(struct adapter *adapter); +void cxgb3_adapter_unofld(struct adapter *adapter); +int cxgb3_offload_activate(struct adapter *adapter); +void cxgb3_offload_deactivate(struct adapter *adapter); + +void cxgb3_set_dummy_ops(struct t3cdev *dev); + +/* + * Client registration. Users of T3 driver must register themselves. + * The T3 driver will call the add function of every client for each T3 + * adapter activated, passing up the t3cdev ptr. Each client fills out an + * array of callback functions to process CPL messages. + */ + +void cxgb3_register_client(struct cxgb3_client *client); +void cxgb3_unregister_client(struct cxgb3_client *client); +void cxgb3_add_clients(struct t3cdev *tdev); +void cxgb3_remove_clients(struct t3cdev *tdev); + +typedef int (*cxgb3_cpl_handler_func)(struct t3cdev *dev, + struct sk_buff *skb, void *ctx); + +struct cxgb3_client { + char *name; + void (*add) (struct t3cdev *); + void (*remove) (struct t3cdev *); + cxgb3_cpl_handler_func *handlers; + int (*redirect)(void *ctx, struct dst_entry *old, + struct dst_entry *new, struct l2t_entry *l2t); + struct list_head client_list; +}; + +/* + * TID allocation services. + */ +int cxgb3_alloc_atid(struct t3cdev *dev, struct cxgb3_client *client, + void *ctx); +int cxgb3_alloc_stid(struct t3cdev *dev, struct cxgb3_client *client, + void *ctx); +void *cxgb3_free_atid(struct t3cdev *dev, int atid); +void cxgb3_free_stid(struct t3cdev *dev, int stid); +void cxgb3_insert_tid(struct t3cdev *dev, struct cxgb3_client *client, + void *ctx, unsigned int tid); +void cxgb3_queue_tid_release(struct t3cdev *dev, unsigned int tid); +void cxgb3_remove_tid(struct t3cdev *dev, void *ctx, unsigned int tid); + +struct t3c_tid_entry { + struct cxgb3_client *client; + void *ctx; +}; + +/* CPL message priority levels */ +enum { + CPL_PRIORITY_DATA = 0, /* data messages */ + CPL_PRIORITY_SETUP = 1, /* connection setup messages */ + CPL_PRIORITY_TEARDOWN = 0, /* connection teardown messages */ + CPL_PRIORITY_LISTEN = 1, /* listen start/stop messages */ + CPL_PRIORITY_ACK = 1, /* RX ACK messages */ + CPL_PRIORITY_CONTROL = 1 /* offload control messages */ +}; + +/* Flags for return value of CPL message handlers */ +enum { + CPL_RET_BUF_DONE = 1, /* buffer processing done, buffer may be freed */ + CPL_RET_BAD_MSG = 2, /* bad CPL message (e.g., unknown opcode) */ + CPL_RET_UNKNOWN_TID = 4 /* unexpected unknown TID */ +}; + +typedef int (*cpl_handler_func)(struct t3cdev *dev, struct sk_buff *skb); + +/* + * Returns a pointer to the first byte of the CPL header in an sk_buff that + * contains a CPL message. + */ +static inline void *cplhdr(struct sk_buff *skb) +{ + return skb->data; +} + +void t3_register_cpl_handler(unsigned int opcode, cpl_handler_func h); + +union listen_entry { + struct t3c_tid_entry t3c_tid; + union listen_entry *next; +}; + +union active_open_entry { + struct t3c_tid_entry t3c_tid; + union active_open_entry *next; +}; + +/* + * Holds the size, base address, free list start, etc of the TID, server TID, + * and active-open TID tables for a offload device. + * The tables themselves are allocated dynamically. + */ +struct tid_info { + struct t3c_tid_entry *tid_tab; + unsigned int ntids; + atomic_t tids_in_use; + + union listen_entry *stid_tab; + unsigned int nstids; + unsigned int stid_base; + + union active_open_entry *atid_tab; + unsigned int natids; + unsigned int atid_base; + + /* + * The following members are accessed R/W so we put them in their own + * cache lines. + * + * XXX We could combine the atid fields above with the lock here since + * atids are use once (unlike other tids). OTOH the above fields are + * usually in cache due to tid_tab. + */ + spinlock_t atid_lock ____cacheline_aligned_in_smp; + union active_open_entry *afree; + unsigned int atids_in_use; + + spinlock_t stid_lock ____cacheline_aligned; + union listen_entry *sfree; + unsigned int stids_in_use; +}; + +struct t3c_data { + struct list_head list_node; + struct t3cdev *dev; + unsigned int tx_max_chunk; /* max payload for TX_DATA */ + unsigned int max_wrs; /* max in-flight WRs per connection */ + unsigned int nmtus; + const unsigned short *mtus; + struct tid_info tid_maps; + + struct t3c_tid_entry *tid_release_list; + spinlock_t tid_release_lock; + struct work_struct tid_release_task; +}; + +/* + * t3cdev -> t3c_data accessor + */ +#define T3C_DATA(dev) (*(struct t3c_data **)&(dev)->l4opt) + +#endif diff -puN /dev/null drivers/net/cxgb3/firmware_exports.h --- /dev/null +++ a/drivers/net/cxgb3/firmware_exports.h @@ -0,0 +1,144 @@ +/* + * ---------------------------------------------------------------------------- + * >>>>>>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + * ---------------------------------------------------------------------------- + * Copyright 2004 (C) Chelsio Communications, Inc. (Chelsio) + * + * Chelsio Communications, Inc. owns the sole copyright to this software. + * You may not make a copy, you may not derive works herefrom, and you may + * not distribute this work to others. Other restrictions of rights may apply + * as well. This is unpublished, confidential information. All rights reserved. + * This software contains confidential information and trade secrets of Chelsio + * Communications, Inc. Use, disclosure, or reproduction is prohibited without + * the prior express written permission of Chelsio Communications, Inc. + * ---------------------------------------------------------------------------- + * >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Warranty <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + * ---------------------------------------------------------------------------- + * CHELSIO MAKES NO WARRANTY OF ANY KIND WITH REGARD TO THE USE OF THIS + * SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * ---------------------------------------------------------------------------- + * + * This is the firmware_exports.h header file, firmware interface defines. + * + * Written January 2005 by felix marti (felix@chelsio.com) + */ +#ifndef _FIRMWARE_EXPORTS_H_ +#define _FIRMWARE_EXPORTS_H_ + +/* WR OPCODES supported by the firmware. + */ +#define FW_WROPCODE_FORWARD 0x01 +#define FW_WROPCODE_BYPASS 0x05 + +#define FW_WROPCODE_TUNNEL_TX_PKT 0x03 + +#define FW_WROPOCDE_ULPTX_DATA_SGL 0x00 +#define FW_WROPCODE_ULPTX_MEM_READ 0x02 +#define FW_WROPCODE_ULPTX_PKT 0x04 +#define FW_WROPCODE_ULPTX_INVALIDATE 0x06 + +#define FW_WROPCODE_TUNNEL_RX_PKT 0x07 + +#define FW_WROPCODE_OFLD_GETTCB_RPL 0x08 +#define FW_WROPCODE_OFLD_CLOSE_CON 0x09 +#define FW_WROPCODE_OFLD_TP_ABORT_CON_REQ 0x0A +#define FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL 0x0F +#define FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ 0x0B +#define FW_WROPCODE_OFLD_TP_ABORT_CON_RPL 0x0C +#define FW_WROPCODE_OFLD_TX_DATA 0x0D +#define FW_WROPCODE_OFLD_TX_DATA_ACK 0x0E + +#define FW_WROPCODE_RI_RDMA_INIT 0x10 +#define FW_WROPCODE_RI_RDMA_WRITE 0x11 +#define FW_WROPCODE_RI_RDMA_READ_REQ 0x12 +#define FW_WROPCODE_RI_RDMA_READ_RESP 0x13 +#define FW_WROPCODE_RI_SEND 0x14 +#define FW_WROPCODE_RI_TERMINATE 0x15 +#define FW_WROPCODE_RI_RDMA_READ 0x16 +#define FW_WROPCODE_RI_RECEIVE 0x17 +#define FW_WROPCODE_RI_BIND_MW 0x18 +#define FW_WROPCODE_RI_FASTREGISTER_MR 0x19 +#define FW_WROPCODE_RI_LOCAL_INV 0x1A +#define FW_WROPCODE_RI_MODIFY_QP 0x1B +#define FW_WROPCODE_RI_BYPASS 0x1C + +#define FW_WROPOCDE_RSVD 0x1E + +#define FW_WROPCODE_SGE_EGRESSCONTEXT_RR 0x1F + +#define FW_WROPCODE_MNGT 0x1D +#define FW_MNGTOPCODE_PKTSCHED_SET 0x00 + +/* Maximum size of a WR sent from the host, limited by the SGE. + * + * Note: WR coming from ULP or TP are only limited by CIM. + */ +#define FW_WR_SIZE 128 + +/* Maximum number of outstanding WRs sent from the host. Value must be + * programmed in the CTRL/TUNNEL/QP SGE Egress Context and used by + * offload modules to limit the number of WRs per connection. + */ +#define FW_T3_WR_NUM 16 +#define FW_N3_WR_NUM 7 + +#ifndef N3 +# define FW_WR_NUM FW_T3_WR_NUM +#else +# define FW_WR_NUM FW_N3_WR_NUM +#endif + +/* FW_TUNNEL_NUM corresponds to the number of supported TUNNEL Queues. These + * queues must start at SGE Egress Context FW_TUNNEL_SGEEC_START and must + * start at 'TID' (or 'uP Token') FW_TUNNEL_TID_START. + * + * Ingress Traffic (e.g. DMA completion credit) for TUNNEL Queue[i] is sent + * to RESP Queue[i]. + */ +#define FW_TUNNEL_NUM 8 +#define FW_TUNNEL_SGEEC_START 8 +#define FW_TUNNEL_TID_START 65544 + +/* FW_CTRL_NUM corresponds to the number of supported CTRL Queues. These queues + * must start at SGE Egress Context FW_CTRL_SGEEC_START and must start at 'TID' + * (or 'uP Token') FW_CTRL_TID_START. + * + * Ingress Traffic for CTRL Queue[i] is sent to RESP Queue[i]. + */ +#define FW_CTRL_NUM 8 +#define FW_CTRL_SGEEC_START 65528 +#define FW_CTRL_TID_START 65536 + +/* FW_OFLD_NUM corresponds to the number of supported OFFLOAD Queues. These + * queues must start at SGE Egress Context FW_OFLD_SGEEC_START. + * + * Note: the 'uP Token' in the SGE Egress Context fields is irrelevant for + * OFFLOAD Queues, as the host is responsible for providing the correct TID in + * every WR. + * + * Ingress Trafffic for OFFLOAD Queue[i] is sent to RESP Queue[i]. + */ +#define FW_OFLD_NUM 8 +#define FW_OFLD_SGEEC_START 0 + +/* + * + */ +#define FW_RI_NUM 1 +#define FW_RI_SGEEC_START 65527 +#define FW_RI_TID_START 65552 + +/* + * The RX_PKT_TID + */ +#define FW_RX_PKT_NUM 1 +#define FW_RX_PKT_TID_START 65553 + +/* FW_WRC_NUM corresponds to the number of Work Request Context that supported + * by the firmware. + */ +#define FW_WRC_NUM \ + (65536 + FW_TUNNEL_NUM + FW_CTRL_NUM + FW_RI_NUM + FW_RX_PKT_NUM) + +#endif /* _FIRMWARE_EXPORTS_H_ */ diff -puN /dev/null drivers/net/cxgb3/l2t.c --- /dev/null +++ a/drivers/net/cxgb3/l2t.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2006 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "t3cdev.h" +#include "cxgb3_defs.h" +#include "l2t.h" +#include "t3_cpl.h" +#include "firmware_exports.h" + +#define VLAN_NONE 0xfff + +/* + * Module locking notes: There is a RW lock protecting the L2 table as a + * whole plus a spinlock per L2T entry. Entry lookups and allocations happen + * under the protection of the table lock, individual entry changes happen + * while holding that entry's spinlock. The table lock nests outside the + * entry locks. Allocations of new entries take the table lock as writers so + * no other lookups can happen while allocating new entries. Entry updates + * take the table lock as readers so multiple entries can be updated in + * parallel. An L2T entry can be dropped by decrementing its reference count + * and therefore can happen in parallel with entry allocation but no entry + * can change state or increment its ref count during allocation as both of + * these perform lookups. + */ + +static inline unsigned int vlan_prio(const struct l2t_entry *e) +{ + return e->vlan >> 13; +} + +static inline unsigned int arp_hash(u32 key, int ifindex, + const struct l2t_data *d) +{ + return jhash_2words(key, ifindex, 0) & (d->nentries - 1); +} + +static inline void neigh_replace(struct l2t_entry *e, struct neighbour *n) +{ + neigh_hold(n); + if (e->neigh) + neigh_release(e->neigh); + e->neigh = n; +} + +/* + * Set up an L2T entry and send any packets waiting in the arp queue. The + * supplied skb is used for the CPL_L2T_WRITE_REQ. Must be called with the + * entry locked. + */ +static int setup_l2e_send_pending(struct t3cdev *dev, struct sk_buff *skb, + struct l2t_entry *e) +{ + struct cpl_l2t_write_req *req; + + if (!skb) { + skb = alloc_skb(sizeof(*req), GFP_ATOMIC); + if (!skb) + return -ENOMEM; + } + + req = (struct cpl_l2t_write_req *)__skb_put(skb, sizeof(*req)); + req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx)); + req->params = htonl(V_L2T_W_IDX(e->idx) | V_L2T_W_IFF(e->smt_idx) | + V_L2T_W_VLAN(e->vlan & VLAN_VID_MASK) | + V_L2T_W_PRIO(vlan_prio(e))); + memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac)); + memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); + skb->priority = CPL_PRIORITY_CONTROL; + cxgb3_ofld_send(dev, skb); + while (e->arpq_head) { + skb = e->arpq_head; + e->arpq_head = skb->next; + skb->next = NULL; + cxgb3_ofld_send(dev, skb); + } + e->arpq_tail = NULL; + e->state = L2T_STATE_VALID; + + return 0; +} + +/* + * Add a packet to the an L2T entry's queue of packets awaiting resolution. + * Must be called with the entry's lock held. + */ +static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) +{ + skb->next = NULL; + if (e->arpq_head) + e->arpq_tail->next = skb; + else + e->arpq_head = skb; + e->arpq_tail = skb; +} + +int t3_l2t_send_slow(struct t3cdev *dev, struct sk_buff *skb, + struct l2t_entry *e) +{ +again: + switch (e->state) { + case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ + neigh_event_send(e->neigh, NULL); + spin_lock_bh(&e->lock); + if (e->state == L2T_STATE_STALE) + e->state = L2T_STATE_VALID; + spin_unlock_bh(&e->lock); + case L2T_STATE_VALID: /* fast-path, send the packet on */ + return cxgb3_ofld_send(dev, skb); + case L2T_STATE_RESOLVING: + spin_lock_bh(&e->lock); + if (e->state != L2T_STATE_RESOLVING) { + /* ARP already completed */ + spin_unlock_bh(&e->lock); + goto again; + } + arpq_enqueue(e, skb); + spin_unlock_bh(&e->lock); + + /* + * Only the first packet added to the arpq should kick off + * resolution. However, because the alloc_skb below can fail, + * we allow each packet added to the arpq to retry resolution + * as a way of recovering from transient memory exhaustion. + * A better way would be to use a work request to retry L2T + * entries when there's no memory. + */ + if (!neigh_event_send(e->neigh, NULL)) { + skb = alloc_skb(sizeof(struct cpl_l2t_write_req), + GFP_ATOMIC); + if (!skb) + break; + + spin_lock_bh(&e->lock); + if (e->arpq_head) + setup_l2e_send_pending(dev, skb, e); + else /* we lost the race */ + __kfree_skb(skb); + spin_unlock_bh(&e->lock); + } + } + return 0; +} + +EXPORT_SYMBOL(t3_l2t_send_slow); + +void t3_l2t_send_event(struct t3cdev *dev, struct l2t_entry *e) +{ +again: + switch (e->state) { + case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ + neigh_event_send(e->neigh, NULL); + spin_lock_bh(&e->lock); + if (e->state == L2T_STATE_STALE) { + e->state = L2T_STATE_VALID; + } + spin_unlock_bh(&e->lock); + return; + case L2T_STATE_VALID: /* fast-path, send the packet on */ + return; + case L2T_STATE_RESOLVING: + spin_lock_bh(&e->lock); + if (e->state != L2T_STATE_RESOLVING) { + /* ARP already completed */ + spin_unlock_bh(&e->lock); + goto again; + } + spin_unlock_bh(&e->lock); + + /* + * Only the first packet added to the arpq should kick off + * resolution. However, because the alloc_skb below can fail, + * we allow each packet added to the arpq to retry resolution + * as a way of recovering from transient memory exhaustion. + * A better way would be to use a work request to retry L2T + * entries when there's no memory. + */ + neigh_event_send(e->neigh, NULL); + } + return; +} + +EXPORT_SYMBOL(t3_l2t_send_event); + +/* + * Allocate a free L2T entry. Must be called with l2t_data.lock held. + */ +static struct l2t_entry *alloc_l2e(struct l2t_data *d) +{ + struct l2t_entry *end, *e, **p; + + if (!atomic_read(&d->nfree)) + return NULL; + + /* there's definitely a free entry */ + for (e = d->rover, end = &d->l2tab[d->nentries]; e != end; ++e) + if (atomic_read(&e->refcnt) == 0) + goto found; + + for (e = &d->l2tab[1]; atomic_read(&e->refcnt); ++e) ; +found: + d->rover = e + 1; + atomic_dec(&d->nfree); + + /* + * The entry we found may be an inactive entry that is + * presently in the hash table. We need to remove it. + */ + if (e->state != L2T_STATE_UNUSED) { + int hash = arp_hash(e->addr, e->ifindex, d); + + for (p = &d->l2tab[hash].first; *p; p = &(*p)->next) + if (*p == e) { + *p = e->next; + break; + } + e->state = L2T_STATE_UNUSED; + } + return e; +} + +/* + * Called when an L2T entry has no more users. The entry is left in the hash + * table since it is likely to be reused but we also bump nfree to indicate + * that the entry can be reallocated for a different neighbor. We also drop + * the existing neighbor reference in case the neighbor is going away and is + * waiting on our reference. + * + * Because entries can be reallocated to other neighbors once their ref count + * drops to 0 we need to take the entry's lock to avoid races with a new + * incarnation. + */ +void t3_l2e_free(struct l2t_data *d, struct l2t_entry *e) +{ + spin_lock_bh(&e->lock); + if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ + if (e->neigh) { + neigh_release(e->neigh); + e->neigh = NULL; + } + } + spin_unlock_bh(&e->lock); + atomic_inc(&d->nfree); +} + +EXPORT_SYMBOL(t3_l2e_free); + +/* + * Update an L2T entry that was previously used for the same next hop as neigh. + * Must be called with softirqs disabled. + */ +static inline void reuse_entry(struct l2t_entry *e, struct neighbour *neigh) +{ + unsigned int nud_state; + + spin_lock(&e->lock); /* avoid race with t3_l2t_free */ + + if (neigh != e->neigh) + neigh_replace(e, neigh); + nud_state = neigh->nud_state; + if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)) || + !(nud_state & NUD_VALID)) + e->state = L2T_STATE_RESOLVING; + else if (nud_state & NUD_CONNECTED) + e->state = L2T_STATE_VALID; + else + e->state = L2T_STATE_STALE; + spin_unlock(&e->lock); +} + +struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh, + struct net_device *dev) +{ + struct l2t_entry *e; + struct l2t_data *d = L2DATA(cdev); + u32 addr = *(u32 *) neigh->primary_key; + int ifidx = neigh->dev->ifindex; + int hash = arp_hash(addr, ifidx, d); + struct port_info *p = netdev_priv(dev); + int smt_idx = p->port_id; + + write_lock_bh(&d->lock); + for (e = d->l2tab[hash].first; e; e = e->next) + if (e->addr == addr && e->ifindex == ifidx && + e->smt_idx == smt_idx) { + l2t_hold(d, e); + if (atomic_read(&e->refcnt) == 1) + reuse_entry(e, neigh); + goto done; + } + + /* Need to allocate a new entry */ + e = alloc_l2e(d); + if (e) { + spin_lock(&e->lock); /* avoid race with t3_l2t_free */ + e->next = d->l2tab[hash].first; + d->l2tab[hash].first = e; + e->state = L2T_STATE_RESOLVING; + e->addr = addr; + e->ifindex = ifidx; + e->smt_idx = smt_idx; + atomic_set(&e->refcnt, 1); + neigh_replace(e, neigh); + if (neigh->dev->priv_flags & IFF_802_1Q_VLAN) + e->vlan = VLAN_DEV_INFO(neigh->dev)->vlan_id; + else + e->vlan = VLAN_NONE; + spin_unlock(&e->lock); + } +done: + write_unlock_bh(&d->lock); + return e; +} + +EXPORT_SYMBOL(t3_l2t_get); + +/* + * Called when address resolution fails for an L2T entry to handle packets + * on the arpq head. If a packet specifies a failure handler it is invoked, + * otherwise the packets is sent to the offload device. + * + * XXX: maybe we should abandon the latter behavior and just require a failure + * handler. + */ +static void handle_failed_resolution(struct t3cdev *dev, struct sk_buff *arpq) +{ + while (arpq) { + struct sk_buff *skb = arpq; + struct l2t_skb_cb *cb = L2T_SKB_CB(skb); + + arpq = skb->next; + skb->next = NULL; + if (cb->arp_failure_handler) + cb->arp_failure_handler(dev, skb); + else + cxgb3_ofld_send(dev, skb); + } +} + +/* + * Called when the host's ARP layer makes a change to some entry that is + * loaded into the HW L2 table. + */ +void t3_l2t_update(struct t3cdev *dev, struct neighbour *neigh) +{ + struct l2t_entry *e; + struct sk_buff *arpq = NULL; + struct l2t_data *d = L2DATA(dev); + u32 addr = *(u32 *) neigh->primary_key; + int ifidx = neigh->dev->ifindex; + int hash = arp_hash(addr, ifidx, d); + + read_lock_bh(&d->lock); + for (e = d->l2tab[hash].first; e; e = e->next) + if (e->addr == addr && e->ifindex == ifidx) { + spin_lock(&e->lock); + goto found; + } + read_unlock_bh(&d->lock); + return; + +found: + read_unlock(&d->lock); + if (atomic_read(&e->refcnt)) { + if (neigh != e->neigh) + neigh_replace(e, neigh); + + if (e->state == L2T_STATE_RESOLVING) { + if (neigh->nud_state & NUD_FAILED) { + arpq = e->arpq_head; + e->arpq_head = e->arpq_tail = NULL; + } else if (neigh_is_connected(neigh)) + setup_l2e_send_pending(dev, NULL, e); + } else { + e->state = neigh_is_connected(neigh) ? + L2T_STATE_VALID : L2T_STATE_STALE; + if (memcmp(e->dmac, neigh->ha, 6)) + setup_l2e_send_pending(dev, NULL, e); + } + } + spin_unlock_bh(&e->lock); + + if (arpq) + handle_failed_resolution(dev, arpq); +} + +struct l2t_data *t3_init_l2t(unsigned int l2t_capacity) +{ + struct l2t_data *d; + int i, size = sizeof(*d) + l2t_capacity * sizeof(struct l2t_entry); + + d = cxgb_alloc_mem(size); + if (!d) + return NULL; + + d->nentries = l2t_capacity; + d->rover = &d->l2tab[1]; /* entry 0 is not used */ + atomic_set(&d->nfree, l2t_capacity - 1); + rwlock_init(&d->lock); + + for (i = 0; i < l2t_capacity; ++i) { + d->l2tab[i].idx = i; + d->l2tab[i].state = L2T_STATE_UNUSED; + spin_lock_init(&d->l2tab[i].lock); + atomic_set(&d->l2tab[i].refcnt, 0); + } + return d; +} + +void t3_free_l2t(struct l2t_data *d) +{ + cxgb_free_mem(d); +} + diff -puN /dev/null drivers/net/cxgb3/l2t.h --- /dev/null +++ a/drivers/net/cxgb3/l2t.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _CHELSIO_L2T_H +#define _CHELSIO_L2T_H + +#include +#include "t3cdev.h" +#include + +enum { + L2T_STATE_VALID, /* entry is up to date */ + L2T_STATE_STALE, /* entry may be used but needs revalidation */ + L2T_STATE_RESOLVING, /* entry needs address resolution */ + L2T_STATE_UNUSED /* entry not in use */ +}; + +struct neighbour; +struct sk_buff; + +/* + * Each L2T entry plays multiple roles. First of all, it keeps state for the + * corresponding entry of the HW L2 table and maintains a queue of offload + * packets awaiting address resolution. Second, it is a node of a hash table + * chain, where the nodes of the chain are linked together through their next + * pointer. Finally, each node is a bucket of a hash table, pointing to the + * first element in its chain through its first pointer. + */ +struct l2t_entry { + u16 state; /* entry state */ + u16 idx; /* entry index */ + u32 addr; /* dest IP address */ + int ifindex; /* neighbor's net_device's ifindex */ + u16 smt_idx; /* SMT index */ + u16 vlan; /* VLAN TCI (id: bits 0-11, prio: 13-15 */ + struct neighbour *neigh; /* associated neighbour */ + struct l2t_entry *first; /* start of hash chain */ + struct l2t_entry *next; /* next l2t_entry on chain */ + struct sk_buff *arpq_head; /* queue of packets awaiting resolution */ + struct sk_buff *arpq_tail; + spinlock_t lock; + atomic_t refcnt; /* entry reference count */ + u8 dmac[6]; /* neighbour's MAC address */ +}; + +struct l2t_data { + unsigned int nentries; /* number of entries */ + struct l2t_entry *rover; /* starting point for next allocation */ + atomic_t nfree; /* number of free entries */ + rwlock_t lock; + struct l2t_entry l2tab[0]; +}; + +typedef void (*arp_failure_handler_func)(struct t3cdev * dev, + struct sk_buff * skb); + +/* + * Callback stored in an skb to handle address resolution failure. + */ +struct l2t_skb_cb { + arp_failure_handler_func arp_failure_handler; +}; + +#define L2T_SKB_CB(skb) ((struct l2t_skb_cb *)(skb)->cb) + +static inline void set_arp_failure_handler(struct sk_buff *skb, + arp_failure_handler_func hnd) +{ + L2T_SKB_CB(skb)->arp_failure_handler = hnd; +} + +/* + * Getting to the L2 data from an offload device. + */ +#define L2DATA(dev) ((dev)->l2opt) + +#define W_TCB_L2T_IX 0 +#define S_TCB_L2T_IX 7 +#define M_TCB_L2T_IX 0x7ffULL +#define V_TCB_L2T_IX(x) ((x) << S_TCB_L2T_IX) + +void t3_l2e_free(struct l2t_data *d, struct l2t_entry *e); +void t3_l2t_update(struct t3cdev *dev, struct neighbour *neigh); +struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh, + struct net_device *dev); +int t3_l2t_send_slow(struct t3cdev *dev, struct sk_buff *skb, + struct l2t_entry *e); +void t3_l2t_send_event(struct t3cdev *dev, struct l2t_entry *e); +struct l2t_data *t3_init_l2t(unsigned int l2t_capacity); +void t3_free_l2t(struct l2t_data *d); + +int cxgb3_ofld_send(struct t3cdev *dev, struct sk_buff *skb); + +static inline int l2t_send(struct t3cdev *dev, struct sk_buff *skb, + struct l2t_entry *e) +{ + if (likely(e->state == L2T_STATE_VALID)) + return cxgb3_ofld_send(dev, skb); + return t3_l2t_send_slow(dev, skb, e); +} + +static inline void l2t_release(struct l2t_data *d, struct l2t_entry *e) +{ + if (atomic_dec_and_test(&e->refcnt)) + t3_l2e_free(d, e); +} + +static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) +{ + if (atomic_add_return(1, &e->refcnt) == 1) /* 0 -> 1 transition */ + atomic_dec(&d->nfree); +} + +#endif diff -puN /dev/null drivers/net/cxgb3/mc5.c --- /dev/null +++ a/drivers/net/cxgb3/mc5.c @@ -0,0 +1,453 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include "common.h" +#include "regs.h" + +enum { + IDT75P52100 = 4, + IDT75N43102 = 5 +}; + +/* DBGI command mode */ +enum { + DBGI_MODE_MBUS = 0, + DBGI_MODE_IDT52100 = 5 +}; + +/* IDT 75P52100 commands */ +#define IDT_CMD_READ 0 +#define IDT_CMD_WRITE 1 +#define IDT_CMD_SEARCH 2 +#define IDT_CMD_LEARN 3 + +/* IDT LAR register address and value for 144-bit mode (low 32 bits) */ +#define IDT_LAR_ADR0 0x180006 +#define IDT_LAR_MODE144 0xffff0000 + +/* IDT SCR and SSR addresses (low 32 bits) */ +#define IDT_SCR_ADR0 0x180000 +#define IDT_SSR0_ADR0 0x180002 +#define IDT_SSR1_ADR0 0x180004 + +/* IDT GMR base address (low 32 bits) */ +#define IDT_GMR_BASE_ADR0 0x180020 + +/* IDT data and mask array base addresses (low 32 bits) */ +#define IDT_DATARY_BASE_ADR0 0 +#define IDT_MSKARY_BASE_ADR0 0x80000 + +/* IDT 75N43102 commands */ +#define IDT4_CMD_SEARCH144 3 +#define IDT4_CMD_WRITE 4 +#define IDT4_CMD_READ 5 + +/* IDT 75N43102 SCR address (low 32 bits) */ +#define IDT4_SCR_ADR0 0x3 + +/* IDT 75N43102 GMR base addresses (low 32 bits) */ +#define IDT4_GMR_BASE0 0x10 +#define IDT4_GMR_BASE1 0x20 +#define IDT4_GMR_BASE2 0x30 + +/* IDT 75N43102 data and mask array base addresses (low 32 bits) */ +#define IDT4_DATARY_BASE_ADR0 0x1000000 +#define IDT4_MSKARY_BASE_ADR0 0x2000000 + +#define MAX_WRITE_ATTEMPTS 5 + +#define MAX_ROUTES 2048 + +/* + * Issue a command to the TCAM and wait for its completion. The address and + * any data required by the command must have been setup by the caller. + */ +static int mc5_cmd_write(struct adapter *adapter, u32 cmd) +{ + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_CMD, cmd); + return t3_wait_op_done(adapter, A_MC5_DB_DBGI_RSP_STATUS, + F_DBGIRSPVALID, 1, MAX_WRITE_ATTEMPTS, 1); +} + +static inline void dbgi_wr_addr3(struct adapter *adapter, u32 v1, u32 v2, + u32 v3) +{ + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR0, v1); + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR1, v2); + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR2, v3); +} + +static inline void dbgi_wr_data3(struct adapter *adapter, u32 v1, u32 v2, + u32 v3) +{ + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_DATA0, v1); + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_DATA1, v2); + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_DATA2, v3); +} + +static inline void dbgi_rd_rsp3(struct adapter *adapter, u32 *v1, u32 *v2, + u32 *v3) +{ + *v1 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA0); + *v2 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA1); + *v3 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA2); +} + +/* + * Write data to the TCAM register at address (0, 0, addr_lo) using the TCAM + * command cmd. The data to be written must have been set up by the caller. + * Returns -1 on failure, 0 on success. + */ +static int mc5_write(struct adapter *adapter, u32 addr_lo, u32 cmd) +{ + t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR0, addr_lo); + if (mc5_cmd_write(adapter, cmd) == 0) + return 0; + CH_ERR(adapter, "MC5 timeout writing to TCAM address 0x%x\n", + addr_lo); + return -1; +} + +static int init_mask_data_array(struct mc5 *mc5, u32 mask_array_base, + u32 data_array_base, u32 write_cmd, + int addr_shift) +{ + unsigned int i; + struct adapter *adap = mc5->adapter; + + /* + * We need the size of the TCAM data and mask arrays in terms of + * 72-bit entries. + */ + unsigned int size72 = mc5->tcam_size; + unsigned int server_base = t3_read_reg(adap, A_MC5_DB_SERVER_INDEX); + + if (mc5->mode == MC5_MODE_144_BIT) { + size72 *= 2; /* 1 144-bit entry is 2 72-bit entries */ + server_base *= 2; + } + + /* Clear the data array */ + dbgi_wr_data3(adap, 0, 0, 0); + for (i = 0; i < size72; i++) + if (mc5_write(adap, data_array_base + (i << addr_shift), + write_cmd)) + return -1; + + /* Initialize the mask array. */ + dbgi_wr_data3(adap, 0xffffffff, 0xffffffff, 0xff); + for (i = 0; i < size72; i++) { + if (i == server_base) /* entering server or routing region */ + t3_write_reg(adap, A_MC5_DB_DBGI_REQ_DATA0, + mc5->mode == MC5_MODE_144_BIT ? + 0xfffffff9 : 0xfffffffd); + if (mc5_write(adap, mask_array_base + (i << addr_shift), + write_cmd)) + return -1; + } + return 0; +} + +static int init_idt52100(struct mc5 *mc5) +{ + int i; + struct adapter *adap = mc5->adapter; + + t3_write_reg(adap, A_MC5_DB_RSP_LATENCY, + V_RDLAT(0x15) | V_LRNLAT(0x15) | V_SRCHLAT(0x15)); + t3_write_reg(adap, A_MC5_DB_PART_ID_INDEX, 2); + + /* + * Use GMRs 14-15 for ELOOKUP, GMRs 12-13 for SYN lookups, and + * GMRs 8-9 for ACK- and AOPEN searches. + */ + t3_write_reg(adap, A_MC5_DB_POPEN_DATA_WR_CMD, IDT_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_POPEN_MASK_WR_CMD, IDT_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_AOPEN_SRCH_CMD, IDT_CMD_SEARCH); + t3_write_reg(adap, A_MC5_DB_AOPEN_LRN_CMD, IDT_CMD_LEARN); + t3_write_reg(adap, A_MC5_DB_SYN_SRCH_CMD, IDT_CMD_SEARCH | 0x6000); + t3_write_reg(adap, A_MC5_DB_SYN_LRN_CMD, IDT_CMD_LEARN); + t3_write_reg(adap, A_MC5_DB_ACK_SRCH_CMD, IDT_CMD_SEARCH); + t3_write_reg(adap, A_MC5_DB_ACK_LRN_CMD, IDT_CMD_LEARN); + t3_write_reg(adap, A_MC5_DB_ILOOKUP_CMD, IDT_CMD_SEARCH); + t3_write_reg(adap, A_MC5_DB_ELOOKUP_CMD, IDT_CMD_SEARCH | 0x7000); + t3_write_reg(adap, A_MC5_DB_DATA_WRITE_CMD, IDT_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_DATA_READ_CMD, IDT_CMD_READ); + + /* Set DBGI command mode for IDT TCAM. */ + t3_write_reg(adap, A_MC5_DB_DBGI_CONFIG, DBGI_MODE_IDT52100); + + /* Set up LAR */ + dbgi_wr_data3(adap, IDT_LAR_MODE144, 0, 0); + if (mc5_write(adap, IDT_LAR_ADR0, IDT_CMD_WRITE)) + goto err; + + /* Set up SSRs */ + dbgi_wr_data3(adap, 0xffffffff, 0xffffffff, 0); + if (mc5_write(adap, IDT_SSR0_ADR0, IDT_CMD_WRITE) || + mc5_write(adap, IDT_SSR1_ADR0, IDT_CMD_WRITE)) + goto err; + + /* Set up GMRs */ + for (i = 0; i < 32; ++i) { + if (i >= 12 && i < 15) + dbgi_wr_data3(adap, 0xfffffff9, 0xffffffff, 0xff); + else if (i == 15) + dbgi_wr_data3(adap, 0xfffffff9, 0xffff8007, 0xff); + else + dbgi_wr_data3(adap, 0xffffffff, 0xffffffff, 0xff); + + if (mc5_write(adap, IDT_GMR_BASE_ADR0 + i, IDT_CMD_WRITE)) + goto err; + } + + /* Set up SCR */ + dbgi_wr_data3(adap, 1, 0, 0); + if (mc5_write(adap, IDT_SCR_ADR0, IDT_CMD_WRITE)) + goto err; + + return init_mask_data_array(mc5, IDT_MSKARY_BASE_ADR0, + IDT_DATARY_BASE_ADR0, IDT_CMD_WRITE, 0); +err: + return -EIO; +} + +static int init_idt43102(struct mc5 *mc5) +{ + int i; + struct adapter *adap = mc5->adapter; + + t3_write_reg(adap, A_MC5_DB_RSP_LATENCY, + adap->params.rev == 0 ? V_RDLAT(0xd) | V_SRCHLAT(0x11) : + V_RDLAT(0xd) | V_SRCHLAT(0x12)); + + /* + * Use GMRs 24-25 for ELOOKUP, GMRs 20-21 for SYN lookups, and no mask + * for ACK- and AOPEN searches. + */ + t3_write_reg(adap, A_MC5_DB_POPEN_DATA_WR_CMD, IDT4_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_POPEN_MASK_WR_CMD, IDT4_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_AOPEN_SRCH_CMD, + IDT4_CMD_SEARCH144 | 0x3800); + t3_write_reg(adap, A_MC5_DB_SYN_SRCH_CMD, IDT4_CMD_SEARCH144); + t3_write_reg(adap, A_MC5_DB_ACK_SRCH_CMD, IDT4_CMD_SEARCH144 | 0x3800); + t3_write_reg(adap, A_MC5_DB_ILOOKUP_CMD, IDT4_CMD_SEARCH144 | 0x3800); + t3_write_reg(adap, A_MC5_DB_ELOOKUP_CMD, IDT4_CMD_SEARCH144 | 0x800); + t3_write_reg(adap, A_MC5_DB_DATA_WRITE_CMD, IDT4_CMD_WRITE); + t3_write_reg(adap, A_MC5_DB_DATA_READ_CMD, IDT4_CMD_READ); + + t3_write_reg(adap, A_MC5_DB_PART_ID_INDEX, 3); + + /* Set DBGI command mode for IDT TCAM. */ + t3_write_reg(adap, A_MC5_DB_DBGI_CONFIG, DBGI_MODE_IDT52100); + + /* Set up GMRs */ + dbgi_wr_data3(adap, 0xffffffff, 0xffffffff, 0xff); + for (i = 0; i < 7; ++i) + if (mc5_write(adap, IDT4_GMR_BASE0 + i, IDT4_CMD_WRITE)) + goto err; + + for (i = 0; i < 4; ++i) + if (mc5_write(adap, IDT4_GMR_BASE2 + i, IDT4_CMD_WRITE)) + goto err; + + dbgi_wr_data3(adap, 0xfffffff9, 0xffffffff, 0xff); + if (mc5_write(adap, IDT4_GMR_BASE1, IDT4_CMD_WRITE) || + mc5_write(adap, IDT4_GMR_BASE1 + 1, IDT4_CMD_WRITE) || + mc5_write(adap, IDT4_GMR_BASE1 + 4, IDT4_CMD_WRITE)) + goto err; + + dbgi_wr_data3(adap, 0xfffffff9, 0xffff8007, 0xff); + if (mc5_write(adap, IDT4_GMR_BASE1 + 5, IDT4_CMD_WRITE)) + goto err; + + /* Set up SCR */ + dbgi_wr_data3(adap, 0xf0000000, 0, 0); + if (mc5_write(adap, IDT4_SCR_ADR0, IDT4_CMD_WRITE)) + goto err; + + return init_mask_data_array(mc5, IDT4_MSKARY_BASE_ADR0, + IDT4_DATARY_BASE_ADR0, IDT4_CMD_WRITE, 1); +err: + return -EIO; +} + +/* Put MC5 in DBGI mode. */ +static inline void mc5_dbgi_mode_enable(const struct mc5 *mc5) +{ + t3_write_reg(mc5->adapter, A_MC5_DB_CONFIG, + V_TMMODE(mc5->mode == MC5_MODE_72_BIT) | F_DBGIEN); +} + +/* Put MC5 in M-Bus mode. */ +static void mc5_dbgi_mode_disable(const struct mc5 *mc5) +{ + t3_write_reg(mc5->adapter, A_MC5_DB_CONFIG, + V_TMMODE(mc5->mode == MC5_MODE_72_BIT) | + V_COMPEN(mc5->mode == MC5_MODE_72_BIT) | + V_PRTYEN(mc5->parity_enabled) | F_MBUSEN); +} + +/* + * Initialization that requires the OS and protocol layers to already + * be intialized goes here. + */ +int t3_mc5_init(struct mc5 *mc5, unsigned int nservers, unsigned int nfilters, + unsigned int nroutes) +{ + u32 cfg; + int err; + unsigned int tcam_size = mc5->tcam_size; + struct adapter *adap = mc5->adapter; + + if (nroutes > MAX_ROUTES || nroutes + nservers + nfilters > tcam_size) + return -EINVAL; + + /* Reset the TCAM */ + cfg = t3_read_reg(adap, A_MC5_DB_CONFIG) & ~F_TMMODE; + cfg |= V_TMMODE(mc5->mode == MC5_MODE_72_BIT) | F_TMRST; + t3_write_reg(adap, A_MC5_DB_CONFIG, cfg); + if (t3_wait_op_done(adap, A_MC5_DB_CONFIG, F_TMRDY, 1, 500, 0)) { + CH_ERR(adap, "TCAM reset timed out\n"); + return -1; + } + + t3_write_reg(adap, A_MC5_DB_ROUTING_TABLE_INDEX, tcam_size - nroutes); + t3_write_reg(adap, A_MC5_DB_FILTER_TABLE, + tcam_size - nroutes - nfilters); + t3_write_reg(adap, A_MC5_DB_SERVER_INDEX, + tcam_size - nroutes - nfilters - nservers); + + mc5->parity_enabled = 1; + + /* All the TCAM addresses we access have only the low 32 bits non 0 */ + t3_write_reg(adap, A_MC5_DB_DBGI_REQ_ADDR1, 0); + t3_write_reg(adap, A_MC5_DB_DBGI_REQ_ADDR2, 0); + + mc5_dbgi_mode_enable(mc5); + + switch (mc5->part_type) { + case IDT75P52100: + err = init_idt52100(mc5); + break; + case IDT75N43102: + err = init_idt43102(mc5); + break; + default: + CH_ERR(adap, "Unsupported TCAM type %d\n", mc5->part_type); + err = -EINVAL; + break; + } + + mc5_dbgi_mode_disable(mc5); + return err; +} + +/* + * read_mc5_range - dump a part of the memory managed by MC5 + * @mc5: the MC5 handle + * @start: the start address for the dump + * @n: number of 72-bit words to read + * @buf: result buffer + * + * Read n 72-bit words from MC5 memory from the given start location. + */ +int t3_read_mc5_range(const struct mc5 *mc5, unsigned int start, + unsigned int n, u32 *buf) +{ + u32 read_cmd; + int err = 0; + struct adapter *adap = mc5->adapter; + + if (mc5->part_type == IDT75P52100) + read_cmd = IDT_CMD_READ; + else if (mc5->part_type == IDT75N43102) + read_cmd = IDT4_CMD_READ; + else + return -EINVAL; + + mc5_dbgi_mode_enable(mc5); + + while (n--) { + t3_write_reg(adap, A_MC5_DB_DBGI_REQ_ADDR0, start++); + if (mc5_cmd_write(adap, read_cmd)) { + err = -EIO; + break; + } + dbgi_rd_rsp3(adap, buf + 2, buf + 1, buf); + buf += 3; + } + + mc5_dbgi_mode_disable(mc5); + return 0; +} + +#define MC5_INT_FATAL (F_PARITYERR | F_REQQPARERR | F_DISPQPARERR) + +/* + * MC5 interrupt handler + */ +void t3_mc5_intr_handler(struct mc5 *mc5) +{ + struct adapter *adap = mc5->adapter; + u32 cause = t3_read_reg(adap, A_MC5_DB_INT_CAUSE); + + if ((cause & F_PARITYERR) && mc5->parity_enabled) { + CH_ALERT(adap, "MC5 parity error\n"); + mc5->stats.parity_err++; + } + + if (cause & F_REQQPARERR) { + CH_ALERT(adap, "MC5 request queue parity error\n"); + mc5->stats.reqq_parity_err++; + } + + if (cause & F_DISPQPARERR) { + CH_ALERT(adap, "MC5 dispatch queue parity error\n"); + mc5->stats.dispq_parity_err++; + } + + if (cause & F_ACTRGNFULL) + mc5->stats.active_rgn_full++; + if (cause & F_NFASRCHFAIL) + mc5->stats.nfa_srch_err++; + if (cause & F_UNKNOWNCMD) + mc5->stats.unknown_cmd++; + if (cause & F_DELACTEMPTY) + mc5->stats.del_act_empty++; + if (cause & MC5_INT_FATAL) + t3_fatal_err(adap); + + t3_write_reg(adap, A_MC5_DB_INT_CAUSE, cause); +} + +void __devinit t3_mc5_prep(struct adapter *adapter, struct mc5 *mc5, int mode) +{ +#define K * 1024 + + static unsigned int tcam_part_size[] = { /* in K 72-bit entries */ + 64 K, 128 K, 256 K, 32 K + }; + +#undef K + + u32 cfg = t3_read_reg(adapter, A_MC5_DB_CONFIG); + + mc5->adapter = adapter; + mc5->mode = (unsigned char)mode; + mc5->part_type = (unsigned char)G_TMTYPE(cfg); + if (cfg & F_TMTYPEHI) + mc5->part_type |= 4; + + mc5->tcam_size = tcam_part_size[G_TMPARTSIZE(cfg)]; + if (mode == MC5_MODE_144_BIT) + mc5->tcam_size /= 2; +} diff -puN /dev/null drivers/net/cxgb3/regs.h --- /dev/null +++ a/drivers/net/cxgb3/regs.h @@ -0,0 +1,2195 @@ +#define A_SG_CONTROL 0x0 + +#define S_DROPPKT 20 +#define V_DROPPKT(x) ((x) << S_DROPPKT) +#define F_DROPPKT V_DROPPKT(1U) + +#define S_EGRGENCTRL 19 +#define V_EGRGENCTRL(x) ((x) << S_EGRGENCTRL) +#define F_EGRGENCTRL V_EGRGENCTRL(1U) + +#define S_USERSPACESIZE 14 +#define M_USERSPACESIZE 0x1f +#define V_USERSPACESIZE(x) ((x) << S_USERSPACESIZE) + +#define S_HOSTPAGESIZE 11 +#define M_HOSTPAGESIZE 0x7 +#define V_HOSTPAGESIZE(x) ((x) << S_HOSTPAGESIZE) + +#define S_FLMODE 9 +#define V_FLMODE(x) ((x) << S_FLMODE) +#define F_FLMODE V_FLMODE(1U) + +#define S_PKTSHIFT 6 +#define M_PKTSHIFT 0x7 +#define V_PKTSHIFT(x) ((x) << S_PKTSHIFT) + +#define S_ONEINTMULTQ 5 +#define V_ONEINTMULTQ(x) ((x) << S_ONEINTMULTQ) +#define F_ONEINTMULTQ V_ONEINTMULTQ(1U) + +#define S_BIGENDIANINGRESS 2 +#define V_BIGENDIANINGRESS(x) ((x) << S_BIGENDIANINGRESS) +#define F_BIGENDIANINGRESS V_BIGENDIANINGRESS(1U) + +#define S_ISCSICOALESCING 1 +#define V_ISCSICOALESCING(x) ((x) << S_ISCSICOALESCING) +#define F_ISCSICOALESCING V_ISCSICOALESCING(1U) + +#define S_GLOBALENABLE 0 +#define V_GLOBALENABLE(x) ((x) << S_GLOBALENABLE) +#define F_GLOBALENABLE V_GLOBALENABLE(1U) + +#define S_AVOIDCQOVFL 24 +#define V_AVOIDCQOVFL(x) ((x) << S_AVOIDCQOVFL) +#define F_AVOIDCQOVFL V_AVOIDCQOVFL(1U) + +#define S_OPTONEINTMULTQ 23 +#define V_OPTONEINTMULTQ(x) ((x) << S_OPTONEINTMULTQ) +#define F_OPTONEINTMULTQ V_OPTONEINTMULTQ(1U) + +#define S_CQCRDTCTRL 22 +#define V_CQCRDTCTRL(x) ((x) << S_CQCRDTCTRL) +#define F_CQCRDTCTRL V_CQCRDTCTRL(1U) + +#define A_SG_KDOORBELL 0x4 + +#define S_SELEGRCNTX 31 +#define V_SELEGRCNTX(x) ((x) << S_SELEGRCNTX) +#define F_SELEGRCNTX V_SELEGRCNTX(1U) + +#define S_EGRCNTX 0 +#define M_EGRCNTX 0xffff +#define V_EGRCNTX(x) ((x) << S_EGRCNTX) + +#define A_SG_GTS 0x8 + +#define S_RSPQ 29 +#define M_RSPQ 0x7 +#define V_RSPQ(x) ((x) << S_RSPQ) +#define G_RSPQ(x) (((x) >> S_RSPQ) & M_RSPQ) + +#define S_NEWTIMER 16 +#define M_NEWTIMER 0x1fff +#define V_NEWTIMER(x) ((x) << S_NEWTIMER) + +#define S_NEWINDEX 0 +#define M_NEWINDEX 0xffff +#define V_NEWINDEX(x) ((x) << S_NEWINDEX) + +#define A_SG_CONTEXT_CMD 0xc + +#define S_CONTEXT_CMD_OPCODE 28 +#define M_CONTEXT_CMD_OPCODE 0xf +#define V_CONTEXT_CMD_OPCODE(x) ((x) << S_CONTEXT_CMD_OPCODE) + +#define S_CONTEXT_CMD_BUSY 27 +#define V_CONTEXT_CMD_BUSY(x) ((x) << S_CONTEXT_CMD_BUSY) +#define F_CONTEXT_CMD_BUSY V_CONTEXT_CMD_BUSY(1U) + +#define S_CQ_CREDIT 20 + +#define M_CQ_CREDIT 0x7f + +#define V_CQ_CREDIT(x) ((x) << S_CQ_CREDIT) + +#define G_CQ_CREDIT(x) (((x) >> S_CQ_CREDIT) & M_CQ_CREDIT) + +#define S_CQ 19 + +#define V_CQ(x) ((x) << S_CQ) +#define F_CQ V_CQ(1U) + +#define S_RESPONSEQ 18 +#define V_RESPONSEQ(x) ((x) << S_RESPONSEQ) +#define F_RESPONSEQ V_RESPONSEQ(1U) + +#define S_EGRESS 17 +#define V_EGRESS(x) ((x) << S_EGRESS) +#define F_EGRESS V_EGRESS(1U) + +#define S_FREELIST 16 +#define V_FREELIST(x) ((x) << S_FREELIST) +#define F_FREELIST V_FREELIST(1U) + +#define S_CONTEXT 0 +#define M_CONTEXT 0xffff +#define V_CONTEXT(x) ((x) << S_CONTEXT) + +#define G_CONTEXT(x) (((x) >> S_CONTEXT) & M_CONTEXT) + +#define A_SG_CONTEXT_DATA0 0x10 + +#define A_SG_CONTEXT_DATA1 0x14 + +#define A_SG_CONTEXT_DATA2 0x18 + +#define A_SG_CONTEXT_DATA3 0x1c + +#define A_SG_CONTEXT_MASK0 0x20 + +#define A_SG_CONTEXT_MASK1 0x24 + +#define A_SG_CONTEXT_MASK2 0x28 + +#define A_SG_CONTEXT_MASK3 0x2c + +#define A_SG_RSPQ_CREDIT_RETURN 0x30 + +#define S_CREDITS 0 +#define M_CREDITS 0xffff +#define V_CREDITS(x) ((x) << S_CREDITS) + +#define A_SG_DATA_INTR 0x34 + +#define S_ERRINTR 31 +#define V_ERRINTR(x) ((x) << S_ERRINTR) +#define F_ERRINTR V_ERRINTR(1U) + +#define A_SG_HI_DRB_HI_THRSH 0x38 + +#define A_SG_HI_DRB_LO_THRSH 0x3c + +#define A_SG_LO_DRB_HI_THRSH 0x40 + +#define A_SG_LO_DRB_LO_THRSH 0x44 + +#define A_SG_RSPQ_FL_STATUS 0x4c + +#define S_RSPQ0DISABLED 8 + +#define A_SG_EGR_RCQ_DRB_THRSH 0x54 + +#define S_HIRCQDRBTHRSH 16 +#define M_HIRCQDRBTHRSH 0x7ff +#define V_HIRCQDRBTHRSH(x) ((x) << S_HIRCQDRBTHRSH) + +#define S_LORCQDRBTHRSH 0 +#define M_LORCQDRBTHRSH 0x7ff +#define V_LORCQDRBTHRSH(x) ((x) << S_LORCQDRBTHRSH) + +#define A_SG_EGR_CNTX_BADDR 0x58 + +#define A_SG_INT_CAUSE 0x5c + +#define S_RSPQDISABLED 3 +#define V_RSPQDISABLED(x) ((x) << S_RSPQDISABLED) +#define F_RSPQDISABLED V_RSPQDISABLED(1U) + +#define S_RSPQCREDITOVERFOW 2 +#define V_RSPQCREDITOVERFOW(x) ((x) << S_RSPQCREDITOVERFOW) +#define F_RSPQCREDITOVERFOW V_RSPQCREDITOVERFOW(1U) + +#define A_SG_INT_ENABLE 0x60 + +#define A_SG_CMDQ_CREDIT_TH 0x64 + +#define S_TIMEOUT 8 +#define M_TIMEOUT 0xffffff +#define V_TIMEOUT(x) ((x) << S_TIMEOUT) + +#define S_THRESHOLD 0 +#define M_THRESHOLD 0xff +#define V_THRESHOLD(x) ((x) << S_THRESHOLD) + +#define A_SG_TIMER_TICK 0x68 + +#define A_SG_CQ_CONTEXT_BADDR 0x6c + +#define A_SG_OCO_BASE 0x70 + +#define S_BASE1 16 +#define M_BASE1 0xffff +#define V_BASE1(x) ((x) << S_BASE1) + +#define A_SG_DRB_PRI_THRESH 0x74 + +#define A_PCIX_INT_ENABLE 0x80 + +#define S_MSIXPARERR 22 +#define M_MSIXPARERR 0x7 + +#define V_MSIXPARERR(x) ((x) << S_MSIXPARERR) + +#define S_CFPARERR 18 +#define M_CFPARERR 0xf + +#define V_CFPARERR(x) ((x) << S_CFPARERR) + +#define S_RFPARERR 14 +#define M_RFPARERR 0xf + +#define V_RFPARERR(x) ((x) << S_RFPARERR) + +#define S_WFPARERR 12 +#define M_WFPARERR 0x3 + +#define V_WFPARERR(x) ((x) << S_WFPARERR) + +#define S_PIOPARERR 11 +#define V_PIOPARERR(x) ((x) << S_PIOPARERR) +#define F_PIOPARERR V_PIOPARERR(1U) + +#define S_DETUNCECCERR 10 +#define V_DETUNCECCERR(x) ((x) << S_DETUNCECCERR) +#define F_DETUNCECCERR V_DETUNCECCERR(1U) + +#define S_DETCORECCERR 9 +#define V_DETCORECCERR(x) ((x) << S_DETCORECCERR) +#define F_DETCORECCERR V_DETCORECCERR(1U) + +#define S_RCVSPLCMPERR 8 +#define V_RCVSPLCMPERR(x) ((x) << S_RCVSPLCMPERR) +#define F_RCVSPLCMPERR V_RCVSPLCMPERR(1U) + +#define S_UNXSPLCMP 7 +#define V_UNXSPLCMP(x) ((x) << S_UNXSPLCMP) +#define F_UNXSPLCMP V_UNXSPLCMP(1U) + +#define S_SPLCMPDIS 6 +#define V_SPLCMPDIS(x) ((x) << S_SPLCMPDIS) +#define F_SPLCMPDIS V_SPLCMPDIS(1U) + +#define S_DETPARERR 5 +#define V_DETPARERR(x) ((x) << S_DETPARERR) +#define F_DETPARERR V_DETPARERR(1U) + +#define S_SIGSYSERR 4 +#define V_SIGSYSERR(x) ((x) << S_SIGSYSERR) +#define F_SIGSYSERR V_SIGSYSERR(1U) + +#define S_RCVMSTABT 3 +#define V_RCVMSTABT(x) ((x) << S_RCVMSTABT) +#define F_RCVMSTABT V_RCVMSTABT(1U) + +#define S_RCVTARABT 2 +#define V_RCVTARABT(x) ((x) << S_RCVTARABT) +#define F_RCVTARABT V_RCVTARABT(1U) + +#define S_SIGTARABT 1 +#define V_SIGTARABT(x) ((x) << S_SIGTARABT) +#define F_SIGTARABT V_SIGTARABT(1U) + +#define S_MSTDETPARERR 0 +#define V_MSTDETPARERR(x) ((x) << S_MSTDETPARERR) +#define F_MSTDETPARERR V_MSTDETPARERR(1U) + +#define A_PCIX_INT_CAUSE 0x84 + +#define A_PCIX_CFG 0x88 + +#define S_CLIDECEN 18 +#define V_CLIDECEN(x) ((x) << S_CLIDECEN) +#define F_CLIDECEN V_CLIDECEN(1U) + +#define A_PCIX_MODE 0x8c + +#define S_PCLKRANGE 6 +#define M_PCLKRANGE 0x3 +#define V_PCLKRANGE(x) ((x) << S_PCLKRANGE) +#define G_PCLKRANGE(x) (((x) >> S_PCLKRANGE) & M_PCLKRANGE) + +#define S_PCIXINITPAT 2 +#define M_PCIXINITPAT 0xf +#define V_PCIXINITPAT(x) ((x) << S_PCIXINITPAT) +#define G_PCIXINITPAT(x) (((x) >> S_PCIXINITPAT) & M_PCIXINITPAT) + +#define S_64BIT 0 +#define V_64BIT(x) ((x) << S_64BIT) +#define F_64BIT V_64BIT(1U) + +#define A_PCIE_INT_ENABLE 0x80 + +#define S_BISTERR 15 +#define M_BISTERR 0xff + +#define V_BISTERR(x) ((x) << S_BISTERR) + +#define S_PCIE_MSIXPARERR 12 +#define M_PCIE_MSIXPARERR 0x7 + +#define V_PCIE_MSIXPARERR(x) ((x) << S_PCIE_MSIXPARERR) + +#define S_PCIE_CFPARERR 11 +#define V_PCIE_CFPARERR(x) ((x) << S_PCIE_CFPARERR) +#define F_PCIE_CFPARERR V_PCIE_CFPARERR(1U) + +#define S_PCIE_RFPARERR 10 +#define V_PCIE_RFPARERR(x) ((x) << S_PCIE_RFPARERR) +#define F_PCIE_RFPARERR V_PCIE_RFPARERR(1U) + +#define S_PCIE_WFPARERR 9 +#define V_PCIE_WFPARERR(x) ((x) << S_PCIE_WFPARERR) +#define F_PCIE_WFPARERR V_PCIE_WFPARERR(1U) + +#define S_PCIE_PIOPARERR 8 +#define V_PCIE_PIOPARERR(x) ((x) << S_PCIE_PIOPARERR) +#define F_PCIE_PIOPARERR V_PCIE_PIOPARERR(1U) + +#define S_UNXSPLCPLERRC 7 +#define V_UNXSPLCPLERRC(x) ((x) << S_UNXSPLCPLERRC) +#define F_UNXSPLCPLERRC V_UNXSPLCPLERRC(1U) + +#define S_UNXSPLCPLERRR 6 +#define V_UNXSPLCPLERRR(x) ((x) << S_UNXSPLCPLERRR) +#define F_UNXSPLCPLERRR V_UNXSPLCPLERRR(1U) + +#define S_PEXERR 0 +#define V_PEXERR(x) ((x) << S_PEXERR) +#define F_PEXERR V_PEXERR(1U) + +#define A_PCIE_INT_CAUSE 0x84 + +#define A_PCIE_CFG 0x88 + +#define S_PCIE_CLIDECEN 16 +#define V_PCIE_CLIDECEN(x) ((x) << S_PCIE_CLIDECEN) +#define F_PCIE_CLIDECEN V_PCIE_CLIDECEN(1U) + +#define S_CRSTWRMMODE 0 +#define V_CRSTWRMMODE(x) ((x) << S_CRSTWRMMODE) +#define F_CRSTWRMMODE V_CRSTWRMMODE(1U) + +#define A_PCIE_MODE 0x8c + +#define S_NUMFSTTRNSEQRX 10 +#define M_NUMFSTTRNSEQRX 0xff +#define V_NUMFSTTRNSEQRX(x) ((x) << S_NUMFSTTRNSEQRX) +#define G_NUMFSTTRNSEQRX(x) (((x) >> S_NUMFSTTRNSEQRX) & M_NUMFSTTRNSEQRX) + +#define A_PCIE_PEX_CTRL0 0x98 + +#define S_NUMFSTTRNSEQ 22 +#define M_NUMFSTTRNSEQ 0xff +#define V_NUMFSTTRNSEQ(x) ((x) << S_NUMFSTTRNSEQ) +#define G_NUMFSTTRNSEQ(x) (((x) >> S_NUMFSTTRNSEQ) & M_NUMFSTTRNSEQ) + +#define S_REPLAYLMT 2 +#define M_REPLAYLMT 0xfffff + +#define V_REPLAYLMT(x) ((x) << S_REPLAYLMT) + +#define A_PCIE_PEX_CTRL1 0x9c + +#define S_T3A_ACKLAT 0 +#define M_T3A_ACKLAT 0x7ff + +#define V_T3A_ACKLAT(x) ((x) << S_T3A_ACKLAT) + +#define S_ACKLAT 0 +#define M_ACKLAT 0x1fff + +#define V_ACKLAT(x) ((x) << S_ACKLAT) + +#define A_PCIE_PEX_ERR 0xa4 + +#define A_T3DBG_GPIO_EN 0xd0 + +#define S_GPIO11_OEN 27 +#define V_GPIO11_OEN(x) ((x) << S_GPIO11_OEN) +#define F_GPIO11_OEN V_GPIO11_OEN(1U) + +#define S_GPIO10_OEN 26 +#define V_GPIO10_OEN(x) ((x) << S_GPIO10_OEN) +#define F_GPIO10_OEN V_GPIO10_OEN(1U) + +#define S_GPIO7_OEN 23 +#define V_GPIO7_OEN(x) ((x) << S_GPIO7_OEN) +#define F_GPIO7_OEN V_GPIO7_OEN(1U) + +#define S_GPIO6_OEN 22 +#define V_GPIO6_OEN(x) ((x) << S_GPIO6_OEN) +#define F_GPIO6_OEN V_GPIO6_OEN(1U) + +#define S_GPIO5_OEN 21 +#define V_GPIO5_OEN(x) ((x) << S_GPIO5_OEN) +#define F_GPIO5_OEN V_GPIO5_OEN(1U) + +#define S_GPIO4_OEN 20 +#define V_GPIO4_OEN(x) ((x) << S_GPIO4_OEN) +#define F_GPIO4_OEN V_GPIO4_OEN(1U) + +#define S_GPIO2_OEN 18 +#define V_GPIO2_OEN(x) ((x) << S_GPIO2_OEN) +#define F_GPIO2_OEN V_GPIO2_OEN(1U) + +#define S_GPIO1_OEN 17 +#define V_GPIO1_OEN(x) ((x) << S_GPIO1_OEN) +#define F_GPIO1_OEN V_GPIO1_OEN(1U) + +#define S_GPIO0_OEN 16 +#define V_GPIO0_OEN(x) ((x) << S_GPIO0_OEN) +#define F_GPIO0_OEN V_GPIO0_OEN(1U) + +#define S_GPIO10_OUT_VAL 10 +#define V_GPIO10_OUT_VAL(x) ((x) << S_GPIO10_OUT_VAL) +#define F_GPIO10_OUT_VAL V_GPIO10_OUT_VAL(1U) + +#define S_GPIO7_OUT_VAL 7 +#define V_GPIO7_OUT_VAL(x) ((x) << S_GPIO7_OUT_VAL) +#define F_GPIO7_OUT_VAL V_GPIO7_OUT_VAL(1U) + +#define S_GPIO6_OUT_VAL 6 +#define V_GPIO6_OUT_VAL(x) ((x) << S_GPIO6_OUT_VAL) +#define F_GPIO6_OUT_VAL V_GPIO6_OUT_VAL(1U) + +#define S_GPIO5_OUT_VAL 5 +#define V_GPIO5_OUT_VAL(x) ((x) << S_GPIO5_OUT_VAL) +#define F_GPIO5_OUT_VAL V_GPIO5_OUT_VAL(1U) + +#define S_GPIO4_OUT_VAL 4 +#define V_GPIO4_OUT_VAL(x) ((x) << S_GPIO4_OUT_VAL) +#define F_GPIO4_OUT_VAL V_GPIO4_OUT_VAL(1U) + +#define S_GPIO2_OUT_VAL 2 +#define V_GPIO2_OUT_VAL(x) ((x) << S_GPIO2_OUT_VAL) +#define F_GPIO2_OUT_VAL V_GPIO2_OUT_VAL(1U) + +#define S_GPIO1_OUT_VAL 1 +#define V_GPIO1_OUT_VAL(x) ((x) << S_GPIO1_OUT_VAL) +#define F_GPIO1_OUT_VAL V_GPIO1_OUT_VAL(1U) + +#define S_GPIO0_OUT_VAL 0 +#define V_GPIO0_OUT_VAL(x) ((x) << S_GPIO0_OUT_VAL) +#define F_GPIO0_OUT_VAL V_GPIO0_OUT_VAL(1U) + +#define A_T3DBG_INT_ENABLE 0xd8 + +#define S_GPIO11 11 +#define V_GPIO11(x) ((x) << S_GPIO11) +#define F_GPIO11 V_GPIO11(1U) + +#define S_GPIO10 10 +#define V_GPIO10(x) ((x) << S_GPIO10) +#define F_GPIO10 V_GPIO10(1U) + +#define S_GPIO7 7 +#define V_GPIO7(x) ((x) << S_GPIO7) +#define F_GPIO7 V_GPIO7(1U) + +#define S_GPIO6 6 +#define V_GPIO6(x) ((x) << S_GPIO6) +#define F_GPIO6 V_GPIO6(1U) + +#define S_GPIO5 5 +#define V_GPIO5(x) ((x) << S_GPIO5) +#define F_GPIO5 V_GPIO5(1U) + +#define S_GPIO4 4 +#define V_GPIO4(x) ((x) << S_GPIO4) +#define F_GPIO4 V_GPIO4(1U) + +#define S_GPIO3 3 +#define V_GPIO3(x) ((x) << S_GPIO3) +#define F_GPIO3 V_GPIO3(1U) + +#define S_GPIO2 2 +#define V_GPIO2(x) ((x) << S_GPIO2) +#define F_GPIO2 V_GPIO2(1U) + +#define S_GPIO1 1 +#define V_GPIO1(x) ((x) << S_GPIO1) +#define F_GPIO1 V_GPIO1(1U) + +#define S_GPIO0 0 +#define V_GPIO0(x) ((x) << S_GPIO0) +#define F_GPIO0 V_GPIO0(1U) + +#define A_T3DBG_INT_CAUSE 0xdc + +#define A_T3DBG_GPIO_ACT_LOW 0xf0 + +#define MC7_PMRX_BASE_ADDR 0x100 + +#define A_MC7_CFG 0x100 + +#define S_IFEN 13 +#define V_IFEN(x) ((x) << S_IFEN) +#define F_IFEN V_IFEN(1U) + +#define S_TERM150 11 +#define V_TERM150(x) ((x) << S_TERM150) +#define F_TERM150 V_TERM150(1U) + +#define S_SLOW 10 +#define V_SLOW(x) ((x) << S_SLOW) +#define F_SLOW V_SLOW(1U) + +#define S_WIDTH 8 +#define M_WIDTH 0x3 +#define V_WIDTH(x) ((x) << S_WIDTH) +#define G_WIDTH(x) (((x) >> S_WIDTH) & M_WIDTH) + +#define S_BKS 6 +#define V_BKS(x) ((x) << S_BKS) +#define F_BKS V_BKS(1U) + +#define S_ORG 5 +#define V_ORG(x) ((x) << S_ORG) +#define F_ORG V_ORG(1U) + +#define S_DEN 2 +#define M_DEN 0x7 +#define V_DEN(x) ((x) << S_DEN) +#define G_DEN(x) (((x) >> S_DEN) & M_DEN) + +#define S_RDY 1 +#define V_RDY(x) ((x) << S_RDY) +#define F_RDY V_RDY(1U) + +#define S_CLKEN 0 +#define V_CLKEN(x) ((x) << S_CLKEN) +#define F_CLKEN V_CLKEN(1U) + +#define A_MC7_MODE 0x104 + +#define S_BUSY 31 +#define V_BUSY(x) ((x) << S_BUSY) +#define F_BUSY V_BUSY(1U) + +#define S_BUSY 31 +#define V_BUSY(x) ((x) << S_BUSY) +#define F_BUSY V_BUSY(1U) + +#define A_MC7_EXT_MODE1 0x108 + +#define A_MC7_EXT_MODE2 0x10c + +#define A_MC7_EXT_MODE3 0x110 + +#define A_MC7_PRE 0x114 + +#define A_MC7_REF 0x118 + +#define S_PREREFDIV 1 +#define M_PREREFDIV 0x3fff +#define V_PREREFDIV(x) ((x) << S_PREREFDIV) + +#define S_PERREFEN 0 +#define V_PERREFEN(x) ((x) << S_PERREFEN) +#define F_PERREFEN V_PERREFEN(1U) + +#define A_MC7_DLL 0x11c + +#define S_DLLENB 1 +#define V_DLLENB(x) ((x) << S_DLLENB) +#define F_DLLENB V_DLLENB(1U) + +#define S_DLLRST 0 +#define V_DLLRST(x) ((x) << S_DLLRST) +#define F_DLLRST V_DLLRST(1U) + +#define A_MC7_PARM 0x120 + +#define S_ACTTOPREDLY 26 +#define M_ACTTOPREDLY 0xf +#define V_ACTTOPREDLY(x) ((x) << S_ACTTOPREDLY) + +#define S_ACTTORDWRDLY 23 +#define M_ACTTORDWRDLY 0x7 +#define V_ACTTORDWRDLY(x) ((x) << S_ACTTORDWRDLY) + +#define S_PRECYC 20 +#define M_PRECYC 0x7 +#define V_PRECYC(x) ((x) << S_PRECYC) + +#define S_REFCYC 13 +#define M_REFCYC 0x7f +#define V_REFCYC(x) ((x) << S_REFCYC) + +#define S_BKCYC 8 +#define M_BKCYC 0x1f +#define V_BKCYC(x) ((x) << S_BKCYC) + +#define S_WRTORDDLY 4 +#define M_WRTORDDLY 0xf +#define V_WRTORDDLY(x) ((x) << S_WRTORDDLY) + +#define S_RDTOWRDLY 0 +#define M_RDTOWRDLY 0xf +#define V_RDTOWRDLY(x) ((x) << S_RDTOWRDLY) + +#define A_MC7_CAL 0x128 + +#define S_BUSY 31 +#define V_BUSY(x) ((x) << S_BUSY) +#define F_BUSY V_BUSY(1U) + +#define S_BUSY 31 +#define V_BUSY(x) ((x) << S_BUSY) +#define F_BUSY V_BUSY(1U) + +#define S_CAL_FAULT 30 +#define V_CAL_FAULT(x) ((x) << S_CAL_FAULT) +#define F_CAL_FAULT V_CAL_FAULT(1U) + +#define S_SGL_CAL_EN 20 +#define V_SGL_CAL_EN(x) ((x) << S_SGL_CAL_EN) +#define F_SGL_CAL_EN V_SGL_CAL_EN(1U) + +#define A_MC7_ERR_ADDR 0x12c + +#define A_MC7_ECC 0x130 + +#define S_ECCCHKEN 1 +#define V_ECCCHKEN(x) ((x) << S_ECCCHKEN) +#define F_ECCCHKEN V_ECCCHKEN(1U) + +#define S_ECCGENEN 0 +#define V_ECCGENEN(x) ((x) << S_ECCGENEN) +#define F_ECCGENEN V_ECCGENEN(1U) + +#define A_MC7_CE_ADDR 0x134 + +#define A_MC7_CE_DATA0 0x138 + +#define A_MC7_CE_DATA1 0x13c + +#define A_MC7_CE_DATA2 0x140 + +#define S_DATA 0 +#define M_DATA 0xff + +#define G_DATA(x) (((x) >> S_DATA) & M_DATA) + +#define A_MC7_UE_ADDR 0x144 + +#define A_MC7_UE_DATA0 0x148 + +#define A_MC7_UE_DATA1 0x14c + +#define A_MC7_UE_DATA2 0x150 + +#define A_MC7_BD_ADDR 0x154 + +#define S_ADDR 3 + +#define M_ADDR 0x1fffffff + +#define A_MC7_BD_DATA0 0x158 + +#define A_MC7_BD_DATA1 0x15c + +#define A_MC7_BD_OP 0x164 + +#define S_OP 0 + +#define V_OP(x) ((x) << S_OP) +#define F_OP V_OP(1U) + +#define F_OP V_OP(1U) +#define A_SF_OP 0x6dc + +#define A_MC7_BIST_ADDR_BEG 0x168 + +#define A_MC7_BIST_ADDR_END 0x16c + +#define A_MC7_BIST_DATA 0x170 + +#define A_MC7_BIST_OP 0x174 + +#define S_CONT 3 +#define V_CONT(x) ((x) << S_CONT) +#define F_CONT V_CONT(1U) + +#define F_CONT V_CONT(1U) + +#define A_MC7_INT_ENABLE 0x178 + +#define S_AE 17 +#define V_AE(x) ((x) << S_AE) +#define F_AE V_AE(1U) + +#define S_PE 2 +#define M_PE 0x7fff + +#define V_PE(x) ((x) << S_PE) + +#define G_PE(x) (((x) >> S_PE) & M_PE) + +#define S_UE 1 +#define V_UE(x) ((x) << S_UE) +#define F_UE V_UE(1U) + +#define S_CE 0 +#define V_CE(x) ((x) << S_CE) +#define F_CE V_CE(1U) + +#define A_MC7_INT_CAUSE 0x17c + +#define MC7_PMTX_BASE_ADDR 0x180 + +#define MC7_CM_BASE_ADDR 0x200 + +#define A_CIM_BOOT_CFG 0x280 + +#define S_BOOTADDR 2 +#define M_BOOTADDR 0x3fffffff +#define V_BOOTADDR(x) ((x) << S_BOOTADDR) + +#define A_CIM_SDRAM_BASE_ADDR 0x28c + +#define A_CIM_SDRAM_ADDR_SIZE 0x290 + +#define A_CIM_HOST_INT_ENABLE 0x298 + +#define A_CIM_HOST_INT_CAUSE 0x29c + +#define S_BLKWRPLINT 12 +#define V_BLKWRPLINT(x) ((x) << S_BLKWRPLINT) +#define F_BLKWRPLINT V_BLKWRPLINT(1U) + +#define S_BLKRDPLINT 11 +#define V_BLKRDPLINT(x) ((x) << S_BLKRDPLINT) +#define F_BLKRDPLINT V_BLKRDPLINT(1U) + +#define S_BLKWRCTLINT 10 +#define V_BLKWRCTLINT(x) ((x) << S_BLKWRCTLINT) +#define F_BLKWRCTLINT V_BLKWRCTLINT(1U) + +#define S_BLKRDCTLINT 9 +#define V_BLKRDCTLINT(x) ((x) << S_BLKRDCTLINT) +#define F_BLKRDCTLINT V_BLKRDCTLINT(1U) + +#define S_BLKWRFLASHINT 8 +#define V_BLKWRFLASHINT(x) ((x) << S_BLKWRFLASHINT) +#define F_BLKWRFLASHINT V_BLKWRFLASHINT(1U) + +#define S_BLKRDFLASHINT 7 +#define V_BLKRDFLASHINT(x) ((x) << S_BLKRDFLASHINT) +#define F_BLKRDFLASHINT V_BLKRDFLASHINT(1U) + +#define S_SGLWRFLASHINT 6 +#define V_SGLWRFLASHINT(x) ((x) << S_SGLWRFLASHINT) +#define F_SGLWRFLASHINT V_SGLWRFLASHINT(1U) + +#define S_WRBLKFLASHINT 5 +#define V_WRBLKFLASHINT(x) ((x) << S_WRBLKFLASHINT) +#define F_WRBLKFLASHINT V_WRBLKFLASHINT(1U) + +#define S_BLKWRBOOTINT 4 +#define V_BLKWRBOOTINT(x) ((x) << S_BLKWRBOOTINT) +#define F_BLKWRBOOTINT V_BLKWRBOOTINT(1U) + +#define S_FLASHRANGEINT 2 +#define V_FLASHRANGEINT(x) ((x) << S_FLASHRANGEINT) +#define F_FLASHRANGEINT V_FLASHRANGEINT(1U) + +#define S_SDRAMRANGEINT 1 +#define V_SDRAMRANGEINT(x) ((x) << S_SDRAMRANGEINT) +#define F_SDRAMRANGEINT V_SDRAMRANGEINT(1U) + +#define S_RSVDSPACEINT 0 +#define V_RSVDSPACEINT(x) ((x) << S_RSVDSPACEINT) +#define F_RSVDSPACEINT V_RSVDSPACEINT(1U) + +#define A_CIM_HOST_ACC_CTRL 0x2b0 + +#define S_HOSTBUSY 17 +#define V_HOSTBUSY(x) ((x) << S_HOSTBUSY) +#define F_HOSTBUSY V_HOSTBUSY(1U) + +#define A_CIM_HOST_ACC_DATA 0x2b4 + +#define A_TP_IN_CONFIG 0x300 + +#define S_NICMODE 14 +#define V_NICMODE(x) ((x) << S_NICMODE) +#define F_NICMODE V_NICMODE(1U) + +#define F_NICMODE V_NICMODE(1U) + +#define S_IPV6ENABLE 15 +#define V_IPV6ENABLE(x) ((x) << S_IPV6ENABLE) +#define F_IPV6ENABLE V_IPV6ENABLE(1U) + +#define A_TP_OUT_CONFIG 0x304 + +#define S_VLANEXTRACTIONENABLE 12 + +#define A_TP_GLOBAL_CONFIG 0x308 + +#define S_TXPACINGENABLE 24 +#define V_TXPACINGENABLE(x) ((x) << S_TXPACINGENABLE) +#define F_TXPACINGENABLE V_TXPACINGENABLE(1U) + +#define S_PATHMTU 15 +#define V_PATHMTU(x) ((x) << S_PATHMTU) +#define F_PATHMTU V_PATHMTU(1U) + +#define S_IPCHECKSUMOFFLOAD 13 +#define V_IPCHECKSUMOFFLOAD(x) ((x) << S_IPCHECKSUMOFFLOAD) +#define F_IPCHECKSUMOFFLOAD V_IPCHECKSUMOFFLOAD(1U) + +#define S_UDPCHECKSUMOFFLOAD 12 +#define V_UDPCHECKSUMOFFLOAD(x) ((x) << S_UDPCHECKSUMOFFLOAD) +#define F_UDPCHECKSUMOFFLOAD V_UDPCHECKSUMOFFLOAD(1U) + +#define S_TCPCHECKSUMOFFLOAD 11 +#define V_TCPCHECKSUMOFFLOAD(x) ((x) << S_TCPCHECKSUMOFFLOAD) +#define F_TCPCHECKSUMOFFLOAD V_TCPCHECKSUMOFFLOAD(1U) + +#define S_IPTTL 0 +#define M_IPTTL 0xff +#define V_IPTTL(x) ((x) << S_IPTTL) + +#define A_TP_CMM_MM_BASE 0x314 + +#define A_TP_CMM_TIMER_BASE 0x318 + +#define S_CMTIMERMAXNUM 28 +#define M_CMTIMERMAXNUM 0x3 +#define V_CMTIMERMAXNUM(x) ((x) << S_CMTIMERMAXNUM) + +#define A_TP_PMM_SIZE 0x31c + +#define A_TP_PMM_TX_BASE 0x320 + +#define A_TP_PMM_RX_BASE 0x328 + +#define A_TP_PMM_RX_PAGE_SIZE 0x32c + +#define A_TP_PMM_RX_MAX_PAGE 0x330 + +#define A_TP_PMM_TX_PAGE_SIZE 0x334 + +#define A_TP_PMM_TX_MAX_PAGE 0x338 + +#define A_TP_TCP_OPTIONS 0x340 + +#define S_MTUDEFAULT 16 +#define M_MTUDEFAULT 0xffff +#define V_MTUDEFAULT(x) ((x) << S_MTUDEFAULT) + +#define S_MTUENABLE 10 +#define V_MTUENABLE(x) ((x) << S_MTUENABLE) +#define F_MTUENABLE V_MTUENABLE(1U) + +#define S_SACKRX 8 +#define V_SACKRX(x) ((x) << S_SACKRX) +#define F_SACKRX V_SACKRX(1U) + +#define S_SACKMODE 4 + +#define M_SACKMODE 0x3 + +#define V_SACKMODE(x) ((x) << S_SACKMODE) + +#define S_WINDOWSCALEMODE 2 +#define M_WINDOWSCALEMODE 0x3 +#define V_WINDOWSCALEMODE(x) ((x) << S_WINDOWSCALEMODE) + +#define S_TIMESTAMPSMODE 0 + +#define M_TIMESTAMPSMODE 0x3 + +#define V_TIMESTAMPSMODE(x) ((x) << S_TIMESTAMPSMODE) + +#define A_TP_DACK_CONFIG 0x344 + +#define S_AUTOSTATE3 30 +#define M_AUTOSTATE3 0x3 +#define V_AUTOSTATE3(x) ((x) << S_AUTOSTATE3) + +#define S_AUTOSTATE2 28 +#define M_AUTOSTATE2 0x3 +#define V_AUTOSTATE2(x) ((x) << S_AUTOSTATE2) + +#define S_AUTOSTATE1 26 +#define M_AUTOSTATE1 0x3 +#define V_AUTOSTATE1(x) ((x) << S_AUTOSTATE1) + +#define S_BYTETHRESHOLD 5 +#define M_BYTETHRESHOLD 0xfffff +#define V_BYTETHRESHOLD(x) ((x) << S_BYTETHRESHOLD) + +#define S_MSSTHRESHOLD 3 +#define M_MSSTHRESHOLD 0x3 +#define V_MSSTHRESHOLD(x) ((x) << S_MSSTHRESHOLD) + +#define S_AUTOCAREFUL 2 +#define V_AUTOCAREFUL(x) ((x) << S_AUTOCAREFUL) +#define F_AUTOCAREFUL V_AUTOCAREFUL(1U) + +#define S_AUTOENABLE 1 +#define V_AUTOENABLE(x) ((x) << S_AUTOENABLE) +#define F_AUTOENABLE V_AUTOENABLE(1U) + +#define S_DACK_MODE 0 +#define V_DACK_MODE(x) ((x) << S_DACK_MODE) +#define F_DACK_MODE V_DACK_MODE(1U) + +#define A_TP_PC_CONFIG 0x348 + +#define S_TXTOSQUEUEMAPMODE 26 +#define V_TXTOSQUEUEMAPMODE(x) ((x) << S_TXTOSQUEUEMAPMODE) +#define F_TXTOSQUEUEMAPMODE V_TXTOSQUEUEMAPMODE(1U) + +#define S_ENABLEEPCMDAFULL 23 +#define V_ENABLEEPCMDAFULL(x) ((x) << S_ENABLEEPCMDAFULL) +#define F_ENABLEEPCMDAFULL V_ENABLEEPCMDAFULL(1U) + +#define S_MODULATEUNIONMODE 22 +#define V_MODULATEUNIONMODE(x) ((x) << S_MODULATEUNIONMODE) +#define F_MODULATEUNIONMODE V_MODULATEUNIONMODE(1U) + +#define S_TXDEFERENABLE 20 +#define V_TXDEFERENABLE(x) ((x) << S_TXDEFERENABLE) +#define F_TXDEFERENABLE V_TXDEFERENABLE(1U) + +#define S_RXCONGESTIONMODE 19 +#define V_RXCONGESTIONMODE(x) ((x) << S_RXCONGESTIONMODE) +#define F_RXCONGESTIONMODE V_RXCONGESTIONMODE(1U) + +#define S_HEARBEATDACK 16 +#define V_HEARBEATDACK(x) ((x) << S_HEARBEATDACK) +#define F_HEARBEATDACK V_HEARBEATDACK(1U) + +#define S_TXCONGESTIONMODE 15 +#define V_TXCONGESTIONMODE(x) ((x) << S_TXCONGESTIONMODE) +#define F_TXCONGESTIONMODE V_TXCONGESTIONMODE(1U) + +#define S_ENABLEOCSPIFULL 30 +#define V_ENABLEOCSPIFULL(x) ((x) << S_ENABLEOCSPIFULL) +#define F_ENABLEOCSPIFULL V_ENABLEOCSPIFULL(1U) + +#define S_LOCKTID 28 +#define V_LOCKTID(x) ((x) << S_LOCKTID) +#define F_LOCKTID V_LOCKTID(1U) + +#define A_TP_PC_CONFIG2 0x34c + +#define S_CHDRAFULL 4 +#define V_CHDRAFULL(x) ((x) << S_CHDRAFULL) +#define F_CHDRAFULL V_CHDRAFULL(1U) + +#define A_TP_TCP_BACKOFF_REG0 0x350 + +#define A_TP_TCP_BACKOFF_REG1 0x354 + +#define A_TP_TCP_BACKOFF_REG2 0x358 + +#define A_TP_TCP_BACKOFF_REG3 0x35c + +#define A_TP_PARA_REG2 0x368 + +#define S_MAXRXDATA 16 +#define M_MAXRXDATA 0xffff +#define V_MAXRXDATA(x) ((x) << S_MAXRXDATA) + +#define S_RXCOALESCESIZE 0 +#define M_RXCOALESCESIZE 0xffff +#define V_RXCOALESCESIZE(x) ((x) << S_RXCOALESCESIZE) + +#define A_TP_PARA_REG3 0x36c + +#define S_TXDATAACKIDX 16 +#define M_TXDATAACKIDX 0xf + +#define V_TXDATAACKIDX(x) ((x) << S_TXDATAACKIDX) + +#define S_TXPACEAUTOSTRICT 10 +#define V_TXPACEAUTOSTRICT(x) ((x) << S_TXPACEAUTOSTRICT) +#define F_TXPACEAUTOSTRICT V_TXPACEAUTOSTRICT(1U) + +#define S_TXPACEFIXED 9 +#define V_TXPACEFIXED(x) ((x) << S_TXPACEFIXED) +#define F_TXPACEFIXED V_TXPACEFIXED(1U) + +#define S_TXPACEAUTO 8 +#define V_TXPACEAUTO(x) ((x) << S_TXPACEAUTO) +#define F_TXPACEAUTO V_TXPACEAUTO(1U) + +#define S_RXCOALESCEENABLE 1 +#define V_RXCOALESCEENABLE(x) ((x) << S_RXCOALESCEENABLE) +#define F_RXCOALESCEENABLE V_RXCOALESCEENABLE(1U) + +#define S_RXCOALESCEPSHEN 0 +#define V_RXCOALESCEPSHEN(x) ((x) << S_RXCOALESCEPSHEN) +#define F_RXCOALESCEPSHEN V_RXCOALESCEPSHEN(1U) + +#define A_TP_PARA_REG4 0x370 + +#define A_TP_PARA_REG6 0x378 + +#define S_T3A_ENABLEESND 13 +#define V_T3A_ENABLEESND(x) ((x) << S_T3A_ENABLEESND) +#define F_T3A_ENABLEESND V_T3A_ENABLEESND(1U) + +#define S_ENABLEESND 11 +#define V_ENABLEESND(x) ((x) << S_ENABLEESND) +#define F_ENABLEESND V_ENABLEESND(1U) + +#define A_TP_PARA_REG7 0x37c + +#define S_PMMAXXFERLEN1 16 +#define M_PMMAXXFERLEN1 0xffff +#define V_PMMAXXFERLEN1(x) ((x) << S_PMMAXXFERLEN1) + +#define S_PMMAXXFERLEN0 0 +#define M_PMMAXXFERLEN0 0xffff +#define V_PMMAXXFERLEN0(x) ((x) << S_PMMAXXFERLEN0) + +#define A_TP_TIMER_RESOLUTION 0x390 + +#define S_TIMERRESOLUTION 16 +#define M_TIMERRESOLUTION 0xff +#define V_TIMERRESOLUTION(x) ((x) << S_TIMERRESOLUTION) + +#define S_TIMESTAMPRESOLUTION 8 +#define M_TIMESTAMPRESOLUTION 0xff +#define V_TIMESTAMPRESOLUTION(x) ((x) << S_TIMESTAMPRESOLUTION) + +#define S_DELAYEDACKRESOLUTION 0 +#define M_DELAYEDACKRESOLUTION 0xff +#define V_DELAYEDACKRESOLUTION(x) ((x) << S_DELAYEDACKRESOLUTION) + +#define A_TP_MSL 0x394 + +#define A_TP_RXT_MIN 0x398 + +#define A_TP_RXT_MAX 0x39c + +#define A_TP_PERS_MIN 0x3a0 + +#define A_TP_PERS_MAX 0x3a4 + +#define A_TP_KEEP_IDLE 0x3a8 + +#define A_TP_KEEP_INTVL 0x3ac + +#define A_TP_INIT_SRTT 0x3b0 + +#define A_TP_DACK_TIMER 0x3b4 + +#define A_TP_FINWAIT2_TIMER 0x3b8 + +#define A_TP_SHIFT_CNT 0x3c0 + +#define S_SYNSHIFTMAX 24 + +#define M_SYNSHIFTMAX 0xff + +#define V_SYNSHIFTMAX(x) ((x) << S_SYNSHIFTMAX) + +#define S_RXTSHIFTMAXR1 20 + +#define M_RXTSHIFTMAXR1 0xf + +#define V_RXTSHIFTMAXR1(x) ((x) << S_RXTSHIFTMAXR1) + +#define S_RXTSHIFTMAXR2 16 + +#define M_RXTSHIFTMAXR2 0xf + +#define V_RXTSHIFTMAXR2(x) ((x) << S_RXTSHIFTMAXR2) + +#define S_PERSHIFTBACKOFFMAX 12 +#define M_PERSHIFTBACKOFFMAX 0xf +#define V_PERSHIFTBACKOFFMAX(x) ((x) << S_PERSHIFTBACKOFFMAX) + +#define S_PERSHIFTMAX 8 +#define M_PERSHIFTMAX 0xf +#define V_PERSHIFTMAX(x) ((x) << S_PERSHIFTMAX) + +#define S_KEEPALIVEMAX 0 + +#define M_KEEPALIVEMAX 0xff + +#define V_KEEPALIVEMAX(x) ((x) << S_KEEPALIVEMAX) + +#define A_TP_MTU_PORT_TABLE 0x3d0 + +#define A_TP_CCTRL_TABLE 0x3dc + +#define A_TP_MTU_TABLE 0x3e4 + +#define A_TP_RSS_MAP_TABLE 0x3e8 + +#define A_TP_RSS_LKP_TABLE 0x3ec + +#define A_TP_RSS_CONFIG 0x3f0 + +#define S_TNL4TUPEN 29 +#define V_TNL4TUPEN(x) ((x) << S_TNL4TUPEN) +#define F_TNL4TUPEN V_TNL4TUPEN(1U) + +#define S_TNL2TUPEN 28 +#define V_TNL2TUPEN(x) ((x) << S_TNL2TUPEN) +#define F_TNL2TUPEN V_TNL2TUPEN(1U) + +#define S_TNLPRTEN 26 +#define V_TNLPRTEN(x) ((x) << S_TNLPRTEN) +#define F_TNLPRTEN V_TNLPRTEN(1U) + +#define S_TNLMAPEN 25 +#define V_TNLMAPEN(x) ((x) << S_TNLMAPEN) +#define F_TNLMAPEN V_TNLMAPEN(1U) + +#define S_TNLLKPEN 24 +#define V_TNLLKPEN(x) ((x) << S_TNLLKPEN) +#define F_TNLLKPEN V_TNLLKPEN(1U) + +#define S_RRCPLCPUSIZE 4 +#define M_RRCPLCPUSIZE 0x7 +#define V_RRCPLCPUSIZE(x) ((x) << S_RRCPLCPUSIZE) + +#define S_RQFEEDBACKENABLE 3 +#define V_RQFEEDBACKENABLE(x) ((x) << S_RQFEEDBACKENABLE) +#define F_RQFEEDBACKENABLE V_RQFEEDBACKENABLE(1U) + +#define S_DISABLE 0 + +#define A_TP_TM_PIO_ADDR 0x418 + +#define A_TP_TM_PIO_DATA 0x41c + +#define A_TP_TX_MOD_QUE_TABLE 0x420 + +#define A_TP_TX_RESOURCE_LIMIT 0x424 + +#define A_TP_TX_MOD_QUEUE_REQ_MAP 0x428 + +#define S_TX_MOD_QUEUE_REQ_MAP 0 +#define M_TX_MOD_QUEUE_REQ_MAP 0xff +#define V_TX_MOD_QUEUE_REQ_MAP(x) ((x) << S_TX_MOD_QUEUE_REQ_MAP) + +#define A_TP_TX_MOD_QUEUE_WEIGHT1 0x42c + +#define A_TP_TX_MOD_QUEUE_WEIGHT0 0x430 + +#define A_TP_MOD_CHANNEL_WEIGHT 0x434 + +#define A_TP_PIO_ADDR 0x440 + +#define A_TP_PIO_DATA 0x444 + +#define A_TP_RESET 0x44c + +#define S_FLSTINITENABLE 1 +#define V_FLSTINITENABLE(x) ((x) << S_FLSTINITENABLE) +#define F_FLSTINITENABLE V_FLSTINITENABLE(1U) + +#define S_TPRESET 0 +#define V_TPRESET(x) ((x) << S_TPRESET) +#define F_TPRESET V_TPRESET(1U) + +#define A_TP_CMM_MM_RX_FLST_BASE 0x460 + +#define A_TP_CMM_MM_TX_FLST_BASE 0x464 + +#define A_TP_CMM_MM_PS_FLST_BASE 0x468 + +#define A_TP_MIB_INDEX 0x450 + +#define A_TP_MIB_RDATA 0x454 + +#define A_TP_CMM_MM_MAX_PSTRUCT 0x46c + +#define A_TP_INT_ENABLE 0x470 + +#define A_TP_INT_CAUSE 0x474 + +#define A_TP_TX_MOD_Q1_Q0_RATE_LIMIT 0x8 + +#define A_TP_TX_DROP_CFG_CH0 0x12b + +#define A_TP_TX_DROP_MODE 0x12f + +#define A_TP_EGRESS_CONFIG 0x145 + +#define S_REWRITEFORCETOSIZE 0 +#define V_REWRITEFORCETOSIZE(x) ((x) << S_REWRITEFORCETOSIZE) +#define F_REWRITEFORCETOSIZE V_REWRITEFORCETOSIZE(1U) + +#define A_TP_TX_TRC_KEY0 0x20 + +#define A_TP_RX_TRC_KEY0 0x120 + +#define A_ULPRX_CTL 0x500 + +#define S_ROUND_ROBIN 4 +#define V_ROUND_ROBIN(x) ((x) << S_ROUND_ROBIN) +#define F_ROUND_ROBIN V_ROUND_ROBIN(1U) + +#define A_ULPRX_INT_ENABLE 0x504 + +#define S_PARERR 0 +#define V_PARERR(x) ((x) << S_PARERR) +#define F_PARERR V_PARERR(1U) + +#define A_ULPRX_INT_CAUSE 0x508 + +#define A_ULPRX_ISCSI_LLIMIT 0x50c + +#define A_ULPRX_ISCSI_ULIMIT 0x510 + +#define A_ULPRX_ISCSI_TAGMASK 0x514 + +#define A_ULPRX_TDDP_LLIMIT 0x51c + +#define A_ULPRX_TDDP_ULIMIT 0x520 + +#define A_ULPRX_STAG_LLIMIT 0x52c + +#define A_ULPRX_STAG_ULIMIT 0x530 + +#define A_ULPRX_RQ_LLIMIT 0x534 +#define A_ULPRX_RQ_LLIMIT 0x534 + +#define A_ULPRX_RQ_ULIMIT 0x538 +#define A_ULPRX_RQ_ULIMIT 0x538 + +#define A_ULPRX_PBL_LLIMIT 0x53c + +#define A_ULPRX_PBL_ULIMIT 0x540 +#define A_ULPRX_PBL_ULIMIT 0x540 + +#define A_ULPRX_TDDP_TAGMASK 0x524 + +#define A_ULPRX_RQ_LLIMIT 0x534 +#define A_ULPRX_RQ_LLIMIT 0x534 + +#define A_ULPRX_RQ_ULIMIT 0x538 +#define A_ULPRX_RQ_ULIMIT 0x538 + +#define A_ULPRX_PBL_ULIMIT 0x540 +#define A_ULPRX_PBL_ULIMIT 0x540 + +#define A_ULPTX_CONFIG 0x580 + +#define S_CFG_RR_ARB 0 +#define V_CFG_RR_ARB(x) ((x) << S_CFG_RR_ARB) +#define F_CFG_RR_ARB V_CFG_RR_ARB(1U) + +#define A_ULPTX_INT_ENABLE 0x584 + +#define S_PBL_BOUND_ERR_CH1 1 +#define V_PBL_BOUND_ERR_CH1(x) ((x) << S_PBL_BOUND_ERR_CH1) +#define F_PBL_BOUND_ERR_CH1 V_PBL_BOUND_ERR_CH1(1U) + +#define S_PBL_BOUND_ERR_CH0 0 +#define V_PBL_BOUND_ERR_CH0(x) ((x) << S_PBL_BOUND_ERR_CH0) +#define F_PBL_BOUND_ERR_CH0 V_PBL_BOUND_ERR_CH0(1U) + +#define A_ULPTX_INT_CAUSE 0x588 + +#define A_ULPTX_TPT_LLIMIT 0x58c + +#define A_ULPTX_TPT_ULIMIT 0x590 + +#define A_ULPTX_PBL_LLIMIT 0x594 + +#define A_ULPTX_PBL_ULIMIT 0x598 + +#define A_ULPTX_DMA_WEIGHT 0x5ac + +#define S_D1_WEIGHT 16 +#define M_D1_WEIGHT 0xffff +#define V_D1_WEIGHT(x) ((x) << S_D1_WEIGHT) + +#define S_D0_WEIGHT 0 +#define M_D0_WEIGHT 0xffff +#define V_D0_WEIGHT(x) ((x) << S_D0_WEIGHT) + +#define A_PM1_RX_CFG 0x5c0 + +#define A_PM1_RX_INT_ENABLE 0x5d8 + +#define S_ZERO_E_CMD_ERROR 18 +#define V_ZERO_E_CMD_ERROR(x) ((x) << S_ZERO_E_CMD_ERROR) +#define F_ZERO_E_CMD_ERROR V_ZERO_E_CMD_ERROR(1U) + +#define S_IESPI0_FIFO2X_RX_FRAMING_ERROR 17 +#define V_IESPI0_FIFO2X_RX_FRAMING_ERROR(x) ((x) << S_IESPI0_FIFO2X_RX_FRAMING_ERROR) +#define F_IESPI0_FIFO2X_RX_FRAMING_ERROR V_IESPI0_FIFO2X_RX_FRAMING_ERROR(1U) + +#define S_IESPI1_FIFO2X_RX_FRAMING_ERROR 16 +#define V_IESPI1_FIFO2X_RX_FRAMING_ERROR(x) ((x) << S_IESPI1_FIFO2X_RX_FRAMING_ERROR) +#define F_IESPI1_FIFO2X_RX_FRAMING_ERROR V_IESPI1_FIFO2X_RX_FRAMING_ERROR(1U) + +#define S_IESPI0_RX_FRAMING_ERROR 15 +#define V_IESPI0_RX_FRAMING_ERROR(x) ((x) << S_IESPI0_RX_FRAMING_ERROR) +#define F_IESPI0_RX_FRAMING_ERROR V_IESPI0_RX_FRAMING_ERROR(1U) + +#define S_IESPI1_RX_FRAMING_ERROR 14 +#define V_IESPI1_RX_FRAMING_ERROR(x) ((x) << S_IESPI1_RX_FRAMING_ERROR) +#define F_IESPI1_RX_FRAMING_ERROR V_IESPI1_RX_FRAMING_ERROR(1U) + +#define S_IESPI0_TX_FRAMING_ERROR 13 +#define V_IESPI0_TX_FRAMING_ERROR(x) ((x) << S_IESPI0_TX_FRAMING_ERROR) +#define F_IESPI0_TX_FRAMING_ERROR V_IESPI0_TX_FRAMING_ERROR(1U) + +#define S_IESPI1_TX_FRAMING_ERROR 12 +#define V_IESPI1_TX_FRAMING_ERROR(x) ((x) << S_IESPI1_TX_FRAMING_ERROR) +#define F_IESPI1_TX_FRAMING_ERROR V_IESPI1_TX_FRAMING_ERROR(1U) + +#define S_OCSPI0_RX_FRAMING_ERROR 11 +#define V_OCSPI0_RX_FRAMING_ERROR(x) ((x) << S_OCSPI0_RX_FRAMING_ERROR) +#define F_OCSPI0_RX_FRAMING_ERROR V_OCSPI0_RX_FRAMING_ERROR(1U) + +#define S_OCSPI1_RX_FRAMING_ERROR 10 +#define V_OCSPI1_RX_FRAMING_ERROR(x) ((x) << S_OCSPI1_RX_FRAMING_ERROR) +#define F_OCSPI1_RX_FRAMING_ERROR V_OCSPI1_RX_FRAMING_ERROR(1U) + +#define S_OCSPI0_TX_FRAMING_ERROR 9 +#define V_OCSPI0_TX_FRAMING_ERROR(x) ((x) << S_OCSPI0_TX_FRAMING_ERROR) +#define F_OCSPI0_TX_FRAMING_ERROR V_OCSPI0_TX_FRAMING_ERROR(1U) + +#define S_OCSPI1_TX_FRAMING_ERROR 8 +#define V_OCSPI1_TX_FRAMING_ERROR(x) ((x) << S_OCSPI1_TX_FRAMING_ERROR) +#define F_OCSPI1_TX_FRAMING_ERROR V_OCSPI1_TX_FRAMING_ERROR(1U) + +#define S_OCSPI0_OFIFO2X_TX_FRAMING_ERROR 7 +#define V_OCSPI0_OFIFO2X_TX_FRAMING_ERROR(x) ((x) << S_OCSPI0_OFIFO2X_TX_FRAMING_ERROR) +#define F_OCSPI0_OFIFO2X_TX_FRAMING_ERROR V_OCSPI0_OFIFO2X_TX_FRAMING_ERROR(1U) + +#define S_OCSPI1_OFIFO2X_TX_FRAMING_ERROR 6 +#define V_OCSPI1_OFIFO2X_TX_FRAMING_ERROR(x) ((x) << S_OCSPI1_OFIFO2X_TX_FRAMING_ERROR) +#define F_OCSPI1_OFIFO2X_TX_FRAMING_ERROR V_OCSPI1_OFIFO2X_TX_FRAMING_ERROR(1U) + +#define S_IESPI_PAR_ERROR 3 +#define M_IESPI_PAR_ERROR 0x7 + +#define V_IESPI_PAR_ERROR(x) ((x) << S_IESPI_PAR_ERROR) + +#define S_OCSPI_PAR_ERROR 0 +#define M_OCSPI_PAR_ERROR 0x7 + +#define V_OCSPI_PAR_ERROR(x) ((x) << S_OCSPI_PAR_ERROR) + +#define A_PM1_RX_INT_CAUSE 0x5dc + +#define A_PM1_TX_CFG 0x5e0 + +#define A_PM1_TX_INT_ENABLE 0x5f8 + +#define S_ZERO_C_CMD_ERROR 18 +#define V_ZERO_C_CMD_ERROR(x) ((x) << S_ZERO_C_CMD_ERROR) +#define F_ZERO_C_CMD_ERROR V_ZERO_C_CMD_ERROR(1U) + +#define S_ICSPI0_FIFO2X_RX_FRAMING_ERROR 17 +#define V_ICSPI0_FIFO2X_RX_FRAMING_ERROR(x) ((x) << S_ICSPI0_FIFO2X_RX_FRAMING_ERROR) +#define F_ICSPI0_FIFO2X_RX_FRAMING_ERROR V_ICSPI0_FIFO2X_RX_FRAMING_ERROR(1U) + +#define S_ICSPI1_FIFO2X_RX_FRAMING_ERROR 16 +#define V_ICSPI1_FIFO2X_RX_FRAMING_ERROR(x) ((x) << S_ICSPI1_FIFO2X_RX_FRAMING_ERROR) +#define F_ICSPI1_FIFO2X_RX_FRAMING_ERROR V_ICSPI1_FIFO2X_RX_FRAMING_ERROR(1U) + +#define S_ICSPI0_RX_FRAMING_ERROR 15 +#define V_ICSPI0_RX_FRAMING_ERROR(x) ((x) << S_ICSPI0_RX_FRAMING_ERROR) +#define F_ICSPI0_RX_FRAMING_ERROR V_ICSPI0_RX_FRAMING_ERROR(1U) + +#define S_ICSPI1_RX_FRAMING_ERROR 14 +#define V_ICSPI1_RX_FRAMING_ERROR(x) ((x) << S_ICSPI1_RX_FRAMING_ERROR) +#define F_ICSPI1_RX_FRAMING_ERROR V_ICSPI1_RX_FRAMING_ERROR(1U) + +#define S_ICSPI0_TX_FRAMING_ERROR 13 +#define V_ICSPI0_TX_FRAMING_ERROR(x) ((x) << S_ICSPI0_TX_FRAMING_ERROR) +#define F_ICSPI0_TX_FRAMING_ERROR V_ICSPI0_TX_FRAMING_ERROR(1U) + +#define S_ICSPI1_TX_FRAMING_ERROR 12 +#define V_ICSPI1_TX_FRAMING_ERROR(x) ((x) << S_ICSPI1_TX_FRAMING_ERROR) +#define F_ICSPI1_TX_FRAMING_ERROR V_ICSPI1_TX_FRAMING_ERROR(1U) + +#define S_OESPI0_RX_FRAMING_ERROR 11 +#define V_OESPI0_RX_FRAMING_ERROR(x) ((x) << S_OESPI0_RX_FRAMING_ERROR) +#define F_OESPI0_RX_FRAMING_ERROR V_OESPI0_RX_FRAMING_ERROR(1U) + +#define S_OESPI1_RX_FRAMING_ERROR 10 +#define V_OESPI1_RX_FRAMING_ERROR(x) ((x) << S_OESPI1_RX_FRAMING_ERROR) +#define F_OESPI1_RX_FRAMING_ERROR V_OESPI1_RX_FRAMING_ERROR(1U) + +#define S_OESPI0_TX_FRAMING_ERROR 9 +#define V_OESPI0_TX_FRAMING_ERROR(x) ((x) << S_OESPI0_TX_FRAMING_ERROR) +#define F_OESPI0_TX_FRAMING_ERROR V_OESPI0_TX_FRAMING_ERROR(1U) + +#define S_OESPI1_TX_FRAMING_ERROR 8 +#define V_OESPI1_TX_FRAMING_ERROR(x) ((x) << S_OESPI1_TX_FRAMING_ERROR) +#define F_OESPI1_TX_FRAMING_ERROR V_OESPI1_TX_FRAMING_ERROR(1U) + +#define S_OESPI0_OFIFO2X_TX_FRAMING_ERROR 7 +#define V_OESPI0_OFIFO2X_TX_FRAMING_ERROR(x) ((x) << S_OESPI0_OFIFO2X_TX_FRAMING_ERROR) +#define F_OESPI0_OFIFO2X_TX_FRAMING_ERROR V_OESPI0_OFIFO2X_TX_FRAMING_ERROR(1U) + +#define S_OESPI1_OFIFO2X_TX_FRAMING_ERROR 6 +#define V_OESPI1_OFIFO2X_TX_FRAMING_ERROR(x) ((x) << S_OESPI1_OFIFO2X_TX_FRAMING_ERROR) +#define F_OESPI1_OFIFO2X_TX_FRAMING_ERROR V_OESPI1_OFIFO2X_TX_FRAMING_ERROR(1U) + +#define S_ICSPI_PAR_ERROR 3 +#define M_ICSPI_PAR_ERROR 0x7 + +#define V_ICSPI_PAR_ERROR(x) ((x) << S_ICSPI_PAR_ERROR) + +#define S_OESPI_PAR_ERROR 0 +#define M_OESPI_PAR_ERROR 0x7 + +#define V_OESPI_PAR_ERROR(x) ((x) << S_OESPI_PAR_ERROR) + +#define A_PM1_TX_INT_CAUSE 0x5fc + +#define A_MPS_CFG 0x600 + +#define S_TPRXPORTEN 4 +#define V_TPRXPORTEN(x) ((x) << S_TPRXPORTEN) +#define F_TPRXPORTEN V_TPRXPORTEN(1U) + +#define S_TPTXPORT1EN 3 +#define V_TPTXPORT1EN(x) ((x) << S_TPTXPORT1EN) +#define F_TPTXPORT1EN V_TPTXPORT1EN(1U) + +#define S_TPTXPORT0EN 2 +#define V_TPTXPORT0EN(x) ((x) << S_TPTXPORT0EN) +#define F_TPTXPORT0EN V_TPTXPORT0EN(1U) + +#define S_PORT1ACTIVE 1 +#define V_PORT1ACTIVE(x) ((x) << S_PORT1ACTIVE) +#define F_PORT1ACTIVE V_PORT1ACTIVE(1U) + +#define S_PORT0ACTIVE 0 +#define V_PORT0ACTIVE(x) ((x) << S_PORT0ACTIVE) +#define F_PORT0ACTIVE V_PORT0ACTIVE(1U) + +#define S_ENFORCEPKT 11 +#define V_ENFORCEPKT(x) ((x) << S_ENFORCEPKT) +#define F_ENFORCEPKT V_ENFORCEPKT(1U) + +#define A_MPS_INT_ENABLE 0x61c + +#define S_MCAPARERRENB 6 +#define M_MCAPARERRENB 0x7 + +#define V_MCAPARERRENB(x) ((x) << S_MCAPARERRENB) + +#define S_RXTPPARERRENB 4 +#define M_RXTPPARERRENB 0x3 + +#define V_RXTPPARERRENB(x) ((x) << S_RXTPPARERRENB) + +#define S_TX1TPPARERRENB 2 +#define M_TX1TPPARERRENB 0x3 + +#define V_TX1TPPARERRENB(x) ((x) << S_TX1TPPARERRENB) + +#define S_TX0TPPARERRENB 0 +#define M_TX0TPPARERRENB 0x3 + +#define V_TX0TPPARERRENB(x) ((x) << S_TX0TPPARERRENB) + +#define A_MPS_INT_CAUSE 0x620 + +#define S_MCAPARERR 6 +#define M_MCAPARERR 0x7 + +#define V_MCAPARERR(x) ((x) << S_MCAPARERR) + +#define S_RXTPPARERR 4 +#define M_RXTPPARERR 0x3 + +#define V_RXTPPARERR(x) ((x) << S_RXTPPARERR) + +#define S_TX1TPPARERR 2 +#define M_TX1TPPARERR 0x3 + +#define V_TX1TPPARERR(x) ((x) << S_TX1TPPARERR) + +#define S_TX0TPPARERR 0 +#define M_TX0TPPARERR 0x3 + +#define V_TX0TPPARERR(x) ((x) << S_TX0TPPARERR) + +#define A_CPL_SWITCH_CNTRL 0x640 + +#define A_CPL_INTR_ENABLE 0x650 + +#define S_CIM_OVFL_ERROR 4 +#define V_CIM_OVFL_ERROR(x) ((x) << S_CIM_OVFL_ERROR) +#define F_CIM_OVFL_ERROR V_CIM_OVFL_ERROR(1U) + +#define S_TP_FRAMING_ERROR 3 +#define V_TP_FRAMING_ERROR(x) ((x) << S_TP_FRAMING_ERROR) +#define F_TP_FRAMING_ERROR V_TP_FRAMING_ERROR(1U) + +#define S_SGE_FRAMING_ERROR 2 +#define V_SGE_FRAMING_ERROR(x) ((x) << S_SGE_FRAMING_ERROR) +#define F_SGE_FRAMING_ERROR V_SGE_FRAMING_ERROR(1U) + +#define S_CIM_FRAMING_ERROR 1 +#define V_CIM_FRAMING_ERROR(x) ((x) << S_CIM_FRAMING_ERROR) +#define F_CIM_FRAMING_ERROR V_CIM_FRAMING_ERROR(1U) + +#define S_ZERO_SWITCH_ERROR 0 +#define V_ZERO_SWITCH_ERROR(x) ((x) << S_ZERO_SWITCH_ERROR) +#define F_ZERO_SWITCH_ERROR V_ZERO_SWITCH_ERROR(1U) + +#define A_CPL_INTR_CAUSE 0x654 + +#define A_CPL_MAP_TBL_DATA 0x65c + +#define A_SMB_GLOBAL_TIME_CFG 0x660 + +#define A_I2C_CFG 0x6a0 + +#define S_I2C_CLKDIV 0 +#define M_I2C_CLKDIV 0xfff +#define V_I2C_CLKDIV(x) ((x) << S_I2C_CLKDIV) + +#define A_MI1_CFG 0x6b0 + +#define S_CLKDIV 5 +#define M_CLKDIV 0xff +#define V_CLKDIV(x) ((x) << S_CLKDIV) + +#define S_ST 3 + +#define M_ST 0x3 + +#define V_ST(x) ((x) << S_ST) + +#define G_ST(x) (((x) >> S_ST) & M_ST) + +#define S_PREEN 2 +#define V_PREEN(x) ((x) << S_PREEN) +#define F_PREEN V_PREEN(1U) + +#define S_MDIINV 1 +#define V_MDIINV(x) ((x) << S_MDIINV) +#define F_MDIINV V_MDIINV(1U) + +#define S_MDIEN 0 +#define V_MDIEN(x) ((x) << S_MDIEN) +#define F_MDIEN V_MDIEN(1U) + +#define A_MI1_ADDR 0x6b4 + +#define S_PHYADDR 5 +#define M_PHYADDR 0x1f +#define V_PHYADDR(x) ((x) << S_PHYADDR) + +#define S_REGADDR 0 +#define M_REGADDR 0x1f +#define V_REGADDR(x) ((x) << S_REGADDR) + +#define A_MI1_DATA 0x6b8 + +#define A_MI1_OP 0x6bc + +#define S_MDI_OP 0 +#define M_MDI_OP 0x3 +#define V_MDI_OP(x) ((x) << S_MDI_OP) + +#define A_SF_DATA 0x6d8 + +#define A_SF_OP 0x6dc + +#define S_BYTECNT 1 +#define M_BYTECNT 0x3 +#define V_BYTECNT(x) ((x) << S_BYTECNT) + +#define A_PL_INT_ENABLE0 0x6e0 + +#define S_T3DBG 23 +#define V_T3DBG(x) ((x) << S_T3DBG) +#define F_T3DBG V_T3DBG(1U) + +#define S_XGMAC0_1 20 +#define V_XGMAC0_1(x) ((x) << S_XGMAC0_1) +#define F_XGMAC0_1 V_XGMAC0_1(1U) + +#define S_XGMAC0_0 19 +#define V_XGMAC0_0(x) ((x) << S_XGMAC0_0) +#define F_XGMAC0_0 V_XGMAC0_0(1U) + +#define S_MC5A 18 +#define V_MC5A(x) ((x) << S_MC5A) +#define F_MC5A V_MC5A(1U) + +#define S_CPL_SWITCH 12 +#define V_CPL_SWITCH(x) ((x) << S_CPL_SWITCH) +#define F_CPL_SWITCH V_CPL_SWITCH(1U) + +#define S_MPS0 11 +#define V_MPS0(x) ((x) << S_MPS0) +#define F_MPS0 V_MPS0(1U) + +#define S_PM1_TX 10 +#define V_PM1_TX(x) ((x) << S_PM1_TX) +#define F_PM1_TX V_PM1_TX(1U) + +#define S_PM1_RX 9 +#define V_PM1_RX(x) ((x) << S_PM1_RX) +#define F_PM1_RX V_PM1_RX(1U) + +#define S_ULP2_TX 8 +#define V_ULP2_TX(x) ((x) << S_ULP2_TX) +#define F_ULP2_TX V_ULP2_TX(1U) + +#define S_ULP2_RX 7 +#define V_ULP2_RX(x) ((x) << S_ULP2_RX) +#define F_ULP2_RX V_ULP2_RX(1U) + +#define S_TP1 6 +#define V_TP1(x) ((x) << S_TP1) +#define F_TP1 V_TP1(1U) + +#define S_CIM 5 +#define V_CIM(x) ((x) << S_CIM) +#define F_CIM V_CIM(1U) + +#define S_MC7_CM 4 +#define V_MC7_CM(x) ((x) << S_MC7_CM) +#define F_MC7_CM V_MC7_CM(1U) + +#define S_MC7_PMTX 3 +#define V_MC7_PMTX(x) ((x) << S_MC7_PMTX) +#define F_MC7_PMTX V_MC7_PMTX(1U) + +#define S_MC7_PMRX 2 +#define V_MC7_PMRX(x) ((x) << S_MC7_PMRX) +#define F_MC7_PMRX V_MC7_PMRX(1U) + +#define S_PCIM0 1 +#define V_PCIM0(x) ((x) << S_PCIM0) +#define F_PCIM0 V_PCIM0(1U) + +#define S_SGE3 0 +#define V_SGE3(x) ((x) << S_SGE3) +#define F_SGE3 V_SGE3(1U) + +#define A_PL_INT_CAUSE0 0x6e4 + +#define A_PL_RST 0x6f0 + +#define S_CRSTWRM 1 +#define V_CRSTWRM(x) ((x) << S_CRSTWRM) +#define F_CRSTWRM V_CRSTWRM(1U) + +#define A_PL_REV 0x6f4 + +#define A_PL_CLI 0x6f8 + +#define A_MC5_DB_CONFIG 0x704 + +#define S_TMTYPEHI 30 +#define V_TMTYPEHI(x) ((x) << S_TMTYPEHI) +#define F_TMTYPEHI V_TMTYPEHI(1U) + +#define S_TMPARTSIZE 28 +#define M_TMPARTSIZE 0x3 +#define V_TMPARTSIZE(x) ((x) << S_TMPARTSIZE) +#define G_TMPARTSIZE(x) (((x) >> S_TMPARTSIZE) & M_TMPARTSIZE) + +#define S_TMTYPE 26 +#define M_TMTYPE 0x3 +#define V_TMTYPE(x) ((x) << S_TMTYPE) +#define G_TMTYPE(x) (((x) >> S_TMTYPE) & M_TMTYPE) + +#define S_COMPEN 17 +#define V_COMPEN(x) ((x) << S_COMPEN) +#define F_COMPEN V_COMPEN(1U) + +#define S_PRTYEN 6 +#define V_PRTYEN(x) ((x) << S_PRTYEN) +#define F_PRTYEN V_PRTYEN(1U) + +#define S_MBUSEN 5 +#define V_MBUSEN(x) ((x) << S_MBUSEN) +#define F_MBUSEN V_MBUSEN(1U) + +#define S_DBGIEN 4 +#define V_DBGIEN(x) ((x) << S_DBGIEN) +#define F_DBGIEN V_DBGIEN(1U) + +#define S_TMRDY 2 +#define V_TMRDY(x) ((x) << S_TMRDY) +#define F_TMRDY V_TMRDY(1U) + +#define S_TMRST 1 +#define V_TMRST(x) ((x) << S_TMRST) +#define F_TMRST V_TMRST(1U) + +#define S_TMMODE 0 +#define V_TMMODE(x) ((x) << S_TMMODE) +#define F_TMMODE V_TMMODE(1U) + +#define F_TMMODE V_TMMODE(1U) + +#define A_MC5_DB_ROUTING_TABLE_INDEX 0x70c + +#define A_MC5_DB_FILTER_TABLE 0x710 + +#define A_MC5_DB_SERVER_INDEX 0x714 + +#define A_MC5_DB_RSP_LATENCY 0x720 + +#define S_RDLAT 16 +#define M_RDLAT 0x1f +#define V_RDLAT(x) ((x) << S_RDLAT) + +#define S_LRNLAT 8 +#define M_LRNLAT 0x1f +#define V_LRNLAT(x) ((x) << S_LRNLAT) + +#define S_SRCHLAT 0 +#define M_SRCHLAT 0x1f +#define V_SRCHLAT(x) ((x) << S_SRCHLAT) + +#define A_MC5_DB_PART_ID_INDEX 0x72c + +#define A_MC5_DB_INT_ENABLE 0x740 + +#define S_DELACTEMPTY 18 +#define V_DELACTEMPTY(x) ((x) << S_DELACTEMPTY) +#define F_DELACTEMPTY V_DELACTEMPTY(1U) + +#define S_DISPQPARERR 17 +#define V_DISPQPARERR(x) ((x) << S_DISPQPARERR) +#define F_DISPQPARERR V_DISPQPARERR(1U) + +#define S_REQQPARERR 16 +#define V_REQQPARERR(x) ((x) << S_REQQPARERR) +#define F_REQQPARERR V_REQQPARERR(1U) + +#define S_UNKNOWNCMD 15 +#define V_UNKNOWNCMD(x) ((x) << S_UNKNOWNCMD) +#define F_UNKNOWNCMD V_UNKNOWNCMD(1U) + +#define S_NFASRCHFAIL 8 +#define V_NFASRCHFAIL(x) ((x) << S_NFASRCHFAIL) +#define F_NFASRCHFAIL V_NFASRCHFAIL(1U) + +#define S_ACTRGNFULL 7 +#define V_ACTRGNFULL(x) ((x) << S_ACTRGNFULL) +#define F_ACTRGNFULL V_ACTRGNFULL(1U) + +#define S_PARITYERR 6 +#define V_PARITYERR(x) ((x) << S_PARITYERR) +#define F_PARITYERR V_PARITYERR(1U) + +#define A_MC5_DB_INT_CAUSE 0x744 + +#define A_MC5_DB_DBGI_CONFIG 0x774 + +#define A_MC5_DB_DBGI_REQ_CMD 0x778 + +#define A_MC5_DB_DBGI_REQ_ADDR0 0x77c + +#define A_MC5_DB_DBGI_REQ_ADDR1 0x780 + +#define A_MC5_DB_DBGI_REQ_ADDR2 0x784 + +#define A_MC5_DB_DBGI_REQ_DATA0 0x788 + +#define A_MC5_DB_DBGI_REQ_DATA1 0x78c + +#define A_MC5_DB_DBGI_REQ_DATA2 0x790 + +#define A_MC5_DB_DBGI_RSP_STATUS 0x7b0 + +#define S_DBGIRSPVALID 0 +#define V_DBGIRSPVALID(x) ((x) << S_DBGIRSPVALID) +#define F_DBGIRSPVALID V_DBGIRSPVALID(1U) + +#define A_MC5_DB_DBGI_RSP_DATA0 0x7b4 + +#define A_MC5_DB_DBGI_RSP_DATA1 0x7b8 + +#define A_MC5_DB_DBGI_RSP_DATA2 0x7bc + +#define A_MC5_DB_POPEN_DATA_WR_CMD 0x7cc + +#define A_MC5_DB_POPEN_MASK_WR_CMD 0x7d0 + +#define A_MC5_DB_AOPEN_SRCH_CMD 0x7d4 + +#define A_MC5_DB_AOPEN_LRN_CMD 0x7d8 + +#define A_MC5_DB_SYN_SRCH_CMD 0x7dc + +#define A_MC5_DB_SYN_LRN_CMD 0x7e0 + +#define A_MC5_DB_ACK_SRCH_CMD 0x7e4 + +#define A_MC5_DB_ACK_LRN_CMD 0x7e8 + +#define A_MC5_DB_ILOOKUP_CMD 0x7ec + +#define A_MC5_DB_ELOOKUP_CMD 0x7f0 + +#define A_MC5_DB_DATA_WRITE_CMD 0x7f4 + +#define A_MC5_DB_DATA_READ_CMD 0x7f8 + +#define XGMAC0_0_BASE_ADDR 0x800 + +#define A_XGM_TX_CTRL 0x800 + +#define S_TXEN 0 +#define V_TXEN(x) ((x) << S_TXEN) +#define F_TXEN V_TXEN(1U) + +#define A_XGM_TX_CFG 0x804 + +#define S_TXPAUSEEN 0 +#define V_TXPAUSEEN(x) ((x) << S_TXPAUSEEN) +#define F_TXPAUSEEN V_TXPAUSEEN(1U) + +#define A_XGM_RX_CTRL 0x80c + +#define S_RXEN 0 +#define V_RXEN(x) ((x) << S_RXEN) +#define F_RXEN V_RXEN(1U) + +#define A_XGM_RX_CFG 0x810 + +#define S_DISPAUSEFRAMES 9 +#define V_DISPAUSEFRAMES(x) ((x) << S_DISPAUSEFRAMES) +#define F_DISPAUSEFRAMES V_DISPAUSEFRAMES(1U) + +#define S_EN1536BFRAMES 8 +#define V_EN1536BFRAMES(x) ((x) << S_EN1536BFRAMES) +#define F_EN1536BFRAMES V_EN1536BFRAMES(1U) + +#define S_ENJUMBO 7 +#define V_ENJUMBO(x) ((x) << S_ENJUMBO) +#define F_ENJUMBO V_ENJUMBO(1U) + +#define S_RMFCS 6 +#define V_RMFCS(x) ((x) << S_RMFCS) +#define F_RMFCS V_RMFCS(1U) + +#define S_ENHASHMCAST 2 +#define V_ENHASHMCAST(x) ((x) << S_ENHASHMCAST) +#define F_ENHASHMCAST V_ENHASHMCAST(1U) + +#define S_COPYALLFRAMES 0 +#define V_COPYALLFRAMES(x) ((x) << S_COPYALLFRAMES) +#define F_COPYALLFRAMES V_COPYALLFRAMES(1U) + +#define A_XGM_RX_HASH_LOW 0x814 + +#define A_XGM_RX_HASH_HIGH 0x818 + +#define A_XGM_RX_EXACT_MATCH_LOW_1 0x81c + +#define A_XGM_RX_EXACT_MATCH_HIGH_1 0x820 + +#define A_XGM_RX_EXACT_MATCH_LOW_2 0x824 + +#define A_XGM_RX_EXACT_MATCH_LOW_3 0x82c + +#define A_XGM_RX_EXACT_MATCH_LOW_4 0x834 + +#define A_XGM_RX_EXACT_MATCH_LOW_5 0x83c + +#define A_XGM_RX_EXACT_MATCH_LOW_6 0x844 + +#define A_XGM_RX_EXACT_MATCH_LOW_7 0x84c + +#define A_XGM_RX_EXACT_MATCH_LOW_8 0x854 + +#define A_XGM_STAT_CTRL 0x880 + +#define S_CLRSTATS 2 +#define V_CLRSTATS(x) ((x) << S_CLRSTATS) +#define F_CLRSTATS V_CLRSTATS(1U) + +#define A_XGM_RXFIFO_CFG 0x884 + +#define S_RXFIFOPAUSEHWM 17 +#define M_RXFIFOPAUSEHWM 0xfff + +#define V_RXFIFOPAUSEHWM(x) ((x) << S_RXFIFOPAUSEHWM) + +#define G_RXFIFOPAUSEHWM(x) (((x) >> S_RXFIFOPAUSEHWM) & M_RXFIFOPAUSEHWM) + +#define S_RXFIFOPAUSELWM 5 +#define M_RXFIFOPAUSELWM 0xfff + +#define V_RXFIFOPAUSELWM(x) ((x) << S_RXFIFOPAUSELWM) + +#define G_RXFIFOPAUSELWM(x) (((x) >> S_RXFIFOPAUSELWM) & M_RXFIFOPAUSELWM) + +#define S_RXSTRFRWRD 1 +#define V_RXSTRFRWRD(x) ((x) << S_RXSTRFRWRD) +#define F_RXSTRFRWRD V_RXSTRFRWRD(1U) + +#define S_DISERRFRAMES 0 +#define V_DISERRFRAMES(x) ((x) << S_DISERRFRAMES) +#define F_DISERRFRAMES V_DISERRFRAMES(1U) + +#define A_XGM_TXFIFO_CFG 0x888 + +#define S_TXFIFOTHRESH 4 +#define M_TXFIFOTHRESH 0x1ff + +#define V_TXFIFOTHRESH(x) ((x) << S_TXFIFOTHRESH) + +#define A_XGM_SERDES_CTRL 0x890 +#define A_XGM_SERDES_CTRL0 0x8e0 + +#define S_SERDESRESET_ 24 +#define V_SERDESRESET_(x) ((x) << S_SERDESRESET_) +#define F_SERDESRESET_ V_SERDESRESET_(1U) + +#define S_RXENABLE 4 +#define V_RXENABLE(x) ((x) << S_RXENABLE) +#define F_RXENABLE V_RXENABLE(1U) + +#define S_TXENABLE 3 +#define V_TXENABLE(x) ((x) << S_TXENABLE) +#define F_TXENABLE V_TXENABLE(1U) + +#define A_XGM_PAUSE_TIMER 0x890 + +#define A_XGM_RGMII_IMP 0x89c + +#define S_XGM_IMPSETUPDATE 6 +#define V_XGM_IMPSETUPDATE(x) ((x) << S_XGM_IMPSETUPDATE) +#define F_XGM_IMPSETUPDATE V_XGM_IMPSETUPDATE(1U) + +#define S_RGMIIIMPPD 3 +#define M_RGMIIIMPPD 0x7 +#define V_RGMIIIMPPD(x) ((x) << S_RGMIIIMPPD) + +#define S_RGMIIIMPPU 0 +#define M_RGMIIIMPPU 0x7 +#define V_RGMIIIMPPU(x) ((x) << S_RGMIIIMPPU) + +#define S_CALRESET 8 +#define V_CALRESET(x) ((x) << S_CALRESET) +#define F_CALRESET V_CALRESET(1U) + +#define S_CALUPDATE 7 +#define V_CALUPDATE(x) ((x) << S_CALUPDATE) +#define F_CALUPDATE V_CALUPDATE(1U) + +#define A_XGM_XAUI_IMP 0x8a0 + +#define S_CALBUSY 31 +#define V_CALBUSY(x) ((x) << S_CALBUSY) +#define F_CALBUSY V_CALBUSY(1U) + +#define S_XGM_CALFAULT 29 +#define V_XGM_CALFAULT(x) ((x) << S_XGM_CALFAULT) +#define F_XGM_CALFAULT V_XGM_CALFAULT(1U) + +#define S_CALIMP 24 +#define M_CALIMP 0x1f +#define V_CALIMP(x) ((x) << S_CALIMP) +#define G_CALIMP(x) (((x) >> S_CALIMP) & M_CALIMP) + +#define S_XAUIIMP 0 +#define M_XAUIIMP 0x7 +#define V_XAUIIMP(x) ((x) << S_XAUIIMP) + +#define A_XGM_RX_MAX_PKT_SIZE 0x8a8 +#define A_XGM_RX_MAX_PKT_SIZE_ERR_CNT 0x9a4 + +#define A_XGM_RESET_CTRL 0x8ac + +#define S_XG2G_RESET_ 3 +#define V_XG2G_RESET_(x) ((x) << S_XG2G_RESET_) +#define F_XG2G_RESET_ V_XG2G_RESET_(1U) + +#define S_RGMII_RESET_ 2 +#define V_RGMII_RESET_(x) ((x) << S_RGMII_RESET_) +#define F_RGMII_RESET_ V_RGMII_RESET_(1U) + +#define S_PCS_RESET_ 1 +#define V_PCS_RESET_(x) ((x) << S_PCS_RESET_) +#define F_PCS_RESET_ V_PCS_RESET_(1U) + +#define S_MAC_RESET_ 0 +#define V_MAC_RESET_(x) ((x) << S_MAC_RESET_) +#define F_MAC_RESET_ V_MAC_RESET_(1U) + +#define A_XGM_PORT_CFG 0x8b8 + +#define S_CLKDIVRESET_ 3 +#define V_CLKDIVRESET_(x) ((x) << S_CLKDIVRESET_) +#define F_CLKDIVRESET_ V_CLKDIVRESET_(1U) + +#define S_PORTSPEED 1 +#define M_PORTSPEED 0x3 + +#define V_PORTSPEED(x) ((x) << S_PORTSPEED) + +#define S_ENRGMII 0 +#define V_ENRGMII(x) ((x) << S_ENRGMII) +#define F_ENRGMII V_ENRGMII(1U) + +#define A_XGM_INT_ENABLE 0x8d4 + +#define S_TXFIFO_PRTY_ERR 17 +#define M_TXFIFO_PRTY_ERR 0x7 + +#define V_TXFIFO_PRTY_ERR(x) ((x) << S_TXFIFO_PRTY_ERR) + +#define S_RXFIFO_PRTY_ERR 14 +#define M_RXFIFO_PRTY_ERR 0x7 + +#define V_RXFIFO_PRTY_ERR(x) ((x) << S_RXFIFO_PRTY_ERR) + +#define S_TXFIFO_UNDERRUN 13 +#define V_TXFIFO_UNDERRUN(x) ((x) << S_TXFIFO_UNDERRUN) +#define F_TXFIFO_UNDERRUN V_TXFIFO_UNDERRUN(1U) + +#define S_RXFIFO_OVERFLOW 12 +#define V_RXFIFO_OVERFLOW(x) ((x) << S_RXFIFO_OVERFLOW) +#define F_RXFIFO_OVERFLOW V_RXFIFO_OVERFLOW(1U) + +#define S_SERDES_LOS 4 +#define M_SERDES_LOS 0xf + +#define V_SERDES_LOS(x) ((x) << S_SERDES_LOS) + +#define S_XAUIPCSCTCERR 3 +#define V_XAUIPCSCTCERR(x) ((x) << S_XAUIPCSCTCERR) +#define F_XAUIPCSCTCERR V_XAUIPCSCTCERR(1U) + +#define S_XAUIPCSALIGNCHANGE 2 +#define V_XAUIPCSALIGNCHANGE(x) ((x) << S_XAUIPCSALIGNCHANGE) +#define F_XAUIPCSALIGNCHANGE V_XAUIPCSALIGNCHANGE(1U) + +#define A_XGM_INT_CAUSE 0x8d8 + +#define A_XGM_XAUI_ACT_CTRL 0x8dc + +#define S_TXACTENABLE 1 +#define V_TXACTENABLE(x) ((x) << S_TXACTENABLE) +#define F_TXACTENABLE V_TXACTENABLE(1U) + +#define A_XGM_SERDES_CTRL0 0x8e0 + +#define S_RESET3 23 +#define V_RESET3(x) ((x) << S_RESET3) +#define F_RESET3 V_RESET3(1U) + +#define S_RESET2 22 +#define V_RESET2(x) ((x) << S_RESET2) +#define F_RESET2 V_RESET2(1U) + +#define S_RESET1 21 +#define V_RESET1(x) ((x) << S_RESET1) +#define F_RESET1 V_RESET1(1U) + +#define S_RESET0 20 +#define V_RESET0(x) ((x) << S_RESET0) +#define F_RESET0 V_RESET0(1U) + +#define S_PWRDN3 19 +#define V_PWRDN3(x) ((x) << S_PWRDN3) +#define F_PWRDN3 V_PWRDN3(1U) + +#define S_PWRDN2 18 +#define V_PWRDN2(x) ((x) << S_PWRDN2) +#define F_PWRDN2 V_PWRDN2(1U) + +#define S_PWRDN1 17 +#define V_PWRDN1(x) ((x) << S_PWRDN1) +#define F_PWRDN1 V_PWRDN1(1U) + +#define S_PWRDN0 16 +#define V_PWRDN0(x) ((x) << S_PWRDN0) +#define F_PWRDN0 V_PWRDN0(1U) + +#define S_RESETPLL23 15 +#define V_RESETPLL23(x) ((x) << S_RESETPLL23) +#define F_RESETPLL23 V_RESETPLL23(1U) + +#define S_RESETPLL01 14 +#define V_RESETPLL01(x) ((x) << S_RESETPLL01) +#define F_RESETPLL01 V_RESETPLL01(1U) + +#define A_XGM_SERDES_STAT0 0x8f0 + +#define S_LOWSIG0 0 +#define V_LOWSIG0(x) ((x) << S_LOWSIG0) +#define F_LOWSIG0 V_LOWSIG0(1U) + +#define A_XGM_SERDES_STAT3 0x8fc + +#define A_XGM_STAT_TX_BYTE_LOW 0x900 + +#define A_XGM_STAT_TX_BYTE_HIGH 0x904 + +#define A_XGM_STAT_TX_FRAME_LOW 0x908 + +#define A_XGM_STAT_TX_FRAME_HIGH 0x90c + +#define A_XGM_STAT_TX_BCAST 0x910 + +#define A_XGM_STAT_TX_MCAST 0x914 + +#define A_XGM_STAT_TX_PAUSE 0x918 + +#define A_XGM_STAT_TX_64B_FRAMES 0x91c + +#define A_XGM_STAT_TX_65_127B_FRAMES 0x920 + +#define A_XGM_STAT_TX_128_255B_FRAMES 0x924 + +#define A_XGM_STAT_TX_256_511B_FRAMES 0x928 + +#define A_XGM_STAT_TX_512_1023B_FRAMES 0x92c + +#define A_XGM_STAT_TX_1024_1518B_FRAMES 0x930 + +#define A_XGM_STAT_TX_1519_MAXB_FRAMES 0x934 + +#define A_XGM_STAT_TX_ERR_FRAMES 0x938 + +#define A_XGM_STAT_RX_BYTES_LOW 0x93c + +#define A_XGM_STAT_RX_BYTES_HIGH 0x940 + +#define A_XGM_STAT_RX_FRAMES_LOW 0x944 + +#define A_XGM_STAT_RX_FRAMES_HIGH 0x948 + +#define A_XGM_STAT_RX_BCAST_FRAMES 0x94c + +#define A_XGM_STAT_RX_MCAST_FRAMES 0x950 + +#define A_XGM_STAT_RX_PAUSE_FRAMES 0x954 + +#define A_XGM_STAT_RX_64B_FRAMES 0x958 + +#define A_XGM_STAT_RX_65_127B_FRAMES 0x95c + +#define A_XGM_STAT_RX_128_255B_FRAMES 0x960 + +#define A_XGM_STAT_RX_256_511B_FRAMES 0x964 + +#define A_XGM_STAT_RX_512_1023B_FRAMES 0x968 + +#define A_XGM_STAT_RX_1024_1518B_FRAMES 0x96c + +#define A_XGM_STAT_RX_1519_MAXB_FRAMES 0x970 + +#define A_XGM_STAT_RX_SHORT_FRAMES 0x974 + +#define A_XGM_STAT_RX_OVERSIZE_FRAMES 0x978 + +#define A_XGM_STAT_RX_JABBER_FRAMES 0x97c + +#define A_XGM_STAT_RX_CRC_ERR_FRAMES 0x980 + +#define A_XGM_STAT_RX_LENGTH_ERR_FRAMES 0x984 + +#define A_XGM_STAT_RX_SYM_CODE_ERR_FRAMES 0x988 + +#define A_XGM_SERDES_STATUS0 0x98c + +#define A_XGM_SERDES_STATUS1 0x990 + +#define S_CMULOCK 31 +#define V_CMULOCK(x) ((x) << S_CMULOCK) +#define F_CMULOCK V_CMULOCK(1U) + +#define A_XGM_RX_MAX_PKT_SIZE_ERR_CNT 0x9a4 + +#define A_XGM_RX_SPI4_SOP_EOP_CNT 0x9ac + +#define XGMAC0_1_BASE_ADDR 0xa00 diff -puN /dev/null drivers/net/cxgb3/sge.c --- /dev/null +++ a/drivers/net/cxgb3/sge.c @@ -0,0 +1,2702 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "regs.h" +#include "sge_defs.h" +#include "t3_cpl.h" +#include "firmware_exports.h" + +#define USE_GTS 0 + +#define SGE_RX_SM_BUF_SIZE 1536 +#define SGE_RX_COPY_THRES 256 + +# define SGE_RX_DROP_THRES 16 + +/* + * Period of the Tx buffer reclaim timer. This timer does not need to run + * frequently as Tx buffers are usually reclaimed by new Tx packets. + */ +#define TX_RECLAIM_PERIOD (HZ / 4) + +/* WR size in bytes */ +#define WR_LEN (WR_FLITS * 8) + +/* + * Types of Tx queues in each queue set. Order here matters, do not change. + */ +enum { TXQ_ETH, TXQ_OFLD, TXQ_CTRL }; + +/* Values for sge_txq.flags */ +enum { + TXQ_RUNNING = 1 << 0, /* fetch engine is running */ + TXQ_LAST_PKT_DB = 1 << 1, /* last packet rang the doorbell */ +}; + +struct tx_desc { + u64 flit[TX_DESC_FLITS]; +}; + +struct rx_desc { + __be32 addr_lo; + __be32 len_gen; + __be32 gen2; + __be32 addr_hi; +}; + +struct tx_sw_desc { /* SW state per Tx descriptor */ + struct sk_buff *skb; +}; + +struct rx_sw_desc { /* SW state per Rx descriptor */ + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(dma_addr); +}; + +struct rsp_desc { /* response queue descriptor */ + struct rss_header rss_hdr; + __be32 flags; + __be32 len_cq; + u8 imm_data[47]; + u8 intr_gen; +}; + +struct unmap_info { /* packet unmapping info, overlays skb->cb */ + int sflit; /* start flit of first SGL entry in Tx descriptor */ + u16 fragidx; /* first page fragment in current Tx descriptor */ + u16 addr_idx; /* buffer index of first SGL entry in descriptor */ + u32 len; /* mapped length of skb main body */ +}; + +/* + * Maps a number of flits to the number of Tx descriptors that can hold them. + * The formula is + * + * desc = 1 + (flits - 2) / (WR_FLITS - 1). + * + * HW allows up to 4 descriptors to be combined into a WR. + */ +static u8 flit_desc_map[] = { + 0, +#if SGE_NUM_GENBITS == 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +#elif SGE_NUM_GENBITS == 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +#else +# error "SGE_NUM_GENBITS must be 1 or 2" +#endif +}; + +static inline struct sge_qset *fl_to_qset(const struct sge_fl *q, int qidx) +{ + return container_of(q, struct sge_qset, fl[qidx]); +} + +static inline struct sge_qset *rspq_to_qset(const struct sge_rspq *q) +{ + return container_of(q, struct sge_qset, rspq); +} + +static inline struct sge_qset *txq_to_qset(const struct sge_txq *q, int qidx) +{ + return container_of(q, struct sge_qset, txq[qidx]); +} + +/** + * refill_rspq - replenish an SGE response queue + * @adapter: the adapter + * @q: the response queue to replenish + * @credits: how many new responses to make available + * + * Replenishes a response queue by making the supplied number of responses + * available to HW. + */ +static inline void refill_rspq(struct adapter *adapter, + const struct sge_rspq *q, unsigned int credits) +{ + t3_write_reg(adapter, A_SG_RSPQ_CREDIT_RETURN, + V_RSPQ(q->cntxt_id) | V_CREDITS(credits)); +} + +/** + * need_skb_unmap - does the platform need unmapping of sk_buffs? + * + * Returns true if the platfrom needs sk_buff unmapping. The compiler + * optimizes away unecessary code if this returns true. + */ +static inline int need_skb_unmap(void) +{ + /* + * This structure is used to tell if the platfrom needs buffer + * unmapping by checking if DECLARE_PCI_UNMAP_ADDR defines anything. + */ + struct dummy { + DECLARE_PCI_UNMAP_ADDR(addr); + }; + + return sizeof(struct dummy) != 0; +} + +/** + * unmap_skb - unmap a packet main body and its page fragments + * @skb: the packet + * @q: the Tx queue containing Tx descriptors for the packet + * @cidx: index of Tx descriptor + * @pdev: the PCI device + * + * Unmap the main body of an sk_buff and its page fragments, if any. + * Because of the fairly complicated structure of our SGLs and the desire + * to conserve space for metadata, we keep the information necessary to + * unmap an sk_buff partly in the sk_buff itself (in its cb), and partly + * in the Tx descriptors (the physical addresses of the various data + * buffers). The send functions initialize the state in skb->cb so we + * can unmap the buffers held in the first Tx descriptor here, and we + * have enough information at this point to update the state for the next + * Tx descriptor. + */ +static inline void unmap_skb(struct sk_buff *skb, struct sge_txq *q, + unsigned int cidx, struct pci_dev *pdev) +{ + const struct sg_ent *sgp; + struct unmap_info *ui = (struct unmap_info *)skb->cb; + int nfrags, frag_idx, curflit, j = ui->addr_idx; + + sgp = (struct sg_ent *)&q->desc[cidx].flit[ui->sflit]; + + if (ui->len) { + pci_unmap_single(pdev, be64_to_cpu(sgp->addr[0]), ui->len, + PCI_DMA_TODEVICE); + ui->len = 0; /* so we know for next descriptor for this skb */ + j = 1; + } + + frag_idx = ui->fragidx; + curflit = ui->sflit + 1 + j; + nfrags = skb_shinfo(skb)->nr_frags; + + while (frag_idx < nfrags && curflit < WR_FLITS) { + pci_unmap_page(pdev, be64_to_cpu(sgp->addr[j]), + skb_shinfo(skb)->frags[frag_idx].size, + PCI_DMA_TODEVICE); + j ^= 1; + if (j == 0) { + sgp++; + curflit++; + } + curflit++; + frag_idx++; + } + + if (frag_idx < nfrags) { /* SGL continues into next Tx descriptor */ + ui->fragidx = frag_idx; + ui->addr_idx = j; + ui->sflit = curflit - WR_FLITS - j; /* sflit can be -1 */ + } +} + +/** + * free_tx_desc - reclaims Tx descriptors and their buffers + * @adapter: the adapter + * @q: the Tx queue to reclaim descriptors from + * @n: the number of descriptors to reclaim + * + * Reclaims Tx descriptors from an SGE Tx queue and frees the associated + * Tx buffers. Called with the Tx queue lock held. + */ +static void free_tx_desc(struct adapter *adapter, struct sge_txq *q, + unsigned int n) +{ + struct tx_sw_desc *d; + struct pci_dev *pdev = adapter->pdev; + unsigned int cidx = q->cidx; + + d = &q->sdesc[cidx]; + while (n--) { + if (d->skb) { /* an SGL is present */ + if (need_skb_unmap()) + unmap_skb(d->skb, q, cidx, pdev); + if (d->skb->priority == cidx) + kfree_skb(d->skb); + } + ++d; + if (++cidx == q->size) { + cidx = 0; + d = q->sdesc; + } + } + q->cidx = cidx; +} + +/** + * reclaim_completed_tx - reclaims completed Tx descriptors + * @adapter: the adapter + * @q: the Tx queue to reclaim completed descriptors from + * + * Reclaims Tx descriptors that the SGE has indicated it has processed, + * and frees the associated buffers if possible. Called with the Tx + * queue's lock held. + */ +static inline void reclaim_completed_tx(struct adapter *adapter, + struct sge_txq *q) +{ + unsigned int reclaim = q->processed - q->cleaned; + + if (reclaim) { + free_tx_desc(adapter, q, reclaim); + q->cleaned += reclaim; + q->in_use -= reclaim; + } +} + +/** + * should_restart_tx - are there enough resources to restart a Tx queue? + * @q: the Tx queue + * + * Checks if there are enough descriptors to restart a suspended Tx queue. + */ +static inline int should_restart_tx(const struct sge_txq *q) +{ + unsigned int r = q->processed - q->cleaned; + + return q->in_use - r < (q->size >> 1); +} + +/** + * free_rx_bufs - free the Rx buffers on an SGE free list + * @pdev: the PCI device associated with the adapter + * @rxq: the SGE free list to clean up + * + * Release the buffers on an SGE free-buffer Rx queue. HW fetching from + * this queue should be stopped before calling this function. + */ +static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q) +{ + unsigned int cidx = q->cidx; + + while (q->credits--) { + struct rx_sw_desc *d = &q->sdesc[cidx]; + + pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr), + q->buf_size, PCI_DMA_FROMDEVICE); + kfree_skb(d->skb); + d->skb = NULL; + if (++cidx == q->size) + cidx = 0; + } +} + +/** + * add_one_rx_buf - add a packet buffer to a free-buffer list + * @skb: the buffer to add + * @len: the buffer length + * @d: the HW Rx descriptor to write + * @sd: the SW Rx descriptor to write + * @gen: the generation bit value + * @pdev: the PCI device associated with the adapter + * + * Add a buffer of the given length to the supplied HW and SW Rx + * descriptors. + */ +static inline void add_one_rx_buf(struct sk_buff *skb, unsigned int len, + struct rx_desc *d, struct rx_sw_desc *sd, + unsigned int gen, struct pci_dev *pdev) +{ + dma_addr_t mapping; + + sd->skb = skb; + mapping = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE); + pci_unmap_addr_set(sd, dma_addr, mapping); + + d->addr_lo = cpu_to_be32(mapping); + d->addr_hi = cpu_to_be32((u64) mapping >> 32); + wmb(); + d->len_gen = cpu_to_be32(V_FLD_GEN1(gen)); + d->gen2 = cpu_to_be32(V_FLD_GEN2(gen)); +} + +/** + * refill_fl - refill an SGE free-buffer list + * @adapter: the adapter + * @q: the free-list to refill + * @n: the number of new buffers to allocate + * @gfp: the gfp flags for allocating new buffers + * + * (Re)populate an SGE free-buffer list with up to @n new packet buffers, + * allocated with the supplied gfp flags. The caller must assure that + * @n does not exceed the queue's capacity. + */ +static void refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp) +{ + struct rx_sw_desc *sd = &q->sdesc[q->pidx]; + struct rx_desc *d = &q->desc[q->pidx]; + + while (n--) { + struct sk_buff *skb = alloc_skb(q->buf_size, gfp); + + if (!skb) + break; + + add_one_rx_buf(skb, q->buf_size, d, sd, q->gen, adap->pdev); + d++; + sd++; + if (++q->pidx == q->size) { + q->pidx = 0; + q->gen ^= 1; + sd = q->sdesc; + d = q->desc; + } + q->credits++; + } + + t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); +} + +static inline void __refill_fl(struct adapter *adap, struct sge_fl *fl) +{ + refill_fl(adap, fl, min(16U, fl->size - fl->credits), GFP_ATOMIC); +} + +/** + * recycle_rx_buf - recycle a receive buffer + * @adapter: the adapter + * @q: the SGE free list + * @idx: index of buffer to recycle + * + * Recycles the specified buffer on the given free list by adding it at + * the next available slot on the list. + */ +static void recycle_rx_buf(struct adapter *adap, struct sge_fl *q, + unsigned int idx) +{ + struct rx_desc *from = &q->desc[idx]; + struct rx_desc *to = &q->desc[q->pidx]; + + q->sdesc[q->pidx] = q->sdesc[idx]; + to->addr_lo = from->addr_lo; /* already big endian */ + to->addr_hi = from->addr_hi; /* likewise */ + wmb(); + to->len_gen = cpu_to_be32(V_FLD_GEN1(q->gen)); + to->gen2 = cpu_to_be32(V_FLD_GEN2(q->gen)); + q->credits++; + + if (++q->pidx == q->size) { + q->pidx = 0; + q->gen ^= 1; + } + t3_write_reg(adap, A_SG_KDOORBELL, V_EGRCNTX(q->cntxt_id)); +} + +/** + * alloc_ring - allocate resources for an SGE descriptor ring + * @pdev: the PCI device + * @nelem: the number of descriptors + * @elem_size: the size of each descriptor + * @sw_size: the size of the SW state associated with each ring element + * @phys: the physical address of the allocated ring + * @metadata: address of the array holding the SW state for the ring + * + * Allocates resources for an SGE descriptor ring, such as Tx queues, + * free buffer lists, or response queues. Each SGE ring requires + * space for its HW descriptors plus, optionally, space for the SW state + * associated with each HW entry (the metadata). The function returns + * three values: the virtual address for the HW ring (the return value + * of the function), the physical address of the HW ring, and the address + * of the SW ring. + */ +static void *alloc_ring(struct pci_dev *pdev, size_t nelem, size_t elem_size, + size_t sw_size, dma_addr_t *phys, void *metadata) +{ + size_t len = nelem * elem_size; + void *s = NULL; + void *p = dma_alloc_coherent(&pdev->dev, len, phys, GFP_KERNEL); + + if (!p) + return NULL; + if (sw_size) { + s = kcalloc(nelem, sw_size, GFP_KERNEL); + + if (!s) { + dma_free_coherent(&pdev->dev, len, p, *phys); + return NULL; + } + } + if (metadata) + *(void **)metadata = s; + memset(p, 0, len); + return p; +} + +/** + * free_qset - free the resources of an SGE queue set + * @adapter: the adapter owning the queue set + * @q: the queue set + * + * Release the HW and SW resources associated with an SGE queue set, such + * as HW contexts, packet buffers, and descriptor rings. Traffic to the + * queue set must be quiesced prior to calling this. + */ +void t3_free_qset(struct adapter *adapter, struct sge_qset *q) +{ + int i; + struct pci_dev *pdev = adapter->pdev; + + if (q->tx_reclaim_timer.function) + del_timer_sync(&q->tx_reclaim_timer); + + for (i = 0; i < SGE_RXQ_PER_SET; ++i) + if (q->fl[i].desc) { + spin_lock(&adapter->sge.reg_lock); + t3_sge_disable_fl(adapter, q->fl[i].cntxt_id); + spin_unlock(&adapter->sge.reg_lock); + free_rx_bufs(pdev, &q->fl[i]); + kfree(q->fl[i].sdesc); + dma_free_coherent(&pdev->dev, + q->fl[i].size * + sizeof(struct rx_desc), q->fl[i].desc, + q->fl[i].phys_addr); + } + + for (i = 0; i < SGE_TXQ_PER_SET; ++i) + if (q->txq[i].desc) { + spin_lock(&adapter->sge.reg_lock); + t3_sge_enable_ecntxt(adapter, q->txq[i].cntxt_id, 0); + spin_unlock(&adapter->sge.reg_lock); + if (q->txq[i].sdesc) { + free_tx_desc(adapter, &q->txq[i], + q->txq[i].in_use); + kfree(q->txq[i].sdesc); + } + dma_free_coherent(&pdev->dev, + q->txq[i].size * + sizeof(struct tx_desc), + q->txq[i].desc, q->txq[i].phys_addr); + __skb_queue_purge(&q->txq[i].sendq); + } + + if (q->rspq.desc) { + spin_lock(&adapter->sge.reg_lock); + t3_sge_disable_rspcntxt(adapter, q->rspq.cntxt_id); + spin_unlock(&adapter->sge.reg_lock); + dma_free_coherent(&pdev->dev, + q->rspq.size * sizeof(struct rsp_desc), + q->rspq.desc, q->rspq.phys_addr); + } + + if (q->netdev) + q->netdev->atalk_ptr = NULL; + + memset(q, 0, sizeof(*q)); +} + +/** + * init_qset_cntxt - initialize an SGE queue set context info + * @qs: the queue set + * @id: the queue set id + * + * Initializes the TIDs and context ids for the queues of a queue set. + */ +static void init_qset_cntxt(struct sge_qset *qs, unsigned int id) +{ + qs->rspq.cntxt_id = id; + qs->fl[0].cntxt_id = 2 * id; + qs->fl[1].cntxt_id = 2 * id + 1; + qs->txq[TXQ_ETH].cntxt_id = FW_TUNNEL_SGEEC_START + id; + qs->txq[TXQ_ETH].token = FW_TUNNEL_TID_START + id; + qs->txq[TXQ_OFLD].cntxt_id = FW_OFLD_SGEEC_START + id; + qs->txq[TXQ_CTRL].cntxt_id = FW_CTRL_SGEEC_START + id; + qs->txq[TXQ_CTRL].token = FW_CTRL_TID_START + id; +} + +/** + * sgl_len - calculates the size of an SGL of the given capacity + * @n: the number of SGL entries + * + * Calculates the number of flits needed for a scatter/gather list that + * can hold the given number of entries. + */ +static inline unsigned int sgl_len(unsigned int n) +{ + /* alternatively: 3 * (n / 2) + 2 * (n & 1) */ + return (3 * n) / 2 + (n & 1); +} + +/** + * flits_to_desc - returns the num of Tx descriptors for the given flits + * @n: the number of flits + * + * Calculates the number of Tx descriptors needed for the supplied number + * of flits. + */ +static inline unsigned int flits_to_desc(unsigned int n) +{ + BUG_ON(n >= ARRAY_SIZE(flit_desc_map)); + return flit_desc_map[n]; +} + +/** + * get_packet - return the next ingress packet buffer from a free list + * @adap: the adapter that received the packet + * @fl: the SGE free list holding the packet + * @len: the packet length including any SGE padding + * @drop_thres: # of remaining buffers before we start dropping packets + * + * Get the next packet from a free list and complete setup of the + * sk_buff. If the packet is small we make a copy and recycle the + * original buffer, otherwise we use the original buffer itself. If a + * positive drop threshold is supplied packets are dropped and their + * buffers recycled if (a) the number of remaining buffers is under the + * threshold and the packet is too big to copy, or (b) the packet should + * be copied but there is no memory for the copy. + */ +static struct sk_buff *get_packet(struct adapter *adap, struct sge_fl *fl, + unsigned int len, unsigned int drop_thres) +{ + struct sk_buff *skb = NULL; + struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; + + prefetch(sd->skb->data); + + if (len <= SGE_RX_COPY_THRES) { + skb = alloc_skb(len, GFP_ATOMIC); + if (likely(skb != NULL)) { + __skb_put(skb, len); + pci_dma_sync_single_for_cpu(adap->pdev, + pci_unmap_addr(sd, + dma_addr), + len, PCI_DMA_FROMDEVICE); + memcpy(skb->data, sd->skb->data, len); + pci_dma_sync_single_for_device(adap->pdev, + pci_unmap_addr(sd, + dma_addr), + len, PCI_DMA_FROMDEVICE); + } else if (!drop_thres) + goto use_orig_buf; + recycle: + recycle_rx_buf(adap, fl, fl->cidx); + return skb; + } + + if (unlikely(fl->credits < drop_thres)) + goto recycle; + + use_orig_buf: + pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr), + fl->buf_size, PCI_DMA_FROMDEVICE); + skb = sd->skb; + skb_put(skb, len); + __refill_fl(adap, fl); + return skb; +} + +/** + * get_imm_packet - return the next ingress packet buffer from a response + * @resp: the response descriptor containing the packet data + * + * Return a packet containing the immediate data of the given response. + */ +static inline struct sk_buff *get_imm_packet(const struct rsp_desc *resp) +{ + struct sk_buff *skb = alloc_skb(IMMED_PKT_SIZE, GFP_ATOMIC); + + if (skb) { + __skb_put(skb, IMMED_PKT_SIZE); + memcpy(skb->data, resp->imm_data, IMMED_PKT_SIZE); + } + return skb; +} + +/** + * calc_tx_descs - calculate the number of Tx descriptors for a packet + * @skb: the packet + * + * Returns the number of Tx descriptors needed for the given Ethernet + * packet. Ethernet packets require addition of WR and CPL headers. + */ +static inline unsigned int calc_tx_descs(const struct sk_buff *skb) +{ + unsigned int flits; + + if (skb->len <= WR_LEN - sizeof(struct cpl_tx_pkt)) + return 1; + + flits = sgl_len(skb_shinfo(skb)->nr_frags + 1) + 2; + if (skb_shinfo(skb)->gso_size) + flits++; + return flits_to_desc(flits); +} + +/** + * make_sgl - populate a scatter/gather list for a packet + * @skb: the packet + * @sgp: the SGL to populate + * @start: start address of skb main body data to include in the SGL + * @len: length of skb main body data to include in the SGL + * @pdev: the PCI device + * + * Generates a scatter/gather list for the buffers that make up a packet + * and returns the SGL size in 8-byte words. The caller must size the SGL + * appropriately. + */ +static inline unsigned int make_sgl(const struct sk_buff *skb, + struct sg_ent *sgp, unsigned char *start, + unsigned int len, struct pci_dev *pdev) +{ + dma_addr_t mapping; + unsigned int i, j = 0, nfrags; + + if (len) { + mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE); + sgp->len[0] = cpu_to_be32(len); + sgp->addr[0] = cpu_to_be64(mapping); + j = 1; + } + + nfrags = skb_shinfo(skb)->nr_frags; + for (i = 0; i < nfrags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + mapping = pci_map_page(pdev, frag->page, frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + sgp->len[j] = cpu_to_be32(frag->size); + sgp->addr[j] = cpu_to_be64(mapping); + j ^= 1; + if (j == 0) + ++sgp; + } + if (j) + sgp->len[j] = 0; + return ((nfrags + (len != 0)) * 3) / 2 + j; +} + +/** + * check_ring_tx_db - check and potentially ring a Tx queue's doorbell + * @adap: the adapter + * @q: the Tx queue + * + * Ring the doorbel if a Tx queue is asleep. There is a natural race, + * where the HW is going to sleep just after we checked, however, + * then the interrupt handler will detect the outstanding TX packet + * and ring the doorbell for us. + * + * When GTS is disabled we unconditionally ring the doorbell. + */ +static inline void check_ring_tx_db(struct adapter *adap, struct sge_txq *q) +{ +#if USE_GTS + clear_bit(TXQ_LAST_PKT_DB, &q->flags); + if (test_and_set_bit(TXQ_RUNNING, &q->flags) == 0) { + set_bit(TXQ_LAST_PKT_DB, &q->flags); + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); + } +#else + wmb(); /* write descriptors before telling HW */ + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); +#endif +} + +static inline void wr_gen2(struct tx_desc *d, unsigned int gen) +{ +#if SGE_NUM_GENBITS == 2 + d->flit[TX_DESC_FLITS - 1] = cpu_to_be64(gen); +#endif +} + +/** + * write_wr_hdr_sgl - write a WR header and, optionally, SGL + * @ndesc: number of Tx descriptors spanned by the SGL + * @skb: the packet corresponding to the WR + * @d: first Tx descriptor to be written + * @pidx: index of above descriptors + * @q: the SGE Tx queue + * @sgl: the SGL + * @flits: number of flits to the start of the SGL in the first descriptor + * @sgl_flits: the SGL size in flits + * @gen: the Tx descriptor generation + * @wr_hi: top 32 bits of WR header based on WR type (big endian) + * @wr_lo: low 32 bits of WR header based on WR type (big endian) + * + * Write a work request header and an associated SGL. If the SGL is + * small enough to fit into one Tx descriptor it has already been written + * and we just need to write the WR header. Otherwise we distribute the + * SGL across the number of descriptors it spans. + */ +static void write_wr_hdr_sgl(unsigned int ndesc, struct sk_buff *skb, + struct tx_desc *d, unsigned int pidx, + const struct sge_txq *q, + const struct sg_ent *sgl, + unsigned int flits, unsigned int sgl_flits, + unsigned int gen, unsigned int wr_hi, + unsigned int wr_lo) +{ + struct work_request_hdr *wrp = (struct work_request_hdr *)d; + struct tx_sw_desc *sd = &q->sdesc[pidx]; + + sd->skb = skb; + if (need_skb_unmap()) { + struct unmap_info *ui = (struct unmap_info *)skb->cb; + + ui->fragidx = 0; + ui->addr_idx = 0; + ui->sflit = flits; + } + + if (likely(ndesc == 1)) { + skb->priority = pidx; + wrp->wr_hi = htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) | + V_WR_SGLSFLT(flits)) | wr_hi; + wmb(); + wrp->wr_lo = htonl(V_WR_LEN(flits + sgl_flits) | + V_WR_GEN(gen)) | wr_lo; + wr_gen2(d, gen); + } else { + unsigned int ogen = gen; + const u64 *fp = (const u64 *)sgl; + struct work_request_hdr *wp = wrp; + + wrp->wr_hi = htonl(F_WR_SOP | V_WR_DATATYPE(1) | + V_WR_SGLSFLT(flits)) | wr_hi; + + while (sgl_flits) { + unsigned int avail = WR_FLITS - flits; + + if (avail > sgl_flits) + avail = sgl_flits; + memcpy(&d->flit[flits], fp, avail * sizeof(*fp)); + sgl_flits -= avail; + ndesc--; + if (!sgl_flits) + break; + + fp += avail; + d++; + sd++; + if (++pidx == q->size) { + pidx = 0; + gen ^= 1; + d = q->desc; + sd = q->sdesc; + } + + sd->skb = skb; + wrp = (struct work_request_hdr *)d; + wrp->wr_hi = htonl(V_WR_DATATYPE(1) | + V_WR_SGLSFLT(1)) | wr_hi; + wrp->wr_lo = htonl(V_WR_LEN(min(WR_FLITS, + sgl_flits + 1)) | + V_WR_GEN(gen)) | wr_lo; + wr_gen2(d, gen); + flits = 1; + } + skb->priority = pidx; + wrp->wr_hi |= htonl(F_WR_EOP); + wmb(); + wp->wr_lo = htonl(V_WR_LEN(WR_FLITS) | V_WR_GEN(ogen)) | wr_lo; + wr_gen2((struct tx_desc *)wp, ogen); + WARN_ON(ndesc != 0); + } +} + +/** + * write_tx_pkt_wr - write a TX_PKT work request + * @adap: the adapter + * @skb: the packet to send + * @pi: the egress interface + * @pidx: index of the first Tx descriptor to write + * @gen: the generation value to use + * @q: the Tx queue + * @ndesc: number of descriptors the packet will occupy + * @compl: the value of the COMPL bit to use + * + * Generate a TX_PKT work request to send the supplied packet. + */ +static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, + const struct port_info *pi, + unsigned int pidx, unsigned int gen, + struct sge_txq *q, unsigned int ndesc, + unsigned int compl) +{ + unsigned int flits, sgl_flits, cntrl, tso_info; + struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1]; + struct tx_desc *d = &q->desc[pidx]; + struct cpl_tx_pkt *cpl = (struct cpl_tx_pkt *)d; + + cpl->len = htonl(skb->len | 0x80000000); + cntrl = V_TXPKT_INTF(pi->port_id); + + if (vlan_tx_tag_present(skb) && pi->vlan_grp) + cntrl |= F_TXPKT_VLAN_VLD | V_TXPKT_VLAN(vlan_tx_tag_get(skb)); + + tso_info = V_LSO_MSS(skb_shinfo(skb)->gso_size); + if (tso_info) { + int eth_type; + struct cpl_tx_pkt_lso *hdr = (struct cpl_tx_pkt_lso *)cpl; + + d->flit[2] = 0; + cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO); + hdr->cntrl = htonl(cntrl); + eth_type = skb->nh.raw - skb->data == ETH_HLEN ? + CPL_ETH_II : CPL_ETH_II_VLAN; + tso_info |= V_LSO_ETH_TYPE(eth_type) | + V_LSO_IPHDR_WORDS(skb->nh.iph->ihl) | + V_LSO_TCPHDR_WORDS(skb->h.th->doff); + hdr->lso_info = htonl(tso_info); + flits = 3; + } else { + cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT); + cntrl |= F_TXPKT_IPCSUM_DIS; /* SW calculates IP csum */ + cntrl |= V_TXPKT_L4CSUM_DIS(skb->ip_summed != CHECKSUM_PARTIAL); + cpl->cntrl = htonl(cntrl); + + if (skb->len <= WR_LEN - sizeof(*cpl)) { + q->sdesc[pidx].skb = NULL; + if (!skb->data_len) + memcpy(&d->flit[2], skb->data, skb->len); + else + skb_copy_bits(skb, 0, &d->flit[2], skb->len); + + flits = (skb->len + 7) / 8 + 2; + cpl->wr.wr_hi = htonl(V_WR_BCNTLFLT(skb->len & 7) | + V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) + | F_WR_SOP | F_WR_EOP | compl); + wmb(); + cpl->wr.wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(gen) | + V_WR_TID(q->token)); + wr_gen2(d, gen); + kfree_skb(skb); + return; + } + + flits = 2; + } + + sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; + sgl_flits = make_sgl(skb, sgp, skb->data, skb_headlen(skb), adap->pdev); + if (need_skb_unmap()) + ((struct unmap_info *)skb->cb)->len = skb_headlen(skb); + + write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen, + htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | compl), + htonl(V_WR_TID(q->token))); +} + +/** + * eth_xmit - add a packet to the Ethernet Tx queue + * @skb: the packet + * @dev: the egress net device + * + * Add a packet to an SGE Tx queue. Runs with softirqs disabled. + */ +int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned int ndesc, pidx, credits, gen, compl; + const struct port_info *pi = netdev_priv(dev); + struct adapter *adap = dev->priv; + struct sge_qset *qs = dev2qset(dev); + struct sge_txq *q = &qs->txq[TXQ_ETH]; + + /* + * The chip min packet length is 9 octets but play safe and reject + * anything shorter than an Ethernet header. + */ + if (unlikely(skb->len < ETH_HLEN)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + spin_lock(&q->lock); + reclaim_completed_tx(adap, q); + + credits = q->size - q->in_use; + ndesc = calc_tx_descs(skb); + + if (unlikely(credits < ndesc)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); + set_bit(TXQ_ETH, &qs->txq_stopped); + q->stops++; + dev_err(&adap->pdev->dev, + "%s: Tx ring %u full while queue awake!\n", + dev->name, q->cntxt_id & 7); + } + spin_unlock(&q->lock); + return NETDEV_TX_BUSY; + } + + q->in_use += ndesc; + if (unlikely(credits - ndesc < q->stop_thres)) { + q->stops++; + netif_stop_queue(dev); + set_bit(TXQ_ETH, &qs->txq_stopped); +#if !USE_GTS + if (should_restart_tx(q) && + test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) { + q->restarts++; + netif_wake_queue(dev); + } +#endif + } + + gen = q->gen; + q->unacked += ndesc; + compl = (q->unacked & 8) << (S_WR_COMPL - 3); + q->unacked &= 7; + pidx = q->pidx; + q->pidx += ndesc; + if (q->pidx >= q->size) { + q->pidx -= q->size; + q->gen ^= 1; + } + + /* update port statistics */ + if (skb->ip_summed == CHECKSUM_COMPLETE) + qs->port_stats[SGE_PSTAT_TX_CSUM]++; + if (skb_shinfo(skb)->gso_size) + qs->port_stats[SGE_PSTAT_TSO]++; + if (vlan_tx_tag_present(skb) && pi->vlan_grp) + qs->port_stats[SGE_PSTAT_VLANINS]++; + + dev->trans_start = jiffies; + spin_unlock(&q->lock); + + /* + * We do not use Tx completion interrupts to free DMAd Tx packets. + * This is good for performamce but means that we rely on new Tx + * packets arriving to run the destructors of completed packets, + * which open up space in their sockets' send queues. Sometimes + * we do not get such new packets causing Tx to stall. A single + * UDP transmitter is a good example of this situation. We have + * a clean up timer that periodically reclaims completed packets + * but it doesn't run often enough (nor do we want it to) to prevent + * lengthy stalls. A solution to this problem is to run the + * destructor early, after the packet is queued but before it's DMAd. + * A cons is that we lie to socket memory accounting, but the amount + * of extra memory is reasonable (limited by the number of Tx + * descriptors), the packets do actually get freed quickly by new + * packets almost always, and for protocols like TCP that wait for + * acks to really free up the data the extra memory is even less. + * On the positive side we run the destructors on the sending CPU + * rather than on a potentially different completing CPU, usually a + * good thing. We also run them without holding our Tx queue lock, + * unlike what reclaim_completed_tx() would otherwise do. + * + * Run the destructor before telling the DMA engine about the packet + * to make sure it doesn't complete and get freed prematurely. + */ + if (likely(!skb_shared(skb))) + skb_orphan(skb); + + write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl); + check_ring_tx_db(adap, q); + return NETDEV_TX_OK; +} + +/** + * write_imm - write a packet into a Tx descriptor as immediate data + * @d: the Tx descriptor to write + * @skb: the packet + * @len: the length of packet data to write as immediate data + * @gen: the generation bit value to write + * + * Writes a packet as immediate data into a Tx descriptor. The packet + * contains a work request at its beginning. We must write the packet + * carefully so the SGE doesn't read accidentally before it's written in + * its entirety. + */ +static inline void write_imm(struct tx_desc *d, struct sk_buff *skb, + unsigned int len, unsigned int gen) +{ + struct work_request_hdr *from = (struct work_request_hdr *)skb->data; + struct work_request_hdr *to = (struct work_request_hdr *)d; + + memcpy(&to[1], &from[1], len - sizeof(*from)); + to->wr_hi = from->wr_hi | htonl(F_WR_SOP | F_WR_EOP | + V_WR_BCNTLFLT(len & 7)); + wmb(); + to->wr_lo = from->wr_lo | htonl(V_WR_GEN(gen) | + V_WR_LEN((len + 7) / 8)); + wr_gen2(d, gen); + kfree_skb(skb); +} + +/** + * check_desc_avail - check descriptor availability on a send queue + * @adap: the adapter + * @q: the send queue + * @skb: the packet needing the descriptors + * @ndesc: the number of Tx descriptors needed + * @qid: the Tx queue number in its queue set (TXQ_OFLD or TXQ_CTRL) + * + * Checks if the requested number of Tx descriptors is available on an + * SGE send queue. If the queue is already suspended or not enough + * descriptors are available the packet is queued for later transmission. + * Must be called with the Tx queue locked. + * + * Returns 0 if enough descriptors are available, 1 if there aren't + * enough descriptors and the packet has been queued, and 2 if the caller + * needs to retry because there weren't enough descriptors at the + * beginning of the call but some freed up in the mean time. + */ +static inline int check_desc_avail(struct adapter *adap, struct sge_txq *q, + struct sk_buff *skb, unsigned int ndesc, + unsigned int qid) +{ + if (unlikely(!skb_queue_empty(&q->sendq))) { + addq_exit:__skb_queue_tail(&q->sendq, skb); + return 1; + } + if (unlikely(q->size - q->in_use < ndesc)) { + struct sge_qset *qs = txq_to_qset(q, qid); + + set_bit(qid, &qs->txq_stopped); + smp_mb__after_clear_bit(); + + if (should_restart_tx(q) && + test_and_clear_bit(qid, &qs->txq_stopped)) + return 2; + + q->stops++; + goto addq_exit; + } + return 0; +} + +/** + * reclaim_completed_tx_imm - reclaim completed control-queue Tx descs + * @q: the SGE control Tx queue + * + * This is a variant of reclaim_completed_tx() that is used for Tx queues + * that send only immediate data (presently just the control queues) and + * thus do not have any sk_buffs to release. + */ +static inline void reclaim_completed_tx_imm(struct sge_txq *q) +{ + unsigned int reclaim = q->processed - q->cleaned; + + q->in_use -= reclaim; + q->cleaned += reclaim; +} + +static inline int immediate(const struct sk_buff *skb) +{ + return skb->len <= WR_LEN && !skb->data_len; +} + +/** + * ctrl_xmit - send a packet through an SGE control Tx queue + * @adap: the adapter + * @q: the control queue + * @skb: the packet + * + * Send a packet through an SGE control Tx queue. Packets sent through + * a control queue must fit entirely as immediate data in a single Tx + * descriptor and have no page fragments. + */ +static int ctrl_xmit(struct adapter *adap, struct sge_txq *q, + struct sk_buff *skb) +{ + int ret; + struct work_request_hdr *wrp = (struct work_request_hdr *)skb->data; + + if (unlikely(!immediate(skb))) { + WARN_ON(1); + dev_kfree_skb(skb); + return NET_XMIT_SUCCESS; + } + + wrp->wr_hi |= htonl(F_WR_SOP | F_WR_EOP); + wrp->wr_lo = htonl(V_WR_TID(q->token)); + + spin_lock(&q->lock); + again:reclaim_completed_tx_imm(q); + + ret = check_desc_avail(adap, q, skb, 1, TXQ_CTRL); + if (unlikely(ret)) { + if (ret == 1) { + spin_unlock(&q->lock); + return NET_XMIT_CN; + } + goto again; + } + + write_imm(&q->desc[q->pidx], skb, skb->len, q->gen); + + q->in_use++; + if (++q->pidx >= q->size) { + q->pidx = 0; + q->gen ^= 1; + } + spin_unlock(&q->lock); + wmb(); + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); + return NET_XMIT_SUCCESS; +} + +/** + * restart_ctrlq - restart a suspended control queue + * @qs: the queue set cotaining the control queue + * + * Resumes transmission on a suspended Tx control queue. + */ +static void restart_ctrlq(unsigned long data) +{ + struct sk_buff *skb; + struct sge_qset *qs = (struct sge_qset *)data; + struct sge_txq *q = &qs->txq[TXQ_CTRL]; + struct adapter *adap = qs->netdev->priv; + + spin_lock(&q->lock); + again:reclaim_completed_tx_imm(q); + + while (q->in_use < q->size && (skb = __skb_dequeue(&q->sendq)) != NULL) { + + write_imm(&q->desc[q->pidx], skb, skb->len, q->gen); + + if (++q->pidx >= q->size) { + q->pidx = 0; + q->gen ^= 1; + } + q->in_use++; + } + + if (!skb_queue_empty(&q->sendq)) { + set_bit(TXQ_CTRL, &qs->txq_stopped); + smp_mb__after_clear_bit(); + + if (should_restart_tx(q) && + test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) + goto again; + q->stops++; + } + + spin_unlock(&q->lock); + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); +} + +/** + * write_ofld_wr - write an offload work request + * @adap: the adapter + * @skb: the packet to send + * @q: the Tx queue + * @pidx: index of the first Tx descriptor to write + * @gen: the generation value to use + * @ndesc: number of descriptors the packet will occupy + * + * Write an offload work request to send the supplied packet. The packet + * data already carry the work request with most fields populated. + */ +static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, + struct sge_txq *q, unsigned int pidx, + unsigned int gen, unsigned int ndesc) +{ + unsigned int sgl_flits, flits; + struct work_request_hdr *from; + struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1]; + struct tx_desc *d = &q->desc[pidx]; + + if (immediate(skb)) { + q->sdesc[pidx].skb = NULL; + write_imm(d, skb, skb->len, gen); + return; + } + + /* Only TX_DATA builds SGLs */ + + from = (struct work_request_hdr *)skb->data; + memcpy(&d->flit[1], &from[1], skb->h.raw - skb->data - sizeof(*from)); + + flits = (skb->h.raw - skb->data) / 8; + sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; + sgl_flits = make_sgl(skb, sgp, skb->h.raw, skb->tail - skb->h.raw, + adap->pdev); + if (need_skb_unmap()) + ((struct unmap_info *)skb->cb)->len = skb->tail - skb->h.raw; + + write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, + gen, from->wr_hi, from->wr_lo); +} + +/** + * calc_tx_descs_ofld - calculate # of Tx descriptors for an offload packet + * @skb: the packet + * + * Returns the number of Tx descriptors needed for the given offload + * packet. These packets are already fully constructed. + */ +static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb) +{ + unsigned int flits, cnt = skb_shinfo(skb)->nr_frags; + + if (skb->len <= WR_LEN && cnt == 0) + return 1; /* packet fits as immediate data */ + + flits = (skb->h.raw - skb->data) / 8; /* headers */ + if (skb->tail != skb->h.raw) + cnt++; + return flits_to_desc(flits + sgl_len(cnt)); +} + +/** + * ofld_xmit - send a packet through an offload queue + * @adap: the adapter + * @q: the Tx offload queue + * @skb: the packet + * + * Send an offload packet through an SGE offload queue. + */ +static int ofld_xmit(struct adapter *adap, struct sge_txq *q, + struct sk_buff *skb) +{ + int ret; + unsigned int ndesc = calc_tx_descs_ofld(skb), pidx, gen; + + spin_lock(&q->lock); + again:reclaim_completed_tx(adap, q); + + ret = check_desc_avail(adap, q, skb, ndesc, TXQ_OFLD); + if (unlikely(ret)) { + if (ret == 1) { + skb->priority = ndesc; /* save for restart */ + spin_unlock(&q->lock); + return NET_XMIT_CN; + } + goto again; + } + + gen = q->gen; + q->in_use += ndesc; + pidx = q->pidx; + q->pidx += ndesc; + if (q->pidx >= q->size) { + q->pidx -= q->size; + q->gen ^= 1; + } + spin_unlock(&q->lock); + + write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + check_ring_tx_db(adap, q); + return NET_XMIT_SUCCESS; +} + +/** + * restart_offloadq - restart a suspended offload queue + * @qs: the queue set cotaining the offload queue + * + * Resumes transmission on a suspended Tx offload queue. + */ +static void restart_offloadq(unsigned long data) +{ + struct sk_buff *skb; + struct sge_qset *qs = (struct sge_qset *)data; + struct sge_txq *q = &qs->txq[TXQ_OFLD]; + struct adapter *adap = qs->netdev->priv; + + spin_lock(&q->lock); + again:reclaim_completed_tx(adap, q); + + while ((skb = skb_peek(&q->sendq)) != NULL) { + unsigned int gen, pidx; + unsigned int ndesc = skb->priority; + + if (unlikely(q->size - q->in_use < ndesc)) { + set_bit(TXQ_OFLD, &qs->txq_stopped); + smp_mb__after_clear_bit(); + + if (should_restart_tx(q) && + test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) + goto again; + q->stops++; + break; + } + + gen = q->gen; + q->in_use += ndesc; + pidx = q->pidx; + q->pidx += ndesc; + if (q->pidx >= q->size) { + q->pidx -= q->size; + q->gen ^= 1; + } + __skb_unlink(skb, &q->sendq); + spin_unlock(&q->lock); + + write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + spin_lock(&q->lock); + } + spin_unlock(&q->lock); + +#if USE_GTS + set_bit(TXQ_RUNNING, &q->flags); + set_bit(TXQ_LAST_PKT_DB, &q->flags); +#endif + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); +} + +/** + * queue_set - return the queue set a packet should use + * @skb: the packet + * + * Maps a packet to the SGE queue set it should use. The desired queue + * set is carried in bits 1-3 in the packet's priority. + */ +static inline int queue_set(const struct sk_buff *skb) +{ + return skb->priority >> 1; +} + +/** + * is_ctrl_pkt - return whether an offload packet is a control packet + * @skb: the packet + * + * Determines whether an offload packet should use an OFLD or a CTRL + * Tx queue. This is indicated by bit 0 in the packet's priority. + */ +static inline int is_ctrl_pkt(const struct sk_buff *skb) +{ + return skb->priority & 1; +} + +/** + * t3_offload_tx - send an offload packet + * @tdev: the offload device to send to + * @skb: the packet + * + * Sends an offload packet. We use the packet priority to select the + * appropriate Tx queue as follows: bit 0 indicates whether the packet + * should be sent as regular or control, bits 1-3 select the queue set. + */ +int t3_offload_tx(struct t3cdev *tdev, struct sk_buff *skb) +{ + struct adapter *adap = tdev2adap(tdev); + struct sge_qset *qs = &adap->sge.qs[queue_set(skb)]; + + if (unlikely(is_ctrl_pkt(skb))) + return ctrl_xmit(adap, &qs->txq[TXQ_CTRL], skb); + + return ofld_xmit(adap, &qs->txq[TXQ_OFLD], skb); +} + +/** + * offload_enqueue - add an offload packet to an SGE offload receive queue + * @q: the SGE response queue + * @skb: the packet + * + * Add a new offload packet to an SGE response queue's offload packet + * queue. If the packet is the first on the queue it schedules the RX + * softirq to process the queue. + */ +static inline void offload_enqueue(struct sge_rspq *q, struct sk_buff *skb) +{ + skb->next = skb->prev = NULL; + if (q->rx_tail) + q->rx_tail->next = skb; + else { + struct sge_qset *qs = rspq_to_qset(q); + + if (__netif_rx_schedule_prep(qs->netdev)) + __netif_rx_schedule(qs->netdev); + q->rx_head = skb; + } + q->rx_tail = skb; +} + +/** + * deliver_partial_bundle - deliver a (partial) bundle of Rx offload pkts + * @tdev: the offload device that will be receiving the packets + * @q: the SGE response queue that assembled the bundle + * @skbs: the partial bundle + * @n: the number of packets in the bundle + * + * Delivers a (partial) bundle of Rx offload packets to an offload device. + */ +static inline void deliver_partial_bundle(struct t3cdev *tdev, + struct sge_rspq *q, + struct sk_buff *skbs[], int n) +{ + if (n) { + q->offload_bundles++; + tdev->recv(tdev, skbs, n); + } +} + +/** + * ofld_poll - NAPI handler for offload packets in interrupt mode + * @dev: the network device doing the polling + * @budget: polling budget + * + * The NAPI handler for offload packets when a response queue is serviced + * by the hard interrupt handler, i.e., when it's operating in non-polling + * mode. Creates small packet batches and sends them through the offload + * receive handler. Batches need to be of modest size as we do prefetches + * on the packets in each. + */ +static int ofld_poll(struct net_device *dev, int *budget) +{ + struct adapter *adapter = dev->priv; + struct sge_qset *qs = dev2qset(dev); + struct sge_rspq *q = &qs->rspq; + int work_done, limit = min(*budget, dev->quota), avail = limit; + + while (avail) { + struct sk_buff *head, *tail, *skbs[RX_BUNDLE_SIZE]; + int ngathered; + + spin_lock_irq(&q->lock); + head = q->rx_head; + if (!head) { + work_done = limit - avail; + *budget -= work_done; + dev->quota -= work_done; + __netif_rx_complete(dev); + spin_unlock_irq(&q->lock); + return 0; + } + + tail = q->rx_tail; + q->rx_head = q->rx_tail = NULL; + spin_unlock_irq(&q->lock); + + for (ngathered = 0; avail && head; avail--) { + prefetch(head->data); + skbs[ngathered] = head; + head = head->next; + skbs[ngathered]->next = NULL; + if (++ngathered == RX_BUNDLE_SIZE) { + q->offload_bundles++; + adapter->tdev.recv(&adapter->tdev, skbs, + ngathered); + ngathered = 0; + } + } + if (head) { /* splice remaining packets back onto Rx queue */ + spin_lock_irq(&q->lock); + tail->next = q->rx_head; + if (!q->rx_head) + q->rx_tail = tail; + q->rx_head = head; + spin_unlock_irq(&q->lock); + } + deliver_partial_bundle(&adapter->tdev, q, skbs, ngathered); + } + work_done = limit - avail; + *budget -= work_done; + dev->quota -= work_done; + return 1; +} + +/** + * rx_offload - process a received offload packet + * @tdev: the offload device receiving the packet + * @rq: the response queue that received the packet + * @skb: the packet + * @rx_gather: a gather list of packets if we are building a bundle + * @gather_idx: index of the next available slot in the bundle + * + * Process an ingress offload pakcet and add it to the offload ingress + * queue. Returns the index of the next available slot in the bundle. + */ +static inline int rx_offload(struct t3cdev *tdev, struct sge_rspq *rq, + struct sk_buff *skb, struct sk_buff *rx_gather[], + unsigned int gather_idx) +{ + rq->offload_pkts++; + skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data; + + if (rq->polling) { + rx_gather[gather_idx++] = skb; + if (gather_idx == RX_BUNDLE_SIZE) { + tdev->recv(tdev, rx_gather, RX_BUNDLE_SIZE); + gather_idx = 0; + rq->offload_bundles++; + } + } else + offload_enqueue(rq, skb); + + return gather_idx; +} + +/** + * update_tx_completed - update the number of processed Tx descriptors + * @qs: the queue set to update + * @idx: which Tx queue within the set to update + * @credits: number of new processed descriptors + * @tx_completed: accumulates credits for the queues + * + * Updates the number of completed Tx descriptors for a queue set's Tx + * queue. On UP systems we updated the information immediately but on + * MP we accumulate the credits locally and update the Tx queue when we + * reach a threshold to avoid cache-line bouncing. + */ +static inline void update_tx_completed(struct sge_qset *qs, int idx, + unsigned int credits, + unsigned int tx_completed[]) +{ +#ifdef CONFIG_SMP + tx_completed[idx] += credits; + if (tx_completed[idx] > 32) { + qs->txq[idx].processed += tx_completed[idx]; + tx_completed[idx] = 0; + } +#else + qs->txq[idx].processed += credits; +#endif +} + +/** + * restart_tx - check whether to restart suspended Tx queues + * @qs: the queue set to resume + * + * Restarts suspended Tx queues of an SGE queue set if they have enough + * free resources to resume operation. + */ +static void restart_tx(struct sge_qset *qs) +{ + if (test_bit(TXQ_ETH, &qs->txq_stopped) && + should_restart_tx(&qs->txq[TXQ_ETH]) && + test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) { + qs->txq[TXQ_ETH].restarts++; + if (netif_running(qs->netdev)) + netif_wake_queue(qs->netdev); + } + + if (test_bit(TXQ_OFLD, &qs->txq_stopped) && + should_restart_tx(&qs->txq[TXQ_OFLD]) && + test_and_clear_bit(TXQ_OFLD, &qs->txq_stopped)) { + qs->txq[TXQ_OFLD].restarts++; + tasklet_schedule(&qs->txq[TXQ_OFLD].qresume_tsk); + } + if (test_bit(TXQ_CTRL, &qs->txq_stopped) && + should_restart_tx(&qs->txq[TXQ_CTRL]) && + test_and_clear_bit(TXQ_CTRL, &qs->txq_stopped)) { + qs->txq[TXQ_CTRL].restarts++; + tasklet_schedule(&qs->txq[TXQ_CTRL].qresume_tsk); + } +} + +/** + * rx_eth - process an ingress ethernet packet + * @adap: the adapter + * @rq: the response queue that received the packet + * @skb: the packet + * @pad: amount of padding at the start of the buffer + * + * Process an ingress ethernet pakcet and deliver it to the stack. + * The padding is 2 if the packet was delivered in an Rx buffer and 0 + * if it was immediate data in a response. + */ +static void rx_eth(struct adapter *adap, struct sge_rspq *rq, + struct sk_buff *skb, int pad) +{ + struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)(skb->data + pad); + struct port_info *pi; + + rq->eth_pkts++; + skb_pull(skb, sizeof(*p) + pad); + skb->dev = adap->port[p->iff]; + skb->dev->last_rx = jiffies; + skb->protocol = eth_type_trans(skb, skb->dev); + pi = netdev_priv(skb->dev); + if (pi->rx_csum_offload && p->csum_valid && p->csum == 0xffff && + !p->fragment) { + rspq_to_qset(rq)->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else + skb->ip_summed = CHECKSUM_NONE; + + if (unlikely(p->vlan_valid)) { + struct vlan_group *grp = pi->vlan_grp; + + rspq_to_qset(rq)->port_stats[SGE_PSTAT_VLANEX]++; + if (likely(grp)) + __vlan_hwaccel_rx(skb, grp, ntohs(p->vlan), + rq->polling); + else + dev_kfree_skb_any(skb); + } else if (rq->polling) + netif_receive_skb(skb); + else + netif_rx(skb); +} + +/** + * handle_rsp_cntrl_info - handles control information in a response + * @qs: the queue set corresponding to the response + * @flags: the response control flags + * @tx_completed: accumulates completion credits for the Tx queues + * + * Handles the control information of an SGE response, such as GTS + * indications and completion credits for the queue set's Tx queues. + */ +static inline void handle_rsp_cntrl_info(struct sge_qset *qs, u32 flags, + unsigned int tx_completed[]) +{ + unsigned int credits; + +#if USE_GTS + if (flags & F_RSPD_TXQ0_GTS) + clear_bit(TXQ_RUNNING, &qs->txq[TXQ_ETH].flags); +#endif + + /* ETH credits are already coalesced, return them immediately. */ + credits = G_RSPD_TXQ0_CR(flags); + if (credits) + qs->txq[TXQ_ETH].processed += credits; + +# if USE_GTS + if (flags & F_RSPD_TXQ1_GTS) + clear_bit(TXQ_RUNNING, &qs->txq[TXQ_OFLD].flags); +# endif + update_tx_completed(qs, TXQ_OFLD, G_RSPD_TXQ1_CR(flags), tx_completed); + update_tx_completed(qs, TXQ_CTRL, G_RSPD_TXQ2_CR(flags), tx_completed); +} + +/** + * flush_tx_completed - returns accumulated Tx completions to Tx queues + * @qs: the queue set to update + * @tx_completed: pending completion credits to return to Tx queues + * + * Updates the number of completed Tx descriptors for a queue set's Tx + * queues with the credits pending in @tx_completed. This does something + * only on MP systems as on UP systems we return the credits immediately. + */ +static inline void flush_tx_completed(struct sge_qset *qs, + unsigned int tx_completed[]) +{ +#if defined(CONFIG_SMP) + if (tx_completed[TXQ_OFLD]) + qs->txq[TXQ_OFLD].processed += tx_completed[TXQ_OFLD]; + if (tx_completed[TXQ_CTRL]) + qs->txq[TXQ_CTRL].processed += tx_completed[TXQ_CTRL]; +#endif +} + +/** + * check_ring_db - check if we need to ring any doorbells + * @adapter: the adapter + * @qs: the queue set whose Tx queues are to be examined + * @sleeping: indicates which Tx queue sent GTS + * + * Checks if some of a queue set's Tx queues need to ring their doorbells + * to resume transmission after idling while they still have unprocessed + * descriptors. + */ +static void check_ring_db(struct adapter *adap, struct sge_qset *qs, + unsigned int sleeping) +{ + if (sleeping & F_RSPD_TXQ0_GTS) { + struct sge_txq *txq = &qs->txq[TXQ_ETH]; + + if (txq->cleaned + txq->in_use != txq->processed && + !test_and_set_bit(TXQ_LAST_PKT_DB, &txq->flags)) { + set_bit(TXQ_RUNNING, &txq->flags); + t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | + V_EGRCNTX(txq->cntxt_id)); + } + } + + if (sleeping & F_RSPD_TXQ1_GTS) { + struct sge_txq *txq = &qs->txq[TXQ_OFLD]; + + if (txq->cleaned + txq->in_use != txq->processed && + !test_and_set_bit(TXQ_LAST_PKT_DB, &txq->flags)) { + set_bit(TXQ_RUNNING, &txq->flags); + t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | + V_EGRCNTX(txq->cntxt_id)); + } + } +} + +/** + * is_new_response - check if a response is newly written + * @r: the response descriptor + * @q: the response queue + * + * Returns true if a response descriptor contains a yet unprocessed + * response. + */ +static inline int is_new_response(const struct rsp_desc *r, + const struct sge_rspq *q) +{ + return (r->intr_gen & F_RSPD_GEN2) == q->gen; +} + +#define RSPD_GTS_MASK (F_RSPD_TXQ0_GTS | F_RSPD_TXQ1_GTS) +#define RSPD_CTRL_MASK (RSPD_GTS_MASK | \ + V_RSPD_TXQ0_CR(M_RSPD_TXQ0_CR) | \ + V_RSPD_TXQ1_CR(M_RSPD_TXQ1_CR) | \ + V_RSPD_TXQ2_CR(M_RSPD_TXQ2_CR)) + +/* How long to delay the next interrupt in case of memory shortage, in 0.1us. */ +#define NOMEM_INTR_DELAY 2500 + +/** + * process_responses - process responses from an SGE response queue + * @adap: the adapter + * @qs: the queue set to which the response queue belongs + * @budget: how many responses can be processed in this round + * + * Process responses from an SGE response queue up to the supplied budget. + * Responses include received packets as well as credits and other events + * for the queues that belong to the response queue's queue set. + * A negative budget is effectively unlimited. + * + * Additionally choose the interrupt holdoff time for the next interrupt + * on this queue. If the system is under memory shortage use a fairly + * long delay to help recovery. + */ +static int process_responses(struct adapter *adap, struct sge_qset *qs, + int budget) +{ + struct sge_rspq *q = &qs->rspq; + struct rsp_desc *r = &q->desc[q->cidx]; + int budget_left = budget; + unsigned int sleeping = 0, tx_completed[3] = { 0, 0, 0 }; + struct sk_buff *offload_skbs[RX_BUNDLE_SIZE]; + int ngathered = 0; + + q->next_holdoff = q->holdoff_tmr; + + while (likely(budget_left && is_new_response(r, q))) { + int eth, ethpad = 0; + struct sk_buff *skb = NULL; + u32 len, flags = ntohl(r->flags); + u32 rss_hi = *(const u32 *)r, rss_lo = r->rss_hdr.rss_hash_val; + + eth = r->rss_hdr.opcode == CPL_RX_PKT; + + if (unlikely(flags & F_RSPD_ASYNC_NOTIF)) { + skb = alloc_skb(AN_PKT_SIZE, GFP_ATOMIC); + if (!skb) + goto no_mem; + + memcpy(__skb_put(skb, AN_PKT_SIZE), r, AN_PKT_SIZE); + skb->data[0] = CPL_ASYNC_NOTIF; + rss_hi = htonl(CPL_ASYNC_NOTIF << 24); + q->async_notif++; + } else if (flags & F_RSPD_IMM_DATA_VALID) { + skb = get_imm_packet(r); + if (unlikely(!skb)) { + no_mem: + q->next_holdoff = NOMEM_INTR_DELAY; + q->nomem++; + /* consume one credit since we tried */ + budget_left--; + break; + } + q->imm_data++; + } else if ((len = ntohl(r->len_cq)) != 0) { + struct sge_fl *fl; + + fl = (len & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; + fl->credits--; + skb = get_packet(adap, fl, G_RSPD_LEN(len), + eth ? SGE_RX_DROP_THRES : 0); + if (!skb) + q->rx_drops++; + else if (r->rss_hdr.opcode == CPL_TRACE_PKT) + __skb_pull(skb, 2); + ethpad = 2; + if (++fl->cidx == fl->size) + fl->cidx = 0; + } else + q->pure_rsps++; + + if (flags & RSPD_CTRL_MASK) { + sleeping |= flags & RSPD_GTS_MASK; + handle_rsp_cntrl_info(qs, flags, tx_completed); + } + + r++; + if (unlikely(++q->cidx == q->size)) { + q->cidx = 0; + q->gen ^= 1; + r = q->desc; + } + prefetch(r); + + if (++q->credits >= (q->size / 4)) { + refill_rspq(adap, q, q->credits); + q->credits = 0; + } + + if (likely(skb != NULL)) { + if (eth) + rx_eth(adap, q, skb, ethpad); + else { + /* Preserve the RSS info in csum & priority */ + skb->csum = rss_hi; + skb->priority = rss_lo; + ngathered = rx_offload(&adap->tdev, q, skb, + offload_skbs, ngathered); + } + } + + --budget_left; + } + + flush_tx_completed(qs, tx_completed); + deliver_partial_bundle(&adap->tdev, q, offload_skbs, ngathered); + if (sleeping) + check_ring_db(adap, qs, sleeping); + + smp_mb(); /* commit Tx queue .processed updates */ + if (unlikely(qs->txq_stopped != 0)) + restart_tx(qs); + + budget -= budget_left; + return budget; +} + +static inline int is_pure_response(const struct rsp_desc *r) +{ + u32 n = ntohl(r->flags) & (F_RSPD_ASYNC_NOTIF | F_RSPD_IMM_DATA_VALID); + + return (n | r->len_cq) == 0; +} + +/** + * napi_rx_handler - the NAPI handler for Rx processing + * @dev: the net device + * @budget: how many packets we can process in this round + * + * Handler for new data events when using NAPI. + */ +static int napi_rx_handler(struct net_device *dev, int *budget) +{ + struct adapter *adap = dev->priv; + struct sge_qset *qs = dev2qset(dev); + int effective_budget = min(*budget, dev->quota); + + int work_done = process_responses(adap, qs, effective_budget); + *budget -= work_done; + dev->quota -= work_done; + + if (work_done >= effective_budget) + return 1; + + netif_rx_complete(dev); + + /* + * Because we don't atomically flush the following write it is + * possible that in very rare cases it can reach the device in a way + * that races with a new response being written plus an error interrupt + * causing the NAPI interrupt handler below to return unhandled status + * to the OS. To protect against this would require flushing the write + * and doing both the write and the flush with interrupts off. Way too + * expensive and unjustifiable given the rarity of the race. + * + * The race cannot happen at all with MSI-X. + */ + t3_write_reg(adap, A_SG_GTS, V_RSPQ(qs->rspq.cntxt_id) | + V_NEWTIMER(qs->rspq.next_holdoff) | + V_NEWINDEX(qs->rspq.cidx)); + return 0; +} + +/* + * Returns true if the device is already scheduled for polling. + */ +static inline int napi_is_scheduled(struct net_device *dev) +{ + return test_bit(__LINK_STATE_RX_SCHED, &dev->state); +} + +/** + * process_pure_responses - process pure responses from a response queue + * @adap: the adapter + * @qs: the queue set owning the response queue + * @r: the first pure response to process + * + * A simpler version of process_responses() that handles only pure (i.e., + * non data-carrying) responses. Such respones are too light-weight to + * justify calling a softirq under NAPI, so we handle them specially in + * the interrupt handler. The function is called with a pointer to a + * response, which the caller must ensure is a valid pure response. + * + * Returns 1 if it encounters a valid data-carrying response, 0 otherwise. + */ +static int process_pure_responses(struct adapter *adap, struct sge_qset *qs, + struct rsp_desc *r) +{ + struct sge_rspq *q = &qs->rspq; + unsigned int sleeping = 0, tx_completed[3] = { 0, 0, 0 }; + + do { + u32 flags = ntohl(r->flags); + + r++; + if (unlikely(++q->cidx == q->size)) { + q->cidx = 0; + q->gen ^= 1; + r = q->desc; + } + prefetch(r); + + if (flags & RSPD_CTRL_MASK) { + sleeping |= flags & RSPD_GTS_MASK; + handle_rsp_cntrl_info(qs, flags, tx_completed); + } + + q->pure_rsps++; + if (++q->credits >= (q->size / 4)) { + refill_rspq(adap, q, q->credits); + q->credits = 0; + } + } while (is_new_response(r, q) && is_pure_response(r)); + + flush_tx_completed(qs, tx_completed); + + if (sleeping) + check_ring_db(adap, qs, sleeping); + + smp_mb(); /* commit Tx queue .processed updates */ + if (unlikely(qs->txq_stopped != 0)) + restart_tx(qs); + + return is_new_response(r, q); +} + +/** + * handle_responses - decide what to do with new responses in NAPI mode + * @adap: the adapter + * @q: the response queue + * + * This is used by the NAPI interrupt handlers to decide what to do with + * new SGE responses. If there are no new responses it returns -1. If + * there are new responses and they are pure (i.e., non-data carrying) + * it handles them straight in hard interrupt context as they are very + * cheap and don't deliver any packets. Finally, if there are any data + * signaling responses it schedules the NAPI handler. Returns 1 if it + * schedules NAPI, 0 if all new responses were pure. + * + * The caller must ascertain NAPI is not already running. + */ +static inline int handle_responses(struct adapter *adap, struct sge_rspq *q) +{ + struct sge_qset *qs = rspq_to_qset(q); + struct rsp_desc *r = &q->desc[q->cidx]; + + if (!is_new_response(r, q)) + return -1; + if (is_pure_response(r) && process_pure_responses(adap, qs, r) == 0) { + t3_write_reg(adap, A_SG_GTS, V_RSPQ(q->cntxt_id) | + V_NEWTIMER(q->holdoff_tmr) | V_NEWINDEX(q->cidx)); + return 0; + } + if (likely(__netif_rx_schedule_prep(qs->netdev))) + __netif_rx_schedule(qs->netdev); + return 1; +} + +/* + * The MSI-X interrupt handler for an SGE response queue for the non-NAPI case + * (i.e., response queue serviced in hard interrupt). + */ +irqreturn_t t3_sge_intr_msix(int irq, void *cookie) +{ + struct sge_qset *qs = cookie; + struct adapter *adap = qs->netdev->priv; + struct sge_rspq *q = &qs->rspq; + + spin_lock(&q->lock); + if (process_responses(adap, qs, -1) == 0) + q->unhandled_irqs++; + t3_write_reg(adap, A_SG_GTS, V_RSPQ(q->cntxt_id) | + V_NEWTIMER(q->next_holdoff) | V_NEWINDEX(q->cidx)); + spin_unlock(&q->lock); + return IRQ_HANDLED; +} + +/* + * The MSI-X interrupt handler for an SGE response queue for the NAPI case + * (i.e., response queue serviced by NAPI polling). + */ +irqreturn_t t3_sge_intr_msix_napi(int irq, void *cookie) +{ + struct sge_qset *qs = cookie; + struct adapter *adap = qs->netdev->priv; + struct sge_rspq *q = &qs->rspq; + + spin_lock(&q->lock); + BUG_ON(napi_is_scheduled(qs->netdev)); + + if (handle_responses(adap, q) < 0) + q->unhandled_irqs++; + spin_unlock(&q->lock); + return IRQ_HANDLED; +} + +/* + * The non-NAPI MSI interrupt handler. This needs to handle data events from + * SGE response queues as well as error and other async events as they all use + * the same MSI vector. We use one SGE response queue per port in this mode + * and protect all response queues with queue 0's lock. + */ +static irqreturn_t t3_intr_msi(int irq, void *cookie) +{ + int new_packets = 0; + struct adapter *adap = cookie; + struct sge_rspq *q = &adap->sge.qs[0].rspq; + + spin_lock(&q->lock); + + if (process_responses(adap, &adap->sge.qs[0], -1)) { + t3_write_reg(adap, A_SG_GTS, V_RSPQ(q->cntxt_id) | + V_NEWTIMER(q->next_holdoff) | V_NEWINDEX(q->cidx)); + new_packets = 1; + } + + if (adap->params.nports == 2 && + process_responses(adap, &adap->sge.qs[1], -1)) { + struct sge_rspq *q1 = &adap->sge.qs[1].rspq; + + t3_write_reg(adap, A_SG_GTS, V_RSPQ(q1->cntxt_id) | + V_NEWTIMER(q1->next_holdoff) | + V_NEWINDEX(q1->cidx)); + new_packets = 1; + } + + if (!new_packets && t3_slow_intr_handler(adap) == 0) + q->unhandled_irqs++; + + spin_unlock(&q->lock); + return IRQ_HANDLED; +} + +static int rspq_check_napi(struct net_device *dev, struct sge_rspq *q) +{ + if (!napi_is_scheduled(dev) && is_new_response(&q->desc[q->cidx], q)) { + if (likely(__netif_rx_schedule_prep(dev))) + __netif_rx_schedule(dev); + return 1; + } + return 0; +} + +/* + * The MSI interrupt handler for the NAPI case (i.e., response queues serviced + * by NAPI polling). Handles data events from SGE response queues as well as + * error and other async events as they all use the same MSI vector. We use + * one SGE response queue per port in this mode and protect all response + * queues with queue 0's lock. + */ +irqreturn_t t3_intr_msi_napi(int irq, void *cookie) +{ + int new_packets; + struct adapter *adap = cookie; + struct sge_rspq *q = &adap->sge.qs[0].rspq; + + spin_lock(&q->lock); + + new_packets = rspq_check_napi(adap->sge.qs[0].netdev, q); + if (adap->params.nports == 2) + new_packets += rspq_check_napi(adap->sge.qs[1].netdev, + &adap->sge.qs[1].rspq); + if (!new_packets && t3_slow_intr_handler(adap) == 0) + q->unhandled_irqs++; + + spin_unlock(&q->lock); + return IRQ_HANDLED; +} + +/* + * A helper function that processes responses and issues GTS. + */ +static inline int process_responses_gts(struct adapter *adap, + struct sge_rspq *rq) +{ + int work; + + work = process_responses(adap, rspq_to_qset(rq), -1); + t3_write_reg(adap, A_SG_GTS, V_RSPQ(rq->cntxt_id) | + V_NEWTIMER(rq->next_holdoff) | V_NEWINDEX(rq->cidx)); + return work; +} + +/* + * The legacy INTx interrupt handler. This needs to handle data events from + * SGE response queues as well as error and other async events as they all use + * the same interrupt pin. We use one SGE response queue per port in this mode + * and protect all response queues with queue 0's lock. + */ +static irqreturn_t t3_intr(int irq, void *cookie) +{ + int work_done, w0, w1; + struct adapter *adap = cookie; + struct sge_rspq *q0 = &adap->sge.qs[0].rspq; + struct sge_rspq *q1 = &adap->sge.qs[1].rspq; + + spin_lock(&q0->lock); + + w0 = is_new_response(&q0->desc[q0->cidx], q0); + w1 = adap->params.nports == 2 && + is_new_response(&q1->desc[q1->cidx], q1); + + if (likely(w0 | w1)) { + t3_write_reg(adap, A_PL_CLI, 0); + t3_read_reg(adap, A_PL_CLI); /* flush */ + + if (likely(w0)) + process_responses_gts(adap, q0); + + if (w1) + process_responses_gts(adap, q1); + + work_done = w0 | w1; + } else + work_done = t3_slow_intr_handler(adap); + + spin_unlock(&q0->lock); + return IRQ_RETVAL(work_done != 0); +} + +/* + * Interrupt handler for legacy INTx interrupts for T3B-based cards. + * Handles data events from SGE response queues as well as error and other + * async events as they all use the same interrupt pin. We use one SGE + * response queue per port in this mode and protect all response queues with + * queue 0's lock. + */ +static irqreturn_t t3b_intr(int irq, void *cookie) +{ + u32 map; + struct adapter *adap = cookie; + struct sge_rspq *q0 = &adap->sge.qs[0].rspq; + + t3_write_reg(adap, A_PL_CLI, 0); + map = t3_read_reg(adap, A_SG_DATA_INTR); + + if (unlikely(!map)) /* shared interrupt, most likely */ + return IRQ_NONE; + + spin_lock(&q0->lock); + + if (unlikely(map & F_ERRINTR)) + t3_slow_intr_handler(adap); + + if (likely(map & 1)) + process_responses_gts(adap, q0); + + if (map & 2) + process_responses_gts(adap, &adap->sge.qs[1].rspq); + + spin_unlock(&q0->lock); + return IRQ_HANDLED; +} + +/* + * NAPI interrupt handler for legacy INTx interrupts for T3B-based cards. + * Handles data events from SGE response queues as well as error and other + * async events as they all use the same interrupt pin. We use one SGE + * response queue per port in this mode and protect all response queues with + * queue 0's lock. + */ +static irqreturn_t t3b_intr_napi(int irq, void *cookie) +{ + u32 map; + struct net_device *dev; + struct adapter *adap = cookie; + struct sge_rspq *q0 = &adap->sge.qs[0].rspq; + + t3_write_reg(adap, A_PL_CLI, 0); + map = t3_read_reg(adap, A_SG_DATA_INTR); + + if (unlikely(!map)) /* shared interrupt, most likely */ + return IRQ_NONE; + + spin_lock(&q0->lock); + + if (unlikely(map & F_ERRINTR)) + t3_slow_intr_handler(adap); + + if (likely(map & 1)) { + dev = adap->sge.qs[0].netdev; + + BUG_ON(napi_is_scheduled(dev)); + if (likely(__netif_rx_schedule_prep(dev))) + __netif_rx_schedule(dev); + } + if (map & 2) { + dev = adap->sge.qs[1].netdev; + + BUG_ON(napi_is_scheduled(dev)); + if (likely(__netif_rx_schedule_prep(dev))) + __netif_rx_schedule(dev); + } + + spin_unlock(&q0->lock); + return IRQ_HANDLED; +} + +/** + * t3_intr_handler - select the top-level interrupt handler + * @adap: the adapter + * @polling: whether using NAPI to service response queues + * + * Selects the top-level interrupt handler based on the type of interrupts + * (MSI-X, MSI, or legacy) and whether NAPI will be used to service the + * response queues. + */ +intr_handler_t t3_intr_handler(struct adapter *adap, int polling) +{ + if (adap->flags & USING_MSIX) + return polling ? t3_sge_intr_msix_napi : t3_sge_intr_msix; + if (adap->flags & USING_MSI) + return polling ? t3_intr_msi_napi : t3_intr_msi; + if (adap->params.rev > 0) + return polling ? t3b_intr_napi : t3b_intr; + return t3_intr; +} + +/** + * t3_sge_err_intr_handler - SGE async event interrupt handler + * @adapter: the adapter + * + * Interrupt handler for SGE asynchronous (non-data) events. + */ +void t3_sge_err_intr_handler(struct adapter *adapter) +{ + unsigned int v, status = t3_read_reg(adapter, A_SG_INT_CAUSE); + + if (status & F_RSPQCREDITOVERFOW) + CH_ALERT(adapter, "SGE response queue credit overflow\n"); + + if (status & F_RSPQDISABLED) { + v = t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS); + + CH_ALERT(adapter, + "packet delivered to disabled response queue " + "(0x%x)\n", (v >> S_RSPQ0DISABLED) & 0xff); + } + + t3_write_reg(adapter, A_SG_INT_CAUSE, status); + if (status & (F_RSPQCREDITOVERFOW | F_RSPQDISABLED)) + t3_fatal_err(adapter); +} + +/** + * sge_timer_cb - perform periodic maintenance of an SGE qset + * @data: the SGE queue set to maintain + * + * Runs periodically from a timer to perform maintenance of an SGE queue + * set. It performs two tasks: + * + * a) Cleans up any completed Tx descriptors that may still be pending. + * Normal descriptor cleanup happens when new packets are added to a Tx + * queue so this timer is relatively infrequent and does any cleanup only + * if the Tx queue has not seen any new packets in a while. We make a + * best effort attempt to reclaim descriptors, in that we don't wait + * around if we cannot get a queue's lock (which most likely is because + * someone else is queueing new packets and so will also handle the clean + * up). Since control queues use immediate data exclusively we don't + * bother cleaning them up here. + * + * b) Replenishes Rx queues that have run out due to memory shortage. + * Normally new Rx buffers are added when existing ones are consumed but + * when out of memory a queue can become empty. We try to add only a few + * buffers here, the queue will be replenished fully as these new buffers + * are used up if memory shortage has subsided. + */ +static void sge_timer_cb(unsigned long data) +{ + spinlock_t *lock; + struct sge_qset *qs = (struct sge_qset *)data; + struct adapter *adap = qs->netdev->priv; + + if (spin_trylock(&qs->txq[TXQ_ETH].lock)) { + reclaim_completed_tx(adap, &qs->txq[TXQ_ETH]); + spin_unlock(&qs->txq[TXQ_ETH].lock); + } + if (spin_trylock(&qs->txq[TXQ_OFLD].lock)) { + reclaim_completed_tx(adap, &qs->txq[TXQ_OFLD]); + spin_unlock(&qs->txq[TXQ_OFLD].lock); + } + lock = (adap->flags & USING_MSIX) ? &qs->rspq.lock : + &adap->sge.qs[0].rspq.lock; + if (spin_trylock_irq(lock)) { + if (!napi_is_scheduled(qs->netdev)) { + if (qs->fl[0].credits < qs->fl[0].size) + __refill_fl(adap, &qs->fl[0]); + if (qs->fl[1].credits < qs->fl[1].size) + __refill_fl(adap, &qs->fl[1]); + } + spin_unlock_irq(lock); + } + mod_timer(&qs->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD); +} + +/** + * t3_update_qset_coalesce - update coalescing settings for a queue set + * @qs: the SGE queue set + * @p: new queue set parameters + * + * Update the coalescing settings for an SGE queue set. Nothing is done + * if the queue set is not initialized yet. + */ +void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p) +{ + if (!qs->netdev) + return; + + qs->rspq.holdoff_tmr = max(p->coalesce_usecs * 10, 1U);/* can't be 0 */ + qs->rspq.polling = p->polling; + qs->netdev->poll = p->polling ? napi_rx_handler : ofld_poll; +} + +/** + * t3_sge_alloc_qset - initialize an SGE queue set + * @adapter: the adapter + * @id: the queue set id + * @nports: how many Ethernet ports will be using this queue set + * @irq_vec_idx: the IRQ vector index for response queue interrupts + * @p: configuration parameters for this queue set + * @ntxq: number of Tx queues for the queue set + * @netdev: net device associated with this queue set + * + * Allocate resources and initialize an SGE queue set. A queue set + * comprises a response queue, two Rx free-buffer queues, and up to 3 + * Tx queues. The Tx queues are assigned roles in the order Ethernet + * queue, offload queue, and control queue. + */ +int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, + int irq_vec_idx, const struct qset_params *p, + int ntxq, struct net_device *netdev) +{ + int i, ret = -ENOMEM; + struct sge_qset *q = &adapter->sge.qs[id]; + + init_qset_cntxt(q, id); + init_timer(&q->tx_reclaim_timer); + q->tx_reclaim_timer.data = (unsigned long)q; + q->tx_reclaim_timer.function = sge_timer_cb; + + q->fl[0].desc = alloc_ring(adapter->pdev, p->fl_size, + sizeof(struct rx_desc), + sizeof(struct rx_sw_desc), + &q->fl[0].phys_addr, &q->fl[0].sdesc); + if (!q->fl[0].desc) + goto err; + + q->fl[1].desc = alloc_ring(adapter->pdev, p->jumbo_size, + sizeof(struct rx_desc), + sizeof(struct rx_sw_desc), + &q->fl[1].phys_addr, &q->fl[1].sdesc); + if (!q->fl[1].desc) + goto err; + + q->rspq.desc = alloc_ring(adapter->pdev, p->rspq_size, + sizeof(struct rsp_desc), 0, + &q->rspq.phys_addr, NULL); + if (!q->rspq.desc) + goto err; + + for (i = 0; i < ntxq; ++i) { + /* + * The control queue always uses immediate data so does not + * need to keep track of any sk_buffs. + */ + size_t sz = i == TXQ_CTRL ? 0 : sizeof(struct tx_sw_desc); + + q->txq[i].desc = alloc_ring(adapter->pdev, p->txq_size[i], + sizeof(struct tx_desc), sz, + &q->txq[i].phys_addr, + &q->txq[i].sdesc); + if (!q->txq[i].desc) + goto err; + + q->txq[i].gen = 1; + q->txq[i].size = p->txq_size[i]; + spin_lock_init(&q->txq[i].lock); + skb_queue_head_init(&q->txq[i].sendq); + } + + tasklet_init(&q->txq[TXQ_OFLD].qresume_tsk, restart_offloadq, + (unsigned long)q); + tasklet_init(&q->txq[TXQ_CTRL].qresume_tsk, restart_ctrlq, + (unsigned long)q); + + q->fl[0].gen = q->fl[1].gen = 1; + q->fl[0].size = p->fl_size; + q->fl[1].size = p->jumbo_size; + + q->rspq.gen = 1; + q->rspq.size = p->rspq_size; + spin_lock_init(&q->rspq.lock); + + q->txq[TXQ_ETH].stop_thres = nports * + flits_to_desc(sgl_len(MAX_SKB_FRAGS + 1) + 3); + + if (ntxq == 1) { + q->fl[0].buf_size = SGE_RX_SM_BUF_SIZE + 2 + + sizeof(struct cpl_rx_pkt); + q->fl[1].buf_size = MAX_FRAME_SIZE + 2 + + sizeof(struct cpl_rx_pkt); + } else { + q->fl[0].buf_size = SGE_RX_SM_BUF_SIZE + + sizeof(struct cpl_rx_data); + q->fl[1].buf_size = (16 * 1024) - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + } + + spin_lock(&adapter->sge.reg_lock); + + /* FL threshold comparison uses < */ + ret = t3_sge_init_rspcntxt(adapter, q->rspq.cntxt_id, irq_vec_idx, + q->rspq.phys_addr, q->rspq.size, + q->fl[0].buf_size, 1, 0); + if (ret) + goto err_unlock; + + for (i = 0; i < SGE_RXQ_PER_SET; ++i) { + ret = t3_sge_init_flcntxt(adapter, q->fl[i].cntxt_id, 0, + q->fl[i].phys_addr, q->fl[i].size, + q->fl[i].buf_size, p->cong_thres, 1, + 0); + if (ret) + goto err_unlock; + } + + ret = t3_sge_init_ecntxt(adapter, q->txq[TXQ_ETH].cntxt_id, USE_GTS, + SGE_CNTXT_ETH, id, q->txq[TXQ_ETH].phys_addr, + q->txq[TXQ_ETH].size, q->txq[TXQ_ETH].token, + 1, 0); + if (ret) + goto err_unlock; + + if (ntxq > 1) { + ret = t3_sge_init_ecntxt(adapter, q->txq[TXQ_OFLD].cntxt_id, + USE_GTS, SGE_CNTXT_OFLD, id, + q->txq[TXQ_OFLD].phys_addr, + q->txq[TXQ_OFLD].size, 0, 1, 0); + if (ret) + goto err_unlock; + } + + if (ntxq > 2) { + ret = t3_sge_init_ecntxt(adapter, q->txq[TXQ_CTRL].cntxt_id, 0, + SGE_CNTXT_CTRL, id, + q->txq[TXQ_CTRL].phys_addr, + q->txq[TXQ_CTRL].size, + q->txq[TXQ_CTRL].token, 1, 0); + if (ret) + goto err_unlock; + } + + spin_unlock(&adapter->sge.reg_lock); + q->netdev = netdev; + t3_update_qset_coalesce(q, p); + + /* + * We use atalk_ptr as a backpointer to a qset. In case a device is + * associated with multiple queue sets only the first one sets + * atalk_ptr. + */ + if (netdev->atalk_ptr == NULL) + netdev->atalk_ptr = q; + + refill_fl(adapter, &q->fl[0], q->fl[0].size, GFP_KERNEL); + refill_fl(adapter, &q->fl[1], q->fl[1].size, GFP_KERNEL); + refill_rspq(adapter, &q->rspq, q->rspq.size - 1); + + t3_write_reg(adapter, A_SG_GTS, V_RSPQ(q->rspq.cntxt_id) | + V_NEWTIMER(q->rspq.holdoff_tmr)); + + mod_timer(&q->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD); + return 0; + + err_unlock: + spin_unlock(&adapter->sge.reg_lock); + err: + t3_free_qset(adapter, q); + return ret; +} + +/** + * t3_free_sge_resources - free SGE resources + * @adap: the adapter + * + * Frees resources used by the SGE queue sets. + */ +void t3_free_sge_resources(struct adapter *adap) +{ + int i; + + for (i = 0; i < SGE_QSETS; ++i) + t3_free_qset(adap, &adap->sge.qs[i]); +} + +/** + * t3_sge_start - enable SGE + * @adap: the adapter + * + * Enables the SGE for DMAs. This is the last step in starting packet + * transfers. + */ +void t3_sge_start(struct adapter *adap) +{ + t3_set_reg_field(adap, A_SG_CONTROL, F_GLOBALENABLE, F_GLOBALENABLE); +} + +/** + * t3_sge_stop - disable SGE operation + * @adap: the adapter + * + * Disables the DMA engine. This can be called in emeregencies (e.g., + * from error interrupts) or from normal process context. In the latter + * case it also disables any pending queue restart tasklets. Note that + * if it is called in interrupt context it cannot disable the restart + * tasklets as it cannot wait, however the tasklets will have no effect + * since the doorbells are disabled and the driver will call this again + * later from process context, at which time the tasklets will be stopped + * if they are still running. + */ +void t3_sge_stop(struct adapter *adap) +{ + t3_set_reg_field(adap, A_SG_CONTROL, F_GLOBALENABLE, 0); + if (!in_interrupt()) { + int i; + + for (i = 0; i < SGE_QSETS; ++i) { + struct sge_qset *qs = &adap->sge.qs[i]; + + tasklet_kill(&qs->txq[TXQ_OFLD].qresume_tsk); + tasklet_kill(&qs->txq[TXQ_CTRL].qresume_tsk); + } + } +} + +/** + * t3_sge_init - initialize SGE + * @adap: the adapter + * @p: the SGE parameters + * + * Performs SGE initialization needed every time after a chip reset. + * We do not initialize any of the queue sets here, instead the driver + * top-level must request those individually. We also do not enable DMA + * here, that should be done after the queues have been set up. + */ +void t3_sge_init(struct adapter *adap, struct sge_params *p) +{ + unsigned int ctrl, ups = ffs(pci_resource_len(adap->pdev, 2) >> 12); + + ctrl = F_DROPPKT | V_PKTSHIFT(2) | F_FLMODE | F_AVOIDCQOVFL | + F_CQCRDTCTRL | + V_HOSTPAGESIZE(PAGE_SHIFT - 11) | F_BIGENDIANINGRESS | + V_USERSPACESIZE(ups ? ups - 1 : 0) | F_ISCSICOALESCING; +#if SGE_NUM_GENBITS == 1 + ctrl |= F_EGRGENCTRL; +#endif + if (adap->params.rev > 0) { + if (!(adap->flags & (USING_MSIX | USING_MSI))) + ctrl |= F_ONEINTMULTQ | F_OPTONEINTMULTQ; + ctrl |= F_CQCRDTCTRL | F_AVOIDCQOVFL; + } + t3_write_reg(adap, A_SG_CONTROL, ctrl); + t3_write_reg(adap, A_SG_EGR_RCQ_DRB_THRSH, V_HIRCQDRBTHRSH(512) | + V_LORCQDRBTHRSH(512)); + t3_write_reg(adap, A_SG_TIMER_TICK, core_ticks_per_usec(adap) / 10); + t3_write_reg(adap, A_SG_CMDQ_CREDIT_TH, V_THRESHOLD(32) | + V_TIMEOUT(100 * core_ticks_per_usec(adap))); + t3_write_reg(adap, A_SG_HI_DRB_HI_THRSH, 1000); + t3_write_reg(adap, A_SG_HI_DRB_LO_THRSH, 256); + t3_write_reg(adap, A_SG_LO_DRB_HI_THRSH, 1000); + t3_write_reg(adap, A_SG_LO_DRB_LO_THRSH, 256); + t3_write_reg(adap, A_SG_OCO_BASE, V_BASE1(0xfff)); + t3_write_reg(adap, A_SG_DRB_PRI_THRESH, 63 * 1024); +} + +/** + * t3_sge_prep - one-time SGE initialization + * @adap: the associated adapter + * @p: SGE parameters + * + * Performs one-time initialization of SGE SW state. Includes determining + * defaults for the assorted SGE parameters, which admins can change until + * they are used to initialize the SGE. + */ +void __devinit t3_sge_prep(struct adapter *adap, struct sge_params *p) +{ + int i; + + p->max_pkt_size = (16 * 1024) - sizeof(struct cpl_rx_data) - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + for (i = 0; i < SGE_QSETS; ++i) { + struct qset_params *q = p->qset + i; + + q->polling = adap->params.rev > 0; + q->coalesce_usecs = 5; + q->rspq_size = 1024; + q->fl_size = 4096; + q->jumbo_size = 512; + q->txq_size[TXQ_ETH] = 1024; + q->txq_size[TXQ_OFLD] = 1024; + q->txq_size[TXQ_CTRL] = 256; + q->cong_thres = 0; + } + + spin_lock_init(&adap->sge.reg_lock); +} + +/** + * t3_get_desc - dump an SGE descriptor for debugging purposes + * @qs: the queue set + * @qnum: identifies the specific queue (0..2: Tx, 3:response, 4..5: Rx) + * @idx: the descriptor index in the queue + * @data: where to dump the descriptor contents + * + * Dumps the contents of a HW descriptor of an SGE queue. Returns the + * size of the descriptor. + */ +int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx, + unsigned char *data) +{ + if (qnum >= 6) + return -EINVAL; + + if (qnum < 3) { + if (!qs->txq[qnum].desc || idx >= qs->txq[qnum].size) + return -EINVAL; + memcpy(data, &qs->txq[qnum].desc[idx], sizeof(struct tx_desc)); + return sizeof(struct tx_desc); + } + + if (qnum == 3) { + if (!qs->rspq.desc || idx >= qs->rspq.size) + return -EINVAL; + memcpy(data, &qs->rspq.desc[idx], sizeof(struct rsp_desc)); + return sizeof(struct rsp_desc); + } + + qnum -= 4; + if (!qs->fl[qnum].desc || idx >= qs->fl[qnum].size) + return -EINVAL; + memcpy(data, &qs->fl[qnum].desc[idx], sizeof(struct rx_desc)); + return sizeof(struct rx_desc); +} diff -puN /dev/null drivers/net/cxgb3/sge_defs.h --- /dev/null +++ a/drivers/net/cxgb3/sge_defs.h @@ -0,0 +1,251 @@ +/* + * This file is automatically generated --- any changes will be lost. + */ + +#ifndef _SGE_DEFS_H +#define _SGE_DEFS_H + +#define S_EC_CREDITS 0 +#define M_EC_CREDITS 0x7FFF +#define V_EC_CREDITS(x) ((x) << S_EC_CREDITS) +#define G_EC_CREDITS(x) (((x) >> S_EC_CREDITS) & M_EC_CREDITS) + +#define S_EC_GTS 15 +#define V_EC_GTS(x) ((x) << S_EC_GTS) +#define F_EC_GTS V_EC_GTS(1U) + +#define S_EC_INDEX 16 +#define M_EC_INDEX 0xFFFF +#define V_EC_INDEX(x) ((x) << S_EC_INDEX) +#define G_EC_INDEX(x) (((x) >> S_EC_INDEX) & M_EC_INDEX) + +#define S_EC_SIZE 0 +#define M_EC_SIZE 0xFFFF +#define V_EC_SIZE(x) ((x) << S_EC_SIZE) +#define G_EC_SIZE(x) (((x) >> S_EC_SIZE) & M_EC_SIZE) + +#define S_EC_BASE_LO 16 +#define M_EC_BASE_LO 0xFFFF +#define V_EC_BASE_LO(x) ((x) << S_EC_BASE_LO) +#define G_EC_BASE_LO(x) (((x) >> S_EC_BASE_LO) & M_EC_BASE_LO) + +#define S_EC_BASE_HI 0 +#define M_EC_BASE_HI 0xF +#define V_EC_BASE_HI(x) ((x) << S_EC_BASE_HI) +#define G_EC_BASE_HI(x) (((x) >> S_EC_BASE_HI) & M_EC_BASE_HI) + +#define S_EC_RESPQ 4 +#define M_EC_RESPQ 0x7 +#define V_EC_RESPQ(x) ((x) << S_EC_RESPQ) +#define G_EC_RESPQ(x) (((x) >> S_EC_RESPQ) & M_EC_RESPQ) + +#define S_EC_TYPE 7 +#define M_EC_TYPE 0x7 +#define V_EC_TYPE(x) ((x) << S_EC_TYPE) +#define G_EC_TYPE(x) (((x) >> S_EC_TYPE) & M_EC_TYPE) + +#define S_EC_GEN 10 +#define V_EC_GEN(x) ((x) << S_EC_GEN) +#define F_EC_GEN V_EC_GEN(1U) + +#define S_EC_UP_TOKEN 11 +#define M_EC_UP_TOKEN 0xFFFFF +#define V_EC_UP_TOKEN(x) ((x) << S_EC_UP_TOKEN) +#define G_EC_UP_TOKEN(x) (((x) >> S_EC_UP_TOKEN) & M_EC_UP_TOKEN) + +#define S_EC_VALID 31 +#define V_EC_VALID(x) ((x) << S_EC_VALID) +#define F_EC_VALID V_EC_VALID(1U) + +#define S_RQ_MSI_VEC 20 +#define M_RQ_MSI_VEC 0x3F +#define V_RQ_MSI_VEC(x) ((x) << S_RQ_MSI_VEC) +#define G_RQ_MSI_VEC(x) (((x) >> S_RQ_MSI_VEC) & M_RQ_MSI_VEC) + +#define S_RQ_INTR_EN 26 +#define V_RQ_INTR_EN(x) ((x) << S_RQ_INTR_EN) +#define F_RQ_INTR_EN V_RQ_INTR_EN(1U) + +#define S_RQ_GEN 28 +#define V_RQ_GEN(x) ((x) << S_RQ_GEN) +#define F_RQ_GEN V_RQ_GEN(1U) + +#define S_CQ_INDEX 0 +#define M_CQ_INDEX 0xFFFF +#define V_CQ_INDEX(x) ((x) << S_CQ_INDEX) +#define G_CQ_INDEX(x) (((x) >> S_CQ_INDEX) & M_CQ_INDEX) + +#define S_CQ_SIZE 16 +#define M_CQ_SIZE 0xFFFF +#define V_CQ_SIZE(x) ((x) << S_CQ_SIZE) +#define G_CQ_SIZE(x) (((x) >> S_CQ_SIZE) & M_CQ_SIZE) + +#define S_CQ_BASE_HI 0 +#define M_CQ_BASE_HI 0xFFFFF +#define V_CQ_BASE_HI(x) ((x) << S_CQ_BASE_HI) +#define G_CQ_BASE_HI(x) (((x) >> S_CQ_BASE_HI) & M_CQ_BASE_HI) + +#define S_CQ_RSPQ 20 +#define M_CQ_RSPQ 0x3F +#define V_CQ_RSPQ(x) ((x) << S_CQ_RSPQ) +#define G_CQ_RSPQ(x) (((x) >> S_CQ_RSPQ) & M_CQ_RSPQ) + +#define S_CQ_ASYNC_NOTIF 26 +#define V_CQ_ASYNC_NOTIF(x) ((x) << S_CQ_ASYNC_NOTIF) +#define F_CQ_ASYNC_NOTIF V_CQ_ASYNC_NOTIF(1U) + +#define S_CQ_ARMED 27 +#define V_CQ_ARMED(x) ((x) << S_CQ_ARMED) +#define F_CQ_ARMED V_CQ_ARMED(1U) + +#define S_CQ_ASYNC_NOTIF_SOL 28 +#define V_CQ_ASYNC_NOTIF_SOL(x) ((x) << S_CQ_ASYNC_NOTIF_SOL) +#define F_CQ_ASYNC_NOTIF_SOL V_CQ_ASYNC_NOTIF_SOL(1U) + +#define S_CQ_GEN 29 +#define V_CQ_GEN(x) ((x) << S_CQ_GEN) +#define F_CQ_GEN V_CQ_GEN(1U) + +#define S_CQ_OVERFLOW_MODE 31 +#define V_CQ_OVERFLOW_MODE(x) ((x) << S_CQ_OVERFLOW_MODE) +#define F_CQ_OVERFLOW_MODE V_CQ_OVERFLOW_MODE(1U) + +#define S_CQ_CREDITS 0 +#define M_CQ_CREDITS 0xFFFF +#define V_CQ_CREDITS(x) ((x) << S_CQ_CREDITS) +#define G_CQ_CREDITS(x) (((x) >> S_CQ_CREDITS) & M_CQ_CREDITS) + +#define S_CQ_CREDIT_THRES 16 +#define M_CQ_CREDIT_THRES 0x1FFF +#define V_CQ_CREDIT_THRES(x) ((x) << S_CQ_CREDIT_THRES) +#define G_CQ_CREDIT_THRES(x) (((x) >> S_CQ_CREDIT_THRES) & M_CQ_CREDIT_THRES) + +#define S_FL_BASE_HI 0 +#define M_FL_BASE_HI 0xFFFFF +#define V_FL_BASE_HI(x) ((x) << S_FL_BASE_HI) +#define G_FL_BASE_HI(x) (((x) >> S_FL_BASE_HI) & M_FL_BASE_HI) + +#define S_FL_INDEX_LO 20 +#define M_FL_INDEX_LO 0xFFF +#define V_FL_INDEX_LO(x) ((x) << S_FL_INDEX_LO) +#define G_FL_INDEX_LO(x) (((x) >> S_FL_INDEX_LO) & M_FL_INDEX_LO) + +#define S_FL_INDEX_HI 0 +#define M_FL_INDEX_HI 0xF +#define V_FL_INDEX_HI(x) ((x) << S_FL_INDEX_HI) +#define G_FL_INDEX_HI(x) (((x) >> S_FL_INDEX_HI) & M_FL_INDEX_HI) + +#define S_FL_SIZE 4 +#define M_FL_SIZE 0xFFFF +#define V_FL_SIZE(x) ((x) << S_FL_SIZE) +#define G_FL_SIZE(x) (((x) >> S_FL_SIZE) & M_FL_SIZE) + +#define S_FL_GEN 20 +#define V_FL_GEN(x) ((x) << S_FL_GEN) +#define F_FL_GEN V_FL_GEN(1U) + +#define S_FL_ENTRY_SIZE_LO 21 +#define M_FL_ENTRY_SIZE_LO 0x7FF +#define V_FL_ENTRY_SIZE_LO(x) ((x) << S_FL_ENTRY_SIZE_LO) +#define G_FL_ENTRY_SIZE_LO(x) (((x) >> S_FL_ENTRY_SIZE_LO) & M_FL_ENTRY_SIZE_LO) + +#define S_FL_ENTRY_SIZE_HI 0 +#define M_FL_ENTRY_SIZE_HI 0x1FFFFF +#define V_FL_ENTRY_SIZE_HI(x) ((x) << S_FL_ENTRY_SIZE_HI) +#define G_FL_ENTRY_SIZE_HI(x) (((x) >> S_FL_ENTRY_SIZE_HI) & M_FL_ENTRY_SIZE_HI) + +#define S_FL_CONG_THRES 21 +#define M_FL_CONG_THRES 0x3FF +#define V_FL_CONG_THRES(x) ((x) << S_FL_CONG_THRES) +#define G_FL_CONG_THRES(x) (((x) >> S_FL_CONG_THRES) & M_FL_CONG_THRES) + +#define S_FL_GTS 31 +#define V_FL_GTS(x) ((x) << S_FL_GTS) +#define F_FL_GTS V_FL_GTS(1U) + +#define S_FLD_GEN1 31 +#define V_FLD_GEN1(x) ((x) << S_FLD_GEN1) +#define F_FLD_GEN1 V_FLD_GEN1(1U) + +#define S_FLD_GEN2 0 +#define V_FLD_GEN2(x) ((x) << S_FLD_GEN2) +#define F_FLD_GEN2 V_FLD_GEN2(1U) + +#define S_RSPD_TXQ1_CR 0 +#define M_RSPD_TXQ1_CR 0x7F +#define V_RSPD_TXQ1_CR(x) ((x) << S_RSPD_TXQ1_CR) +#define G_RSPD_TXQ1_CR(x) (((x) >> S_RSPD_TXQ1_CR) & M_RSPD_TXQ1_CR) + +#define S_RSPD_TXQ1_GTS 7 +#define V_RSPD_TXQ1_GTS(x) ((x) << S_RSPD_TXQ1_GTS) +#define F_RSPD_TXQ1_GTS V_RSPD_TXQ1_GTS(1U) + +#define S_RSPD_TXQ2_CR 8 +#define M_RSPD_TXQ2_CR 0x7F +#define V_RSPD_TXQ2_CR(x) ((x) << S_RSPD_TXQ2_CR) +#define G_RSPD_TXQ2_CR(x) (((x) >> S_RSPD_TXQ2_CR) & M_RSPD_TXQ2_CR) + +#define S_RSPD_TXQ2_GTS 15 +#define V_RSPD_TXQ2_GTS(x) ((x) << S_RSPD_TXQ2_GTS) +#define F_RSPD_TXQ2_GTS V_RSPD_TXQ2_GTS(1U) + +#define S_RSPD_TXQ0_CR 16 +#define M_RSPD_TXQ0_CR 0x7F +#define V_RSPD_TXQ0_CR(x) ((x) << S_RSPD_TXQ0_CR) +#define G_RSPD_TXQ0_CR(x) (((x) >> S_RSPD_TXQ0_CR) & M_RSPD_TXQ0_CR) + +#define S_RSPD_TXQ0_GTS 23 +#define V_RSPD_TXQ0_GTS(x) ((x) << S_RSPD_TXQ0_GTS) +#define F_RSPD_TXQ0_GTS V_RSPD_TXQ0_GTS(1U) + +#define S_RSPD_EOP 24 +#define V_RSPD_EOP(x) ((x) << S_RSPD_EOP) +#define F_RSPD_EOP V_RSPD_EOP(1U) + +#define S_RSPD_SOP 25 +#define V_RSPD_SOP(x) ((x) << S_RSPD_SOP) +#define F_RSPD_SOP V_RSPD_SOP(1U) + +#define S_RSPD_ASYNC_NOTIF 26 +#define V_RSPD_ASYNC_NOTIF(x) ((x) << S_RSPD_ASYNC_NOTIF) +#define F_RSPD_ASYNC_NOTIF V_RSPD_ASYNC_NOTIF(1U) + +#define S_RSPD_FL0_GTS 27 +#define V_RSPD_FL0_GTS(x) ((x) << S_RSPD_FL0_GTS) +#define F_RSPD_FL0_GTS V_RSPD_FL0_GTS(1U) + +#define S_RSPD_FL1_GTS 28 +#define V_RSPD_FL1_GTS(x) ((x) << S_RSPD_FL1_GTS) +#define F_RSPD_FL1_GTS V_RSPD_FL1_GTS(1U) + +#define S_RSPD_IMM_DATA_VALID 29 +#define V_RSPD_IMM_DATA_VALID(x) ((x) << S_RSPD_IMM_DATA_VALID) +#define F_RSPD_IMM_DATA_VALID V_RSPD_IMM_DATA_VALID(1U) + +#define S_RSPD_OFFLOAD 30 +#define V_RSPD_OFFLOAD(x) ((x) << S_RSPD_OFFLOAD) +#define F_RSPD_OFFLOAD V_RSPD_OFFLOAD(1U) + +#define S_RSPD_GEN1 31 +#define V_RSPD_GEN1(x) ((x) << S_RSPD_GEN1) +#define F_RSPD_GEN1 V_RSPD_GEN1(1U) + +#define S_RSPD_LEN 0 +#define M_RSPD_LEN 0x7FFFFFFF +#define V_RSPD_LEN(x) ((x) << S_RSPD_LEN) +#define G_RSPD_LEN(x) (((x) >> S_RSPD_LEN) & M_RSPD_LEN) + +#define S_RSPD_FLQ 31 +#define V_RSPD_FLQ(x) ((x) << S_RSPD_FLQ) +#define F_RSPD_FLQ V_RSPD_FLQ(1U) + +#define S_RSPD_GEN2 0 +#define V_RSPD_GEN2(x) ((x) << S_RSPD_GEN2) +#define F_RSPD_GEN2 V_RSPD_GEN2(1U) + +#define S_RSPD_INR_VEC 1 +#define M_RSPD_INR_VEC 0x7F +#define V_RSPD_INR_VEC(x) ((x) << S_RSPD_INR_VEC) +#define G_RSPD_INR_VEC(x) (((x) >> S_RSPD_INR_VEC) & M_RSPD_INR_VEC) + +#endif /* _SGE_DEFS_H */ diff -puN /dev/null drivers/net/cxgb3/t3_cpl.h --- /dev/null +++ a/drivers/net/cxgb3/t3_cpl.h @@ -0,0 +1,1426 @@ +/* + * Definitions of the CPL 5 commands and status codes. + * + * Copyright (C) 2004-2006 Chelsio Communications. All rights reserved. + * + * Written by Dimitris Michailidis (dm@chelsio.com) + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#ifndef T3_CPL_H +#define T3_CPL_H + +#if !defined(__LITTLE_ENDIAN_BITFIELD) && !defined(__BIG_ENDIAN_BITFIELD) +# include +#endif + +enum CPL_opcode { + CPL_PASS_OPEN_REQ = 0x1, + CPL_PASS_ACCEPT_RPL = 0x2, + CPL_ACT_OPEN_REQ = 0x3, + CPL_SET_TCB = 0x4, + CPL_SET_TCB_FIELD = 0x5, + CPL_GET_TCB = 0x6, + CPL_PCMD = 0x7, + CPL_CLOSE_CON_REQ = 0x8, + CPL_CLOSE_LISTSRV_REQ = 0x9, + CPL_ABORT_REQ = 0xA, + CPL_ABORT_RPL = 0xB, + CPL_TX_DATA = 0xC, + CPL_RX_DATA_ACK = 0xD, + CPL_TX_PKT = 0xE, + CPL_RTE_DELETE_REQ = 0xF, + CPL_RTE_WRITE_REQ = 0x10, + CPL_RTE_READ_REQ = 0x11, + CPL_L2T_WRITE_REQ = 0x12, + CPL_L2T_READ_REQ = 0x13, + CPL_SMT_WRITE_REQ = 0x14, + CPL_SMT_READ_REQ = 0x15, + CPL_TX_PKT_LSO = 0x16, + CPL_PCMD_READ = 0x17, + CPL_BARRIER = 0x18, + CPL_TID_RELEASE = 0x1A, + + CPL_CLOSE_LISTSRV_RPL = 0x20, + CPL_ERROR = 0x21, + CPL_GET_TCB_RPL = 0x22, + CPL_L2T_WRITE_RPL = 0x23, + CPL_PCMD_READ_RPL = 0x24, + CPL_PCMD_RPL = 0x25, + CPL_PEER_CLOSE = 0x26, + CPL_RTE_DELETE_RPL = 0x27, + CPL_RTE_WRITE_RPL = 0x28, + CPL_RX_DDP_COMPLETE = 0x29, + CPL_RX_PHYS_ADDR = 0x2A, + CPL_RX_PKT = 0x2B, + CPL_RX_URG_NOTIFY = 0x2C, + CPL_SET_TCB_RPL = 0x2D, + CPL_SMT_WRITE_RPL = 0x2E, + CPL_TX_DATA_ACK = 0x2F, + + CPL_ABORT_REQ_RSS = 0x30, + CPL_ABORT_RPL_RSS = 0x31, + CPL_CLOSE_CON_RPL = 0x32, + CPL_ISCSI_HDR = 0x33, + CPL_L2T_READ_RPL = 0x34, + CPL_RDMA_CQE = 0x35, + CPL_RDMA_CQE_READ_RSP = 0x36, + CPL_RDMA_CQE_ERR = 0x37, + CPL_RTE_READ_RPL = 0x38, + CPL_RX_DATA = 0x39, + + CPL_ACT_OPEN_RPL = 0x40, + CPL_PASS_OPEN_RPL = 0x41, + CPL_RX_DATA_DDP = 0x42, + CPL_SMT_READ_RPL = 0x43, + + CPL_ACT_ESTABLISH = 0x50, + CPL_PASS_ESTABLISH = 0x51, + + CPL_PASS_ACCEPT_REQ = 0x70, + + CPL_ASYNC_NOTIF = 0x80, /* fake opcode for async notifications */ + + CPL_TX_DMA_ACK = 0xA0, + CPL_RDMA_READ_REQ = 0xA1, + CPL_RDMA_TERMINATE = 0xA2, + CPL_TRACE_PKT = 0xA3, + CPL_RDMA_EC_STATUS = 0xA5, + + NUM_CPL_CMDS /* must be last and previous entries must be sorted */ +}; + +enum CPL_error { + CPL_ERR_NONE = 0, + CPL_ERR_TCAM_PARITY = 1, + CPL_ERR_TCAM_FULL = 3, + CPL_ERR_CONN_RESET = 20, + CPL_ERR_CONN_EXIST = 22, + CPL_ERR_ARP_MISS = 23, + CPL_ERR_BAD_SYN = 24, + CPL_ERR_CONN_TIMEDOUT = 30, + CPL_ERR_XMIT_TIMEDOUT = 31, + CPL_ERR_PERSIST_TIMEDOUT = 32, + CPL_ERR_FINWAIT2_TIMEDOUT = 33, + CPL_ERR_KEEPALIVE_TIMEDOUT = 34, + CPL_ERR_RTX_NEG_ADVICE = 35, + CPL_ERR_PERSIST_NEG_ADVICE = 36, + CPL_ERR_ABORT_FAILED = 42, + CPL_ERR_GENERAL = 99 +}; + +enum { + CPL_CONN_POLICY_AUTO = 0, + CPL_CONN_POLICY_ASK = 1, + CPL_CONN_POLICY_DENY = 3 +}; + +enum { + ULP_MODE_NONE = 0, + ULP_MODE_ISCSI = 2, + ULP_MODE_RDMA = 4, + ULP_MODE_TCPDDP = 5 +}; + +enum { + ULP_CRC_HEADER = 1 << 0, + ULP_CRC_DATA = 1 << 1 +}; + +enum { + CPL_PASS_OPEN_ACCEPT, + CPL_PASS_OPEN_REJECT +}; + +enum { + CPL_ABORT_SEND_RST = 0, + CPL_ABORT_NO_RST, + CPL_ABORT_POST_CLOSE_REQ = 2 +}; + +enum { /* TX_PKT_LSO ethernet types */ + CPL_ETH_II, + CPL_ETH_II_VLAN, + CPL_ETH_802_3, + CPL_ETH_802_3_VLAN +}; + +enum { /* TCP congestion control algorithms */ + CONG_ALG_RENO, + CONG_ALG_TAHOE, + CONG_ALG_NEWRENO, + CONG_ALG_HIGHSPEED +}; + +union opcode_tid { + __be32 opcode_tid; + __u8 opcode; +}; + +#define S_OPCODE 24 +#define V_OPCODE(x) ((x) << S_OPCODE) +#define G_OPCODE(x) (((x) >> S_OPCODE) & 0xFF) +#define G_TID(x) ((x) & 0xFFFFFF) + +/* tid is assumed to be 24-bits */ +#define MK_OPCODE_TID(opcode, tid) (V_OPCODE(opcode) | (tid)) + +#define OPCODE_TID(cmd) ((cmd)->ot.opcode_tid) + +/* extract the TID from a CPL command */ +#define GET_TID(cmd) (G_TID(ntohl(OPCODE_TID(cmd)))) + +struct tcp_options { + __be16 mss; + __u8 wsf; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:5; + __u8 ecn:1; + __u8 sack:1; + __u8 tstamp:1; +#else + __u8 tstamp:1; + __u8 sack:1; + __u8 ecn:1; + __u8:5; +#endif +}; + +struct rss_header { + __u8 opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 cpu_idx:6; + __u8 hash_type:2; +#else + __u8 hash_type:2; + __u8 cpu_idx:6; +#endif + __be16 cq_idx; + __be32 rss_hash_val; +}; + +#ifndef CHELSIO_FW +struct work_request_hdr { + __be32 wr_hi; + __be32 wr_lo; +}; + +/* wr_hi fields */ +#define S_WR_SGE_CREDITS 0 +#define M_WR_SGE_CREDITS 0xFF +#define V_WR_SGE_CREDITS(x) ((x) << S_WR_SGE_CREDITS) +#define G_WR_SGE_CREDITS(x) (((x) >> S_WR_SGE_CREDITS) & M_WR_SGE_CREDITS) + +#define S_WR_SGLSFLT 8 +#define M_WR_SGLSFLT 0xFF +#define V_WR_SGLSFLT(x) ((x) << S_WR_SGLSFLT) +#define G_WR_SGLSFLT(x) (((x) >> S_WR_SGLSFLT) & M_WR_SGLSFLT) + +#define S_WR_BCNTLFLT 16 +#define M_WR_BCNTLFLT 0xF +#define V_WR_BCNTLFLT(x) ((x) << S_WR_BCNTLFLT) +#define G_WR_BCNTLFLT(x) (((x) >> S_WR_BCNTLFLT) & M_WR_BCNTLFLT) + +#define S_WR_DATATYPE 20 +#define V_WR_DATATYPE(x) ((x) << S_WR_DATATYPE) +#define F_WR_DATATYPE V_WR_DATATYPE(1U) + +#define S_WR_COMPL 21 +#define V_WR_COMPL(x) ((x) << S_WR_COMPL) +#define F_WR_COMPL V_WR_COMPL(1U) + +#define S_WR_EOP 22 +#define V_WR_EOP(x) ((x) << S_WR_EOP) +#define F_WR_EOP V_WR_EOP(1U) + +#define S_WR_SOP 23 +#define V_WR_SOP(x) ((x) << S_WR_SOP) +#define F_WR_SOP V_WR_SOP(1U) + +#define S_WR_OP 24 +#define M_WR_OP 0xFF +#define V_WR_OP(x) ((x) << S_WR_OP) +#define G_WR_OP(x) (((x) >> S_WR_OP) & M_WR_OP) + +/* wr_lo fields */ +#define S_WR_LEN 0 +#define M_WR_LEN 0xFF +#define V_WR_LEN(x) ((x) << S_WR_LEN) +#define G_WR_LEN(x) (((x) >> S_WR_LEN) & M_WR_LEN) + +#define S_WR_TID 8 +#define M_WR_TID 0xFFFFF +#define V_WR_TID(x) ((x) << S_WR_TID) +#define G_WR_TID(x) (((x) >> S_WR_TID) & M_WR_TID) + +#define S_WR_CR_FLUSH 30 +#define V_WR_CR_FLUSH(x) ((x) << S_WR_CR_FLUSH) +#define F_WR_CR_FLUSH V_WR_CR_FLUSH(1U) + +#define S_WR_GEN 31 +#define V_WR_GEN(x) ((x) << S_WR_GEN) +#define F_WR_GEN V_WR_GEN(1U) + +# define WR_HDR struct work_request_hdr wr +# define RSS_HDR +#else +# define WR_HDR +# define RSS_HDR struct rss_header rss_hdr; +#endif + +/* option 0 lower-half fields */ +#define S_CPL_STATUS 0 +#define M_CPL_STATUS 0xFF +#define V_CPL_STATUS(x) ((x) << S_CPL_STATUS) +#define G_CPL_STATUS(x) (((x) >> S_CPL_STATUS) & M_CPL_STATUS) + +#define S_INJECT_TIMER 6 +#define V_INJECT_TIMER(x) ((x) << S_INJECT_TIMER) +#define F_INJECT_TIMER V_INJECT_TIMER(1U) + +#define S_NO_OFFLOAD 7 +#define V_NO_OFFLOAD(x) ((x) << S_NO_OFFLOAD) +#define F_NO_OFFLOAD V_NO_OFFLOAD(1U) + +#define S_ULP_MODE 8 +#define M_ULP_MODE 0xF +#define V_ULP_MODE(x) ((x) << S_ULP_MODE) +#define G_ULP_MODE(x) (((x) >> S_ULP_MODE) & M_ULP_MODE) + +#define S_RCV_BUFSIZ 12 +#define M_RCV_BUFSIZ 0x3FFF +#define V_RCV_BUFSIZ(x) ((x) << S_RCV_BUFSIZ) +#define G_RCV_BUFSIZ(x) (((x) >> S_RCV_BUFSIZ) & M_RCV_BUFSIZ) + +#define S_TOS 26 +#define M_TOS 0x3F +#define V_TOS(x) ((x) << S_TOS) +#define G_TOS(x) (((x) >> S_TOS) & M_TOS) + +/* option 0 upper-half fields */ +#define S_DELACK 0 +#define V_DELACK(x) ((x) << S_DELACK) +#define F_DELACK V_DELACK(1U) + +#define S_NO_CONG 1 +#define V_NO_CONG(x) ((x) << S_NO_CONG) +#define F_NO_CONG V_NO_CONG(1U) + +#define S_SRC_MAC_SEL 2 +#define M_SRC_MAC_SEL 0x3 +#define V_SRC_MAC_SEL(x) ((x) << S_SRC_MAC_SEL) +#define G_SRC_MAC_SEL(x) (((x) >> S_SRC_MAC_SEL) & M_SRC_MAC_SEL) + +#define S_L2T_IDX 4 +#define M_L2T_IDX 0x7FF +#define V_L2T_IDX(x) ((x) << S_L2T_IDX) +#define G_L2T_IDX(x) (((x) >> S_L2T_IDX) & M_L2T_IDX) + +#define S_TX_CHANNEL 15 +#define V_TX_CHANNEL(x) ((x) << S_TX_CHANNEL) +#define F_TX_CHANNEL V_TX_CHANNEL(1U) + +#define S_TCAM_BYPASS 16 +#define V_TCAM_BYPASS(x) ((x) << S_TCAM_BYPASS) +#define F_TCAM_BYPASS V_TCAM_BYPASS(1U) + +#define S_NAGLE 17 +#define V_NAGLE(x) ((x) << S_NAGLE) +#define F_NAGLE V_NAGLE(1U) + +#define S_WND_SCALE 18 +#define M_WND_SCALE 0xF +#define V_WND_SCALE(x) ((x) << S_WND_SCALE) +#define G_WND_SCALE(x) (((x) >> S_WND_SCALE) & M_WND_SCALE) + +#define S_KEEP_ALIVE 22 +#define V_KEEP_ALIVE(x) ((x) << S_KEEP_ALIVE) +#define F_KEEP_ALIVE V_KEEP_ALIVE(1U) + +#define S_MAX_RETRANS 23 +#define M_MAX_RETRANS 0xF +#define V_MAX_RETRANS(x) ((x) << S_MAX_RETRANS) +#define G_MAX_RETRANS(x) (((x) >> S_MAX_RETRANS) & M_MAX_RETRANS) + +#define S_MAX_RETRANS_OVERRIDE 27 +#define V_MAX_RETRANS_OVERRIDE(x) ((x) << S_MAX_RETRANS_OVERRIDE) +#define F_MAX_RETRANS_OVERRIDE V_MAX_RETRANS_OVERRIDE(1U) + +#define S_MSS_IDX 28 +#define M_MSS_IDX 0xF +#define V_MSS_IDX(x) ((x) << S_MSS_IDX) +#define G_MSS_IDX(x) (((x) >> S_MSS_IDX) & M_MSS_IDX) + +/* option 1 fields */ +#define S_RSS_ENABLE 0 +#define V_RSS_ENABLE(x) ((x) << S_RSS_ENABLE) +#define F_RSS_ENABLE V_RSS_ENABLE(1U) + +#define S_RSS_MASK_LEN 1 +#define M_RSS_MASK_LEN 0x7 +#define V_RSS_MASK_LEN(x) ((x) << S_RSS_MASK_LEN) +#define G_RSS_MASK_LEN(x) (((x) >> S_RSS_MASK_LEN) & M_RSS_MASK_LEN) + +#define S_CPU_IDX 4 +#define M_CPU_IDX 0x3F +#define V_CPU_IDX(x) ((x) << S_CPU_IDX) +#define G_CPU_IDX(x) (((x) >> S_CPU_IDX) & M_CPU_IDX) + +#define S_MAC_MATCH_VALID 18 +#define V_MAC_MATCH_VALID(x) ((x) << S_MAC_MATCH_VALID) +#define F_MAC_MATCH_VALID V_MAC_MATCH_VALID(1U) + +#define S_CONN_POLICY 19 +#define M_CONN_POLICY 0x3 +#define V_CONN_POLICY(x) ((x) << S_CONN_POLICY) +#define G_CONN_POLICY(x) (((x) >> S_CONN_POLICY) & M_CONN_POLICY) + +#define S_SYN_DEFENSE 21 +#define V_SYN_DEFENSE(x) ((x) << S_SYN_DEFENSE) +#define F_SYN_DEFENSE V_SYN_DEFENSE(1U) + +#define S_VLAN_PRI 22 +#define M_VLAN_PRI 0x3 +#define V_VLAN_PRI(x) ((x) << S_VLAN_PRI) +#define G_VLAN_PRI(x) (((x) >> S_VLAN_PRI) & M_VLAN_PRI) + +#define S_VLAN_PRI_VALID 24 +#define V_VLAN_PRI_VALID(x) ((x) << S_VLAN_PRI_VALID) +#define F_VLAN_PRI_VALID V_VLAN_PRI_VALID(1U) + +#define S_PKT_TYPE 25 +#define M_PKT_TYPE 0x3 +#define V_PKT_TYPE(x) ((x) << S_PKT_TYPE) +#define G_PKT_TYPE(x) (((x) >> S_PKT_TYPE) & M_PKT_TYPE) + +#define S_MAC_MATCH 27 +#define M_MAC_MATCH 0x1F +#define V_MAC_MATCH(x) ((x) << S_MAC_MATCH) +#define G_MAC_MATCH(x) (((x) >> S_MAC_MATCH) & M_MAC_MATCH) + +/* option 2 fields */ +#define S_CPU_INDEX 0 +#define M_CPU_INDEX 0x7F +#define V_CPU_INDEX(x) ((x) << S_CPU_INDEX) +#define G_CPU_INDEX(x) (((x) >> S_CPU_INDEX) & M_CPU_INDEX) + +#define S_CPU_INDEX_VALID 7 +#define V_CPU_INDEX_VALID(x) ((x) << S_CPU_INDEX_VALID) +#define F_CPU_INDEX_VALID V_CPU_INDEX_VALID(1U) + +#define S_RX_COALESCE 8 +#define M_RX_COALESCE 0x3 +#define V_RX_COALESCE(x) ((x) << S_RX_COALESCE) +#define G_RX_COALESCE(x) (((x) >> S_RX_COALESCE) & M_RX_COALESCE) + +#define S_RX_COALESCE_VALID 10 +#define V_RX_COALESCE_VALID(x) ((x) << S_RX_COALESCE_VALID) +#define F_RX_COALESCE_VALID V_RX_COALESCE_VALID(1U) + +#define S_CONG_CONTROL_FLAVOR 11 +#define M_CONG_CONTROL_FLAVOR 0x3 +#define V_CONG_CONTROL_FLAVOR(x) ((x) << S_CONG_CONTROL_FLAVOR) +#define G_CONG_CONTROL_FLAVOR(x) (((x) >> S_CONG_CONTROL_FLAVOR) & M_CONG_CONTROL_FLAVOR) + +#define S_PACING_FLAVOR 13 +#define M_PACING_FLAVOR 0x3 +#define V_PACING_FLAVOR(x) ((x) << S_PACING_FLAVOR) +#define G_PACING_FLAVOR(x) (((x) >> S_PACING_FLAVOR) & M_PACING_FLAVOR) + +#define S_FLAVORS_VALID 15 +#define V_FLAVORS_VALID(x) ((x) << S_FLAVORS_VALID) +#define F_FLAVORS_VALID V_FLAVORS_VALID(1U) + +#define S_RX_FC_DISABLE 16 +#define V_RX_FC_DISABLE(x) ((x) << S_RX_FC_DISABLE) +#define F_RX_FC_DISABLE V_RX_FC_DISABLE(1U) + +#define S_RX_FC_VALID 17 +#define V_RX_FC_VALID(x) ((x) << S_RX_FC_VALID) +#define F_RX_FC_VALID V_RX_FC_VALID(1U) + +struct cpl_pass_open_req { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 opt0h; + __be32 opt0l; + __be32 peer_netmask; + __be32 opt1; +}; + +struct cpl_pass_open_rpl { + RSS_HDR union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __u8 resvd[7]; + __u8 status; +}; + +struct cpl_pass_establish { + RSS_HDR union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 tos_tid; + __be16 l2t_idx; + __be16 tcp_opt; + __be32 snd_isn; + __be32 rcv_isn; +}; + +/* cpl_pass_establish.tos_tid fields */ +#define S_PASS_OPEN_TID 0 +#define M_PASS_OPEN_TID 0xFFFFFF +#define V_PASS_OPEN_TID(x) ((x) << S_PASS_OPEN_TID) +#define G_PASS_OPEN_TID(x) (((x) >> S_PASS_OPEN_TID) & M_PASS_OPEN_TID) + +#define S_PASS_OPEN_TOS 24 +#define M_PASS_OPEN_TOS 0xFF +#define V_PASS_OPEN_TOS(x) ((x) << S_PASS_OPEN_TOS) +#define G_PASS_OPEN_TOS(x) (((x) >> S_PASS_OPEN_TOS) & M_PASS_OPEN_TOS) + +/* cpl_pass_establish.l2t_idx fields */ +#define S_L2T_IDX16 5 +#define M_L2T_IDX16 0x7FF +#define V_L2T_IDX16(x) ((x) << S_L2T_IDX16) +#define G_L2T_IDX16(x) (((x) >> S_L2T_IDX16) & M_L2T_IDX16) + +/* cpl_pass_establish.tcp_opt fields (also applies act_open_establish) */ +#define G_TCPOPT_WSCALE_OK(x) (((x) >> 5) & 1) +#define G_TCPOPT_SACK(x) (((x) >> 6) & 1) +#define G_TCPOPT_TSTAMP(x) (((x) >> 7) & 1) +#define G_TCPOPT_SND_WSCALE(x) (((x) >> 8) & 0xf) +#define G_TCPOPT_MSS(x) (((x) >> 12) & 0xf) + +struct cpl_pass_accept_req { + RSS_HDR union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 tos_tid; + struct tcp_options tcp_options; + __u8 dst_mac[6]; + __be16 vlan_tag; + __u8 src_mac[6]; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:3; + __u8 addr_idx:3; + __u8 port_idx:1; + __u8 exact_match:1; +#else + __u8 exact_match:1; + __u8 port_idx:1; + __u8 addr_idx:3; + __u8:3; +#endif + __u8 rsvd; + __be32 rcv_isn; + __be32 rsvd2; +}; + +struct cpl_pass_accept_rpl { + WR_HDR; + union opcode_tid ot; + __be32 opt2; + __be32 rsvd; + __be32 peer_ip; + __be32 opt0h; + __be32 opt0l_status; +}; + +struct cpl_act_open_req { + WR_HDR; + union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 opt0h; + __be32 opt0l; + __be32 params; + __be32 opt2; +}; + +/* cpl_act_open_req.params fields */ +#define S_AOPEN_VLAN_PRI 9 +#define M_AOPEN_VLAN_PRI 0x3 +#define V_AOPEN_VLAN_PRI(x) ((x) << S_AOPEN_VLAN_PRI) +#define G_AOPEN_VLAN_PRI(x) (((x) >> S_AOPEN_VLAN_PRI) & M_AOPEN_VLAN_PRI) + +#define S_AOPEN_VLAN_PRI_VALID 11 +#define V_AOPEN_VLAN_PRI_VALID(x) ((x) << S_AOPEN_VLAN_PRI_VALID) +#define F_AOPEN_VLAN_PRI_VALID V_AOPEN_VLAN_PRI_VALID(1U) + +#define S_AOPEN_PKT_TYPE 12 +#define M_AOPEN_PKT_TYPE 0x3 +#define V_AOPEN_PKT_TYPE(x) ((x) << S_AOPEN_PKT_TYPE) +#define G_AOPEN_PKT_TYPE(x) (((x) >> S_AOPEN_PKT_TYPE) & M_AOPEN_PKT_TYPE) + +#define S_AOPEN_MAC_MATCH 14 +#define M_AOPEN_MAC_MATCH 0x1F +#define V_AOPEN_MAC_MATCH(x) ((x) << S_AOPEN_MAC_MATCH) +#define G_AOPEN_MAC_MATCH(x) (((x) >> S_AOPEN_MAC_MATCH) & M_AOPEN_MAC_MATCH) + +#define S_AOPEN_MAC_MATCH_VALID 19 +#define V_AOPEN_MAC_MATCH_VALID(x) ((x) << S_AOPEN_MAC_MATCH_VALID) +#define F_AOPEN_MAC_MATCH_VALID V_AOPEN_MAC_MATCH_VALID(1U) + +#define S_AOPEN_IFF_VLAN 20 +#define M_AOPEN_IFF_VLAN 0xFFF +#define V_AOPEN_IFF_VLAN(x) ((x) << S_AOPEN_IFF_VLAN) +#define G_AOPEN_IFF_VLAN(x) (((x) >> S_AOPEN_IFF_VLAN) & M_AOPEN_IFF_VLAN) + +struct cpl_act_open_rpl { + RSS_HDR union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 atid; + __u8 rsvd[3]; + __u8 status; +}; + +struct cpl_act_establish { + RSS_HDR union opcode_tid ot; + __be16 local_port; + __be16 peer_port; + __be32 local_ip; + __be32 peer_ip; + __be32 tos_tid; + __be16 l2t_idx; + __be16 tcp_opt; + __be32 snd_isn; + __be32 rcv_isn; +}; + +struct cpl_get_tcb { + WR_HDR; + union opcode_tid ot; + __be16 cpuno; + __be16 rsvd; +}; + +struct cpl_get_tcb_rpl { + RSS_HDR union opcode_tid ot; + __u8 rsvd; + __u8 status; + __be16 len; +}; + +struct cpl_set_tcb { + WR_HDR; + union opcode_tid ot; + __u8 reply; + __u8 cpu_idx; + __be16 len; +}; + +/* cpl_set_tcb.reply fields */ +#define S_NO_REPLY 7 +#define V_NO_REPLY(x) ((x) << S_NO_REPLY) +#define F_NO_REPLY V_NO_REPLY(1U) + +struct cpl_set_tcb_field { + WR_HDR; + union opcode_tid ot; + __u8 reply; + __u8 cpu_idx; + __be16 word; + __be64 mask; + __be64 val; +}; + +struct cpl_set_tcb_rpl { + RSS_HDR union opcode_tid ot; + __u8 rsvd[3]; + __u8 status; +}; + +struct cpl_pcmd { + WR_HDR; + union opcode_tid ot; + __u8 rsvd[3]; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 src:1; + __u8 bundle:1; + __u8 channel:1; + __u8:5; +#else + __u8:5; + __u8 channel:1; + __u8 bundle:1; + __u8 src:1; +#endif + __be32 pcmd_parm[2]; +}; + +struct cpl_pcmd_reply { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd; + __be16 len; +}; + +struct cpl_close_con_req { + WR_HDR; + union opcode_tid ot; + __be32 rsvd; +}; + +struct cpl_close_con_rpl { + RSS_HDR union opcode_tid ot; + __u8 rsvd[3]; + __u8 status; + __be32 snd_nxt; + __be32 rcv_nxt; +}; + +struct cpl_close_listserv_req { + WR_HDR; + union opcode_tid ot; + __u8 rsvd0; + __u8 cpu_idx; + __be16 rsvd1; +}; + +struct cpl_close_listserv_rpl { + RSS_HDR union opcode_tid ot; + __u8 rsvd[3]; + __u8 status; +}; + +struct cpl_abort_req_rss { + RSS_HDR union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 status; + __u8 rsvd2[6]; +}; + +struct cpl_abort_req { + WR_HDR; + union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 cmd; + __u8 rsvd2[6]; +}; + +struct cpl_abort_rpl_rss { + RSS_HDR union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 status; + __u8 rsvd2[6]; +}; + +struct cpl_abort_rpl { + WR_HDR; + union opcode_tid ot; + __be32 rsvd0; + __u8 rsvd1; + __u8 cmd; + __u8 rsvd2[6]; +}; + +struct cpl_peer_close { + RSS_HDR union opcode_tid ot; + __be32 rcv_nxt; +}; + +struct tx_data_wr { + __be32 wr_hi; + __be32 wr_lo; + __be32 len; + __be32 flags; + __be32 sndseq; + __be32 param; +}; + +/* tx_data_wr.param fields */ +#define S_TX_PORT 0 +#define M_TX_PORT 0x7 +#define V_TX_PORT(x) ((x) << S_TX_PORT) +#define G_TX_PORT(x) (((x) >> S_TX_PORT) & M_TX_PORT) + +#define S_TX_MSS 4 +#define M_TX_MSS 0xF +#define V_TX_MSS(x) ((x) << S_TX_MSS) +#define G_TX_MSS(x) (((x) >> S_TX_MSS) & M_TX_MSS) + +#define S_TX_QOS 8 +#define M_TX_QOS 0xFF +#define V_TX_QOS(x) ((x) << S_TX_QOS) +#define G_TX_QOS(x) (((x) >> S_TX_QOS) & M_TX_QOS) + +#define S_TX_SNDBUF 16 +#define M_TX_SNDBUF 0xFFFF +#define V_TX_SNDBUF(x) ((x) << S_TX_SNDBUF) +#define G_TX_SNDBUF(x) (((x) >> S_TX_SNDBUF) & M_TX_SNDBUF) + +struct cpl_tx_data { + union opcode_tid ot; + __be32 len; + __be32 rsvd; + __be16 urg; + __be16 flags; +}; + +/* cpl_tx_data.flags fields */ +#define S_TX_ULP_SUBMODE 6 +#define M_TX_ULP_SUBMODE 0xF +#define V_TX_ULP_SUBMODE(x) ((x) << S_TX_ULP_SUBMODE) +#define G_TX_ULP_SUBMODE(x) (((x) >> S_TX_ULP_SUBMODE) & M_TX_ULP_SUBMODE) + +#define S_TX_ULP_MODE 10 +#define M_TX_ULP_MODE 0xF +#define V_TX_ULP_MODE(x) ((x) << S_TX_ULP_MODE) +#define G_TX_ULP_MODE(x) (((x) >> S_TX_ULP_MODE) & M_TX_ULP_MODE) + +#define S_TX_SHOVE 14 +#define V_TX_SHOVE(x) ((x) << S_TX_SHOVE) +#define F_TX_SHOVE V_TX_SHOVE(1U) + +#define S_TX_MORE 15 +#define V_TX_MORE(x) ((x) << S_TX_MORE) +#define F_TX_MORE V_TX_MORE(1U) + +/* additional tx_data_wr.flags fields */ +#define S_TX_CPU_IDX 0 +#define M_TX_CPU_IDX 0x3F +#define V_TX_CPU_IDX(x) ((x) << S_TX_CPU_IDX) +#define G_TX_CPU_IDX(x) (((x) >> S_TX_CPU_IDX) & M_TX_CPU_IDX) + +#define S_TX_URG 16 +#define V_TX_URG(x) ((x) << S_TX_URG) +#define F_TX_URG V_TX_URG(1U) + +#define S_TX_CLOSE 17 +#define V_TX_CLOSE(x) ((x) << S_TX_CLOSE) +#define F_TX_CLOSE V_TX_CLOSE(1U) + +#define S_TX_INIT 18 +#define V_TX_INIT(x) ((x) << S_TX_INIT) +#define F_TX_INIT V_TX_INIT(1U) + +#define S_TX_IMM_ACK 19 +#define V_TX_IMM_ACK(x) ((x) << S_TX_IMM_ACK) +#define F_TX_IMM_ACK V_TX_IMM_ACK(1U) + +#define S_TX_IMM_DMA 20 +#define V_TX_IMM_DMA(x) ((x) << S_TX_IMM_DMA) +#define F_TX_IMM_DMA V_TX_IMM_DMA(1U) + +struct cpl_tx_data_ack { + RSS_HDR union opcode_tid ot; + __be32 ack_seq; +}; + +struct cpl_wr_ack { + RSS_HDR union opcode_tid ot; + __be16 credits; + __be16 rsvd; + __be32 snd_nxt; + __be32 snd_una; +}; + +struct cpl_rdma_ec_status { + RSS_HDR union opcode_tid ot; + __u8 rsvd[3]; + __u8 status; +}; + +struct mngt_pktsched_wr { + __be32 wr_hi; + __be32 wr_lo; + __u8 mngt_opcode; + __u8 rsvd[7]; + __u8 sched; + __u8 idx; + __u8 min; + __u8 max; + __u8 binding; + __u8 rsvd1[3]; +}; + +struct cpl_iscsi_hdr { + RSS_HDR union opcode_tid ot; + __be16 pdu_len_ddp; + __be16 len; + __be32 seq; + __be16 urg; + __u8 rsvd; + __u8 status; +}; + +/* cpl_iscsi_hdr.pdu_len_ddp fields */ +#define S_ISCSI_PDU_LEN 0 +#define M_ISCSI_PDU_LEN 0x7FFF +#define V_ISCSI_PDU_LEN(x) ((x) << S_ISCSI_PDU_LEN) +#define G_ISCSI_PDU_LEN(x) (((x) >> S_ISCSI_PDU_LEN) & M_ISCSI_PDU_LEN) + +#define S_ISCSI_DDP 15 +#define V_ISCSI_DDP(x) ((x) << S_ISCSI_DDP) +#define F_ISCSI_DDP V_ISCSI_DDP(1U) + +struct cpl_rx_data { + RSS_HDR union opcode_tid ot; + __be16 rsvd; + __be16 len; + __be32 seq; + __be16 urg; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 dack_mode:2; + __u8 psh:1; + __u8 heartbeat:1; + __u8:4; +#else + __u8:4; + __u8 heartbeat:1; + __u8 psh:1; + __u8 dack_mode:2; +#endif + __u8 status; +}; + +struct cpl_rx_data_ack { + WR_HDR; + union opcode_tid ot; + __be32 credit_dack; +}; + +/* cpl_rx_data_ack.ack_seq fields */ +#define S_RX_CREDITS 0 +#define M_RX_CREDITS 0x7FFFFFF +#define V_RX_CREDITS(x) ((x) << S_RX_CREDITS) +#define G_RX_CREDITS(x) (((x) >> S_RX_CREDITS) & M_RX_CREDITS) + +#define S_RX_MODULATE 27 +#define V_RX_MODULATE(x) ((x) << S_RX_MODULATE) +#define F_RX_MODULATE V_RX_MODULATE(1U) + +#define S_RX_FORCE_ACK 28 +#define V_RX_FORCE_ACK(x) ((x) << S_RX_FORCE_ACK) +#define F_RX_FORCE_ACK V_RX_FORCE_ACK(1U) + +#define S_RX_DACK_MODE 29 +#define M_RX_DACK_MODE 0x3 +#define V_RX_DACK_MODE(x) ((x) << S_RX_DACK_MODE) +#define G_RX_DACK_MODE(x) (((x) >> S_RX_DACK_MODE) & M_RX_DACK_MODE) + +#define S_RX_DACK_CHANGE 31 +#define V_RX_DACK_CHANGE(x) ((x) << S_RX_DACK_CHANGE) +#define F_RX_DACK_CHANGE V_RX_DACK_CHANGE(1U) + +struct cpl_rx_urg_notify { + RSS_HDR union opcode_tid ot; + __be32 seq; +}; + +struct cpl_rx_ddp_complete { + RSS_HDR union opcode_tid ot; + __be32 ddp_report; +}; + +struct cpl_rx_data_ddp { + RSS_HDR union opcode_tid ot; + __be16 urg; + __be16 len; + __be32 seq; + union { + __be32 nxt_seq; + __be32 ddp_report; + }; + __be32 ulp_crc; + __be32 ddpvld_status; +}; + +/* cpl_rx_data_ddp.ddpvld_status fields */ +#define S_DDP_STATUS 0 +#define M_DDP_STATUS 0xFF +#define V_DDP_STATUS(x) ((x) << S_DDP_STATUS) +#define G_DDP_STATUS(x) (((x) >> S_DDP_STATUS) & M_DDP_STATUS) + +#define S_DDP_VALID 15 +#define M_DDP_VALID 0x1FFFF +#define V_DDP_VALID(x) ((x) << S_DDP_VALID) +#define G_DDP_VALID(x) (((x) >> S_DDP_VALID) & M_DDP_VALID) + +#define S_DDP_PPOD_MISMATCH 15 +#define V_DDP_PPOD_MISMATCH(x) ((x) << S_DDP_PPOD_MISMATCH) +#define F_DDP_PPOD_MISMATCH V_DDP_PPOD_MISMATCH(1U) + +#define S_DDP_PDU 16 +#define V_DDP_PDU(x) ((x) << S_DDP_PDU) +#define F_DDP_PDU V_DDP_PDU(1U) + +#define S_DDP_LLIMIT_ERR 17 +#define V_DDP_LLIMIT_ERR(x) ((x) << S_DDP_LLIMIT_ERR) +#define F_DDP_LLIMIT_ERR V_DDP_LLIMIT_ERR(1U) + +#define S_DDP_PPOD_PARITY_ERR 18 +#define V_DDP_PPOD_PARITY_ERR(x) ((x) << S_DDP_PPOD_PARITY_ERR) +#define F_DDP_PPOD_PARITY_ERR V_DDP_PPOD_PARITY_ERR(1U) + +#define S_DDP_PADDING_ERR 19 +#define V_DDP_PADDING_ERR(x) ((x) << S_DDP_PADDING_ERR) +#define F_DDP_PADDING_ERR V_DDP_PADDING_ERR(1U) + +#define S_DDP_HDRCRC_ERR 20 +#define V_DDP_HDRCRC_ERR(x) ((x) << S_DDP_HDRCRC_ERR) +#define F_DDP_HDRCRC_ERR V_DDP_HDRCRC_ERR(1U) + +#define S_DDP_DATACRC_ERR 21 +#define V_DDP_DATACRC_ERR(x) ((x) << S_DDP_DATACRC_ERR) +#define F_DDP_DATACRC_ERR V_DDP_DATACRC_ERR(1U) + +#define S_DDP_INVALID_TAG 22 +#define V_DDP_INVALID_TAG(x) ((x) << S_DDP_INVALID_TAG) +#define F_DDP_INVALID_TAG V_DDP_INVALID_TAG(1U) + +#define S_DDP_ULIMIT_ERR 23 +#define V_DDP_ULIMIT_ERR(x) ((x) << S_DDP_ULIMIT_ERR) +#define F_DDP_ULIMIT_ERR V_DDP_ULIMIT_ERR(1U) + +#define S_DDP_OFFSET_ERR 24 +#define V_DDP_OFFSET_ERR(x) ((x) << S_DDP_OFFSET_ERR) +#define F_DDP_OFFSET_ERR V_DDP_OFFSET_ERR(1U) + +#define S_DDP_COLOR_ERR 25 +#define V_DDP_COLOR_ERR(x) ((x) << S_DDP_COLOR_ERR) +#define F_DDP_COLOR_ERR V_DDP_COLOR_ERR(1U) + +#define S_DDP_TID_MISMATCH 26 +#define V_DDP_TID_MISMATCH(x) ((x) << S_DDP_TID_MISMATCH) +#define F_DDP_TID_MISMATCH V_DDP_TID_MISMATCH(1U) + +#define S_DDP_INVALID_PPOD 27 +#define V_DDP_INVALID_PPOD(x) ((x) << S_DDP_INVALID_PPOD) +#define F_DDP_INVALID_PPOD V_DDP_INVALID_PPOD(1U) + +#define S_DDP_ULP_MODE 28 +#define M_DDP_ULP_MODE 0xF +#define V_DDP_ULP_MODE(x) ((x) << S_DDP_ULP_MODE) +#define G_DDP_ULP_MODE(x) (((x) >> S_DDP_ULP_MODE) & M_DDP_ULP_MODE) + +/* cpl_rx_data_ddp.ddp_report fields */ +#define S_DDP_OFFSET 0 +#define M_DDP_OFFSET 0x3FFFFF +#define V_DDP_OFFSET(x) ((x) << S_DDP_OFFSET) +#define G_DDP_OFFSET(x) (((x) >> S_DDP_OFFSET) & M_DDP_OFFSET) + +#define S_DDP_URG 24 +#define V_DDP_URG(x) ((x) << S_DDP_URG) +#define F_DDP_URG V_DDP_URG(1U) + +#define S_DDP_PSH 25 +#define V_DDP_PSH(x) ((x) << S_DDP_PSH) +#define F_DDP_PSH V_DDP_PSH(1U) + +#define S_DDP_BUF_COMPLETE 26 +#define V_DDP_BUF_COMPLETE(x) ((x) << S_DDP_BUF_COMPLETE) +#define F_DDP_BUF_COMPLETE V_DDP_BUF_COMPLETE(1U) + +#define S_DDP_BUF_TIMED_OUT 27 +#define V_DDP_BUF_TIMED_OUT(x) ((x) << S_DDP_BUF_TIMED_OUT) +#define F_DDP_BUF_TIMED_OUT V_DDP_BUF_TIMED_OUT(1U) + +#define S_DDP_BUF_IDX 28 +#define V_DDP_BUF_IDX(x) ((x) << S_DDP_BUF_IDX) +#define F_DDP_BUF_IDX V_DDP_BUF_IDX(1U) + +struct cpl_tx_pkt { + WR_HDR; + __be32 cntrl; + __be32 len; +}; + +struct cpl_tx_pkt_lso { + WR_HDR; + __be32 cntrl; + __be32 len; + + __be32 rsvd; + __be32 lso_info; +}; + +/* cpl_tx_pkt*.cntrl fields */ +#define S_TXPKT_VLAN 0 +#define M_TXPKT_VLAN 0xFFFF +#define V_TXPKT_VLAN(x) ((x) << S_TXPKT_VLAN) +#define G_TXPKT_VLAN(x) (((x) >> S_TXPKT_VLAN) & M_TXPKT_VLAN) + +#define S_TXPKT_INTF 16 +#define M_TXPKT_INTF 0xF +#define V_TXPKT_INTF(x) ((x) << S_TXPKT_INTF) +#define G_TXPKT_INTF(x) (((x) >> S_TXPKT_INTF) & M_TXPKT_INTF) + +#define S_TXPKT_IPCSUM_DIS 20 +#define V_TXPKT_IPCSUM_DIS(x) ((x) << S_TXPKT_IPCSUM_DIS) +#define F_TXPKT_IPCSUM_DIS V_TXPKT_IPCSUM_DIS(1U) + +#define S_TXPKT_L4CSUM_DIS 21 +#define V_TXPKT_L4CSUM_DIS(x) ((x) << S_TXPKT_L4CSUM_DIS) +#define F_TXPKT_L4CSUM_DIS V_TXPKT_L4CSUM_DIS(1U) + +#define S_TXPKT_VLAN_VLD 22 +#define V_TXPKT_VLAN_VLD(x) ((x) << S_TXPKT_VLAN_VLD) +#define F_TXPKT_VLAN_VLD V_TXPKT_VLAN_VLD(1U) + +#define S_TXPKT_LOOPBACK 23 +#define V_TXPKT_LOOPBACK(x) ((x) << S_TXPKT_LOOPBACK) +#define F_TXPKT_LOOPBACK V_TXPKT_LOOPBACK(1U) + +#define S_TXPKT_OPCODE 24 +#define M_TXPKT_OPCODE 0xFF +#define V_TXPKT_OPCODE(x) ((x) << S_TXPKT_OPCODE) +#define G_TXPKT_OPCODE(x) (((x) >> S_TXPKT_OPCODE) & M_TXPKT_OPCODE) + +/* cpl_tx_pkt_lso.lso_info fields */ +#define S_LSO_MSS 0 +#define M_LSO_MSS 0x3FFF +#define V_LSO_MSS(x) ((x) << S_LSO_MSS) +#define G_LSO_MSS(x) (((x) >> S_LSO_MSS) & M_LSO_MSS) + +#define S_LSO_ETH_TYPE 14 +#define M_LSO_ETH_TYPE 0x3 +#define V_LSO_ETH_TYPE(x) ((x) << S_LSO_ETH_TYPE) +#define G_LSO_ETH_TYPE(x) (((x) >> S_LSO_ETH_TYPE) & M_LSO_ETH_TYPE) + +#define S_LSO_TCPHDR_WORDS 16 +#define M_LSO_TCPHDR_WORDS 0xF +#define V_LSO_TCPHDR_WORDS(x) ((x) << S_LSO_TCPHDR_WORDS) +#define G_LSO_TCPHDR_WORDS(x) (((x) >> S_LSO_TCPHDR_WORDS) & M_LSO_TCPHDR_WORDS) + +#define S_LSO_IPHDR_WORDS 20 +#define M_LSO_IPHDR_WORDS 0xF +#define V_LSO_IPHDR_WORDS(x) ((x) << S_LSO_IPHDR_WORDS) +#define G_LSO_IPHDR_WORDS(x) (((x) >> S_LSO_IPHDR_WORDS) & M_LSO_IPHDR_WORDS) + +#define S_LSO_IPV6 24 +#define V_LSO_IPV6(x) ((x) << S_LSO_IPV6) +#define F_LSO_IPV6 V_LSO_IPV6(1U) + +struct cpl_trace_pkt { +#ifdef CHELSIO_FW + __u8 rss_opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 err:1; + __u8:7; +#else + __u8:7; + __u8 err:1; +#endif + __u8 rsvd0; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 qid:4; + __u8:4; +#else + __u8:4; + __u8 qid:4; +#endif + __be32 tstamp; +#endif /* CHELSIO_FW */ + + __u8 opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 iff:4; + __u8:4; +#else + __u8:4; + __u8 iff:4; +#endif + __u8 rsvd[4]; + __be16 len; +}; + +struct cpl_rx_pkt { + RSS_HDR __u8 opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 iff:4; + __u8 csum_valid:1; + __u8 ipmi_pkt:1; + __u8 vlan_valid:1; + __u8 fragment:1; +#else + __u8 fragment:1; + __u8 vlan_valid:1; + __u8 ipmi_pkt:1; + __u8 csum_valid:1; + __u8 iff:4; +#endif + __be16 csum; + __be16 vlan; + __be16 len; +}; + +struct cpl_l2t_write_req { + WR_HDR; + union opcode_tid ot; + __be32 params; + __u8 rsvd[2]; + __u8 dst_mac[6]; +}; + +/* cpl_l2t_write_req.params fields */ +#define S_L2T_W_IDX 0 +#define M_L2T_W_IDX 0x7FF +#define V_L2T_W_IDX(x) ((x) << S_L2T_W_IDX) +#define G_L2T_W_IDX(x) (((x) >> S_L2T_W_IDX) & M_L2T_W_IDX) + +#define S_L2T_W_VLAN 11 +#define M_L2T_W_VLAN 0xFFF +#define V_L2T_W_VLAN(x) ((x) << S_L2T_W_VLAN) +#define G_L2T_W_VLAN(x) (((x) >> S_L2T_W_VLAN) & M_L2T_W_VLAN) + +#define S_L2T_W_IFF 23 +#define M_L2T_W_IFF 0xF +#define V_L2T_W_IFF(x) ((x) << S_L2T_W_IFF) +#define G_L2T_W_IFF(x) (((x) >> S_L2T_W_IFF) & M_L2T_W_IFF) + +#define S_L2T_W_PRIO 27 +#define M_L2T_W_PRIO 0x7 +#define V_L2T_W_PRIO(x) ((x) << S_L2T_W_PRIO) +#define G_L2T_W_PRIO(x) (((x) >> S_L2T_W_PRIO) & M_L2T_W_PRIO) + +struct cpl_l2t_write_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd[3]; +}; + +struct cpl_l2t_read_req { + WR_HDR; + union opcode_tid ot; + __be16 rsvd; + __be16 l2t_idx; +}; + +struct cpl_l2t_read_rpl { + RSS_HDR union opcode_tid ot; + __be32 params; + __u8 rsvd[2]; + __u8 dst_mac[6]; +}; + +/* cpl_l2t_read_rpl.params fields */ +#define S_L2T_R_PRIO 0 +#define M_L2T_R_PRIO 0x7 +#define V_L2T_R_PRIO(x) ((x) << S_L2T_R_PRIO) +#define G_L2T_R_PRIO(x) (((x) >> S_L2T_R_PRIO) & M_L2T_R_PRIO) + +#define S_L2T_R_VLAN 8 +#define M_L2T_R_VLAN 0xFFF +#define V_L2T_R_VLAN(x) ((x) << S_L2T_R_VLAN) +#define G_L2T_R_VLAN(x) (((x) >> S_L2T_R_VLAN) & M_L2T_R_VLAN) + +#define S_L2T_R_IFF 20 +#define M_L2T_R_IFF 0xF +#define V_L2T_R_IFF(x) ((x) << S_L2T_R_IFF) +#define G_L2T_R_IFF(x) (((x) >> S_L2T_R_IFF) & M_L2T_R_IFF) + +#define S_L2T_STATUS 24 +#define M_L2T_STATUS 0xFF +#define V_L2T_STATUS(x) ((x) << S_L2T_STATUS) +#define G_L2T_STATUS(x) (((x) >> S_L2T_STATUS) & M_L2T_STATUS) + +struct cpl_smt_write_req { + WR_HDR; + union opcode_tid ot; + __u8 rsvd0; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 mtu_idx:4; + __u8 iff:4; +#else + __u8 iff:4; + __u8 mtu_idx:4; +#endif + __be16 rsvd2; + __be16 rsvd3; + __u8 src_mac1[6]; + __be16 rsvd4; + __u8 src_mac0[6]; +}; + +struct cpl_smt_write_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd[3]; +}; + +struct cpl_smt_read_req { + WR_HDR; + union opcode_tid ot; + __u8 rsvd0; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:4; + __u8 iff:4; +#else + __u8 iff:4; + __u8:4; +#endif + __be16 rsvd2; +}; + +struct cpl_smt_read_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 mtu_idx:4; + __u8:4; +#else + __u8:4; + __u8 mtu_idx:4; +#endif + __be16 rsvd2; + __be16 rsvd3; + __u8 src_mac1[6]; + __be16 rsvd4; + __u8 src_mac0[6]; +}; + +struct cpl_rte_delete_req { + WR_HDR; + union opcode_tid ot; + __be32 params; +}; + +/* { cpl_rte_delete_req, cpl_rte_read_req }.params fields */ +#define S_RTE_REQ_LUT_IX 8 +#define M_RTE_REQ_LUT_IX 0x7FF +#define V_RTE_REQ_LUT_IX(x) ((x) << S_RTE_REQ_LUT_IX) +#define G_RTE_REQ_LUT_IX(x) (((x) >> S_RTE_REQ_LUT_IX) & M_RTE_REQ_LUT_IX) + +#define S_RTE_REQ_LUT_BASE 19 +#define M_RTE_REQ_LUT_BASE 0x7FF +#define V_RTE_REQ_LUT_BASE(x) ((x) << S_RTE_REQ_LUT_BASE) +#define G_RTE_REQ_LUT_BASE(x) (((x) >> S_RTE_REQ_LUT_BASE) & M_RTE_REQ_LUT_BASE) + +#define S_RTE_READ_REQ_SELECT 31 +#define V_RTE_READ_REQ_SELECT(x) ((x) << S_RTE_READ_REQ_SELECT) +#define F_RTE_READ_REQ_SELECT V_RTE_READ_REQ_SELECT(1U) + +struct cpl_rte_delete_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd[3]; +}; + +struct cpl_rte_write_req { + WR_HDR; + union opcode_tid ot; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:6; + __u8 write_tcam:1; + __u8 write_l2t_lut:1; +#else + __u8 write_l2t_lut:1; + __u8 write_tcam:1; + __u8:6; +#endif + __u8 rsvd[3]; + __be32 lut_params; + __be16 rsvd2; + __be16 l2t_idx; + __be32 netmask; + __be32 faddr; +}; + +/* cpl_rte_write_req.lut_params fields */ +#define S_RTE_WRITE_REQ_LUT_IX 10 +#define M_RTE_WRITE_REQ_LUT_IX 0x7FF +#define V_RTE_WRITE_REQ_LUT_IX(x) ((x) << S_RTE_WRITE_REQ_LUT_IX) +#define G_RTE_WRITE_REQ_LUT_IX(x) (((x) >> S_RTE_WRITE_REQ_LUT_IX) & M_RTE_WRITE_REQ_LUT_IX) + +#define S_RTE_WRITE_REQ_LUT_BASE 21 +#define M_RTE_WRITE_REQ_LUT_BASE 0x7FF +#define V_RTE_WRITE_REQ_LUT_BASE(x) ((x) << S_RTE_WRITE_REQ_LUT_BASE) +#define G_RTE_WRITE_REQ_LUT_BASE(x) (((x) >> S_RTE_WRITE_REQ_LUT_BASE) & M_RTE_WRITE_REQ_LUT_BASE) + +struct cpl_rte_write_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd[3]; +}; + +struct cpl_rte_read_req { + WR_HDR; + union opcode_tid ot; + __be32 params; +}; + +struct cpl_rte_read_rpl { + RSS_HDR union opcode_tid ot; + __u8 status; + __u8 rsvd0; + __be16 l2t_idx; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8:7; + __u8 select:1; +#else + __u8 select:1; + __u8:7; +#endif + __u8 rsvd2[3]; + __be32 addr; +}; + +struct cpl_tid_release { + WR_HDR; + union opcode_tid ot; + __be32 rsvd; +}; + +struct cpl_barrier { + WR_HDR; + __u8 opcode; + __u8 rsvd[7]; +}; + +struct cpl_rdma_read_req { + __u8 opcode; + __u8 rsvd[15]; +}; + +struct cpl_rdma_terminate { +#ifdef CHELSIO_FW + __u8 opcode; + __u8 rsvd[2]; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 rspq:3; + __u8:5; +#else + __u8:5; + __u8 rspq:3; +#endif + __be32 tid_len; +#endif + __be32 msn; + __be32 mo; + __u8 data[0]; +}; + +/* cpl_rdma_terminate.tid_len fields */ +#define S_FLIT_CNT 0 +#define M_FLIT_CNT 0xFF +#define V_FLIT_CNT(x) ((x) << S_FLIT_CNT) +#define G_FLIT_CNT(x) (((x) >> S_FLIT_CNT) & M_FLIT_CNT) + +#define S_TERM_TID 8 +#define M_TERM_TID 0xFFFFF +#define V_TERM_TID(x) ((x) << S_TERM_TID) +#define G_TERM_TID(x) (((x) >> S_TERM_TID) & M_TERM_TID) +#endif /* T3_CPL_H */ diff -puN /dev/null drivers/net/cxgb3/t3_hw.c --- /dev/null +++ a/drivers/net/cxgb3/t3_hw.c @@ -0,0 +1,3354 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include "common.h" +#include "regs.h" +#include "sge_defs.h" +#include "firmware_exports.h" + + /** + * t3_wait_op_done_val - wait until an operation is completed + * @adapter: the adapter performing the operation + * @reg: the register to check for completion + * @mask: a single-bit field within @reg that indicates completion + * @polarity: the value of the field when the operation is completed + * @attempts: number of check iterations + * @delay: delay in usecs between iterations + * @valp: where to store the value of the register at completion time + * + * Wait until an operation is completed by checking a bit in a register + * up to @attempts times. If @valp is not NULL the value of the register + * at the time it indicated completion is stored there. Returns 0 if the + * operation completes and -EAGAIN otherwise. + */ + +int t3_wait_op_done_val(struct adapter *adapter, int reg, u32 mask, + int polarity, int attempts, int delay, u32 *valp) +{ + while (1) { + u32 val = t3_read_reg(adapter, reg); + + if (!!(val & mask) == polarity) { + if (valp) + *valp = val; + return 0; + } + if (--attempts == 0) + return -EAGAIN; + if (delay) + udelay(delay); + } +} + +/** + * t3_write_regs - write a bunch of registers + * @adapter: the adapter to program + * @p: an array of register address/register value pairs + * @n: the number of address/value pairs + * @offset: register address offset + * + * Takes an array of register address/register value pairs and writes each + * value to the corresponding register. Register addresses are adjusted + * by the supplied offset. + */ +void t3_write_regs(struct adapter *adapter, const struct addr_val_pair *p, + int n, unsigned int offset) +{ + while (n--) { + t3_write_reg(adapter, p->reg_addr + offset, p->val); + p++; + } +} + +/** + * t3_set_reg_field - set a register field to a value + * @adapter: the adapter to program + * @addr: the register address + * @mask: specifies the portion of the register to modify + * @val: the new value for the register field + * + * Sets a register field specified by the supplied mask to the + * given value. + */ +void t3_set_reg_field(struct adapter *adapter, unsigned int addr, u32 mask, + u32 val) +{ + u32 v = t3_read_reg(adapter, addr) & ~mask; + + t3_write_reg(adapter, addr, v | val); + t3_read_reg(adapter, addr); /* flush */ +} + +/** + * t3_read_indirect - read indirectly addressed registers + * @adap: the adapter + * @addr_reg: register holding the indirect address + * @data_reg: register holding the value of the indirect register + * @vals: where the read register values are stored + * @start_idx: index of first indirect register to read + * @nregs: how many indirect registers to read + * + * Reads registers that are accessed indirectly through an address/data + * register pair. + */ +void t3_read_indirect(struct adapter *adap, unsigned int addr_reg, + unsigned int data_reg, u32 *vals, unsigned int nregs, + unsigned int start_idx) +{ + while (nregs--) { + t3_write_reg(adap, addr_reg, start_idx); + *vals++ = t3_read_reg(adap, data_reg); + start_idx++; + } +} + +/** + * t3_mc7_bd_read - read from MC7 through backdoor accesses + * @mc7: identifies MC7 to read from + * @start: index of first 64-bit word to read + * @n: number of 64-bit words to read + * @buf: where to store the read result + * + * Read n 64-bit words from MC7 starting at word start, using backdoor + * accesses. + */ +int t3_mc7_bd_read(struct mc7 *mc7, unsigned int start, unsigned int n, + u64 *buf) +{ + static const int shift[] = { 0, 0, 16, 24 }; + static const int step[] = { 0, 32, 16, 8 }; + + unsigned int size64 = mc7->size / 8; /* # of 64-bit words */ + struct adapter *adap = mc7->adapter; + + if (start >= size64 || start + n > size64) + return -EINVAL; + + start *= (8 << mc7->width); + while (n--) { + int i; + u64 val64 = 0; + + for (i = (1 << mc7->width) - 1; i >= 0; --i) { + int attempts = 10; + u32 val; + + t3_write_reg(adap, mc7->offset + A_MC7_BD_ADDR, start); + t3_write_reg(adap, mc7->offset + A_MC7_BD_OP, 0); + val = t3_read_reg(adap, mc7->offset + A_MC7_BD_OP); + while ((val & F_BUSY) && attempts--) + val = t3_read_reg(adap, + mc7->offset + A_MC7_BD_OP); + if (val & F_BUSY) + return -EIO; + + val = t3_read_reg(adap, mc7->offset + A_MC7_BD_DATA1); + if (mc7->width == 0) { + val64 = t3_read_reg(adap, + mc7->offset + + A_MC7_BD_DATA0); + val64 |= (u64) val << 32; + } else { + if (mc7->width > 1) + val >>= shift[mc7->width]; + val64 |= (u64) val << (step[mc7->width] * i); + } + start += 8; + } + *buf++ = val64; + } + return 0; +} + +/* + * Initialize MI1. + */ +static void mi1_init(struct adapter *adap, const struct adapter_info *ai) +{ + u32 clkdiv = adap->params.vpd.cclk / (2 * adap->params.vpd.mdc) - 1; + u32 val = F_PREEN | V_MDIINV(ai->mdiinv) | V_MDIEN(ai->mdien) | + V_CLKDIV(clkdiv); + + if (!(ai->caps & SUPPORTED_10000baseT_Full)) + val |= V_ST(1); + t3_write_reg(adap, A_MI1_CFG, val); +} + +#define MDIO_ATTEMPTS 10 + +/* + * MI1 read/write operations for direct-addressed PHYs. + */ +static int mi1_read(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int *valp) +{ + int ret; + u32 addr = V_REGADDR(reg_addr) | V_PHYADDR(phy_addr); + + if (mmd_addr) + return -EINVAL; + + mutex_lock(&adapter->mdio_lock); + t3_write_reg(adapter, A_MI1_ADDR, addr); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(2)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + if (!ret) + *valp = t3_read_reg(adapter, A_MI1_DATA); + mutex_unlock(&adapter->mdio_lock); + return ret; +} + +static int mi1_write(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int val) +{ + int ret; + u32 addr = V_REGADDR(reg_addr) | V_PHYADDR(phy_addr); + + if (mmd_addr) + return -EINVAL; + + mutex_lock(&adapter->mdio_lock); + t3_write_reg(adapter, A_MI1_ADDR, addr); + t3_write_reg(adapter, A_MI1_DATA, val); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + mutex_unlock(&adapter->mdio_lock); + return ret; +} + +static const struct mdio_ops mi1_mdio_ops = { + mi1_read, + mi1_write +}; + +/* + * MI1 read/write operations for indirect-addressed PHYs. + */ +static int mi1_ext_read(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int *valp) +{ + int ret; + u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr); + + mutex_lock(&adapter->mdio_lock); + t3_write_reg(adapter, A_MI1_ADDR, addr); + t3_write_reg(adapter, A_MI1_DATA, reg_addr); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + if (!ret) { + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(3)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, + MDIO_ATTEMPTS, 20); + if (!ret) + *valp = t3_read_reg(adapter, A_MI1_DATA); + } + mutex_unlock(&adapter->mdio_lock); + return ret; +} + +static int mi1_ext_write(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int val) +{ + int ret; + u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr); + + mutex_lock(&adapter->mdio_lock); + t3_write_reg(adapter, A_MI1_ADDR, addr); + t3_write_reg(adapter, A_MI1_DATA, reg_addr); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + if (!ret) { + t3_write_reg(adapter, A_MI1_DATA, val); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1)); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, + MDIO_ATTEMPTS, 20); + } + mutex_unlock(&adapter->mdio_lock); + return ret; +} + +static const struct mdio_ops mi1_mdio_ext_ops = { + mi1_ext_read, + mi1_ext_write +}; + +/** + * t3_mdio_change_bits - modify the value of a PHY register + * @phy: the PHY to operate on + * @mmd: the device address + * @reg: the register address + * @clear: what part of the register value to mask off + * @set: what part of the register value to set + * + * Changes the value of a PHY register by applying a mask to its current + * value and ORing the result with a new value. + */ +int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear, + unsigned int set) +{ + int ret; + unsigned int val; + + ret = mdio_read(phy, mmd, reg, &val); + if (!ret) { + val &= ~clear; + ret = mdio_write(phy, mmd, reg, val | set); + } + return ret; +} + +/** + * t3_phy_reset - reset a PHY block + * @phy: the PHY to operate on + * @mmd: the device address of the PHY block to reset + * @wait: how long to wait for the reset to complete in 1ms increments + * + * Resets a PHY block and optionally waits for the reset to complete. + * @mmd should be 0 for 10/100/1000 PHYs and the device address to reset + * for 10G PHYs. + */ +int t3_phy_reset(struct cphy *phy, int mmd, int wait) +{ + int err; + unsigned int ctl; + + err = t3_mdio_change_bits(phy, mmd, MII_BMCR, BMCR_PDOWN, BMCR_RESET); + if (err || !wait) + return err; + + do { + err = mdio_read(phy, mmd, MII_BMCR, &ctl); + if (err) + return err; + ctl &= BMCR_RESET; + if (ctl) + msleep(1); + } while (ctl && --wait); + + return ctl ? -1 : 0; +} + +/** + * t3_phy_advertise - set the PHY advertisement registers for autoneg + * @phy: the PHY to operate on + * @advert: bitmap of capabilities the PHY should advertise + * + * Sets a 10/100/1000 PHY's advertisement registers to advertise the + * requested capabilities. + */ +int t3_phy_advertise(struct cphy *phy, unsigned int advert) +{ + int err; + unsigned int val = 0; + + err = mdio_read(phy, 0, MII_CTRL1000, &val); + if (err) + return err; + + val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + if (advert & ADVERTISED_1000baseT_Half) + val |= ADVERTISE_1000HALF; + if (advert & ADVERTISED_1000baseT_Full) + val |= ADVERTISE_1000FULL; + + err = mdio_write(phy, 0, MII_CTRL1000, val); + if (err) + return err; + + val = 1; + if (advert & ADVERTISED_10baseT_Half) + val |= ADVERTISE_10HALF; + if (advert & ADVERTISED_10baseT_Full) + val |= ADVERTISE_10FULL; + if (advert & ADVERTISED_100baseT_Half) + val |= ADVERTISE_100HALF; + if (advert & ADVERTISED_100baseT_Full) + val |= ADVERTISE_100FULL; + if (advert & ADVERTISED_Pause) + val |= ADVERTISE_PAUSE_CAP; + if (advert & ADVERTISED_Asym_Pause) + val |= ADVERTISE_PAUSE_ASYM; + return mdio_write(phy, 0, MII_ADVERTISE, val); +} + +/** + * t3_set_phy_speed_duplex - force PHY speed and duplex + * @phy: the PHY to operate on + * @speed: requested PHY speed + * @duplex: requested PHY duplex + * + * Force a 10/100/1000 PHY's speed and duplex. This also disables + * auto-negotiation except for GigE, where auto-negotiation is mandatory. + */ +int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex) +{ + int err; + unsigned int ctl; + + err = mdio_read(phy, 0, MII_BMCR, &ctl); + if (err) + return err; + + if (speed >= 0) { + ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); + if (speed == SPEED_100) + ctl |= BMCR_SPEED100; + else if (speed == SPEED_1000) + ctl |= BMCR_SPEED1000; + } + if (duplex >= 0) { + ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE); + if (duplex == DUPLEX_FULL) + ctl |= BMCR_FULLDPLX; + } + if (ctl & BMCR_SPEED1000) /* auto-negotiation required for GigE */ + ctl |= BMCR_ANENABLE; + return mdio_write(phy, 0, MII_BMCR, ctl); +} + +static const struct adapter_info t3_adap_info[] = { + {2, 0, 0, 0, + F_GPIO2_OEN | F_GPIO4_OEN | + F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, + SUPPORTED_OFFLOAD, + &mi1_mdio_ops, "Chelsio PE9000"}, + {2, 0, 0, 0, + F_GPIO2_OEN | F_GPIO4_OEN | + F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, + SUPPORTED_OFFLOAD, + &mi1_mdio_ops, "Chelsio T302"}, + {1, 0, 0, 0, + F_GPIO1_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN | + F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, 0, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_OFFLOAD, + &mi1_mdio_ext_ops, "Chelsio T310"}, + {2, 0, 0, 0, + F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO5_OEN | F_GPIO6_OEN | + F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL | + F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, 0, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_OFFLOAD, + &mi1_mdio_ext_ops, "Chelsio T320"}, +}; + +/* + * Return the adapter_info structure with a given index. Out-of-range indices + * return NULL. + */ +const struct adapter_info *t3_get_adapter_info(unsigned int id) +{ + return id < ARRAY_SIZE(t3_adap_info) ? &t3_adap_info[id] : NULL; +} + +#define CAPS_1G (SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | \ + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII) +#define CAPS_10G (SUPPORTED_10000baseT_Full | SUPPORTED_AUI) + +static const struct port_type_info port_types[] = { + {NULL}, + {t3_ael1002_phy_prep, CAPS_10G | SUPPORTED_FIBRE, + "10GBASE-XR"}, + {t3_vsc8211_phy_prep, CAPS_1G | SUPPORTED_TP | SUPPORTED_IRQ, + "10/100/1000BASE-T"}, + {NULL, CAPS_1G | SUPPORTED_TP | SUPPORTED_IRQ, + "10/100/1000BASE-T"}, + {t3_xaui_direct_phy_prep, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, + {NULL, CAPS_10G, "10GBASE-KX4"}, + {t3_qt2045_phy_prep, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, + {t3_ael1006_phy_prep, CAPS_10G | SUPPORTED_FIBRE, + "10GBASE-SR"}, + {NULL, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, +}; + +#undef CAPS_1G +#undef CAPS_10G + +#define VPD_ENTRY(name, len) \ + u8 name##_kword[2]; u8 name##_len; u8 name##_data[len] + +/* + * Partial EEPROM Vital Product Data structure. Includes only the ID and + * VPD-R sections. + */ +struct t3_vpd { + u8 id_tag; + u8 id_len[2]; + u8 id_data[16]; + u8 vpdr_tag; + u8 vpdr_len[2]; + VPD_ENTRY(pn, 16); /* part number */ + VPD_ENTRY(ec, 16); /* EC level */ + VPD_ENTRY(sn, 16); /* serial number */ + VPD_ENTRY(na, 12); /* MAC address base */ + VPD_ENTRY(cclk, 6); /* core clock */ + VPD_ENTRY(mclk, 6); /* mem clock */ + VPD_ENTRY(uclk, 6); /* uP clk */ + VPD_ENTRY(mdc, 6); /* MDIO clk */ + VPD_ENTRY(mt, 2); /* mem timing */ + VPD_ENTRY(xaui0cfg, 6); /* XAUI0 config */ + VPD_ENTRY(xaui1cfg, 6); /* XAUI1 config */ + VPD_ENTRY(port0, 2); /* PHY0 complex */ + VPD_ENTRY(port1, 2); /* PHY1 complex */ + VPD_ENTRY(port2, 2); /* PHY2 complex */ + VPD_ENTRY(port3, 2); /* PHY3 complex */ + VPD_ENTRY(rv, 1); /* csum */ + u32 pad; /* for multiple-of-4 sizing and alignment */ +}; + +#define EEPROM_MAX_POLL 4 +#define EEPROM_STAT_ADDR 0x4000 +#define VPD_BASE 0xc00 + +/** + * t3_seeprom_read - read a VPD EEPROM location + * @adapter: adapter to read + * @addr: EEPROM address + * @data: where to store the read data + * + * Read a 32-bit word from a location in VPD EEPROM using the card's PCI + * VPD ROM capability. A zero is written to the flag bit when the + * addres is written to the control register. The hardware device will + * set the flag to 1 when 4 bytes have been read into the data register. + */ +int t3_seeprom_read(struct adapter *adapter, u32 addr, u32 *data) +{ + u16 val; + int attempts = EEPROM_MAX_POLL; + unsigned int base = adapter->params.pci.vpd_cap_addr; + + if ((addr >= EEPROMSIZE && addr != EEPROM_STAT_ADDR) || (addr & 3)) + return -EINVAL; + + pci_write_config_word(adapter->pdev, base + PCI_VPD_ADDR, addr); + do { + udelay(10); + pci_read_config_word(adapter->pdev, base + PCI_VPD_ADDR, &val); + } while (!(val & PCI_VPD_ADDR_F) && --attempts); + + if (!(val & PCI_VPD_ADDR_F)) { + CH_ERR(adapter, "reading EEPROM address 0x%x failed\n", addr); + return -EIO; + } + pci_read_config_dword(adapter->pdev, base + PCI_VPD_DATA, data); + *data = le32_to_cpu(*data); + return 0; +} + +/** + * t3_seeprom_write - write a VPD EEPROM location + * @adapter: adapter to write + * @addr: EEPROM address + * @data: value to write + * + * Write a 32-bit word to a location in VPD EEPROM using the card's PCI + * VPD ROM capability. + */ +int t3_seeprom_write(struct adapter *adapter, u32 addr, u32 data) +{ + u16 val; + int attempts = EEPROM_MAX_POLL; + unsigned int base = adapter->params.pci.vpd_cap_addr; + + if ((addr >= EEPROMSIZE && addr != EEPROM_STAT_ADDR) || (addr & 3)) + return -EINVAL; + + pci_write_config_dword(adapter->pdev, base + PCI_VPD_DATA, + cpu_to_le32(data)); + pci_write_config_word(adapter->pdev,base + PCI_VPD_ADDR, + addr | PCI_VPD_ADDR_F); + do { + msleep(1); + pci_read_config_word(adapter->pdev, base + PCI_VPD_ADDR, &val); + } while ((val & PCI_VPD_ADDR_F) && --attempts); + + if (val & PCI_VPD_ADDR_F) { + CH_ERR(adapter, "write to EEPROM address 0x%x failed\n", addr); + return -EIO; + } + return 0; +} + +/** + * t3_seeprom_wp - enable/disable EEPROM write protection + * @adapter: the adapter + * @enable: 1 to enable write protection, 0 to disable it + * + * Enables or disables write protection on the serial EEPROM. + */ +int t3_seeprom_wp(struct adapter *adapter, int enable) +{ + return t3_seeprom_write(adapter, EEPROM_STAT_ADDR, enable ? 0xc : 0); +} + +/* + * Convert a character holding a hex digit to a number. + */ +static unsigned int hex2int(unsigned char c) +{ + return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; +} + +/** + * get_vpd_params - read VPD parameters from VPD EEPROM + * @adapter: adapter to read + * @p: where to store the parameters + * + * Reads card parameters stored in VPD EEPROM. + */ +static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) +{ + int i, addr, ret; + struct t3_vpd vpd; + + /* + * Card information is normally at VPD_BASE but some early cards had + * it at 0. + */ + ret = t3_seeprom_read(adapter, VPD_BASE, (u32 *)&vpd); + if (ret) + return ret; + addr = vpd.id_tag == 0x82 ? VPD_BASE : 0; + + for (i = 0; i < sizeof(vpd); i += 4) { + ret = t3_seeprom_read(adapter, addr + i, + (u32 *)((u8 *)&vpd + i)); + if (ret) + return ret; + } + + p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10); + p->mclk = simple_strtoul(vpd.mclk_data, NULL, 10); + p->uclk = simple_strtoul(vpd.uclk_data, NULL, 10); + p->mdc = simple_strtoul(vpd.mdc_data, NULL, 10); + p->mem_timing = simple_strtoul(vpd.mt_data, NULL, 10); + + /* Old eeproms didn't have port information */ + if (adapter->params.rev == 0 && !vpd.port0_data[0]) { + p->port_type[0] = uses_xaui(adapter) ? 1 : 2; + p->port_type[1] = uses_xaui(adapter) ? 6 : 2; + } else { + p->port_type[0] = hex2int(vpd.port0_data[0]); + p->port_type[1] = hex2int(vpd.port1_data[0]); + p->xauicfg[0] = simple_strtoul(vpd.xaui0cfg_data, NULL, 16); + p->xauicfg[1] = simple_strtoul(vpd.xaui1cfg_data, NULL, 16); + } + + for (i = 0; i < 6; i++) + p->eth_base[i] = hex2int(vpd.na_data[2 * i]) * 16 + + hex2int(vpd.na_data[2 * i + 1]); + return 0; +} + +/* serial flash and firmware constants */ +enum { + SF_ATTEMPTS = 5, /* max retries for SF1 operations */ + SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */ + SF_SIZE = SF_SEC_SIZE * 8, /* serial flash size */ + + /* flash command opcodes */ + SF_PROG_PAGE = 2, /* program page */ + SF_WR_DISABLE = 4, /* disable writes */ + SF_RD_STATUS = 5, /* read status register */ + SF_WR_ENABLE = 6, /* enable writes */ + SF_RD_DATA_FAST = 0xb, /* read flash */ + SF_ERASE_SECTOR = 0xd8, /* erase sector */ + + FW_FLASH_BOOT_ADDR = 0x70000, /* start address of FW in flash */ + FW_VERS_ADDR = 0x77ffc /* flash address holding FW version */ +}; + +/** + * sf1_read - read data from the serial flash + * @adapter: the adapter + * @byte_cnt: number of bytes to read + * @cont: whether another operation will be chained + * @valp: where to store the read data + * + * Reads up to 4 bytes of data from the serial flash. The location of + * the read needs to be specified prior to calling this by issuing the + * appropriate commands to the serial flash. + */ +static int sf1_read(struct adapter *adapter, unsigned int byte_cnt, int cont, + u32 *valp) +{ + int ret; + + if (!byte_cnt || byte_cnt > 4) + return -EINVAL; + if (t3_read_reg(adapter, A_SF_OP) & F_BUSY) + return -EBUSY; + t3_write_reg(adapter, A_SF_OP, V_CONT(cont) | V_BYTECNT(byte_cnt - 1)); + ret = t3_wait_op_done(adapter, A_SF_OP, F_BUSY, 0, SF_ATTEMPTS, 10); + if (!ret) + *valp = t3_read_reg(adapter, A_SF_DATA); + return ret; +} + +/** + * sf1_write - write data to the serial flash + * @adapter: the adapter + * @byte_cnt: number of bytes to write + * @cont: whether another operation will be chained + * @val: value to write + * + * Writes up to 4 bytes of data to the serial flash. The location of + * the write needs to be specified prior to calling this by issuing the + * appropriate commands to the serial flash. + */ +static int sf1_write(struct adapter *adapter, unsigned int byte_cnt, int cont, + u32 val) +{ + if (!byte_cnt || byte_cnt > 4) + return -EINVAL; + if (t3_read_reg(adapter, A_SF_OP) & F_BUSY) + return -EBUSY; + t3_write_reg(adapter, A_SF_DATA, val); + t3_write_reg(adapter, A_SF_OP, + V_CONT(cont) | V_BYTECNT(byte_cnt - 1) | V_OP(1)); + return t3_wait_op_done(adapter, A_SF_OP, F_BUSY, 0, SF_ATTEMPTS, 10); +} + +/** + * flash_wait_op - wait for a flash operation to complete + * @adapter: the adapter + * @attempts: max number of polls of the status register + * @delay: delay between polls in ms + * + * Wait for a flash operation to complete by polling the status register. + */ +static int flash_wait_op(struct adapter *adapter, int attempts, int delay) +{ + int ret; + u32 status; + + while (1) { + if ((ret = sf1_write(adapter, 1, 1, SF_RD_STATUS)) != 0 || + (ret = sf1_read(adapter, 1, 0, &status)) != 0) + return ret; + if (!(status & 1)) + return 0; + if (--attempts == 0) + return -EAGAIN; + if (delay) + msleep(delay); + } +} + +/** + * t3_read_flash - read words from serial flash + * @adapter: the adapter + * @addr: the start address for the read + * @nwords: how many 32-bit words to read + * @data: where to store the read data + * @byte_oriented: whether to store data as bytes or as words + * + * Read the specified number of 32-bit words from the serial flash. + * If @byte_oriented is set the read data is stored as a byte array + * (i.e., big-endian), otherwise as 32-bit words in the platform's + * natural endianess. + */ +int t3_read_flash(struct adapter *adapter, unsigned int addr, + unsigned int nwords, u32 *data, int byte_oriented) +{ + int ret; + + if (addr + nwords * sizeof(u32) > SF_SIZE || (addr & 3)) + return -EINVAL; + + addr = swab32(addr) | SF_RD_DATA_FAST; + + if ((ret = sf1_write(adapter, 4, 1, addr)) != 0 || + (ret = sf1_read(adapter, 1, 1, data)) != 0) + return ret; + + for (; nwords; nwords--, data++) { + ret = sf1_read(adapter, 4, nwords > 1, data); + if (ret) + return ret; + if (byte_oriented) + *data = htonl(*data); + } + return 0; +} + +/** + * t3_write_flash - write up to a page of data to the serial flash + * @adapter: the adapter + * @addr: the start address to write + * @n: length of data to write + * @data: the data to write + * + * Writes up to a page of data (256 bytes) to the serial flash starting + * at the given address. + */ +static int t3_write_flash(struct adapter *adapter, unsigned int addr, + unsigned int n, const u8 *data) +{ + int ret; + u32 buf[64]; + unsigned int i, c, left, val, offset = addr & 0xff; + + if (addr + n > SF_SIZE || offset + n > 256) + return -EINVAL; + + val = swab32(addr) | SF_PROG_PAGE; + + if ((ret = sf1_write(adapter, 1, 0, SF_WR_ENABLE)) != 0 || + (ret = sf1_write(adapter, 4, 1, val)) != 0) + return ret; + + for (left = n; left; left -= c) { + c = min(left, 4U); + for (val = 0, i = 0; i < c; ++i) + val = (val << 8) + *data++; + + ret = sf1_write(adapter, c, c != left, val); + if (ret) + return ret; + } + if ((ret = flash_wait_op(adapter, 5, 1)) != 0) + return ret; + + /* Read the page to verify the write succeeded */ + ret = t3_read_flash(adapter, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); + if (ret) + return ret; + + if (memcmp(data - n, (u8 *) buf + offset, n)) + return -EIO; + return 0; +} + +/** + * t3_get_fw_version - read the firmware version + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the FW version from flash. + */ +int t3_get_fw_version(struct adapter *adapter, u32 *vers) +{ + return t3_read_flash(adapter, FW_VERS_ADDR, 1, vers, 0); +} + +/** + * t3_check_fw_version - check if the FW is compatible with this driver + * @adapter: the adapter + * + * Checks if an adapter's FW is compatible with the driver. Returns 0 + * if the versions are compatible, a negative error otherwise. + */ +int t3_check_fw_version(struct adapter *adapter) +{ + int ret; + u32 vers; + + ret = t3_get_fw_version(adapter, &vers); + if (ret) + return ret; + + /* Minor 0xfff means the FW is an internal development-only version. */ + if ((vers & 0xfff) == 0xfff) + return 0; + + if (vers == 0x1002009) + return 0; + + CH_ERR(adapter, "found wrong FW version, driver needs version 2.9\n"); + return -EINVAL; +} + +/** + * t3_flash_erase_sectors - erase a range of flash sectors + * @adapter: the adapter + * @start: the first sector to erase + * @end: the last sector to erase + * + * Erases the sectors in the given range. + */ +static int t3_flash_erase_sectors(struct adapter *adapter, int start, int end) +{ + while (start <= end) { + int ret; + + if ((ret = sf1_write(adapter, 1, 0, SF_WR_ENABLE)) != 0 || + (ret = sf1_write(adapter, 4, 0, + SF_ERASE_SECTOR | (start << 8))) != 0 || + (ret = flash_wait_op(adapter, 5, 500)) != 0) + return ret; + start++; + } + return 0; +} + +/* + * t3_load_fw - download firmware + * @adapter: the adapter + * @fw_data: the firrware image to write + * @size: image size + * + * Write the supplied firmware image to the card's serial flash. + * The FW image has the following sections: @size - 8 bytes of code and + * data, followed by 4 bytes of FW version, followed by the 32-bit + * 1's complement checksum of the whole image. + */ +int t3_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size) +{ + u32 csum; + unsigned int i; + const u32 *p = (const u32 *)fw_data; + int ret, addr, fw_sector = FW_FLASH_BOOT_ADDR >> 16; + + if (size & 3) + return -EINVAL; + if (size > FW_VERS_ADDR + 8 - FW_FLASH_BOOT_ADDR) + return -EFBIG; + + for (csum = 0, i = 0; i < size / sizeof(csum); i++) + csum += ntohl(p[i]); + if (csum != 0xffffffff) { + CH_ERR(adapter, "corrupted firmware image, checksum %u\n", + csum); + return -EINVAL; + } + + ret = t3_flash_erase_sectors(adapter, fw_sector, fw_sector); + if (ret) + goto out; + + size -= 8; /* trim off version and checksum */ + for (addr = FW_FLASH_BOOT_ADDR; size;) { + unsigned int chunk_size = min(size, 256U); + + ret = t3_write_flash(adapter, addr, chunk_size, fw_data); + if (ret) + goto out; + + addr += chunk_size; + fw_data += chunk_size; + size -= chunk_size; + } + + ret = t3_write_flash(adapter, FW_VERS_ADDR, 4, fw_data); +out: + if (ret) + CH_ERR(adapter, "firmware download failed, error %d\n", ret); + return ret; +} + +#define CIM_CTL_BASE 0x2000 + +/** + * t3_cim_ctl_blk_read - read a block from CIM control region + * + * @adap: the adapter + * @addr: the start address within the CIM control region + * @n: number of words to read + * @valp: where to store the result + * + * Reads a block of 4-byte words from the CIM control region. + */ +int t3_cim_ctl_blk_read(struct adapter *adap, unsigned int addr, + unsigned int n, unsigned int *valp) +{ + int ret = 0; + + if (t3_read_reg(adap, A_CIM_HOST_ACC_CTRL) & F_HOSTBUSY) + return -EBUSY; + + for ( ; !ret && n--; addr += 4) { + t3_write_reg(adap, A_CIM_HOST_ACC_CTRL, CIM_CTL_BASE + addr); + ret = t3_wait_op_done(adap, A_CIM_HOST_ACC_CTRL, F_HOSTBUSY, + 0, 5, 2); + if (!ret) + *valp++ = t3_read_reg(adap, A_CIM_HOST_ACC_DATA); + } + return ret; +} + + +/** + * t3_link_changed - handle interface link changes + * @adapter: the adapter + * @port_id: the port index that changed link state + * + * Called when a port's link settings change to propagate the new values + * to the associated PHY and MAC. After performing the common tasks it + * invokes an OS-specific handler. + */ +void t3_link_changed(struct adapter *adapter, int port_id) +{ + int link_ok, speed, duplex, fc; + struct port_info *pi = adap2pinfo(adapter, port_id); + struct cphy *phy = &pi->phy; + struct cmac *mac = &pi->mac; + struct link_config *lc = &pi->link_config; + + phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc); + + if (link_ok != lc->link_ok && adapter->params.rev > 0 && + uses_xaui(adapter)) { + if (link_ok) + t3b_pcs_reset(mac); + t3_write_reg(adapter, A_XGM_XAUI_ACT_CTRL + mac->offset, + link_ok ? F_TXACTENABLE | F_RXEN : 0); + } + lc->link_ok = link_ok; + lc->speed = speed < 0 ? SPEED_INVALID : speed; + lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex; + if (lc->requested_fc & PAUSE_AUTONEG) + fc &= lc->requested_fc; + else + fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + + if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) { + /* Set MAC speed, duplex, and flow control to match PHY. */ + t3_mac_set_speed_duplex_fc(mac, speed, duplex, fc); + lc->fc = fc; + } + + t3_os_link_changed(adapter, port_id, link_ok, speed, duplex, fc); +} + +/** + * t3_link_start - apply link configuration to MAC/PHY + * @phy: the PHY to setup + * @mac: the MAC to setup + * @lc: the requested link configuration + * + * Set up a port's MAC and PHY according to a desired link configuration. + * - If the PHY can auto-negotiate first decide what to advertise, then + * enable/disable auto-negotiation as desired, and reset. + * - If the PHY does not auto-negotiate just reset it. + * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, + * otherwise do it later based on the outcome of auto-negotiation. + */ +int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc) +{ + unsigned int fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + + lc->link_ok = 0; + if (lc->supported & SUPPORTED_Autoneg) { + lc->advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); + if (fc) { + lc->advertising |= ADVERTISED_Asym_Pause; + if (fc & PAUSE_RX) + lc->advertising |= ADVERTISED_Pause; + } + phy->ops->advertise(phy, lc->advertising); + + if (lc->autoneg == AUTONEG_DISABLE) { + lc->speed = lc->requested_speed; + lc->duplex = lc->requested_duplex; + lc->fc = (unsigned char)fc; + t3_mac_set_speed_duplex_fc(mac, lc->speed, lc->duplex, + fc); + /* Also disables autoneg */ + phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex); + phy->ops->reset(phy, 0); + } else + phy->ops->autoneg_enable(phy); + } else { + t3_mac_set_speed_duplex_fc(mac, -1, -1, fc); + lc->fc = (unsigned char)fc; + phy->ops->reset(phy, 0); + } + return 0; +} + +/** + * t3_set_vlan_accel - control HW VLAN extraction + * @adapter: the adapter + * @ports: bitmap of adapter ports to operate on + * @on: enable (1) or disable (0) HW VLAN extraction + * + * Enables or disables HW extraction of VLAN tags for the given port. + */ +void t3_set_vlan_accel(struct adapter *adapter, unsigned int ports, int on) +{ + t3_set_reg_field(adapter, A_TP_OUT_CONFIG, + ports << S_VLANEXTRACTIONENABLE, + on ? (ports << S_VLANEXTRACTIONENABLE) : 0); +} + +struct intr_info { + unsigned int mask; /* bits to check in interrupt status */ + const char *msg; /* message to print or NULL */ + short stat_idx; /* stat counter to increment or -1 */ + unsigned short fatal:1; /* whether the condition reported is fatal */ +}; + +/** + * t3_handle_intr_status - table driven interrupt handler + * @adapter: the adapter that generated the interrupt + * @reg: the interrupt status register to process + * @mask: a mask to apply to the interrupt status + * @acts: table of interrupt actions + * @stats: statistics counters tracking interrupt occurences + * + * A table driven interrupt handler that applies a set of masks to an + * interrupt status word and performs the corresponding actions if the + * interrupts described by the mask have occured. The actions include + * optionally printing a warning or alert message, and optionally + * incrementing a stat counter. The table is terminated by an entry + * specifying mask 0. Returns the number of fatal interrupt conditions. + */ +static int t3_handle_intr_status(struct adapter *adapter, unsigned int reg, + unsigned int mask, + const struct intr_info *acts, + unsigned long *stats) +{ + int fatal = 0; + unsigned int status = t3_read_reg(adapter, reg) & mask; + + for (; acts->mask; ++acts) { + if (!(status & acts->mask)) + continue; + if (acts->fatal) { + fatal++; + CH_ALERT(adapter, "%s (0x%x)\n", + acts->msg, status & acts->mask); + } else if (acts->msg) + CH_WARN(adapter, "%s (0x%x)\n", + acts->msg, status & acts->mask); + if (acts->stat_idx >= 0) + stats[acts->stat_idx]++; + } + if (status) /* clear processed interrupts */ + t3_write_reg(adapter, reg, status); + return fatal; +} + +#define SGE_INTR_MASK (F_RSPQDISABLED) +#define MC5_INTR_MASK (F_PARITYERR | F_ACTRGNFULL | F_UNKNOWNCMD | \ + F_REQQPARERR | F_DISPQPARERR | F_DELACTEMPTY | \ + F_NFASRCHFAIL) +#define MC7_INTR_MASK (F_AE | F_UE | F_CE | V_PE(M_PE)) +#define XGM_INTR_MASK (V_TXFIFO_PRTY_ERR(M_TXFIFO_PRTY_ERR) | \ + V_RXFIFO_PRTY_ERR(M_RXFIFO_PRTY_ERR) | \ + F_TXFIFO_UNDERRUN | F_RXFIFO_OVERFLOW) +#define PCIX_INTR_MASK (F_MSTDETPARERR | F_SIGTARABT | F_RCVTARABT | \ + F_RCVMSTABT | F_SIGSYSERR | F_DETPARERR | \ + F_SPLCMPDIS | F_UNXSPLCMP | F_RCVSPLCMPERR | \ + F_DETCORECCERR | F_DETUNCECCERR | F_PIOPARERR | \ + V_WFPARERR(M_WFPARERR) | V_RFPARERR(M_RFPARERR) | \ + V_CFPARERR(M_CFPARERR) /* | V_MSIXPARERR(M_MSIXPARERR) */) +#define PCIE_INTR_MASK (F_UNXSPLCPLERRR | F_UNXSPLCPLERRC | F_PCIE_PIOPARERR |\ + F_PCIE_WFPARERR | F_PCIE_RFPARERR | F_PCIE_CFPARERR | \ + /* V_PCIE_MSIXPARERR(M_PCIE_MSIXPARERR) | */ \ + V_BISTERR(M_BISTERR) | F_PEXERR) +#define ULPRX_INTR_MASK F_PARERR +#define ULPTX_INTR_MASK 0 +#define CPLSW_INTR_MASK (F_TP_FRAMING_ERROR | \ + F_SGE_FRAMING_ERROR | F_CIM_FRAMING_ERROR | \ + F_ZERO_SWITCH_ERROR) +#define CIM_INTR_MASK (F_BLKWRPLINT | F_BLKRDPLINT | F_BLKWRCTLINT | \ + F_BLKRDCTLINT | F_BLKWRFLASHINT | F_BLKRDFLASHINT | \ + F_SGLWRFLASHINT | F_WRBLKFLASHINT | F_BLKWRBOOTINT | \ + F_FLASHRANGEINT | F_SDRAMRANGEINT | F_RSVDSPACEINT) +#define PMTX_INTR_MASK (F_ZERO_C_CMD_ERROR | ICSPI_FRM_ERR | OESPI_FRM_ERR | \ + V_ICSPI_PAR_ERROR(M_ICSPI_PAR_ERROR) | \ + V_OESPI_PAR_ERROR(M_OESPI_PAR_ERROR)) +#define PMRX_INTR_MASK (F_ZERO_E_CMD_ERROR | IESPI_FRM_ERR | OCSPI_FRM_ERR | \ + V_IESPI_PAR_ERROR(M_IESPI_PAR_ERROR) | \ + V_OCSPI_PAR_ERROR(M_OCSPI_PAR_ERROR)) +#define MPS_INTR_MASK (V_TX0TPPARERRENB(M_TX0TPPARERRENB) | \ + V_TX1TPPARERRENB(M_TX1TPPARERRENB) | \ + V_RXTPPARERRENB(M_RXTPPARERRENB) | \ + V_MCAPARERRENB(M_MCAPARERRENB)) +#define PL_INTR_MASK (F_T3DBG | F_XGMAC0_0 | F_XGMAC0_1 | F_MC5A | F_PM1_TX | \ + F_PM1_RX | F_ULP2_TX | F_ULP2_RX | F_TP1 | F_CIM | \ + F_MC7_CM | F_MC7_PMTX | F_MC7_PMRX | F_SGE3 | F_PCIM0 | \ + F_MPS0 | F_CPL_SWITCH) + +/* + * Interrupt handler for the PCIX1 module. + */ +static void pci_intr_handler(struct adapter *adapter) +{ + static const struct intr_info pcix1_intr_info[] = { + { F_PEXERR, "PCI PEX error", -1, 1 }, + {F_MSTDETPARERR, "PCI master detected parity error", -1, 1}, + {F_SIGTARABT, "PCI signaled target abort", -1, 1}, + {F_RCVTARABT, "PCI received target abort", -1, 1}, + {F_RCVMSTABT, "PCI received master abort", -1, 1}, + {F_SIGSYSERR, "PCI signaled system error", -1, 1}, + {F_DETPARERR, "PCI detected parity error", -1, 1}, + {F_SPLCMPDIS, "PCI split completion discarded", -1, 1}, + {F_UNXSPLCMP, "PCI unexpected split completion error", -1, 1}, + {F_RCVSPLCMPERR, "PCI received split completion error", -1, + 1}, + {F_DETCORECCERR, "PCI correctable ECC error", + STAT_PCI_CORR_ECC, 0}, + {F_DETUNCECCERR, "PCI uncorrectable ECC error", -1, 1}, + {F_PIOPARERR, "PCI PIO FIFO parity error", -1, 1}, + {V_WFPARERR(M_WFPARERR), "PCI write FIFO parity error", -1, + 1}, + {V_RFPARERR(M_RFPARERR), "PCI read FIFO parity error", -1, + 1}, + {V_CFPARERR(M_CFPARERR), "PCI command FIFO parity error", -1, + 1}, + {V_MSIXPARERR(M_MSIXPARERR), "PCI MSI-X table/PBA parity " + "error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_PCIX_INT_CAUSE, PCIX_INTR_MASK, + pcix1_intr_info, adapter->irq_stats)) + t3_fatal_err(adapter); +} + +/* + * Interrupt handler for the PCIE module. + */ +static void pcie_intr_handler(struct adapter *adapter) +{ + static const struct intr_info pcie_intr_info[] = { + {F_UNXSPLCPLERRR, + "PCI unexpected split completion DMA read error", -1, 1}, + {F_UNXSPLCPLERRC, + "PCI unexpected split completion DMA command error", -1, 1}, + {F_PCIE_PIOPARERR, "PCI PIO FIFO parity error", -1, 1}, + {F_PCIE_WFPARERR, "PCI write FIFO parity error", -1, 1}, + {F_PCIE_RFPARERR, "PCI read FIFO parity error", -1, 1}, + {F_PCIE_CFPARERR, "PCI command FIFO parity error", -1, 1}, + {V_PCIE_MSIXPARERR(M_PCIE_MSIXPARERR), + "PCI MSI-X table/PBA parity error", -1, 1}, + {V_BISTERR(M_BISTERR), "PCI BIST error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_PCIE_INT_CAUSE, PCIE_INTR_MASK, + pcie_intr_info, adapter->irq_stats)) + t3_fatal_err(adapter); +} + +/* + * TP interrupt handler. + */ +static void tp_intr_handler(struct adapter *adapter) +{ + static const struct intr_info tp_intr_info[] = { + {0xffffff, "TP parity error", -1, 1}, + {0x1000000, "TP out of Rx pages", -1, 1}, + {0x2000000, "TP out of Tx pages", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_TP_INT_CAUSE, 0xffffffff, + tp_intr_info, NULL)) + t3_fatal_err(adapter); +} + +/* + * CIM interrupt handler. + */ +static void cim_intr_handler(struct adapter *adapter) +{ + static const struct intr_info cim_intr_info[] = { + {F_RSVDSPACEINT, "CIM reserved space write", -1, 1}, + {F_SDRAMRANGEINT, "CIM SDRAM address out of range", -1, 1}, + {F_FLASHRANGEINT, "CIM flash address out of range", -1, 1}, + {F_BLKWRBOOTINT, "CIM block write to boot space", -1, 1}, + {F_WRBLKFLASHINT, "CIM write to cached flash space", -1, 1}, + {F_SGLWRFLASHINT, "CIM single write to flash space", -1, 1}, + {F_BLKRDFLASHINT, "CIM block read from flash space", -1, 1}, + {F_BLKWRFLASHINT, "CIM block write to flash space", -1, 1}, + {F_BLKRDCTLINT, "CIM block read from CTL space", -1, 1}, + {F_BLKWRCTLINT, "CIM block write to CTL space", -1, 1}, + {F_BLKRDPLINT, "CIM block read from PL space", -1, 1}, + {F_BLKWRPLINT, "CIM block write to PL space", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_CIM_HOST_INT_CAUSE, 0xffffffff, + cim_intr_info, NULL)) + t3_fatal_err(adapter); +} + +/* + * ULP RX interrupt handler. + */ +static void ulprx_intr_handler(struct adapter *adapter) +{ + static const struct intr_info ulprx_intr_info[] = { + {F_PARERR, "ULP RX parity error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_ULPRX_INT_CAUSE, 0xffffffff, + ulprx_intr_info, NULL)) + t3_fatal_err(adapter); +} + +/* + * ULP TX interrupt handler. + */ +static void ulptx_intr_handler(struct adapter *adapter) +{ + static const struct intr_info ulptx_intr_info[] = { + {F_PBL_BOUND_ERR_CH0, "ULP TX channel 0 PBL out of bounds", + STAT_ULP_CH0_PBL_OOB, 0}, + {F_PBL_BOUND_ERR_CH1, "ULP TX channel 1 PBL out of bounds", + STAT_ULP_CH1_PBL_OOB, 0}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_ULPTX_INT_CAUSE, 0xffffffff, + ulptx_intr_info, adapter->irq_stats)) + t3_fatal_err(adapter); +} + +#define ICSPI_FRM_ERR (F_ICSPI0_FIFO2X_RX_FRAMING_ERROR | \ + F_ICSPI1_FIFO2X_RX_FRAMING_ERROR | F_ICSPI0_RX_FRAMING_ERROR | \ + F_ICSPI1_RX_FRAMING_ERROR | F_ICSPI0_TX_FRAMING_ERROR | \ + F_ICSPI1_TX_FRAMING_ERROR) +#define OESPI_FRM_ERR (F_OESPI0_RX_FRAMING_ERROR | \ + F_OESPI1_RX_FRAMING_ERROR | F_OESPI0_TX_FRAMING_ERROR | \ + F_OESPI1_TX_FRAMING_ERROR | F_OESPI0_OFIFO2X_TX_FRAMING_ERROR | \ + F_OESPI1_OFIFO2X_TX_FRAMING_ERROR) + +/* + * PM TX interrupt handler. + */ +static void pmtx_intr_handler(struct adapter *adapter) +{ + static const struct intr_info pmtx_intr_info[] = { + {F_ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1}, + {ICSPI_FRM_ERR, "PMTX ispi framing error", -1, 1}, + {OESPI_FRM_ERR, "PMTX ospi framing error", -1, 1}, + {V_ICSPI_PAR_ERROR(M_ICSPI_PAR_ERROR), + "PMTX ispi parity error", -1, 1}, + {V_OESPI_PAR_ERROR(M_OESPI_PAR_ERROR), + "PMTX ospi parity error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_PM1_TX_INT_CAUSE, 0xffffffff, + pmtx_intr_info, NULL)) + t3_fatal_err(adapter); +} + +#define IESPI_FRM_ERR (F_IESPI0_FIFO2X_RX_FRAMING_ERROR | \ + F_IESPI1_FIFO2X_RX_FRAMING_ERROR | F_IESPI0_RX_FRAMING_ERROR | \ + F_IESPI1_RX_FRAMING_ERROR | F_IESPI0_TX_FRAMING_ERROR | \ + F_IESPI1_TX_FRAMING_ERROR) +#define OCSPI_FRM_ERR (F_OCSPI0_RX_FRAMING_ERROR | \ + F_OCSPI1_RX_FRAMING_ERROR | F_OCSPI0_TX_FRAMING_ERROR | \ + F_OCSPI1_TX_FRAMING_ERROR | F_OCSPI0_OFIFO2X_TX_FRAMING_ERROR | \ + F_OCSPI1_OFIFO2X_TX_FRAMING_ERROR) + +/* + * PM RX interrupt handler. + */ +static void pmrx_intr_handler(struct adapter *adapter) +{ + static const struct intr_info pmrx_intr_info[] = { + {F_ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1}, + {IESPI_FRM_ERR, "PMRX ispi framing error", -1, 1}, + {OCSPI_FRM_ERR, "PMRX ospi framing error", -1, 1}, + {V_IESPI_PAR_ERROR(M_IESPI_PAR_ERROR), + "PMRX ispi parity error", -1, 1}, + {V_OCSPI_PAR_ERROR(M_OCSPI_PAR_ERROR), + "PMRX ospi parity error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_PM1_RX_INT_CAUSE, 0xffffffff, + pmrx_intr_info, NULL)) + t3_fatal_err(adapter); +} + +/* + * CPL switch interrupt handler. + */ +static void cplsw_intr_handler(struct adapter *adapter) +{ + static const struct intr_info cplsw_intr_info[] = { +/* { F_CIM_OVFL_ERROR, "CPL switch CIM overflow", -1, 1 }, */ + {F_TP_FRAMING_ERROR, "CPL switch TP framing error", -1, 1}, + {F_SGE_FRAMING_ERROR, "CPL switch SGE framing error", -1, 1}, + {F_CIM_FRAMING_ERROR, "CPL switch CIM framing error", -1, 1}, + {F_ZERO_SWITCH_ERROR, "CPL switch no-switch error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_CPL_INTR_CAUSE, 0xffffffff, + cplsw_intr_info, NULL)) + t3_fatal_err(adapter); +} + +/* + * MPS interrupt handler. + */ +static void mps_intr_handler(struct adapter *adapter) +{ + static const struct intr_info mps_intr_info[] = { + {0x1ff, "MPS parity error", -1, 1}, + {0} + }; + + if (t3_handle_intr_status(adapter, A_MPS_INT_CAUSE, 0xffffffff, + mps_intr_info, NULL)) + t3_fatal_err(adapter); +} + +#define MC7_INTR_FATAL (F_UE | V_PE(M_PE) | F_AE) + +/* + * MC7 interrupt handler. + */ +static void mc7_intr_handler(struct mc7 *mc7) +{ + struct adapter *adapter = mc7->adapter; + u32 cause = t3_read_reg(adapter, mc7->offset + A_MC7_INT_CAUSE); + + if (cause & F_CE) { + mc7->stats.corr_err++; + CH_WARN(adapter, "%s MC7 correctable error at addr 0x%x, " + "data 0x%x 0x%x 0x%x\n", mc7->name, + t3_read_reg(adapter, mc7->offset + A_MC7_CE_ADDR), + t3_read_reg(adapter, mc7->offset + A_MC7_CE_DATA0), + t3_read_reg(adapter, mc7->offset + A_MC7_CE_DATA1), + t3_read_reg(adapter, mc7->offset + A_MC7_CE_DATA2)); + } + + if (cause & F_UE) { + mc7->stats.uncorr_err++; + CH_ALERT(adapter, "%s MC7 uncorrectable error at addr 0x%x, " + "data 0x%x 0x%x 0x%x\n", mc7->name, + t3_read_reg(adapter, mc7->offset + A_MC7_UE_ADDR), + t3_read_reg(adapter, mc7->offset + A_MC7_UE_DATA0), + t3_read_reg(adapter, mc7->offset + A_MC7_UE_DATA1), + t3_read_reg(adapter, mc7->offset + A_MC7_UE_DATA2)); + } + + if (G_PE(cause)) { + mc7->stats.parity_err++; + CH_ALERT(adapter, "%s MC7 parity error 0x%x\n", + mc7->name, G_PE(cause)); + } + + if (cause & F_AE) { + u32 addr = 0; + + if (adapter->params.rev > 0) + addr = t3_read_reg(adapter, + mc7->offset + A_MC7_ERR_ADDR); + mc7->stats.addr_err++; + CH_ALERT(adapter, "%s MC7 address error: 0x%x\n", + mc7->name, addr); + } + + if (cause & MC7_INTR_FATAL) + t3_fatal_err(adapter); + + t3_write_reg(adapter, mc7->offset + A_MC7_INT_CAUSE, cause); +} + +#define XGM_INTR_FATAL (V_TXFIFO_PRTY_ERR(M_TXFIFO_PRTY_ERR) | \ + V_RXFIFO_PRTY_ERR(M_RXFIFO_PRTY_ERR)) +/* + * XGMAC interrupt handler. + */ +static int mac_intr_handler(struct adapter *adap, unsigned int idx) +{ + struct cmac *mac = &adap2pinfo(adap, idx)->mac; + u32 cause = t3_read_reg(adap, A_XGM_INT_CAUSE + mac->offset); + + if (cause & V_TXFIFO_PRTY_ERR(M_TXFIFO_PRTY_ERR)) { + mac->stats.tx_fifo_parity_err++; + CH_ALERT(adap, "port%d: MAC TX FIFO parity error\n", idx); + } + if (cause & V_RXFIFO_PRTY_ERR(M_RXFIFO_PRTY_ERR)) { + mac->stats.rx_fifo_parity_err++; + CH_ALERT(adap, "port%d: MAC RX FIFO parity error\n", idx); + } + if (cause & F_TXFIFO_UNDERRUN) + mac->stats.tx_fifo_urun++; + if (cause & F_RXFIFO_OVERFLOW) + mac->stats.rx_fifo_ovfl++; + if (cause & V_SERDES_LOS(M_SERDES_LOS)) + mac->stats.serdes_signal_loss++; + if (cause & F_XAUIPCSCTCERR) + mac->stats.xaui_pcs_ctc_err++; + if (cause & F_XAUIPCSALIGNCHANGE) + mac->stats.xaui_pcs_align_change++; + + t3_write_reg(adap, A_XGM_INT_CAUSE + mac->offset, cause); + if (cause & XGM_INTR_FATAL) + t3_fatal_err(adap); + return cause != 0; +} + +/* + * Interrupt handler for PHY events. + */ +int t3_phy_intr_handler(struct adapter *adapter) +{ + static const int intr_gpio_bits[] = { 8, 0x20 }; + + u32 i, cause = t3_read_reg(adapter, A_T3DBG_INT_CAUSE); + + for_each_port(adapter, i) { + if (cause & intr_gpio_bits[i]) { + struct cphy *phy = &adap2pinfo(adapter, i)->phy; + int phy_cause = phy->ops->intr_handler(phy); + + if (phy_cause & cphy_cause_link_change) + t3_link_changed(adapter, i); + if (phy_cause & cphy_cause_fifo_error) + phy->fifo_errors++; + } + } + + t3_write_reg(adapter, A_T3DBG_INT_CAUSE, cause); + return 0; +} + +/* + * T3 slow path (non-data) interrupt handler. + */ +int t3_slow_intr_handler(struct adapter *adapter) +{ + u32 cause = t3_read_reg(adapter, A_PL_INT_CAUSE0); + + cause &= adapter->slow_intr_mask; + if (!cause) + return 0; + if (cause & F_PCIM0) { + if (is_pcie(adapter)) + pcie_intr_handler(adapter); + else + pci_intr_handler(adapter); + } + if (cause & F_SGE3) + t3_sge_err_intr_handler(adapter); + if (cause & F_MC7_PMRX) + mc7_intr_handler(&adapter->pmrx); + if (cause & F_MC7_PMTX) + mc7_intr_handler(&adapter->pmtx); + if (cause & F_MC7_CM) + mc7_intr_handler(&adapter->cm); + if (cause & F_CIM) + cim_intr_handler(adapter); + if (cause & F_TP1) + tp_intr_handler(adapter); + if (cause & F_ULP2_RX) + ulprx_intr_handler(adapter); + if (cause & F_ULP2_TX) + ulptx_intr_handler(adapter); + if (cause & F_PM1_RX) + pmrx_intr_handler(adapter); + if (cause & F_PM1_TX) + pmtx_intr_handler(adapter); + if (cause & F_CPL_SWITCH) + cplsw_intr_handler(adapter); + if (cause & F_MPS0) + mps_intr_handler(adapter); + if (cause & F_MC5A) + t3_mc5_intr_handler(&adapter->mc5); + if (cause & F_XGMAC0_0) + mac_intr_handler(adapter, 0); + if (cause & F_XGMAC0_1) + mac_intr_handler(adapter, 1); + if (cause & F_T3DBG) + t3_os_ext_intr_handler(adapter); + + /* Clear the interrupts just processed. */ + t3_write_reg(adapter, A_PL_INT_CAUSE0, cause); + t3_read_reg(adapter, A_PL_INT_CAUSE0); /* flush */ + return 1; +} + +/** + * t3_intr_enable - enable interrupts + * @adapter: the adapter whose interrupts should be enabled + * + * Enable interrupts by setting the interrupt enable registers of the + * various HW modules and then enabling the top-level interrupt + * concentrator. + */ +void t3_intr_enable(struct adapter *adapter) +{ + static const struct addr_val_pair intr_en_avp[] = { + {A_SG_INT_ENABLE, SGE_INTR_MASK}, + {A_MC7_INT_ENABLE, MC7_INTR_MASK}, + {A_MC7_INT_ENABLE - MC7_PMRX_BASE_ADDR + MC7_PMTX_BASE_ADDR, + MC7_INTR_MASK}, + {A_MC7_INT_ENABLE - MC7_PMRX_BASE_ADDR + MC7_CM_BASE_ADDR, + MC7_INTR_MASK}, + {A_MC5_DB_INT_ENABLE, MC5_INTR_MASK}, + {A_ULPRX_INT_ENABLE, ULPRX_INTR_MASK}, + {A_TP_INT_ENABLE, 0x3bfffff}, + {A_PM1_TX_INT_ENABLE, PMTX_INTR_MASK}, + {A_PM1_RX_INT_ENABLE, PMRX_INTR_MASK}, + {A_CIM_HOST_INT_ENABLE, CIM_INTR_MASK}, + {A_MPS_INT_ENABLE, MPS_INTR_MASK}, + }; + + adapter->slow_intr_mask = PL_INTR_MASK; + + t3_write_regs(adapter, intr_en_avp, ARRAY_SIZE(intr_en_avp), 0); + + if (adapter->params.rev > 0) { + t3_write_reg(adapter, A_CPL_INTR_ENABLE, + CPLSW_INTR_MASK | F_CIM_OVFL_ERROR); + t3_write_reg(adapter, A_ULPTX_INT_ENABLE, + ULPTX_INTR_MASK | F_PBL_BOUND_ERR_CH0 | + F_PBL_BOUND_ERR_CH1); + } else { + t3_write_reg(adapter, A_CPL_INTR_ENABLE, CPLSW_INTR_MASK); + t3_write_reg(adapter, A_ULPTX_INT_ENABLE, ULPTX_INTR_MASK); + } + + t3_write_reg(adapter, A_T3DBG_GPIO_ACT_LOW, + adapter_info(adapter)->gpio_intr); + t3_write_reg(adapter, A_T3DBG_INT_ENABLE, + adapter_info(adapter)->gpio_intr); + if (is_pcie(adapter)) + t3_write_reg(adapter, A_PCIE_INT_ENABLE, PCIE_INTR_MASK); + else + t3_write_reg(adapter, A_PCIX_INT_ENABLE, PCIX_INTR_MASK); + t3_write_reg(adapter, A_PL_INT_ENABLE0, adapter->slow_intr_mask); + t3_read_reg(adapter, A_PL_INT_ENABLE0); /* flush */ +} + +/** + * t3_intr_disable - disable a card's interrupts + * @adapter: the adapter whose interrupts should be disabled + * + * Disable interrupts. We only disable the top-level interrupt + * concentrator and the SGE data interrupts. + */ +void t3_intr_disable(struct adapter *adapter) +{ + t3_write_reg(adapter, A_PL_INT_ENABLE0, 0); + t3_read_reg(adapter, A_PL_INT_ENABLE0); /* flush */ + adapter->slow_intr_mask = 0; +} + +/** + * t3_intr_clear - clear all interrupts + * @adapter: the adapter whose interrupts should be cleared + * + * Clears all interrupts. + */ +void t3_intr_clear(struct adapter *adapter) +{ + static const unsigned int cause_reg_addr[] = { + A_SG_INT_CAUSE, + A_SG_RSPQ_FL_STATUS, + A_PCIX_INT_CAUSE, + A_MC7_INT_CAUSE, + A_MC7_INT_CAUSE - MC7_PMRX_BASE_ADDR + MC7_PMTX_BASE_ADDR, + A_MC7_INT_CAUSE - MC7_PMRX_BASE_ADDR + MC7_CM_BASE_ADDR, + A_CIM_HOST_INT_CAUSE, + A_TP_INT_CAUSE, + A_MC5_DB_INT_CAUSE, + A_ULPRX_INT_CAUSE, + A_ULPTX_INT_CAUSE, + A_CPL_INTR_CAUSE, + A_PM1_TX_INT_CAUSE, + A_PM1_RX_INT_CAUSE, + A_MPS_INT_CAUSE, + A_T3DBG_INT_CAUSE, + }; + unsigned int i; + + /* Clear PHY and MAC interrupts for each port. */ + for_each_port(adapter, i) + t3_port_intr_clear(adapter, i); + + for (i = 0; i < ARRAY_SIZE(cause_reg_addr); ++i) + t3_write_reg(adapter, cause_reg_addr[i], 0xffffffff); + + t3_write_reg(adapter, A_PL_INT_CAUSE0, 0xffffffff); + t3_read_reg(adapter, A_PL_INT_CAUSE0); /* flush */ +} + +/** + * t3_port_intr_enable - enable port-specific interrupts + * @adapter: associated adapter + * @idx: index of port whose interrupts should be enabled + * + * Enable port-specific (i.e., MAC and PHY) interrupts for the given + * adapter port. + */ +void t3_port_intr_enable(struct adapter *adapter, int idx) +{ + struct cphy *phy = &adap2pinfo(adapter, idx)->phy; + + t3_write_reg(adapter, XGM_REG(A_XGM_INT_ENABLE, idx), XGM_INTR_MASK); + t3_read_reg(adapter, XGM_REG(A_XGM_INT_ENABLE, idx)); /* flush */ + phy->ops->intr_enable(phy); +} + +/** + * t3_port_intr_disable - disable port-specific interrupts + * @adapter: associated adapter + * @idx: index of port whose interrupts should be disabled + * + * Disable port-specific (i.e., MAC and PHY) interrupts for the given + * adapter port. + */ +void t3_port_intr_disable(struct adapter *adapter, int idx) +{ + struct cphy *phy = &adap2pinfo(adapter, idx)->phy; + + t3_write_reg(adapter, XGM_REG(A_XGM_INT_ENABLE, idx), 0); + t3_read_reg(adapter, XGM_REG(A_XGM_INT_ENABLE, idx)); /* flush */ + phy->ops->intr_disable(phy); +} + +/** + * t3_port_intr_clear - clear port-specific interrupts + * @adapter: associated adapter + * @idx: index of port whose interrupts to clear + * + * Clear port-specific (i.e., MAC and PHY) interrupts for the given + * adapter port. + */ +void t3_port_intr_clear(struct adapter *adapter, int idx) +{ + struct cphy *phy = &adap2pinfo(adapter, idx)->phy; + + t3_write_reg(adapter, XGM_REG(A_XGM_INT_CAUSE, idx), 0xffffffff); + t3_read_reg(adapter, XGM_REG(A_XGM_INT_CAUSE, idx)); /* flush */ + phy->ops->intr_clear(phy); +} + +/** + * t3_sge_write_context - write an SGE context + * @adapter: the adapter + * @id: the context id + * @type: the context type + * + * Program an SGE context with the values already loaded in the + * CONTEXT_DATA? registers. + */ +static int t3_sge_write_context(struct adapter *adapter, unsigned int id, + unsigned int type) +{ + t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff); + t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff); + t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff); + t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id)); + return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1); +} + +/** + * t3_sge_init_ecntxt - initialize an SGE egress context + * @adapter: the adapter to configure + * @id: the context id + * @gts_enable: whether to enable GTS for the context + * @type: the egress context type + * @respq: associated response queue + * @base_addr: base address of queue + * @size: number of queue entries + * @token: uP token + * @gen: initial generation value for the context + * @cidx: consumer pointer + * + * Initialize an SGE egress context and make it ready for use. If the + * platform allows concurrent context operations, the caller is + * responsible for appropriate locking. + */ +int t3_sge_init_ecntxt(struct adapter *adapter, unsigned int id, int gts_enable, + enum sge_context_type type, int respq, u64 base_addr, + unsigned int size, unsigned int token, int gen, + unsigned int cidx) +{ + unsigned int credits = type == SGE_CNTXT_OFLD ? 0 : FW_WR_NUM; + + if (base_addr & 0xfff) /* must be 4K aligned */ + return -EINVAL; + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + base_addr >>= 12; + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, V_EC_INDEX(cidx) | + V_EC_CREDITS(credits) | V_EC_GTS(gts_enable)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA1, V_EC_SIZE(size) | + V_EC_BASE_LO(base_addr & 0xffff)); + base_addr >>= 16; + t3_write_reg(adapter, A_SG_CONTEXT_DATA2, base_addr); + base_addr >>= 32; + t3_write_reg(adapter, A_SG_CONTEXT_DATA3, + V_EC_BASE_HI(base_addr & 0xf) | V_EC_RESPQ(respq) | + V_EC_TYPE(type) | V_EC_GEN(gen) | V_EC_UP_TOKEN(token) | + F_EC_VALID); + return t3_sge_write_context(adapter, id, F_EGRESS); +} + +/** + * t3_sge_init_flcntxt - initialize an SGE free-buffer list context + * @adapter: the adapter to configure + * @id: the context id + * @gts_enable: whether to enable GTS for the context + * @base_addr: base address of queue + * @size: number of queue entries + * @bsize: size of each buffer for this queue + * @cong_thres: threshold to signal congestion to upstream producers + * @gen: initial generation value for the context + * @cidx: consumer pointer + * + * Initialize an SGE free list context and make it ready for use. The + * caller is responsible for ensuring only one context operation occurs + * at a time. + */ +int t3_sge_init_flcntxt(struct adapter *adapter, unsigned int id, + int gts_enable, u64 base_addr, unsigned int size, + unsigned int bsize, unsigned int cong_thres, int gen, + unsigned int cidx) +{ + if (base_addr & 0xfff) /* must be 4K aligned */ + return -EINVAL; + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + base_addr >>= 12; + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, base_addr); + base_addr >>= 32; + t3_write_reg(adapter, A_SG_CONTEXT_DATA1, + V_FL_BASE_HI((u32) base_addr) | + V_FL_INDEX_LO(cidx & M_FL_INDEX_LO)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA2, V_FL_SIZE(size) | + V_FL_GEN(gen) | V_FL_INDEX_HI(cidx >> 12) | + V_FL_ENTRY_SIZE_LO(bsize & M_FL_ENTRY_SIZE_LO)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA3, + V_FL_ENTRY_SIZE_HI(bsize >> (32 - S_FL_ENTRY_SIZE_LO)) | + V_FL_CONG_THRES(cong_thres) | V_FL_GTS(gts_enable)); + return t3_sge_write_context(adapter, id, F_FREELIST); +} + +/** + * t3_sge_init_rspcntxt - initialize an SGE response queue context + * @adapter: the adapter to configure + * @id: the context id + * @irq_vec_idx: MSI-X interrupt vector index, 0 if no MSI-X, -1 if no IRQ + * @base_addr: base address of queue + * @size: number of queue entries + * @fl_thres: threshold for selecting the normal or jumbo free list + * @gen: initial generation value for the context + * @cidx: consumer pointer + * + * Initialize an SGE response queue context and make it ready for use. + * The caller is responsible for ensuring only one context operation + * occurs at a time. + */ +int t3_sge_init_rspcntxt(struct adapter *adapter, unsigned int id, + int irq_vec_idx, u64 base_addr, unsigned int size, + unsigned int fl_thres, int gen, unsigned int cidx) +{ + unsigned int intr = 0; + + if (base_addr & 0xfff) /* must be 4K aligned */ + return -EINVAL; + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + base_addr >>= 12; + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, V_CQ_SIZE(size) | + V_CQ_INDEX(cidx)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA1, base_addr); + base_addr >>= 32; + if (irq_vec_idx >= 0) + intr = V_RQ_MSI_VEC(irq_vec_idx) | F_RQ_INTR_EN; + t3_write_reg(adapter, A_SG_CONTEXT_DATA2, + V_CQ_BASE_HI((u32) base_addr) | intr | V_RQ_GEN(gen)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA3, fl_thres); + return t3_sge_write_context(adapter, id, F_RESPONSEQ); +} + +/** + * t3_sge_init_cqcntxt - initialize an SGE completion queue context + * @adapter: the adapter to configure + * @id: the context id + * @base_addr: base address of queue + * @size: number of queue entries + * @rspq: response queue for async notifications + * @ovfl_mode: CQ overflow mode + * @credits: completion queue credits + * @credit_thres: the credit threshold + * + * Initialize an SGE completion queue context and make it ready for use. + * The caller is responsible for ensuring only one context operation + * occurs at a time. + */ +int t3_sge_init_cqcntxt(struct adapter *adapter, unsigned int id, u64 base_addr, + unsigned int size, int rspq, int ovfl_mode, + unsigned int credits, unsigned int credit_thres) +{ + if (base_addr & 0xfff) /* must be 4K aligned */ + return -EINVAL; + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + base_addr >>= 12; + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, V_CQ_SIZE(size)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA1, base_addr); + base_addr >>= 32; + t3_write_reg(adapter, A_SG_CONTEXT_DATA2, + V_CQ_BASE_HI((u32) base_addr) | V_CQ_RSPQ(rspq) | + V_CQ_GEN(1) | V_CQ_OVERFLOW_MODE(ovfl_mode)); + t3_write_reg(adapter, A_SG_CONTEXT_DATA3, V_CQ_CREDITS(credits) | + V_CQ_CREDIT_THRES(credit_thres)); + return t3_sge_write_context(adapter, id, F_CQ); +} + +/** + * t3_sge_enable_ecntxt - enable/disable an SGE egress context + * @adapter: the adapter + * @id: the egress context id + * @enable: enable (1) or disable (0) the context + * + * Enable or disable an SGE egress context. The caller is responsible for + * ensuring only one context operation occurs at a time. + */ +int t3_sge_enable_ecntxt(struct adapter *adapter, unsigned int id, int enable) +{ + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK3, F_EC_VALID); + t3_write_reg(adapter, A_SG_CONTEXT_DATA3, V_EC_VALID(enable)); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(1) | F_EGRESS | V_CONTEXT(id)); + return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1); +} + +/** + * t3_sge_disable_fl - disable an SGE free-buffer list + * @adapter: the adapter + * @id: the free list context id + * + * Disable an SGE free-buffer list. The caller is responsible for + * ensuring only one context operation occurs at a time. + */ +int t3_sge_disable_fl(struct adapter *adapter, unsigned int id) +{ + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK2, V_FL_SIZE(M_FL_SIZE)); + t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0); + t3_write_reg(adapter, A_SG_CONTEXT_DATA2, 0); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(1) | F_FREELIST | V_CONTEXT(id)); + return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1); +} + +/** + * t3_sge_disable_rspcntxt - disable an SGE response queue + * @adapter: the adapter + * @id: the response queue context id + * + * Disable an SGE response queue. The caller is responsible for + * ensuring only one context operation occurs at a time. + */ +int t3_sge_disable_rspcntxt(struct adapter *adapter, unsigned int id) +{ + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_MASK0, V_CQ_SIZE(M_CQ_SIZE)); + t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0); + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, 0); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(1) | F_RESPONSEQ | V_CONTEXT(id)); + return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1); +} + +/** + * t3_sge_disable_cqcntxt - disable an SGE completion queue + * @adapter: the adapter + * @id: the completion queue context id + * + * Disable an SGE completion queue. The caller is responsible for + * ensuring only one context operation occurs at a time. + */ +int t3_sge_disable_cqcntxt(struct adapter *adapter, unsigned int id) +{ + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_MASK0, V_CQ_SIZE(M_CQ_SIZE)); + t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0); + t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0); + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, 0); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(1) | F_CQ | V_CONTEXT(id)); + return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1); +} + +/** + * t3_sge_cqcntxt_op - perform an operation on a completion queue context + * @adapter: the adapter + * @id: the context id + * @op: the operation to perform + * + * Perform the selected operation on an SGE completion queue context. + * The caller is responsible for ensuring only one context operation + * occurs at a time. + */ +int t3_sge_cqcntxt_op(struct adapter *adapter, unsigned int id, unsigned int op, + unsigned int credits) +{ + u32 val; + + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_DATA0, credits << 16); + t3_write_reg(adapter, A_SG_CONTEXT_CMD, V_CONTEXT_CMD_OPCODE(op) | + V_CONTEXT(id) | F_CQ); + if (t3_wait_op_done_val(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, + 0, 5, 1, &val)) + return -EIO; + + if (op >= 2 && op < 7) { + if (adapter->params.rev > 0) + return G_CQ_INDEX(val); + + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(0) | F_CQ | V_CONTEXT(id)); + if (t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, + F_CONTEXT_CMD_BUSY, 0, 5, 1)) + return -EIO; + return G_CQ_INDEX(t3_read_reg(adapter, A_SG_CONTEXT_DATA0)); + } + return 0; +} + +/** + * t3_sge_read_context - read an SGE context + * @type: the context type + * @adapter: the adapter + * @id: the context id + * @data: holds the retrieved context + * + * Read an SGE egress context. The caller is responsible for ensuring + * only one context operation occurs at a time. + */ +static int t3_sge_read_context(unsigned int type, struct adapter *adapter, + unsigned int id, u32 data[4]) +{ + if (t3_read_reg(adapter, A_SG_CONTEXT_CMD) & F_CONTEXT_CMD_BUSY) + return -EBUSY; + + t3_write_reg(adapter, A_SG_CONTEXT_CMD, + V_CONTEXT_CMD_OPCODE(0) | type | V_CONTEXT(id)); + if (t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY, 0, + 5, 1)) + return -EIO; + data[0] = t3_read_reg(adapter, A_SG_CONTEXT_DATA0); + data[1] = t3_read_reg(adapter, A_SG_CONTEXT_DATA1); + data[2] = t3_read_reg(adapter, A_SG_CONTEXT_DATA2); + data[3] = t3_read_reg(adapter, A_SG_CONTEXT_DATA3); + return 0; +} + +/** + * t3_sge_read_ecntxt - read an SGE egress context + * @adapter: the adapter + * @id: the context id + * @data: holds the retrieved context + * + * Read an SGE egress context. The caller is responsible for ensuring + * only one context operation occurs at a time. + */ +int t3_sge_read_ecntxt(struct adapter *adapter, unsigned int id, u32 data[4]) +{ + if (id >= 65536) + return -EINVAL; + return t3_sge_read_context(F_EGRESS, adapter, id, data); +} + +/** + * t3_sge_read_cq - read an SGE CQ context + * @adapter: the adapter + * @id: the context id + * @data: holds the retrieved context + * + * Read an SGE CQ context. The caller is responsible for ensuring + * only one context operation occurs at a time. + */ +int t3_sge_read_cq(struct adapter *adapter, unsigned int id, u32 data[4]) +{ + if (id >= 65536) + return -EINVAL; + return t3_sge_read_context(F_CQ, adapter, id, data); +} + +/** + * t3_sge_read_fl - read an SGE free-list context + * @adapter: the adapter + * @id: the context id + * @data: holds the retrieved context + * + * Read an SGE free-list context. The caller is responsible for ensuring + * only one context operation occurs at a time. + */ +int t3_sge_read_fl(struct adapter *adapter, unsigned int id, u32 data[4]) +{ + if (id >= SGE_QSETS * 2) + return -EINVAL; + return t3_sge_read_context(F_FREELIST, adapter, id, data); +} + +/** + * t3_sge_read_rspq - read an SGE response queue context + * @adapter: the adapter + * @id: the context id + * @data: holds the retrieved context + * + * Read an SGE response queue context. The caller is responsible for + * ensuring only one context operation occurs at a time. + */ +int t3_sge_read_rspq(struct adapter *adapter, unsigned int id, u32 data[4]) +{ + if (id >= SGE_QSETS) + return -EINVAL; + return t3_sge_read_context(F_RESPONSEQ, adapter, id, data); +} + +/** + * t3_config_rss - configure Rx packet steering + * @adapter: the adapter + * @rss_config: RSS settings (written to TP_RSS_CONFIG) + * @cpus: values for the CPU lookup table (0xff terminated) + * @rspq: values for the response queue lookup table (0xffff terminated) + * + * Programs the receive packet steering logic. @cpus and @rspq provide + * the values for the CPU and response queue lookup tables. If they + * provide fewer values than the size of the tables the supplied values + * are used repeatedly until the tables are fully populated. + */ +void t3_config_rss(struct adapter *adapter, unsigned int rss_config, + const u8 * cpus, const u16 *rspq) +{ + int i, j, cpu_idx = 0, q_idx = 0; + + if (cpus) + for (i = 0; i < RSS_TABLE_SIZE; ++i) { + u32 val = i << 16; + + for (j = 0; j < 2; ++j) { + val |= (cpus[cpu_idx++] & 0x3f) << (8 * j); + if (cpus[cpu_idx] == 0xff) + cpu_idx = 0; + } + t3_write_reg(adapter, A_TP_RSS_LKP_TABLE, val); + } + + if (rspq) + for (i = 0; i < RSS_TABLE_SIZE; ++i) { + t3_write_reg(adapter, A_TP_RSS_MAP_TABLE, + (i << 16) | rspq[q_idx++]); + if (rspq[q_idx] == 0xffff) + q_idx = 0; + } + + t3_write_reg(adapter, A_TP_RSS_CONFIG, rss_config); +} + +/** + * t3_read_rss - read the contents of the RSS tables + * @adapter: the adapter + * @lkup: holds the contents of the RSS lookup table + * @map: holds the contents of the RSS map table + * + * Reads the contents of the receive packet steering tables. + */ +int t3_read_rss(struct adapter *adapter, u8 * lkup, u16 *map) +{ + int i; + u32 val; + + if (lkup) + for (i = 0; i < RSS_TABLE_SIZE; ++i) { + t3_write_reg(adapter, A_TP_RSS_LKP_TABLE, + 0xffff0000 | i); + val = t3_read_reg(adapter, A_TP_RSS_LKP_TABLE); + if (!(val & 0x80000000)) + return -EAGAIN; + *lkup++ = val; + *lkup++ = (val >> 8); + } + + if (map) + for (i = 0; i < RSS_TABLE_SIZE; ++i) { + t3_write_reg(adapter, A_TP_RSS_MAP_TABLE, + 0xffff0000 | i); + val = t3_read_reg(adapter, A_TP_RSS_MAP_TABLE); + if (!(val & 0x80000000)) + return -EAGAIN; + *map++ = val; + } + return 0; +} + +/** + * t3_tp_set_offload_mode - put TP in NIC/offload mode + * @adap: the adapter + * @enable: 1 to select offload mode, 0 for regular NIC + * + * Switches TP to NIC/offload mode. + */ +void t3_tp_set_offload_mode(struct adapter *adap, int enable) +{ + if (is_offload(adap) || !enable) + t3_set_reg_field(adap, A_TP_IN_CONFIG, F_NICMODE, + V_NICMODE(!enable)); +} + +/** + * pm_num_pages - calculate the number of pages of the payload memory + * @mem_size: the size of the payload memory + * @pg_size: the size of each payload memory page + * + * Calculate the number of pages, each of the given size, that fit in a + * memory of the specified size, respecting the HW requirement that the + * number of pages must be a multiple of 24. + */ +static inline unsigned int pm_num_pages(unsigned int mem_size, + unsigned int pg_size) +{ + unsigned int n = mem_size / pg_size; + + return n - n % 24; +} + +#define mem_region(adap, start, size, reg) \ + t3_write_reg((adap), A_ ## reg, (start)); \ + start += size + +/* + * partition_mem - partition memory and configure TP memory settings + * @adap: the adapter + * @p: the TP parameters + * + * Partitions context and payload memory and configures TP's memory + * registers. + */ +static void partition_mem(struct adapter *adap, const struct tp_params *p) +{ + unsigned int m, pstructs, tids = t3_mc5_size(&adap->mc5); + unsigned int timers = 0, timers_shift = 22; + + if (adap->params.rev > 0) { + if (tids <= 16 * 1024) { + timers = 1; + timers_shift = 16; + } else if (tids <= 64 * 1024) { + timers = 2; + timers_shift = 18; + } else if (tids <= 256 * 1024) { + timers = 3; + timers_shift = 20; + } + } + + t3_write_reg(adap, A_TP_PMM_SIZE, + p->chan_rx_size | (p->chan_tx_size >> 16)); + + t3_write_reg(adap, A_TP_PMM_TX_BASE, 0); + t3_write_reg(adap, A_TP_PMM_TX_PAGE_SIZE, p->tx_pg_size); + t3_write_reg(adap, A_TP_PMM_TX_MAX_PAGE, p->tx_num_pgs); + t3_set_reg_field(adap, A_TP_PARA_REG3, V_TXDATAACKIDX(M_TXDATAACKIDX), + V_TXDATAACKIDX(fls(p->tx_pg_size) - 12)); + + t3_write_reg(adap, A_TP_PMM_RX_BASE, 0); + t3_write_reg(adap, A_TP_PMM_RX_PAGE_SIZE, p->rx_pg_size); + t3_write_reg(adap, A_TP_PMM_RX_MAX_PAGE, p->rx_num_pgs); + + pstructs = p->rx_num_pgs + p->tx_num_pgs; + /* Add a bit of headroom and make multiple of 24 */ + pstructs += 48; + pstructs -= pstructs % 24; + t3_write_reg(adap, A_TP_CMM_MM_MAX_PSTRUCT, pstructs); + + m = tids * TCB_SIZE; + mem_region(adap, m, (64 << 10) * 64, SG_EGR_CNTX_BADDR); + mem_region(adap, m, (64 << 10) * 64, SG_CQ_CONTEXT_BADDR); + t3_write_reg(adap, A_TP_CMM_TIMER_BASE, V_CMTIMERMAXNUM(timers) | m); + m += ((p->ntimer_qs - 1) << timers_shift) + (1 << 22); + mem_region(adap, m, pstructs * 64, TP_CMM_MM_BASE); + mem_region(adap, m, 64 * (pstructs / 24), TP_CMM_MM_PS_FLST_BASE); + mem_region(adap, m, 64 * (p->rx_num_pgs / 24), TP_CMM_MM_RX_FLST_BASE); + mem_region(adap, m, 64 * (p->tx_num_pgs / 24), TP_CMM_MM_TX_FLST_BASE); + + m = (m + 4095) & ~0xfff; + t3_write_reg(adap, A_CIM_SDRAM_BASE_ADDR, m); + t3_write_reg(adap, A_CIM_SDRAM_ADDR_SIZE, p->cm_size - m); + + tids = (p->cm_size - m - (3 << 20)) / 3072 - 32; + m = t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers - + adap->params.mc5.nfilters - adap->params.mc5.nroutes; + if (tids < m) + adap->params.mc5.nservers += m - tids; +} + +static inline void tp_wr_indirect(struct adapter *adap, unsigned int addr, + u32 val) +{ + t3_write_reg(adap, A_TP_PIO_ADDR, addr); + t3_write_reg(adap, A_TP_PIO_DATA, val); +} + +static void tp_config(struct adapter *adap, const struct tp_params *p) +{ + unsigned int v; + + t3_write_reg(adap, A_TP_GLOBAL_CONFIG, F_TXPACINGENABLE | F_PATHMTU | + F_IPCHECKSUMOFFLOAD | F_UDPCHECKSUMOFFLOAD | + F_TCPCHECKSUMOFFLOAD | V_IPTTL(64)); + t3_write_reg(adap, A_TP_TCP_OPTIONS, V_MTUDEFAULT(576) | + F_MTUENABLE | V_WINDOWSCALEMODE(1) | + V_TIMESTAMPSMODE(1) | V_SACKMODE(1) | V_SACKRX(1)); + t3_write_reg(adap, A_TP_DACK_CONFIG, V_AUTOSTATE3(1) | + V_AUTOSTATE2(1) | V_AUTOSTATE1(0) | + V_BYTETHRESHOLD(16384) | V_MSSTHRESHOLD(2) | + F_AUTOCAREFUL | F_AUTOENABLE | V_DACK_MODE(1)); + t3_set_reg_field(adap, A_TP_IN_CONFIG, F_IPV6ENABLE | F_NICMODE, + F_IPV6ENABLE | F_NICMODE); + t3_write_reg(adap, A_TP_TX_RESOURCE_LIMIT, 0x18141814); + t3_write_reg(adap, A_TP_PARA_REG4, 0x5050105); + t3_set_reg_field(adap, A_TP_PARA_REG6, + adap->params.rev > 0 ? F_ENABLEESND : F_T3A_ENABLEESND, + 0); + + v = t3_read_reg(adap, A_TP_PC_CONFIG); + v &= ~(F_ENABLEEPCMDAFULL | F_ENABLEOCSPIFULL); + t3_write_reg(adap, A_TP_PC_CONFIG, v | F_TXDEFERENABLE | + F_MODULATEUNIONMODE | F_HEARBEATDACK | + F_TXCONGESTIONMODE | F_RXCONGESTIONMODE); + + v = t3_read_reg(adap, A_TP_PC_CONFIG2); + v &= ~F_CHDRAFULL; + t3_write_reg(adap, A_TP_PC_CONFIG2, v); + + if (adap->params.rev > 0) { + tp_wr_indirect(adap, A_TP_EGRESS_CONFIG, F_REWRITEFORCETOSIZE); + t3_set_reg_field(adap, A_TP_PARA_REG3, F_TXPACEAUTO, + F_TXPACEAUTO); + t3_set_reg_field(adap, A_TP_PC_CONFIG, F_LOCKTID, F_LOCKTID); + t3_set_reg_field(adap, A_TP_PARA_REG3, 0, F_TXPACEAUTOSTRICT); + } else + t3_set_reg_field(adap, A_TP_PARA_REG3, 0, F_TXPACEFIXED); + + t3_write_reg(adap, A_TP_TX_MOD_QUEUE_WEIGHT1, 0x12121212); + t3_write_reg(adap, A_TP_TX_MOD_QUEUE_WEIGHT0, 0x12121212); + t3_write_reg(adap, A_TP_MOD_CHANNEL_WEIGHT, 0x1212); +} + +/* Desired TP timer resolution in usec */ +#define TP_TMR_RES 50 + +/* TCP timer values in ms */ +#define TP_DACK_TIMER 50 +#define TP_RTO_MIN 250 + +/** + * tp_set_timers - set TP timing parameters + * @adap: the adapter to set + * @core_clk: the core clock frequency in Hz + * + * Set TP's timing parameters, such as the various timer resolutions and + * the TCP timer values. + */ +static void tp_set_timers(struct adapter *adap, unsigned int core_clk) +{ + unsigned int tre = fls(core_clk / (1000000 / TP_TMR_RES)) - 1; + unsigned int dack_re = fls(core_clk / 5000) - 1; /* 200us */ + unsigned int tstamp_re = fls(core_clk / 1000); /* 1ms, at least */ + unsigned int tps = core_clk >> tre; + + t3_write_reg(adap, A_TP_TIMER_RESOLUTION, V_TIMERRESOLUTION(tre) | + V_DELAYEDACKRESOLUTION(dack_re) | + V_TIMESTAMPRESOLUTION(tstamp_re)); + t3_write_reg(adap, A_TP_DACK_TIMER, + (core_clk >> dack_re) / (1000 / TP_DACK_TIMER)); + t3_write_reg(adap, A_TP_TCP_BACKOFF_REG0, 0x3020100); + t3_write_reg(adap, A_TP_TCP_BACKOFF_REG1, 0x7060504); + t3_write_reg(adap, A_TP_TCP_BACKOFF_REG2, 0xb0a0908); + t3_write_reg(adap, A_TP_TCP_BACKOFF_REG3, 0xf0e0d0c); + t3_write_reg(adap, A_TP_SHIFT_CNT, V_SYNSHIFTMAX(6) | + V_RXTSHIFTMAXR1(4) | V_RXTSHIFTMAXR2(15) | + V_PERSHIFTBACKOFFMAX(8) | V_PERSHIFTMAX(8) | + V_KEEPALIVEMAX(9)); + +#define SECONDS * tps + + t3_write_reg(adap, A_TP_MSL, adap->params.rev > 0 ? 0 : 2 SECONDS); + t3_write_reg(adap, A_TP_RXT_MIN, tps / (1000 / TP_RTO_MIN)); + t3_write_reg(adap, A_TP_RXT_MAX, 64 SECONDS); + t3_write_reg(adap, A_TP_PERS_MIN, 5 SECONDS); + t3_write_reg(adap, A_TP_PERS_MAX, 64 SECONDS); + t3_write_reg(adap, A_TP_KEEP_IDLE, 7200 SECONDS); + t3_write_reg(adap, A_TP_KEEP_INTVL, 75 SECONDS); + t3_write_reg(adap, A_TP_INIT_SRTT, 3 SECONDS); + t3_write_reg(adap, A_TP_FINWAIT2_TIMER, 600 SECONDS); + +#undef SECONDS +} + +/** + * t3_tp_set_coalescing_size - set receive coalescing size + * @adap: the adapter + * @size: the receive coalescing size + * @psh: whether a set PSH bit should deliver coalesced data + * + * Set the receive coalescing size and PSH bit handling. + */ +int t3_tp_set_coalescing_size(struct adapter *adap, unsigned int size, int psh) +{ + u32 val; + + if (size > MAX_RX_COALESCING_LEN) + return -EINVAL; + + val = t3_read_reg(adap, A_TP_PARA_REG3); + val &= ~(F_RXCOALESCEENABLE | F_RXCOALESCEPSHEN); + + if (size) { + val |= F_RXCOALESCEENABLE; + if (psh) + val |= F_RXCOALESCEPSHEN; + t3_write_reg(adap, A_TP_PARA_REG2, V_RXCOALESCESIZE(size) | + V_MAXRXDATA(MAX_RX_COALESCING_LEN)); + } + t3_write_reg(adap, A_TP_PARA_REG3, val); + return 0; +} + +/** + * t3_tp_set_max_rxsize - set the max receive size + * @adap: the adapter + * @size: the max receive size + * + * Set TP's max receive size. This is the limit that applies when + * receive coalescing is disabled. + */ +void t3_tp_set_max_rxsize(struct adapter *adap, unsigned int size) +{ + t3_write_reg(adap, A_TP_PARA_REG7, + V_PMMAXXFERLEN0(size) | V_PMMAXXFERLEN1(size)); +} + +static void __devinit init_mtus(unsigned short mtus[]) +{ + /* + * See draft-mathis-plpmtud-00.txt for the values. The min is 88 so + * it can accomodate max size TCP/IP headers when SACK and timestamps + * are enabled and still have at least 8 bytes of payload. + */ + mtus[0] = 88; + mtus[1] = 256; + mtus[2] = 512; + mtus[3] = 576; + mtus[4] = 808; + mtus[5] = 1024; + mtus[6] = 1280; + mtus[7] = 1492; + mtus[8] = 1500; + mtus[9] = 2002; + mtus[10] = 2048; + mtus[11] = 4096; + mtus[12] = 4352; + mtus[13] = 8192; + mtus[14] = 9000; + mtus[15] = 9600; +} + +/* + * Initial congestion control parameters. + */ +static void __devinit init_cong_ctrl(unsigned short *a, unsigned short *b) +{ + a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = a[8] = 1; + a[9] = 2; + a[10] = 3; + a[11] = 4; + a[12] = 5; + a[13] = 6; + a[14] = 7; + a[15] = 8; + a[16] = 9; + a[17] = 10; + a[18] = 14; + a[19] = 17; + a[20] = 21; + a[21] = 25; + a[22] = 30; + a[23] = 35; + a[24] = 45; + a[25] = 60; + a[26] = 80; + a[27] = 100; + a[28] = 200; + a[29] = 300; + a[30] = 400; + a[31] = 500; + + b[0] = b[1] = b[2] = b[3] = b[4] = b[5] = b[6] = b[7] = b[8] = 0; + b[9] = b[10] = 1; + b[11] = b[12] = 2; + b[13] = b[14] = b[15] = b[16] = 3; + b[17] = b[18] = b[19] = b[20] = b[21] = 4; + b[22] = b[23] = b[24] = b[25] = b[26] = b[27] = 5; + b[28] = b[29] = 6; + b[30] = b[31] = 7; +} + +/* The minimum additive increment value for the congestion control table */ +#define CC_MIN_INCR 2U + +/** + * t3_load_mtus - write the MTU and congestion control HW tables + * @adap: the adapter + * @mtus: the unrestricted values for the MTU table + * @alphs: the values for the congestion control alpha parameter + * @beta: the values for the congestion control beta parameter + * @mtu_cap: the maximum permitted effective MTU + * + * Write the MTU table with the supplied MTUs capping each at &mtu_cap. + * Update the high-speed congestion control table with the supplied alpha, + * beta, and MTUs. + */ +void t3_load_mtus(struct adapter *adap, unsigned short mtus[NMTUS], + unsigned short alpha[NCCTRL_WIN], + unsigned short beta[NCCTRL_WIN], unsigned short mtu_cap) +{ + static const unsigned int avg_pkts[NCCTRL_WIN] = { + 2, 6, 10, 14, 20, 28, 40, 56, 80, 112, 160, 224, 320, 448, 640, + 896, 1281, 1792, 2560, 3584, 5120, 7168, 10240, 14336, 20480, + 28672, 40960, 57344, 81920, 114688, 163840, 229376 + }; + + unsigned int i, w; + + for (i = 0; i < NMTUS; ++i) { + unsigned int mtu = min(mtus[i], mtu_cap); + unsigned int log2 = fls(mtu); + + if (!(mtu & ((1 << log2) >> 2))) /* round */ + log2--; + t3_write_reg(adap, A_TP_MTU_TABLE, + (i << 24) | (log2 << 16) | mtu); + + for (w = 0; w < NCCTRL_WIN; ++w) { + unsigned int inc; + + inc = max(((mtu - 40) * alpha[w]) / avg_pkts[w], + CC_MIN_INCR); + + t3_write_reg(adap, A_TP_CCTRL_TABLE, (i << 21) | + (w << 16) | (beta[w] << 13) | inc); + } + } +} + +/** + * t3_read_hw_mtus - returns the values in the HW MTU table + * @adap: the adapter + * @mtus: where to store the HW MTU values + * + * Reads the HW MTU table. + */ +void t3_read_hw_mtus(struct adapter *adap, unsigned short mtus[NMTUS]) +{ + int i; + + for (i = 0; i < NMTUS; ++i) { + unsigned int val; + + t3_write_reg(adap, A_TP_MTU_TABLE, 0xff000000 | i); + val = t3_read_reg(adap, A_TP_MTU_TABLE); + mtus[i] = val & 0x3fff; + } +} + +/** + * t3_get_cong_cntl_tab - reads the congestion control table + * @adap: the adapter + * @incr: where to store the alpha values + * + * Reads the additive increments programmed into the HW congestion + * control table. + */ +void t3_get_cong_cntl_tab(struct adapter *adap, + unsigned short incr[NMTUS][NCCTRL_WIN]) +{ + unsigned int mtu, w; + + for (mtu = 0; mtu < NMTUS; ++mtu) + for (w = 0; w < NCCTRL_WIN; ++w) { + t3_write_reg(adap, A_TP_CCTRL_TABLE, + 0xffff0000 | (mtu << 5) | w); + incr[mtu][w] = t3_read_reg(adap, A_TP_CCTRL_TABLE) & + 0x1fff; + } +} + +/** + * t3_tp_get_mib_stats - read TP's MIB counters + * @adap: the adapter + * @tps: holds the returned counter values + * + * Returns the values of TP's MIB counters. + */ +void t3_tp_get_mib_stats(struct adapter *adap, struct tp_mib_stats *tps) +{ + t3_read_indirect(adap, A_TP_MIB_INDEX, A_TP_MIB_RDATA, (u32 *) tps, + sizeof(*tps) / sizeof(u32), 0); +} + +#define ulp_region(adap, name, start, len) \ + t3_write_reg((adap), A_ULPRX_ ## name ## _LLIMIT, (start)); \ + t3_write_reg((adap), A_ULPRX_ ## name ## _ULIMIT, \ + (start) + (len) - 1); \ + start += len + +#define ulptx_region(adap, name, start, len) \ + t3_write_reg((adap), A_ULPTX_ ## name ## _LLIMIT, (start)); \ + t3_write_reg((adap), A_ULPTX_ ## name ## _ULIMIT, \ + (start) + (len) - 1) + +static void ulp_config(struct adapter *adap, const struct tp_params *p) +{ + unsigned int m = p->chan_rx_size; + + ulp_region(adap, ISCSI, m, p->chan_rx_size / 8); + ulp_region(adap, TDDP, m, p->chan_rx_size / 8); + ulptx_region(adap, TPT, m, p->chan_rx_size / 4); + ulp_region(adap, STAG, m, p->chan_rx_size / 4); + ulp_region(adap, RQ, m, p->chan_rx_size / 4); + ulptx_region(adap, PBL, m, p->chan_rx_size / 4); + ulp_region(adap, PBL, m, p->chan_rx_size / 4); + t3_write_reg(adap, A_ULPRX_TDDP_TAGMASK, 0xffffffff); +} + +void t3_config_trace_filter(struct adapter *adapter, + const struct trace_params *tp, int filter_index, + int invert, int enable) +{ + u32 addr, key[4], mask[4]; + + key[0] = tp->sport | (tp->sip << 16); + key[1] = (tp->sip >> 16) | (tp->dport << 16); + key[2] = tp->dip; + key[3] = tp->proto | (tp->vlan << 8) | (tp->intf << 20); + + mask[0] = tp->sport_mask | (tp->sip_mask << 16); + mask[1] = (tp->sip_mask >> 16) | (tp->dport_mask << 16); + mask[2] = tp->dip_mask; + mask[3] = tp->proto_mask | (tp->vlan_mask << 8) | (tp->intf_mask << 20); + + if (invert) + key[3] |= (1 << 29); + if (enable) + key[3] |= (1 << 28); + + addr = filter_index ? A_TP_RX_TRC_KEY0 : A_TP_TX_TRC_KEY0; + tp_wr_indirect(adapter, addr++, key[0]); + tp_wr_indirect(adapter, addr++, mask[0]); + tp_wr_indirect(adapter, addr++, key[1]); + tp_wr_indirect(adapter, addr++, mask[1]); + tp_wr_indirect(adapter, addr++, key[2]); + tp_wr_indirect(adapter, addr++, mask[2]); + tp_wr_indirect(adapter, addr++, key[3]); + tp_wr_indirect(adapter, addr, mask[3]); + t3_read_reg(adapter, A_TP_PIO_DATA); +} + +/** + * t3_config_sched - configure a HW traffic scheduler + * @adap: the adapter + * @kbps: target rate in Kbps + * @sched: the scheduler index + * + * Configure a HW scheduler for the target rate + */ +int t3_config_sched(struct adapter *adap, unsigned int kbps, int sched) +{ + unsigned int v, tps, cpt, bpt, delta, mindelta = ~0; + unsigned int clk = adap->params.vpd.cclk * 1000; + unsigned int selected_cpt = 0, selected_bpt = 0; + + if (kbps > 0) { + kbps *= 125; /* -> bytes */ + for (cpt = 1; cpt <= 255; cpt++) { + tps = clk / cpt; + bpt = (kbps + tps / 2) / tps; + if (bpt > 0 && bpt <= 255) { + v = bpt * tps; + delta = v >= kbps ? v - kbps : kbps - v; + if (delta <= mindelta) { + mindelta = delta; + selected_cpt = cpt; + selected_bpt = bpt; + } + } else if (selected_cpt) + break; + } + if (!selected_cpt) + return -EINVAL; + } + t3_write_reg(adap, A_TP_TM_PIO_ADDR, + A_TP_TX_MOD_Q1_Q0_RATE_LIMIT - sched / 2); + v = t3_read_reg(adap, A_TP_TM_PIO_DATA); + if (sched & 1) + v = (v & 0xffff) | (selected_cpt << 16) | (selected_bpt << 24); + else + v = (v & 0xffff0000) | selected_cpt | (selected_bpt << 8); + t3_write_reg(adap, A_TP_TM_PIO_DATA, v); + return 0; +} + +static int tp_init(struct adapter *adap, const struct tp_params *p) +{ + int busy = 0; + + tp_config(adap, p); + t3_set_vlan_accel(adap, 3, 0); + + if (is_offload(adap)) { + tp_set_timers(adap, adap->params.vpd.cclk * 1000); + t3_write_reg(adap, A_TP_RESET, F_FLSTINITENABLE); + busy = t3_wait_op_done(adap, A_TP_RESET, F_FLSTINITENABLE, + 0, 1000, 5); + if (busy) + CH_ERR(adap, "TP initialization timed out\n"); + } + + if (!busy) + t3_write_reg(adap, A_TP_RESET, F_TPRESET); + return busy; +} + +int t3_mps_set_active_ports(struct adapter *adap, unsigned int port_mask) +{ + if (port_mask & ~((1 << adap->params.nports) - 1)) + return -EINVAL; + t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE | F_PORT0ACTIVE, + port_mask << S_PORT0ACTIVE); + return 0; +} + +/* + * Perform the bits of HW initialization that are dependent on the number + * of available ports. + */ +static void init_hw_for_avail_ports(struct adapter *adap, int nports) +{ + int i; + + if (nports == 1) { + t3_set_reg_field(adap, A_ULPRX_CTL, F_ROUND_ROBIN, 0); + t3_set_reg_field(adap, A_ULPTX_CONFIG, F_CFG_RR_ARB, 0); + t3_write_reg(adap, A_MPS_CFG, F_TPRXPORTEN | F_TPTXPORT0EN | + F_PORT0ACTIVE | F_ENFORCEPKT); + t3_write_reg(adap, A_PM1_TX_CFG, 0xc000c000); + } else { + t3_set_reg_field(adap, A_ULPRX_CTL, 0, F_ROUND_ROBIN); + t3_set_reg_field(adap, A_ULPTX_CONFIG, 0, F_CFG_RR_ARB); + t3_write_reg(adap, A_ULPTX_DMA_WEIGHT, + V_D1_WEIGHT(16) | V_D0_WEIGHT(16)); + t3_write_reg(adap, A_MPS_CFG, F_TPTXPORT0EN | F_TPTXPORT1EN | + F_TPRXPORTEN | F_PORT0ACTIVE | F_PORT1ACTIVE | + F_ENFORCEPKT); + t3_write_reg(adap, A_PM1_TX_CFG, 0x80008000); + t3_set_reg_field(adap, A_TP_PC_CONFIG, 0, F_TXTOSQUEUEMAPMODE); + t3_write_reg(adap, A_TP_TX_MOD_QUEUE_REQ_MAP, + V_TX_MOD_QUEUE_REQ_MAP(0xaa)); + for (i = 0; i < 16; i++) + t3_write_reg(adap, A_TP_TX_MOD_QUE_TABLE, + (i << 16) | 0x1010); + } +} + +static int calibrate_xgm(struct adapter *adapter) +{ + if (uses_xaui(adapter)) { + unsigned int v, i; + + for (i = 0; i < 5; ++i) { + t3_write_reg(adapter, A_XGM_XAUI_IMP, 0); + t3_read_reg(adapter, A_XGM_XAUI_IMP); + msleep(1); + v = t3_read_reg(adapter, A_XGM_XAUI_IMP); + if (!(v & (F_XGM_CALFAULT | F_CALBUSY))) { + t3_write_reg(adapter, A_XGM_XAUI_IMP, + V_XAUIIMP(G_CALIMP(v) >> 2)); + return 0; + } + } + CH_ERR(adapter, "MAC calibration failed\n"); + return -1; + } else { + t3_write_reg(adapter, A_XGM_RGMII_IMP, + V_RGMIIIMPPD(2) | V_RGMIIIMPPU(3)); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, F_XGM_IMPSETUPDATE, + F_XGM_IMPSETUPDATE); + } + return 0; +} + +static void calibrate_xgm_t3b(struct adapter *adapter) +{ + if (!uses_xaui(adapter)) { + t3_write_reg(adapter, A_XGM_RGMII_IMP, F_CALRESET | + F_CALUPDATE | V_RGMIIIMPPD(2) | V_RGMIIIMPPU(3)); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, F_CALRESET, 0); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, 0, + F_XGM_IMPSETUPDATE); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, F_XGM_IMPSETUPDATE, + 0); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, F_CALUPDATE, 0); + t3_set_reg_field(adapter, A_XGM_RGMII_IMP, 0, F_CALUPDATE); + } +} + +struct mc7_timing_params { + unsigned char ActToPreDly; + unsigned char ActToRdWrDly; + unsigned char PreCyc; + unsigned char RefCyc[5]; + unsigned char BkCyc; + unsigned char WrToRdDly; + unsigned char RdToWrDly; +}; + +/* + * Write a value to a register and check that the write completed. These + * writes normally complete in a cycle or two, so one read should suffice. + * The very first read exists to flush the posted write to the device. + */ +static int wrreg_wait(struct adapter *adapter, unsigned int addr, u32 val) +{ + t3_write_reg(adapter, addr, val); + t3_read_reg(adapter, addr); /* flush */ + if (!(t3_read_reg(adapter, addr) & F_BUSY)) + return 0; + CH_ERR(adapter, "write to MC7 register 0x%x timed out\n", addr); + return -EIO; +} + +static int mc7_init(struct mc7 *mc7, unsigned int mc7_clock, int mem_type) +{ + static const unsigned int mc7_mode[] = { + 0x632, 0x642, 0x652, 0x432, 0x442 + }; + static const struct mc7_timing_params mc7_timings[] = { + {12, 3, 4, {20, 28, 34, 52, 0}, 15, 6, 4}, + {12, 4, 5, {20, 28, 34, 52, 0}, 16, 7, 4}, + {12, 5, 6, {20, 28, 34, 52, 0}, 17, 8, 4}, + {9, 3, 4, {15, 21, 26, 39, 0}, 12, 6, 4}, + {9, 4, 5, {15, 21, 26, 39, 0}, 13, 7, 4} + }; + + u32 val; + unsigned int width, density, slow, attempts; + struct adapter *adapter = mc7->adapter; + const struct mc7_timing_params *p = &mc7_timings[mem_type]; + + val = t3_read_reg(adapter, mc7->offset + A_MC7_CFG); + slow = val & F_SLOW; + width = G_WIDTH(val); + density = G_DEN(val); + + t3_write_reg(adapter, mc7->offset + A_MC7_CFG, val | F_IFEN); + val = t3_read_reg(adapter, mc7->offset + A_MC7_CFG); /* flush */ + msleep(1); + + if (!slow) { + t3_write_reg(adapter, mc7->offset + A_MC7_CAL, F_SGL_CAL_EN); + t3_read_reg(adapter, mc7->offset + A_MC7_CAL); + msleep(1); + if (t3_read_reg(adapter, mc7->offset + A_MC7_CAL) & + (F_BUSY | F_SGL_CAL_EN | F_CAL_FAULT)) { + CH_ERR(adapter, "%s MC7 calibration timed out\n", + mc7->name); + goto out_fail; + } + } + + t3_write_reg(adapter, mc7->offset + A_MC7_PARM, + V_ACTTOPREDLY(p->ActToPreDly) | + V_ACTTORDWRDLY(p->ActToRdWrDly) | V_PRECYC(p->PreCyc) | + V_REFCYC(p->RefCyc[density]) | V_BKCYC(p->BkCyc) | + V_WRTORDDLY(p->WrToRdDly) | V_RDTOWRDLY(p->RdToWrDly)); + + t3_write_reg(adapter, mc7->offset + A_MC7_CFG, + val | F_CLKEN | F_TERM150); + t3_read_reg(adapter, mc7->offset + A_MC7_CFG); /* flush */ + + if (!slow) + t3_set_reg_field(adapter, mc7->offset + A_MC7_DLL, F_DLLENB, + F_DLLENB); + udelay(1); + + val = slow ? 3 : 6; + if (wrreg_wait(adapter, mc7->offset + A_MC7_PRE, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_EXT_MODE2, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_EXT_MODE3, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_EXT_MODE1, val)) + goto out_fail; + + if (!slow) { + t3_write_reg(adapter, mc7->offset + A_MC7_MODE, 0x100); + t3_set_reg_field(adapter, mc7->offset + A_MC7_DLL, F_DLLRST, 0); + udelay(5); + } + + if (wrreg_wait(adapter, mc7->offset + A_MC7_PRE, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_REF, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_REF, 0) || + wrreg_wait(adapter, mc7->offset + A_MC7_MODE, + mc7_mode[mem_type]) || + wrreg_wait(adapter, mc7->offset + A_MC7_EXT_MODE1, val | 0x380) || + wrreg_wait(adapter, mc7->offset + A_MC7_EXT_MODE1, val)) + goto out_fail; + + /* clock value is in KHz */ + mc7_clock = mc7_clock * 7812 + mc7_clock / 2; /* ns */ + mc7_clock /= 1000000; /* KHz->MHz, ns->us */ + + t3_write_reg(adapter, mc7->offset + A_MC7_REF, + F_PERREFEN | V_PREREFDIV(mc7_clock)); + t3_read_reg(adapter, mc7->offset + A_MC7_REF); /* flush */ + + t3_write_reg(adapter, mc7->offset + A_MC7_ECC, F_ECCGENEN | F_ECCCHKEN); + t3_write_reg(adapter, mc7->offset + A_MC7_BIST_DATA, 0); + t3_write_reg(adapter, mc7->offset + A_MC7_BIST_ADDR_BEG, 0); + t3_write_reg(adapter, mc7->offset + A_MC7_BIST_ADDR_END, + (mc7->size << width) - 1); + t3_write_reg(adapter, mc7->offset + A_MC7_BIST_OP, V_OP(1)); + t3_read_reg(adapter, mc7->offset + A_MC7_BIST_OP); /* flush */ + + attempts = 50; + do { + msleep(250); + val = t3_read_reg(adapter, mc7->offset + A_MC7_BIST_OP); + } while ((val & F_BUSY) && --attempts); + if (val & F_BUSY) { + CH_ERR(adapter, "%s MC7 BIST timed out\n", mc7->name); + goto out_fail; + } + + /* Enable normal memory accesses. */ + t3_set_reg_field(adapter, mc7->offset + A_MC7_CFG, 0, F_RDY); + return 0; + +out_fail: + return -1; +} + +static void config_pcie(struct adapter *adap) +{ + static const u16 ack_lat[4][6] = { + {237, 416, 559, 1071, 2095, 4143}, + {128, 217, 289, 545, 1057, 2081}, + {73, 118, 154, 282, 538, 1050}, + {67, 107, 86, 150, 278, 534} + }; + static const u16 rpl_tmr[4][6] = { + {711, 1248, 1677, 3213, 6285, 12429}, + {384, 651, 867, 1635, 3171, 6243}, + {219, 354, 462, 846, 1614, 3150}, + {201, 321, 258, 450, 834, 1602} + }; + + u16 val; + unsigned int log2_width, pldsize; + unsigned int fst_trn_rx, fst_trn_tx, acklat, rpllmt; + + pci_read_config_word(adap->pdev, + adap->params.pci.pcie_cap_addr + PCI_EXP_DEVCTL, + &val); + pldsize = (val & PCI_EXP_DEVCTL_PAYLOAD) >> 5; + pci_read_config_word(adap->pdev, + adap->params.pci.pcie_cap_addr + PCI_EXP_LNKCTL, + &val); + + fst_trn_tx = G_NUMFSTTRNSEQ(t3_read_reg(adap, A_PCIE_PEX_CTRL0)); + fst_trn_rx = adap->params.rev == 0 ? fst_trn_tx : + G_NUMFSTTRNSEQRX(t3_read_reg(adap, A_PCIE_MODE)); + log2_width = fls(adap->params.pci.width) - 1; + acklat = ack_lat[log2_width][pldsize]; + if (val & 1) /* check LOsEnable */ + acklat += fst_trn_tx * 4; + rpllmt = rpl_tmr[log2_width][pldsize] + fst_trn_rx * 4; + + if (adap->params.rev == 0) + t3_set_reg_field(adap, A_PCIE_PEX_CTRL1, + V_T3A_ACKLAT(M_T3A_ACKLAT), + V_T3A_ACKLAT(acklat)); + else + t3_set_reg_field(adap, A_PCIE_PEX_CTRL1, V_ACKLAT(M_ACKLAT), + V_ACKLAT(acklat)); + + t3_set_reg_field(adap, A_PCIE_PEX_CTRL0, V_REPLAYLMT(M_REPLAYLMT), + V_REPLAYLMT(rpllmt)); + + t3_write_reg(adap, A_PCIE_PEX_ERR, 0xffffffff); + t3_set_reg_field(adap, A_PCIE_CFG, F_PCIE_CLIDECEN, F_PCIE_CLIDECEN); +} + +/* + * Initialize and configure T3 HW modules. This performs the + * initialization steps that need to be done once after a card is reset. + * MAC and PHY initialization is handled separarely whenever a port is enabled. + * + * fw_params are passed to FW and their value is platform dependent. Only the + * top 8 bits are available for use, the rest must be 0. + */ +int t3_init_hw(struct adapter *adapter, u32 fw_params) +{ + int err = -EIO, attempts = 100; + const struct vpd_params *vpd = &adapter->params.vpd; + + if (adapter->params.rev > 0) + calibrate_xgm_t3b(adapter); + else if (calibrate_xgm(adapter)) + goto out_err; + + if (vpd->mclk) { + partition_mem(adapter, &adapter->params.tp); + + if (mc7_init(&adapter->pmrx, vpd->mclk, vpd->mem_timing) || + mc7_init(&adapter->pmtx, vpd->mclk, vpd->mem_timing) || + mc7_init(&adapter->cm, vpd->mclk, vpd->mem_timing) || + t3_mc5_init(&adapter->mc5, adapter->params.mc5.nservers, + adapter->params.mc5.nfilters, + adapter->params.mc5.nroutes)) + goto out_err; + } + + if (tp_init(adapter, &adapter->params.tp)) + goto out_err; + + t3_tp_set_coalescing_size(adapter, + min(adapter->params.sge.max_pkt_size, + MAX_RX_COALESCING_LEN), 1); + t3_tp_set_max_rxsize(adapter, + min(adapter->params.sge.max_pkt_size, 16384U)); + ulp_config(adapter, &adapter->params.tp); + + if (is_pcie(adapter)) + config_pcie(adapter); + else + t3_set_reg_field(adapter, A_PCIX_CFG, 0, F_CLIDECEN); + + t3_write_reg(adapter, A_PM1_RX_CFG, 0xf000f000); + init_hw_for_avail_ports(adapter, adapter->params.nports); + t3_sge_init(adapter, &adapter->params.sge); + + t3_write_reg(adapter, A_CIM_HOST_ACC_DATA, vpd->uclk | fw_params); + t3_write_reg(adapter, A_CIM_BOOT_CFG, + V_BOOTADDR(FW_FLASH_BOOT_ADDR >> 2)); + t3_read_reg(adapter, A_CIM_BOOT_CFG); /* flush */ + + do { /* wait for uP to initialize */ + msleep(20); + } while (t3_read_reg(adapter, A_CIM_HOST_ACC_DATA) && --attempts); + if (!attempts) + goto out_err; + + err = 0; +out_err: + return err; +} + +/** + * get_pci_mode - determine a card's PCI mode + * @adapter: the adapter + * @p: where to store the PCI settings + * + * Determines a card's PCI mode and associated parameters, such as speed + * and width. + */ +static void __devinit get_pci_mode(struct adapter *adapter, + struct pci_params *p) +{ + static unsigned short speed_map[] = { 33, 66, 100, 133 }; + u32 pci_mode, pcie_cap; + + pcie_cap = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP); + if (pcie_cap) { + u16 val; + + p->variant = PCI_VARIANT_PCIE; + p->pcie_cap_addr = pcie_cap; + pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA, + &val); + p->width = (val >> 4) & 0x3f; + return; + } + + pci_mode = t3_read_reg(adapter, A_PCIX_MODE); + p->speed = speed_map[G_PCLKRANGE(pci_mode)]; + p->width = (pci_mode & F_64BIT) ? 64 : 32; + pci_mode = G_PCIXINITPAT(pci_mode); + if (pci_mode == 0) + p->variant = PCI_VARIANT_PCI; + else if (pci_mode < 4) + p->variant = PCI_VARIANT_PCIX_MODE1_PARITY; + else if (pci_mode < 8) + p->variant = PCI_VARIANT_PCIX_MODE1_ECC; + else + p->variant = PCI_VARIANT_PCIX_266_MODE2; +} + +/** + * init_link_config - initialize a link's SW state + * @lc: structure holding the link state + * @ai: information about the current card + * + * Initializes the SW state maintained for each link, including the link's + * capabilities and default speed/duplex/flow-control/autonegotiation + * settings. + */ +static void __devinit init_link_config(struct link_config *lc, + unsigned int caps) +{ + lc->supported = caps; + lc->requested_speed = lc->speed = SPEED_INVALID; + lc->requested_duplex = lc->duplex = DUPLEX_INVALID; + lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; + if (lc->supported & SUPPORTED_Autoneg) { + lc->advertising = lc->supported; + lc->autoneg = AUTONEG_ENABLE; + lc->requested_fc |= PAUSE_AUTONEG; + } else { + lc->advertising = 0; + lc->autoneg = AUTONEG_DISABLE; + } +} + +/** + * mc7_calc_size - calculate MC7 memory size + * @cfg: the MC7 configuration + * + * Calculates the size of an MC7 memory in bytes from the value of its + * configuration register. + */ +static unsigned int __devinit mc7_calc_size(u32 cfg) +{ + unsigned int width = G_WIDTH(cfg); + unsigned int banks = !!(cfg & F_BKS) + 1; + unsigned int org = !!(cfg & F_ORG) + 1; + unsigned int density = G_DEN(cfg); + unsigned int MBs = ((256 << density) * banks) / (org << width); + + return MBs << 20; +} + +static void __devinit mc7_prep(struct adapter *adapter, struct mc7 *mc7, + unsigned int base_addr, const char *name) +{ + u32 cfg; + + mc7->adapter = adapter; + mc7->name = name; + mc7->offset = base_addr - MC7_PMRX_BASE_ADDR; + cfg = t3_read_reg(adapter, mc7->offset + A_MC7_CFG); + mc7->size = mc7_calc_size(cfg); + mc7->width = G_WIDTH(cfg); +} + +void mac_prep(struct cmac *mac, struct adapter *adapter, int index) +{ + mac->adapter = adapter; + mac->offset = (XGMAC0_1_BASE_ADDR - XGMAC0_0_BASE_ADDR) * index; + mac->nucast = 1; + + if (adapter->params.rev == 0 && uses_xaui(adapter)) { + t3_write_reg(adapter, A_XGM_SERDES_CTRL + mac->offset, + is_10G(adapter) ? 0x2901c04 : 0x2301c04); + t3_set_reg_field(adapter, A_XGM_PORT_CFG + mac->offset, + F_ENRGMII, 0); + } +} + +void early_hw_init(struct adapter *adapter, const struct adapter_info *ai) +{ + u32 val = V_PORTSPEED(is_10G(adapter) ? 3 : 2); + + mi1_init(adapter, ai); + t3_write_reg(adapter, A_I2C_CFG, /* set for 80KHz */ + V_I2C_CLKDIV(adapter->params.vpd.cclk / 80 - 1)); + t3_write_reg(adapter, A_T3DBG_GPIO_EN, + ai->gpio_out | F_GPIO0_OEN | F_GPIO0_OUT_VAL); + + if (adapter->params.rev == 0 || !uses_xaui(adapter)) + val |= F_ENRGMII; + + /* Enable MAC clocks so we can access the registers */ + t3_write_reg(adapter, A_XGM_PORT_CFG, val); + t3_read_reg(adapter, A_XGM_PORT_CFG); + + val |= F_CLKDIVRESET_; + t3_write_reg(adapter, A_XGM_PORT_CFG, val); + t3_read_reg(adapter, A_XGM_PORT_CFG); + t3_write_reg(adapter, XGM_REG(A_XGM_PORT_CFG, 1), val); + t3_read_reg(adapter, A_XGM_PORT_CFG); +} + +/* + * Reset the adapter. PCIe cards lose their config space during reset, PCI-X + * ones don't. + */ +int t3_reset_adapter(struct adapter *adapter) +{ + int i; + uint16_t devid = 0; + + if (is_pcie(adapter)) + pci_save_state(adapter->pdev); + t3_write_reg(adapter, A_PL_RST, F_CRSTWRM | F_CRSTWRMMODE); + + /* + * Delay. Give Some time to device to reset fully. + * XXX The delay time should be modified. + */ + for (i = 0; i < 10; i++) { + msleep(50); + pci_read_config_word(adapter->pdev, 0x00, &devid); + if (devid == 0x1425) + break; + } + + if (devid != 0x1425) + return -1; + + if (is_pcie(adapter)) + pci_restore_state(adapter->pdev); + return 0; +} + +/* + * Initialize adapter SW state for the various HW modules, set initial values + * for some adapter tunables, take PHYs out of reset, and initialize the MDIO + * interface. + */ +int __devinit t3_prep_adapter(struct adapter *adapter, + const struct adapter_info *ai, int reset) +{ + int ret; + unsigned int i, j = 0; + + get_pci_mode(adapter, &adapter->params.pci); + + adapter->params.info = ai; + adapter->params.nports = ai->nports; + adapter->params.rev = t3_read_reg(adapter, A_PL_REV); + adapter->params.linkpoll_period = 0; + adapter->params.stats_update_period = is_10G(adapter) ? + MAC_STATS_ACCUM_SECS : (MAC_STATS_ACCUM_SECS * 10); + adapter->params.pci.vpd_cap_addr = + pci_find_capability(adapter->pdev, PCI_CAP_ID_VPD); + ret = get_vpd_params(adapter, &adapter->params.vpd); + if (ret < 0) + return ret; + + if (reset && t3_reset_adapter(adapter)) + return -1; + + t3_sge_prep(adapter, &adapter->params.sge); + + if (adapter->params.vpd.mclk) { + struct tp_params *p = &adapter->params.tp; + + mc7_prep(adapter, &adapter->pmrx, MC7_PMRX_BASE_ADDR, "PMRX"); + mc7_prep(adapter, &adapter->pmtx, MC7_PMTX_BASE_ADDR, "PMTX"); + mc7_prep(adapter, &adapter->cm, MC7_CM_BASE_ADDR, "CM"); + + p->nchan = ai->nports; + p->pmrx_size = t3_mc7_size(&adapter->pmrx); + p->pmtx_size = t3_mc7_size(&adapter->pmtx); + p->cm_size = t3_mc7_size(&adapter->cm); + p->chan_rx_size = p->pmrx_size / 2; /* only 1 Rx channel */ + p->chan_tx_size = p->pmtx_size / p->nchan; + p->rx_pg_size = 64 * 1024; + p->tx_pg_size = is_10G(adapter) ? 64 * 1024 : 16 * 1024; + p->rx_num_pgs = pm_num_pages(p->chan_rx_size, p->rx_pg_size); + p->tx_num_pgs = pm_num_pages(p->chan_tx_size, p->tx_pg_size); + p->ntimer_qs = p->cm_size >= (128 << 20) || + adapter->params.rev > 0 ? 12 : 6; + + adapter->params.mc5.nservers = DEFAULT_NSERVERS; + adapter->params.mc5.nfilters = adapter->params.rev > 0 ? + DEFAULT_NFILTERS : 0; + adapter->params.mc5.nroutes = 0; + t3_mc5_prep(adapter, &adapter->mc5, MC5_MODE_144_BIT); + + init_mtus(adapter->params.mtus); + init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd); + } + + early_hw_init(adapter, ai); + + for_each_port(adapter, i) { + u8 hw_addr[6]; + struct port_info *p = adap2pinfo(adapter, i); + + while (!adapter->params.vpd.port_type[j]) + ++j; + + p->port_type = &port_types[adapter->params.vpd.port_type[j]]; + p->port_type->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, + ai->mdio_ops); + mac_prep(&p->mac, adapter, j); + ++j; + + /* + * The VPD EEPROM stores the base Ethernet address for the + * card. A port's address is derived from the base by adding + * the port's index to the base's low octet. + */ + memcpy(hw_addr, adapter->params.vpd.eth_base, 5); + hw_addr[5] = adapter->params.vpd.eth_base[5] + i; + + memcpy(adapter->port[i]->dev_addr, hw_addr, + ETH_ALEN); + memcpy(adapter->port[i]->perm_addr, hw_addr, + ETH_ALEN); + init_link_config(&p->link_config, p->port_type->caps); + p->phy.ops->power_down(&p->phy, 1); + if (!(p->port_type->caps & SUPPORTED_IRQ)) + adapter->params.linkpoll_period = 10; + } + + return 0; +} + +void t3_led_ready(struct adapter *adapter) +{ + t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, + F_GPIO0_OUT_VAL); +} diff -puN /dev/null drivers/net/cxgb3/t3cdev.h --- /dev/null +++ a/drivers/net/cxgb3/t3cdev.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2003-2006 Chelsio Communications. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _T3CDEV_H_ +#define _T3CDEV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define T3CNAMSIZ 16 + +/* Get the t3cdev associated with a net_device */ +#define T3CDEV(netdev) (struct t3cdev *)(netdev->priv) + +struct cxgb3_client; + +enum t3ctype { + T3A = 0, + T3B +}; + +struct t3cdev { + char name[T3CNAMSIZ]; /* T3C device name */ + enum t3ctype type; + struct list_head ofld_dev_list; /* for list linking */ + struct net_device *lldev; /* LL dev associated with T3C messages */ + struct proc_dir_entry *proc_dir; /* root of proc dir for this T3C */ + int (*send)(struct t3cdev *dev, struct sk_buff *skb); + int (*recv)(struct t3cdev *dev, struct sk_buff **skb, int n); + int (*ctl)(struct t3cdev *dev, unsigned int req, void *data); + void (*neigh_update)(struct t3cdev *dev, struct neighbour *neigh); + void *priv; /* driver private data */ + void *l2opt; /* optional layer 2 data */ + void *l3opt; /* optional layer 3 data */ + void *l4opt; /* optional layer 4 data */ + void *ulp; /* ulp stuff */ +}; + +#endif /* _T3CDEV_H_ */ diff -puN /dev/null drivers/net/cxgb3/version.h --- /dev/null +++ a/drivers/net/cxgb3/version.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * * + * File: * + * version.h * + * * + * Description: * + * Chelsio driver version defines. * + * * + * Copyright (c) 2003 - 2006 Chelsio Communications, Inc. * + * All rights reserved. * + * * + * Maintainers: maintainers@chelsio.com * + * * + * http://www.chelsio.com * + * * + ****************************************************************************/ +/* $Date: 2006/10/31 18:57:51 $ $RCSfile: version.h,v $ $Revision: 1.3 $ */ +#ifndef __CHELSIO_VERSION_H +#define __CHELSIO_VERSION_H +#define DRV_DESC "Chelsio T3 Network Driver" +#define DRV_NAME "cxgb3" +/* Driver version */ +#define DRV_VERSION "1.0" +#endif /* __CHELSIO_VERSION_H */ diff -puN /dev/null drivers/net/cxgb3/vsc8211.c --- /dev/null +++ a/drivers/net/cxgb3/vsc8211.c @@ -0,0 +1,208 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include "common.h" + +/* VSC8211 PHY specific registers. */ +enum { + VSC8211_INTR_ENABLE = 25, + VSC8211_INTR_STATUS = 26, + VSC8211_AUX_CTRL_STAT = 28, +}; + +enum { + VSC_INTR_RX_ERR = 1 << 0, + VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ + VSC_INTR_CABLE = 1 << 2, /* cable impairment */ + VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ + VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ + VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ + VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ + VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ + VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ + VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ + VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ + VSC_INTR_LINK_CHG = 1 << 13, /* link change */ + VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ +}; + +#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ + VSC_INTR_NEG_DONE) +#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ + VSC_INTR_ENABLE) + +/* PHY specific auxiliary control & status register fields */ +#define S_ACSR_ACTIPHY_TMR 0 +#define M_ACSR_ACTIPHY_TMR 0x3 +#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) + +#define S_ACSR_SPEED 3 +#define M_ACSR_SPEED 0x3 +#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) + +#define S_ACSR_DUPLEX 5 +#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) + +#define S_ACSR_ACTIPHY 6 +#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) + +/* + * Reset the PHY. This PHY completes reset immediately so we never wait. + */ +static int vsc8211_reset(struct cphy *cphy, int wait) +{ + return t3_phy_reset(cphy, 0, 0); +} + +static int vsc8211_intr_enable(struct cphy *cphy) +{ + return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK); +} + +static int vsc8211_intr_disable(struct cphy *cphy) +{ + return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0); +} + +static int vsc8211_intr_clear(struct cphy *cphy) +{ + u32 val; + + /* Clear PHY interrupts by reading the register. */ + return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val); +} + +static int vsc8211_autoneg_enable(struct cphy *cphy) +{ + return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, + BMCR_ANENABLE | BMCR_ANRESTART); +} + +static int vsc8211_autoneg_restart(struct cphy *cphy) +{ + return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, + BMCR_ANRESTART); +} + +static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, + int *speed, int *duplex, int *fc) +{ + unsigned int bmcr, status, lpa, adv; + int err, sp = -1, dplx = -1, pause = 0; + + err = mdio_read(cphy, 0, MII_BMCR, &bmcr); + if (!err) + err = mdio_read(cphy, 0, MII_BMSR, &status); + if (err) + return err; + + if (link_ok) { + /* + * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it + * once more to get the current link state. + */ + if (!(status & BMSR_LSTATUS)) + err = mdio_read(cphy, 0, MII_BMSR, &status); + if (err) + return err; + *link_ok = (status & BMSR_LSTATUS) != 0; + } + if (!(bmcr & BMCR_ANENABLE)) { + dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + if (bmcr & BMCR_SPEED1000) + sp = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + sp = SPEED_100; + else + sp = SPEED_10; + } else if (status & BMSR_ANEGCOMPLETE) { + err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status); + if (err) + return err; + + dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; + sp = G_ACSR_SPEED(status); + if (sp == 0) + sp = SPEED_10; + else if (sp == 1) + sp = SPEED_100; + else + sp = SPEED_1000; + + if (fc && dplx == DUPLEX_FULL) { + err = mdio_read(cphy, 0, MII_LPA, &lpa); + if (!err) + err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); + if (err) + return err; + + if (lpa & adv & ADVERTISE_PAUSE_CAP) + pause = PAUSE_RX | PAUSE_TX; + else if ((lpa & ADVERTISE_PAUSE_CAP) && + (lpa & ADVERTISE_PAUSE_ASYM) && + (adv & ADVERTISE_PAUSE_ASYM)) + pause = PAUSE_TX; + else if ((lpa & ADVERTISE_PAUSE_ASYM) && + (adv & ADVERTISE_PAUSE_CAP)) + pause = PAUSE_RX; + } + } + if (speed) + *speed = sp; + if (duplex) + *duplex = dplx; + if (fc) + *fc = pause; + return 0; +} + +static int vsc8211_power_down(struct cphy *cphy, int enable) +{ + return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, + enable ? BMCR_PDOWN : 0); +} + +static int vsc8211_intr_handler(struct cphy *cphy) +{ + unsigned int cause; + int err, cphy_cause = 0; + + err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause); + if (err) + return err; + + cause &= INTR_MASK; + if (cause & CFG_CHG_INTR_MASK) + cphy_cause |= cphy_cause_link_change; + if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) + cphy_cause |= cphy_cause_fifo_error; + return cphy_cause; +} + +static struct cphy_ops vsc8211_ops = { + .reset = vsc8211_reset, + .intr_enable = vsc8211_intr_enable, + .intr_disable = vsc8211_intr_disable, + .intr_clear = vsc8211_intr_clear, + .intr_handler = vsc8211_intr_handler, + .autoneg_enable = vsc8211_autoneg_enable, + .autoneg_restart = vsc8211_autoneg_restart, + .advertise = t3_phy_advertise, + .set_speed_duplex = t3_set_phy_speed_duplex, + .get_link_status = vsc8211_get_link_status, + .power_down = vsc8211_power_down, +}; + +void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) +{ + cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops); +} diff -puN /dev/null drivers/net/cxgb3/xgmac.c --- /dev/null +++ a/drivers/net/cxgb3/xgmac.c @@ -0,0 +1,389 @@ +/* + * This file is part of the Chelsio T3 Ethernet driver. + * + * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include "common.h" +#include "regs.h" + +/* + * # of exact address filters. The first one is used for the station address, + * the rest are available for multicast addresses. + */ +#define EXACT_ADDR_FILTERS 8 + +static inline int macidx(const struct cmac *mac) +{ + return mac->offset / (XGMAC0_1_BASE_ADDR - XGMAC0_0_BASE_ADDR); +} + +static void xaui_serdes_reset(struct cmac *mac) +{ + static const unsigned int clear[] = { + F_PWRDN0 | F_PWRDN1, F_RESETPLL01, F_RESET0 | F_RESET1, + F_PWRDN2 | F_PWRDN3, F_RESETPLL23, F_RESET2 | F_RESET3 + }; + + int i; + struct adapter *adap = mac->adapter; + u32 ctrl = A_XGM_SERDES_CTRL0 + mac->offset; + + t3_write_reg(adap, ctrl, adap->params.vpd.xauicfg[macidx(mac)] | + F_RESET3 | F_RESET2 | F_RESET1 | F_RESET0 | + F_PWRDN3 | F_PWRDN2 | F_PWRDN1 | F_PWRDN0 | + F_RESETPLL23 | F_RESETPLL01); + t3_read_reg(adap, ctrl); + udelay(15); + + for (i = 0; i < ARRAY_SIZE(clear); i++) { + t3_set_reg_field(adap, ctrl, clear[i], 0); + udelay(15); + } +} + +void t3b_pcs_reset(struct cmac *mac) +{ + t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset, + F_PCS_RESET_, 0); + udelay(20); + t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset, 0, + F_PCS_RESET_); +} + +int t3_mac_reset(struct cmac *mac) +{ + static const struct addr_val_pair mac_reset_avp[] = { + {A_XGM_TX_CTRL, 0}, + {A_XGM_RX_CTRL, 0}, + {A_XGM_RX_CFG, F_DISPAUSEFRAMES | F_EN1536BFRAMES | + F_RMFCS | F_ENJUMBO | F_ENHASHMCAST}, + {A_XGM_RX_HASH_LOW, 0}, + {A_XGM_RX_HASH_HIGH, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_1, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_2, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_3, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_4, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_5, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_6, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_7, 0}, + {A_XGM_RX_EXACT_MATCH_LOW_8, 0}, + {A_XGM_STAT_CTRL, F_CLRSTATS} + }; + u32 val; + struct adapter *adap = mac->adapter; + unsigned int oft = mac->offset; + + t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_); + t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + + t3_write_regs(adap, mac_reset_avp, ARRAY_SIZE(mac_reset_avp), oft); + t3_set_reg_field(adap, A_XGM_RXFIFO_CFG + oft, + F_RXSTRFRWRD | F_DISERRFRAMES, + uses_xaui(adap) ? 0 : F_RXSTRFRWRD); + + if (uses_xaui(adap)) { + if (adap->params.rev == 0) { + t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0, + F_RXENABLE | F_TXENABLE); + if (t3_wait_op_done(adap, A_XGM_SERDES_STATUS1 + oft, + F_CMULOCK, 1, 5, 2)) { + CH_ERR(adap, + "MAC %d XAUI SERDES CMU lock failed\n", + macidx(mac)); + return -1; + } + t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0, + F_SERDESRESET_); + } else + xaui_serdes_reset(mac); + } + + if (adap->params.rev > 0) + t3_write_reg(adap, A_XGM_PAUSE_TIMER + oft, 0xf000); + + val = F_MAC_RESET_; + if (is_10G(adap)) + val |= F_PCS_RESET_; + else if (uses_xaui(adap)) + val |= F_PCS_RESET_ | F_XG2G_RESET_; + else + val |= F_RGMII_RESET_ | F_XG2G_RESET_; + t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val); + t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + if ((val & F_PCS_RESET_) && adap->params.rev) { + msleep(1); + t3b_pcs_reset(mac); + } + + memset(&mac->stats, 0, sizeof(mac->stats)); + return 0; +} + +/* + * Set the exact match register 'idx' to recognize the given Ethernet address. + */ +static void set_addr_filter(struct cmac *mac, int idx, const u8 * addr) +{ + u32 addr_lo, addr_hi; + unsigned int oft = mac->offset + idx * 8; + + addr_lo = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + addr_hi = (addr[5] << 8) | addr[4]; + + t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1 + oft, addr_lo); + t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_HIGH_1 + oft, addr_hi); +} + +/* Set one of the station's unicast MAC addresses. */ +int t3_mac_set_address(struct cmac *mac, unsigned int idx, u8 addr[6]) +{ + if (idx >= mac->nucast) + return -EINVAL; + set_addr_filter(mac, idx, addr); + return 0; +} + +/* + * Specify the number of exact address filters that should be reserved for + * unicast addresses. Caller should reload the unicast and multicast addresses + * after calling this. + */ +int t3_mac_set_num_ucast(struct cmac *mac, int n) +{ + if (n > EXACT_ADDR_FILTERS) + return -EINVAL; + mac->nucast = n; + return 0; +} + +/* Calculate the RX hash filter index of an Ethernet address */ +static int hash_hw_addr(const u8 * addr) +{ + int hash = 0, octet, bit, i = 0, c; + + for (octet = 0; octet < 6; ++octet) + for (c = addr[octet], bit = 0; bit < 8; c >>= 1, ++bit) { + hash ^= (c & 1) << i; + if (++i == 6) + i = 0; + } + return hash; +} + +int t3_mac_set_rx_mode(struct cmac *mac, struct t3_rx_mode *rm) +{ + u32 val, hash_lo, hash_hi; + struct adapter *adap = mac->adapter; + unsigned int oft = mac->offset; + + val = t3_read_reg(adap, A_XGM_RX_CFG + oft) & ~F_COPYALLFRAMES; + if (rm->dev->flags & IFF_PROMISC) + val |= F_COPYALLFRAMES; + t3_write_reg(adap, A_XGM_RX_CFG + oft, val); + + if (rm->dev->flags & IFF_ALLMULTI) + hash_lo = hash_hi = 0xffffffff; + else { + u8 *addr; + int exact_addr_idx = mac->nucast; + + hash_lo = hash_hi = 0; + while ((addr = t3_get_next_mcaddr(rm))) + if (exact_addr_idx < EXACT_ADDR_FILTERS) + set_addr_filter(mac, exact_addr_idx++, addr); + else { + int hash = hash_hw_addr(addr); + + if (hash < 32) + hash_lo |= (1 << hash); + else + hash_hi |= (1 << (hash - 32)); + } + } + + t3_write_reg(adap, A_XGM_RX_HASH_LOW + oft, hash_lo); + t3_write_reg(adap, A_XGM_RX_HASH_HIGH + oft, hash_hi); + return 0; +} + +int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu) +{ + int hwm, lwm; + unsigned int thres, v; + struct adapter *adap = mac->adapter; + + /* + * MAX_FRAME_SIZE inludes header + FCS, mtu doesn't. The HW max + * packet size register includes header, but not FCS. + */ + mtu += 14; + if (mtu > MAX_FRAME_SIZE - 4) + return -EINVAL; + t3_write_reg(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset, mtu); + + /* + * Adjust the PAUSE frame watermarks. We always set the LWM, and the + * HWM only if flow-control is enabled. + */ + hwm = max(MAC_RXFIFO_SIZE - 3 * mtu, MAC_RXFIFO_SIZE / 2U); + hwm = min(hwm, 3 * MAC_RXFIFO_SIZE / 4 + 1024); + lwm = hwm - 1024; + v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset); + v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM); + v |= V_RXFIFOPAUSELWM(lwm / 8); + if (G_RXFIFOPAUSEHWM(v)) + v = (v & ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM)) | + V_RXFIFOPAUSEHWM(hwm / 8); + t3_write_reg(adap, A_XGM_RXFIFO_CFG + mac->offset, v); + + /* Adjust the TX FIFO threshold based on the MTU */ + thres = (adap->params.vpd.cclk * 1000) / 15625; + thres = (thres * mtu) / 1000; + if (is_10G(adap)) + thres /= 10; + thres = mtu > thres ? (mtu - thres + 7) / 8 : 0; + thres = max(thres, 8U); /* need at least 8 */ + t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset, + V_TXFIFOTHRESH(M_TXFIFOTHRESH), V_TXFIFOTHRESH(thres)); + return 0; +} + +int t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc) +{ + u32 val; + struct adapter *adap = mac->adapter; + unsigned int oft = mac->offset; + + if (duplex >= 0 && duplex != DUPLEX_FULL) + return -EINVAL; + if (speed >= 0) { + if (speed == SPEED_10) + val = V_PORTSPEED(0); + else if (speed == SPEED_100) + val = V_PORTSPEED(1); + else if (speed == SPEED_1000) + val = V_PORTSPEED(2); + else if (speed == SPEED_10000) + val = V_PORTSPEED(3); + else + return -EINVAL; + + t3_set_reg_field(adap, A_XGM_PORT_CFG + oft, + V_PORTSPEED(M_PORTSPEED), val); + } + + val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft); + val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM); + if (fc & PAUSE_TX) + val |= V_RXFIFOPAUSEHWM(G_RXFIFOPAUSELWM(val) + 128); /* +1KB */ + t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val); + + t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, + (fc & PAUSE_RX) ? F_TXPAUSEEN : 0); + return 0; +} + +int t3_mac_enable(struct cmac *mac, int which) +{ + int idx = macidx(mac); + struct adapter *adap = mac->adapter; + unsigned int oft = mac->offset; + + if (which & MAC_DIRECTION_TX) { + t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN); + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); + t3_write_reg(adap, A_TP_PIO_DATA, 0xbf000001); + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); + t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); + } + if (which & MAC_DIRECTION_RX) + t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN); + return 0; +} + +int t3_mac_disable(struct cmac *mac, int which) +{ + int idx = macidx(mac); + struct adapter *adap = mac->adapter; + + if (which & MAC_DIRECTION_TX) { + t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0); + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); + t3_write_reg(adap, A_TP_PIO_DATA, 0xc000001f); + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); + t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 0); + } + if (which & MAC_DIRECTION_RX) + t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0); + return 0; +} + +/* + * This function is called periodically to accumulate the current values of the + * RMON counters into the port statistics. Since the packet counters are only + * 32 bits they can overflow in ~286 secs at 10G, so the function should be + * called more frequently than that. The byte counters are 45-bit wide, they + * would overflow in ~7.8 hours. + */ +const struct mac_stats *t3_mac_update_stats(struct cmac *mac) +{ +#define RMON_READ(mac, addr) t3_read_reg(mac->adapter, addr + mac->offset) +#define RMON_UPDATE(mac, name, reg) \ + (mac)->stats.name += (u64)RMON_READ(mac, A_XGM_STAT_##reg) +#define RMON_UPDATE64(mac, name, reg_lo, reg_hi) \ + (mac)->stats.name += RMON_READ(mac, A_XGM_STAT_##reg_lo) + \ + ((u64)RMON_READ(mac, A_XGM_STAT_##reg_hi) << 32) + + u32 v, lo; + + RMON_UPDATE64(mac, rx_octets, RX_BYTES_LOW, RX_BYTES_HIGH); + RMON_UPDATE64(mac, rx_frames, RX_FRAMES_LOW, RX_FRAMES_HIGH); + RMON_UPDATE(mac, rx_mcast_frames, RX_MCAST_FRAMES); + RMON_UPDATE(mac, rx_bcast_frames, RX_BCAST_FRAMES); + RMON_UPDATE(mac, rx_fcs_errs, RX_CRC_ERR_FRAMES); + RMON_UPDATE(mac, rx_pause, RX_PAUSE_FRAMES); + RMON_UPDATE(mac, rx_jabber, RX_JABBER_FRAMES); + RMON_UPDATE(mac, rx_short, RX_SHORT_FRAMES); + RMON_UPDATE(mac, rx_symbol_errs, RX_SYM_CODE_ERR_FRAMES); + + RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES); + mac->stats.rx_too_long += RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); + + RMON_UPDATE(mac, rx_frames_64, RX_64B_FRAMES); + RMON_UPDATE(mac, rx_frames_65_127, RX_65_127B_FRAMES); + RMON_UPDATE(mac, rx_frames_128_255, RX_128_255B_FRAMES); + RMON_UPDATE(mac, rx_frames_256_511, RX_256_511B_FRAMES); + RMON_UPDATE(mac, rx_frames_512_1023, RX_512_1023B_FRAMES); + RMON_UPDATE(mac, rx_frames_1024_1518, RX_1024_1518B_FRAMES); + RMON_UPDATE(mac, rx_frames_1519_max, RX_1519_MAXB_FRAMES); + + RMON_UPDATE64(mac, tx_octets, TX_BYTE_LOW, TX_BYTE_HIGH); + RMON_UPDATE64(mac, tx_frames, TX_FRAME_LOW, TX_FRAME_HIGH); + RMON_UPDATE(mac, tx_mcast_frames, TX_MCAST); + RMON_UPDATE(mac, tx_bcast_frames, TX_BCAST); + RMON_UPDATE(mac, tx_pause, TX_PAUSE); + /* This counts error frames in general (bad FCS, underrun, etc). */ + RMON_UPDATE(mac, tx_underrun, TX_ERR_FRAMES); + + RMON_UPDATE(mac, tx_frames_64, TX_64B_FRAMES); + RMON_UPDATE(mac, tx_frames_65_127, TX_65_127B_FRAMES); + RMON_UPDATE(mac, tx_frames_128_255, TX_128_255B_FRAMES); + RMON_UPDATE(mac, tx_frames_256_511, TX_256_511B_FRAMES); + RMON_UPDATE(mac, tx_frames_512_1023, TX_512_1023B_FRAMES); + RMON_UPDATE(mac, tx_frames_1024_1518, TX_1024_1518B_FRAMES); + RMON_UPDATE(mac, tx_frames_1519_max, TX_1519_MAXB_FRAMES); + + /* The next stat isn't clear-on-read. */ + t3_write_reg(mac->adapter, A_TP_MIB_INDEX, mac->offset ? 51 : 50); + v = t3_read_reg(mac->adapter, A_TP_MIB_RDATA); + lo = (u32) mac->stats.rx_cong_drops; + mac->stats.rx_cong_drops += (u64) (v - lo); + + return &mac->stats; +} diff -puN drivers/net/e100.c~git-netdev-all drivers/net/e100.c --- a/drivers/net/e100.c~git-netdev-all +++ a/drivers/net/e100.c @@ -282,12 +282,6 @@ enum scb_status { rus_mask = 0x3C, }; -enum ru_state { - RU_SUSPENDED = 0, - RU_RUNNING = 1, - RU_UNINITIALIZED = -1, -}; - enum scb_stat_ack { stat_ack_not_ours = 0x00, stat_ack_sw_gen = 0x04, @@ -529,7 +523,6 @@ struct nic { struct rx *rx_to_use; struct rx *rx_to_clean; struct rfd blank_rfd; - enum ru_state ru_running; spinlock_t cb_lock ____cacheline_aligned; spinlock_t cmd_lock; @@ -951,7 +944,7 @@ static void e100_get_defaults(struct nic ((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i)); /* Template for a freshly allocated RFD */ - nic->blank_rfd.command = cpu_to_le16(cb_el); + nic->blank_rfd.command = cpu_to_le16(cb_el & cb_s); nic->blank_rfd.rbd = 0xFFFFFFFF; nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN); @@ -1746,19 +1739,11 @@ static int e100_alloc_cbs(struct nic *ni return 0; } -static inline void e100_start_receiver(struct nic *nic, struct rx *rx) +static inline void e100_start_receiver(struct nic *nic) { - if(!nic->rxs) return; - if(RU_SUSPENDED != nic->ru_running) return; - - /* handle init time starts */ - if(!rx) rx = nic->rxs; - - /* (Re)start RU if suspended or idle and RFA is non-NULL */ - if(rx->skb) { - e100_exec_cmd(nic, ruc_start, rx->dma_addr); - nic->ru_running = RU_RUNNING; - } + /* Start if RFA is non-NULL */ + if(nic->rx_to_clean->skb) + e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr); } #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) @@ -1787,7 +1772,7 @@ static int e100_rx_alloc_skb(struct nic put_unaligned(cpu_to_le32(rx->dma_addr), (u32 *)&prev_rfd->link); wmb(); - prev_rfd->command &= ~cpu_to_le16(cb_el); + prev_rfd->command &= ~cpu_to_le16(cb_el & cb_s); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, sizeof(struct rfd), PCI_DMA_TODEVICE); } @@ -1825,10 +1810,6 @@ static int e100_rx_indicate(struct nic * pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); - /* this allows for a fast restart without re-enabling interrupts */ - if(le16_to_cpu(rfd->command) & cb_el) - nic->ru_running = RU_SUSPENDED; - /* Pull off the RFD and put the actual data (minus eth hdr) */ skb_reserve(skb, sizeof(struct rfd)); skb_put(skb, actual_size); @@ -1859,45 +1840,18 @@ static void e100_rx_clean(struct nic *ni unsigned int work_to_do) { struct rx *rx; - int restart_required = 0; - struct rx *rx_to_start = NULL; - - /* are we already rnr? then pay attention!!! this ensures that - * the state machine progression never allows a start with a - * partially cleaned list, avoiding a race between hardware - * and rx_to_clean when in NAPI mode */ - if(RU_SUSPENDED == nic->ru_running) - restart_required = 1; /* Indicate newly arrived packets */ for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { - int err = e100_rx_indicate(nic, rx, work_done, work_to_do); - if(-EAGAIN == err) { - /* hit quota so have more work to do, restart once - * cleanup is complete */ - restart_required = 0; - break; - } else if(-ENODATA == err) + if(e100_rx_indicate(nic, rx, work_done, work_to_do)) break; /* No more to clean */ } - /* save our starting point as the place we'll restart the receiver */ - if(restart_required) - rx_to_start = nic->rx_to_clean; - /* Alloc new skbs to refill list */ for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; /* Better luck next time (see watchdog) */ } - - if(restart_required) { - // ack the rnr? - writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); - e100_start_receiver(nic, rx_to_start); - if(work_done) - (*work_done)++; - } } static void e100_rx_clean_list(struct nic *nic) @@ -1905,8 +1859,6 @@ static void e100_rx_clean_list(struct ni struct rx *rx; unsigned int i, count = nic->params.rfds.count; - nic->ru_running = RU_UNINITIALIZED; - if(nic->rxs) { for(rx = nic->rxs, i = 0; i < count; rx++, i++) { if(rx->skb) { @@ -1928,7 +1880,6 @@ static int e100_rx_alloc_list(struct nic unsigned int i, count = nic->params.rfds.count; nic->rx_to_use = nic->rx_to_clean = NULL; - nic->ru_running = RU_UNINITIALIZED; if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC))) return -ENOMEM; @@ -1943,7 +1894,6 @@ static int e100_rx_alloc_list(struct nic } nic->rx_to_use = nic->rx_to_clean = nic->rxs; - nic->ru_running = RU_SUSPENDED; return 0; } @@ -1963,10 +1913,6 @@ static irqreturn_t e100_intr(int irq, vo /* Ack interrupt(s) */ writeb(stat_ack, &nic->csr->scb.stat_ack); - /* We hit Receive No Resource (RNR); restart RU after cleaning */ - if(stat_ack & stat_ack_rnr) - nic->ru_running = RU_SUSPENDED; - if(likely(netif_rx_schedule_prep(netdev))) { e100_disable_irq(nic); __netif_rx_schedule(netdev); @@ -2058,7 +2004,7 @@ static int e100_up(struct nic *nic) if((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); mod_timer(&nic->watchdog, jiffies); if((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, nic->netdev->name, nic->netdev))) @@ -2139,7 +2085,7 @@ static int e100_loopback_test(struct nic mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, BMCR_LOOPBACK); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); if(!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { err = -ENOMEM; diff -puN drivers/net/e1000/e1000.h~git-netdev-all drivers/net/e1000/e1000.h --- a/drivers/net/e1000/e1000.h~git-netdev-all +++ a/drivers/net/e1000/e1000.h @@ -59,17 +59,13 @@ #include #include #include -#ifdef NETIF_F_TSO6 #include -#endif #include #include #include #include #include -#ifdef NETIF_F_TSO #include -#endif #include #include #include @@ -257,7 +253,6 @@ struct e1000_adapter { spinlock_t tx_queue_lock; #endif atomic_t irq_sem; - unsigned int detect_link; unsigned int total_tx_bytes; unsigned int total_tx_packets; unsigned int total_rx_bytes; @@ -348,9 +343,7 @@ struct e1000_adapter { boolean_t have_msi; #endif /* to not mess up cache alignment, always add to the bottom */ -#ifdef NETIF_F_TSO boolean_t tso_force; -#endif boolean_t smart_power_down; /* phy smart power down */ boolean_t quad_port_a; unsigned long flags; diff -puN drivers/net/e1000/e1000_ethtool.c~git-netdev-all drivers/net/e1000/e1000_ethtool.c --- a/drivers/net/e1000/e1000_ethtool.c~git-netdev-all +++ a/drivers/net/e1000/e1000_ethtool.c @@ -338,7 +338,6 @@ e1000_set_tx_csum(struct net_device *net return 0; } -#ifdef NETIF_F_TSO static int e1000_set_tso(struct net_device *netdev, uint32_t data) { @@ -352,18 +351,15 @@ e1000_set_tso(struct net_device *netdev, else netdev->features &= ~NETIF_F_TSO; -#ifdef NETIF_F_TSO6 if (data) netdev->features |= NETIF_F_TSO6; else netdev->features &= ~NETIF_F_TSO6; -#endif DPRINTK(PROBE, INFO, "TSO is %s\n", data ? "Enabled" : "Disabled"); adapter->tso_force = TRUE; return 0; } -#endif /* NETIF_F_TSO */ static uint32_t e1000_get_msglevel(struct net_device *netdev) @@ -1971,10 +1967,8 @@ static const struct ethtool_ops e1000_et .set_tx_csum = e1000_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, -#ifdef NETIF_F_TSO .get_tso = ethtool_op_get_tso, .set_tso = e1000_set_tso, -#endif .self_test_count = e1000_diag_test_count, .self_test = e1000_diag_test, .get_strings = e1000_get_strings, diff -puN drivers/net/e1000/e1000_main.c~git-netdev-all drivers/net/e1000/e1000_main.c --- a/drivers/net/e1000/e1000_main.c~git-netdev-all +++ a/drivers/net/e1000/e1000_main.c @@ -36,7 +36,7 @@ static char e1000_driver_string[] = "Int #else #define DRIVERNAPI "-NAPI" #endif -#define DRV_VERSION "7.3.15-k2"DRIVERNAPI +#define DRV_VERSION "7.3.20-k2"DRIVERNAPI char e1000_driver_version[] = DRV_VERSION; static char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; @@ -990,16 +990,12 @@ e1000_probe(struct pci_dev *pdev, netdev->features &= ~NETIF_F_HW_VLAN_FILTER; } -#ifdef NETIF_F_TSO if ((adapter->hw.mac_type >= e1000_82544) && (adapter->hw.mac_type != e1000_82547)) netdev->features |= NETIF_F_TSO; -#ifdef NETIF_F_TSO6 if (adapter->hw.mac_type > e1000_82547_rev_2) netdev->features |= NETIF_F_TSO6; -#endif -#endif if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; @@ -2583,15 +2579,22 @@ e1000_watchdog(unsigned long data) if (link) { if (!netif_carrier_ok(netdev)) { + uint32_t ctrl; boolean_t txb2b = 1; e1000_get_speed_and_duplex(&adapter->hw, &adapter->link_speed, &adapter->link_duplex); - DPRINTK(LINK, INFO, "NIC Link is Up %d Mbps %s\n", - adapter->link_speed, - adapter->link_duplex == FULL_DUPLEX ? - "Full Duplex" : "Half Duplex"); + ctrl = E1000_READ_REG(&adapter->hw, CTRL); + DPRINTK(LINK, INFO, "NIC Link is Up %d Mbps %s, " + "Flow Control: %s\n", + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? + "Full Duplex" : "Half Duplex", + ((ctrl & E1000_CTRL_TFCE) && (ctrl & + E1000_CTRL_RFCE)) ? "RX/TX" : ((ctrl & + E1000_CTRL_RFCE) ? "RX" : ((ctrl & + E1000_CTRL_TFCE) ? "TX" : "None" ))); /* tweak tx_queue_len according to speed/duplex * and adjust the timeout factor */ @@ -2619,7 +2622,6 @@ e1000_watchdog(unsigned long data) E1000_WRITE_REG(&adapter->hw, TARC0, tarc0); } -#ifdef NETIF_F_TSO /* disable TSO for pcie and 10/100 speeds, to avoid * some hardware issues */ if (!adapter->tso_force && @@ -2630,22 +2632,17 @@ e1000_watchdog(unsigned long data) DPRINTK(PROBE,INFO, "10/100 speed: disabling TSO\n"); netdev->features &= ~NETIF_F_TSO; -#ifdef NETIF_F_TSO6 netdev->features &= ~NETIF_F_TSO6; -#endif break; case SPEED_1000: netdev->features |= NETIF_F_TSO; -#ifdef NETIF_F_TSO6 netdev->features |= NETIF_F_TSO6; -#endif break; default: /* oops */ break; } } -#endif /* enable transmits in the hardware, need to do this * after setting TARC0 */ @@ -2875,7 +2872,6 @@ static int e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, struct sk_buff *skb) { -#ifdef NETIF_F_TSO struct e1000_context_desc *context_desc; struct e1000_buffer *buffer_info; unsigned int i; @@ -2904,7 +2900,6 @@ e1000_tso(struct e1000_adapter *adapter, 0); cmd_length = E1000_TXD_CMD_IP; ipcse = skb->h.raw - skb->data - 1; -#ifdef NETIF_F_TSO6 } else if (skb->protocol == htons(ETH_P_IPV6)) { skb->nh.ipv6h->payload_len = 0; skb->h.th->check = @@ -2914,7 +2909,6 @@ e1000_tso(struct e1000_adapter *adapter, IPPROTO_TCP, 0); ipcse = 0; -#endif } ipcss = skb->nh.raw - skb->data; ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; @@ -2947,8 +2941,6 @@ e1000_tso(struct e1000_adapter *adapter, return TRUE; } -#endif - return FALSE; } @@ -2968,8 +2960,9 @@ e1000_tx_csum(struct e1000_adapter *adap buffer_info = &tx_ring->buffer_info[i]; context_desc = E1000_CONTEXT_DESC(*tx_ring, i); + context_desc->lower_setup.ip_config = 0; context_desc->upper_setup.tcp_fields.tucss = css; - context_desc->upper_setup.tcp_fields.tucso = css + skb->csum_offset; + context_desc->upper_setup.tcp_fields.tucso = css + skb->csum; context_desc->upper_setup.tcp_fields.tucse = 0; context_desc->tcp_seg_setup.data = 0; context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT); @@ -3005,7 +2998,6 @@ e1000_tx_map(struct e1000_adapter *adapt while (len) { buffer_info = &tx_ring->buffer_info[i]; size = min(len, max_per_txd); -#ifdef NETIF_F_TSO /* Workaround for Controller erratum -- * descriptor for non-tso packet in a linear SKB that follows a * tso gets written back prematurely before the data is fully @@ -3020,7 +3012,6 @@ e1000_tx_map(struct e1000_adapter *adapt * in TSO mode. Append 4-byte sentinel desc */ if (unlikely(mss && !nr_frags && size == len && size > 8)) size -= 4; -#endif /* work-around for errata 10 and it applies * to all controllers in PCI-X mode * The fix is to make sure that the first descriptor of a @@ -3062,12 +3053,10 @@ e1000_tx_map(struct e1000_adapter *adapt while (len) { buffer_info = &tx_ring->buffer_info[i]; size = min(len, max_per_txd); -#ifdef NETIF_F_TSO /* Workaround for premature desc write-backs * in TSO mode. Append 4-byte sentinel desc */ if (unlikely(mss && f == (nr_frags-1) && size == len && size > 8)) size -= 4; -#endif /* Workaround for potential 82544 hang in PCI-X. * Avoid terminating buffers within evenly-aligned * dwords. */ @@ -3292,7 +3281,6 @@ e1000_xmit_frame(struct sk_buff *skb, st if (adapter->hw.mac_type >= e1000_82571) max_per_txd = 8192; -#ifdef NETIF_F_TSO mss = skb_shinfo(skb)->gso_size; /* The controller does a simple calculation to * make sure there is enough room in the FIFO before @@ -3346,16 +3334,10 @@ e1000_xmit_frame(struct sk_buff *skb, st if ((mss) || (skb->ip_summed == CHECKSUM_PARTIAL)) count++; count++; -#else - if (skb->ip_summed == CHECKSUM_PARTIAL) - count++; -#endif -#ifdef NETIF_F_TSO /* Controller Erratum workaround */ if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb)) count++; -#endif count += TXD_USE_COUNT(len, max_txd_pwr); @@ -3765,8 +3747,8 @@ e1000_update_stats(struct e1000_adapter * @data: pointer to a network interface device structure **/ -static -irqreturn_t e1000_intr_msi(int irq, void *data) +static irqreturn_t +e1000_intr_msi(int irq, void *data) { struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); @@ -3774,49 +3756,27 @@ irqreturn_t e1000_intr_msi(int irq, void #ifndef CONFIG_E1000_NAPI int i; #endif + uint32_t icr = E1000_READ_REG(hw, ICR); - /* this code avoids the read of ICR but has to get 1000 interrupts - * at every link change event before it will notice the change */ - if (++adapter->detect_link >= 1000) { - uint32_t icr = E1000_READ_REG(hw, ICR); #ifdef CONFIG_E1000_NAPI - /* read ICR disables interrupts using IAM, so keep up with our - * enable/disable accounting */ - atomic_inc(&adapter->irq_sem); + /* read ICR disables interrupts using IAM, so keep up with our + * enable/disable accounting */ + atomic_inc(&adapter->irq_sem); #endif - adapter->detect_link = 0; - if ((icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) && - (icr & E1000_ICR_INT_ASSERTED)) { - hw->get_link_status = 1; - /* 80003ES2LAN workaround-- - * For packet buffer work-around on link down event; - * disable receives here in the ISR and - * reset adapter in watchdog - */ - if (netif_carrier_ok(netdev) && - (adapter->hw.mac_type == e1000_80003es2lan)) { - /* disable receives */ - uint32_t rctl = E1000_READ_REG(hw, RCTL); - E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); - } - /* guard against interrupt when we're going down */ - if (!test_bit(__E1000_DOWN, &adapter->flags)) - mod_timer(&adapter->watchdog_timer, - jiffies + 1); + if (icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + hw->get_link_status = 1; + /* 80003ES2LAN workaround-- For packet buffer work-around on + * link down event; disable receives here in the ISR and reset + * adapter in watchdog */ + if (netif_carrier_ok(netdev) && + (adapter->hw.mac_type == e1000_80003es2lan)) { + /* disable receives */ + uint32_t rctl = E1000_READ_REG(hw, RCTL); + E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); } - } else { - E1000_WRITE_REG(hw, ICR, (0xffffffff & ~(E1000_ICR_RXSEQ | - E1000_ICR_LSC))); - /* bummer we have to flush here, but things break otherwise as - * some event appears to be lost or delayed and throughput - * drops. In almost all tests this flush is un-necessary */ - E1000_WRITE_FLUSH(hw); -#ifdef CONFIG_E1000_NAPI - /* Interrupt Auto-Mask (IAM)...upon writing ICR, interrupts are - * masked. No need for the IMC write, but it does mean we - * should account for it ASAP. */ - atomic_inc(&adapter->irq_sem); -#endif + /* guard against interrupt when we're going down */ + if (!test_bit(__E1000_DOWN, &adapter->flags)) + mod_timer(&adapter->watchdog_timer, jiffies + 1); } #ifdef CONFIG_E1000_NAPI @@ -3836,7 +3796,7 @@ irqreturn_t e1000_intr_msi(int irq, void for (i = 0; i < E1000_MAX_INTR; i++) if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) & - !e1000_clean_tx_irq(adapter, adapter->tx_ring))) + e1000_clean_tx_irq(adapter, adapter->tx_ring))) break; if (likely(adapter->itr_setting & 3)) @@ -3939,7 +3899,7 @@ e1000_intr(int irq, void *data) for (i = 0; i < E1000_MAX_INTR; i++) if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) & - !e1000_clean_tx_irq(adapter, adapter->tx_ring))) + e1000_clean_tx_irq(adapter, adapter->tx_ring))) break; if (likely(adapter->itr_setting & 3)) @@ -3989,7 +3949,7 @@ e1000_clean(struct net_device *poll_dev, poll_dev->quota -= work_done; /* If no Tx and not enough Rx work done, exit the polling mode */ - if ((!tx_cleaned && (work_done == 0)) || + if ((tx_cleaned && (work_done < work_to_do)) || !netif_running(poll_dev)) { quit_polling: if (likely(adapter->itr_setting & 3)) @@ -4019,7 +3979,7 @@ e1000_clean_tx_irq(struct e1000_adapter #ifdef CONFIG_E1000_NAPI unsigned int count = 0; #endif - boolean_t cleaned = FALSE; + boolean_t cleaned = TRUE; unsigned int total_tx_bytes=0, total_tx_packets=0; i = tx_ring->next_to_clean; @@ -4034,10 +3994,13 @@ e1000_clean_tx_irq(struct e1000_adapter if (cleaned) { struct sk_buff *skb = buffer_info->skb; - unsigned int segs = skb_shinfo(skb)->gso_segs; + unsigned int segs, bytecount; + segs = skb_shinfo(skb)->gso_segs ?: 1; + /* multiply data chunks by size of headers */ + bytecount = ((segs - 1) * skb_headlen(skb)) + + skb->len; total_tx_packets += segs; - total_tx_packets++; - total_tx_bytes += skb->len; + total_tx_bytes += bytecount; } e1000_unmap_and_free_tx_resource(adapter, buffer_info); tx_desc->upper.data = 0; @@ -4050,7 +4013,10 @@ e1000_clean_tx_irq(struct e1000_adapter #ifdef CONFIG_E1000_NAPI #define E1000_TX_WEIGHT 64 /* weight of a sort for tx, to avoid endless transmit cleanup */ - if (count++ == E1000_TX_WEIGHT) break; + if (count++ == E1000_TX_WEIGHT) { + cleaned = FALSE; + break; + } #endif } diff -puN drivers/net/e1000/e1000_osdep.h~git-netdev-all drivers/net/e1000/e1000_osdep.h --- a/drivers/net/e1000/e1000_osdep.h~git-netdev-all +++ a/drivers/net/e1000/e1000_osdep.h @@ -48,8 +48,6 @@ typedef enum { TRUE = 1 } boolean_t; -#define MSGOUT(S, A, B) printk(KERN_DEBUG S "\n", A, B) - #ifdef DBG #define DEBUGOUT(S) printk(KERN_DEBUG S "\n") #define DEBUGOUT1(S, A...) printk(KERN_DEBUG S "\n", A) @@ -58,7 +56,7 @@ typedef enum { #define DEBUGOUT1(S, A...) #endif -#define DEBUGFUNC(F) DEBUGOUT(F) +#define DEBUGFUNC(F) DEBUGOUT(F "\n") #define DEBUGOUT2 DEBUGOUT1 #define DEBUGOUT3 DEBUGOUT2 #define DEBUGOUT7 DEBUGOUT3 diff -puN drivers/net/e1000/e1000_param.c~git-netdev-all drivers/net/e1000/e1000_param.c --- a/drivers/net/e1000/e1000_param.c~git-netdev-all +++ a/drivers/net/e1000/e1000_param.c @@ -760,22 +760,13 @@ e1000_check_copper_options(struct e1000_ case SPEED_1000: DPRINTK(PROBE, INFO, "1000 Mbps Speed specified without " "Duplex\n"); - DPRINTK(PROBE, INFO, - "Using Autonegotiation at 1000 Mbps " - "Full Duplex only\n"); - adapter->hw.autoneg = adapter->fc_autoneg = 1; - adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; - break; + goto full_duplex_only; case SPEED_1000 + HALF_DUPLEX: DPRINTK(PROBE, INFO, "Half Duplex is not supported at 1000 Mbps\n"); - DPRINTK(PROBE, INFO, - "Using Autonegotiation at 1000 Mbps " - "Full Duplex only\n"); - adapter->hw.autoneg = adapter->fc_autoneg = 1; - adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; - break; + /* fall through */ case SPEED_1000 + FULL_DUPLEX: +full_duplex_only: DPRINTK(PROBE, INFO, "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; diff -puN drivers/net/forcedeth.c~git-netdev-all drivers/net/forcedeth.c --- a/drivers/net/forcedeth.c~git-netdev-all +++ a/drivers/net/forcedeth.c @@ -111,6 +111,7 @@ * 0.57: 14 May 2006: Mac address set in probe/remove and order corrections. * 0.58: 30 Oct 2006: Added support for sideband management unit. * 0.59: 30 Oct 2006: Added support for recoverable error. + * 0.60: 20 Jan 2007: Code optimizations for rings, rx & tx data paths, and stats. * * Known bugs: * We suspect that on some hardware no TX done interrupts are generated. @@ -127,7 +128,7 @@ #else #define DRIVERNAPI #endif -#define FORCEDETH_VERSION "0.59" +#define FORCEDETH_VERSION "0.60" #define DRV_NAME "forcedeth" #include @@ -173,9 +174,10 @@ #define DEV_HAS_MSI_X 0x0080 /* device supports MSI-X */ #define DEV_HAS_POWER_CNTRL 0x0100 /* device supports power savings */ #define DEV_HAS_PAUSEFRAME_TX 0x0200 /* device supports tx pause frames */ -#define DEV_HAS_STATISTICS 0x0400 /* device supports hw statistics */ -#define DEV_HAS_TEST_EXTENDED 0x0800 /* device supports extended diagnostic test */ -#define DEV_HAS_MGMT_UNIT 0x1000 /* device supports management unit */ +#define DEV_HAS_STATISTICS_V1 0x0400 /* device supports hw statistics version 1 */ +#define DEV_HAS_STATISTICS_V2 0x0800 /* device supports hw statistics version 2 */ +#define DEV_HAS_TEST_EXTENDED 0x1000 /* device supports extended diagnostic test */ +#define DEV_HAS_MGMT_UNIT 0x2000 /* device supports management unit */ enum { NvRegIrqStatus = 0x000, @@ -210,7 +212,7 @@ enum { * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms */ NvRegPollingInterval = 0x00c, -#define NVREG_POLL_DEFAULT_THROUGHPUT 970 +#define NVREG_POLL_DEFAULT_THROUGHPUT 970 /* backup tx cleanup if loop max reached */ #define NVREG_POLL_DEFAULT_CPU 13 NvRegMSIMap0 = 0x020, NvRegMSIMap1 = 0x024, @@ -304,8 +306,8 @@ enum { #define NVREG_TXRXCTL_RESET 0x0010 #define NVREG_TXRXCTL_RXCHECK 0x0400 #define NVREG_TXRXCTL_DESC_1 0 -#define NVREG_TXRXCTL_DESC_2 0x02100 -#define NVREG_TXRXCTL_DESC_3 0x02200 +#define NVREG_TXRXCTL_DESC_2 0x002100 +#define NVREG_TXRXCTL_DESC_3 0xc02200 #define NVREG_TXRXCTL_VLANSTRIP 0x00040 #define NVREG_TXRXCTL_VLANINS 0x00080 NvRegTxRingPhysAddrHigh = 0x148, @@ -487,7 +489,8 @@ union ring_type { /* Miscelaneous hardware related defines: */ #define NV_PCI_REGSZ_VER1 0x270 -#define NV_PCI_REGSZ_VER2 0x604 +#define NV_PCI_REGSZ_VER2 0x2d4 +#define NV_PCI_REGSZ_VER3 0x604 /* various timeout delays: all in usec */ #define NV_TXRX_RESET_DELAY 4 @@ -518,12 +521,6 @@ union ring_type { #define TX_RING_MIN 64 #define RING_MAX_DESC_VER_1 1024 #define RING_MAX_DESC_VER_2_3 16384 -/* - * Difference between the get and put pointers for the tx ring. - * This is used to throttle the amount of data outstanding in the - * tx ring. - */ -#define TX_LIMIT_DIFFERENCE 1 /* rx/tx mac addr + type + vlan + align + slack*/ #define NV_RX_HEADERS (64) @@ -611,9 +608,6 @@ static const struct nv_ethtool_str nv_es { "tx_carrier_errors" }, { "tx_excess_deferral" }, { "tx_retry_error" }, - { "tx_deferral" }, - { "tx_packets" }, - { "tx_pause" }, { "rx_frame_error" }, { "rx_extra_byte" }, { "rx_late_collision" }, @@ -626,11 +620,17 @@ static const struct nv_ethtool_str nv_es { "rx_unicast" }, { "rx_multicast" }, { "rx_broadcast" }, + { "rx_packets" }, + { "rx_errors_total" }, + { "tx_errors_total" }, + + /* version 2 stats */ + { "tx_deferral" }, + { "tx_packets" }, { "rx_bytes" }, + { "tx_pause" }, { "rx_pause" }, - { "rx_drop_frame" }, - { "rx_packets" }, - { "rx_errors_total" } + { "rx_drop_frame" } }; struct nv_ethtool_stats { @@ -643,9 +643,6 @@ struct nv_ethtool_stats { u64 tx_carrier_errors; u64 tx_excess_deferral; u64 tx_retry_error; - u64 tx_deferral; - u64 tx_packets; - u64 tx_pause; u64 rx_frame_error; u64 rx_extra_byte; u64 rx_late_collision; @@ -658,13 +655,22 @@ struct nv_ethtool_stats { u64 rx_unicast; u64 rx_multicast; u64 rx_broadcast; + u64 rx_packets; + u64 rx_errors_total; + u64 tx_errors_total; + + /* version 2 stats */ + u64 tx_deferral; + u64 tx_packets; u64 rx_bytes; + u64 tx_pause; u64 rx_pause; u64 rx_drop_frame; - u64 rx_packets; - u64 rx_errors_total; }; +#define NV_DEV_STATISTICS_V2_COUNT (sizeof(struct nv_ethtool_stats)/sizeof(u64)) +#define NV_DEV_STATISTICS_V1_COUNT (NV_DEV_STATISTICS_V2_COUNT - 6) + /* diagnostics */ #define NV_TEST_COUNT_BASE 3 #define NV_TEST_COUNT_EXTENDED 4 @@ -691,6 +697,12 @@ static const struct register_test nv_reg { 0,0 } }; +struct nv_skb_map { + struct sk_buff *skb; + dma_addr_t dma; + unsigned int dma_len; +}; + /* * SMP locking: * All hardware access under dev->priv->lock, except the performance @@ -741,10 +753,12 @@ struct fe_priv { /* rx specific fields. * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); */ + union ring_type get_rx, put_rx, first_rx, last_rx; + struct nv_skb_map *get_rx_ctx, *put_rx_ctx; + struct nv_skb_map *first_rx_ctx, *last_rx_ctx; + struct nv_skb_map *rx_skb; + union ring_type rx_ring; - unsigned int cur_rx, refill_rx; - struct sk_buff **rx_skbuff; - dma_addr_t *rx_dma; unsigned int rx_buf_sz; unsigned int pkt_limit; struct timer_list oom_kick; @@ -761,15 +775,15 @@ struct fe_priv { /* * tx specific fields. */ + union ring_type get_tx, put_tx, first_tx, last_tx; + struct nv_skb_map *get_tx_ctx, *put_tx_ctx; + struct nv_skb_map *first_tx_ctx, *last_tx_ctx; + struct nv_skb_map *tx_skb; + union ring_type tx_ring; - unsigned int next_tx, nic_tx; - struct sk_buff **tx_skbuff; - dma_addr_t *tx_dma; - unsigned int *tx_dma_len; u32 tx_flags; int tx_ring_size; - int tx_limit_start; - int tx_limit_stop; + int tx_stop; /* vlan fields */ struct vlan_group *vlangrp; @@ -921,16 +935,10 @@ static void free_rings(struct net_device pci_free_consistent(np->pci_dev, sizeof(struct ring_desc_ex) * (np->rx_ring_size + np->tx_ring_size), np->rx_ring.ex, np->ring_addr); } - if (np->rx_skbuff) - kfree(np->rx_skbuff); - if (np->rx_dma) - kfree(np->rx_dma); - if (np->tx_skbuff) - kfree(np->tx_skbuff); - if (np->tx_dma) - kfree(np->tx_dma); - if (np->tx_dma_len) - kfree(np->tx_dma_len); + if (np->rx_skb) + kfree(np->rx_skb); + if (np->tx_skb) + kfree(np->tx_skb); } static int using_multi_irqs(struct net_device *dev) @@ -1279,6 +1287,61 @@ static void nv_mac_reset(struct net_devi pci_push(base); } +static void nv_get_hw_stats(struct net_device *dev) +{ + struct fe_priv *np = netdev_priv(dev); + u8 __iomem *base = get_hwbase(dev); + + np->estats.tx_bytes += readl(base + NvRegTxCnt); + np->estats.tx_zero_rexmt += readl(base + NvRegTxZeroReXmt); + np->estats.tx_one_rexmt += readl(base + NvRegTxOneReXmt); + np->estats.tx_many_rexmt += readl(base + NvRegTxManyReXmt); + np->estats.tx_late_collision += readl(base + NvRegTxLateCol); + np->estats.tx_fifo_errors += readl(base + NvRegTxUnderflow); + np->estats.tx_carrier_errors += readl(base + NvRegTxLossCarrier); + np->estats.tx_excess_deferral += readl(base + NvRegTxExcessDef); + np->estats.tx_retry_error += readl(base + NvRegTxRetryErr); + np->estats.rx_frame_error += readl(base + NvRegRxFrameErr); + np->estats.rx_extra_byte += readl(base + NvRegRxExtraByte); + np->estats.rx_late_collision += readl(base + NvRegRxLateCol); + np->estats.rx_runt += readl(base + NvRegRxRunt); + np->estats.rx_frame_too_long += readl(base + NvRegRxFrameTooLong); + np->estats.rx_over_errors += readl(base + NvRegRxOverflow); + np->estats.rx_crc_errors += readl(base + NvRegRxFCSErr); + np->estats.rx_frame_align_error += readl(base + NvRegRxFrameAlignErr); + np->estats.rx_length_error += readl(base + NvRegRxLenErr); + np->estats.rx_unicast += readl(base + NvRegRxUnicast); + np->estats.rx_multicast += readl(base + NvRegRxMulticast); + np->estats.rx_broadcast += readl(base + NvRegRxBroadcast); + np->estats.rx_packets = + np->estats.rx_unicast + + np->estats.rx_multicast + + np->estats.rx_broadcast; + np->estats.rx_errors_total = + np->estats.rx_crc_errors + + np->estats.rx_over_errors + + np->estats.rx_frame_error + + (np->estats.rx_frame_align_error - np->estats.rx_extra_byte) + + np->estats.rx_late_collision + + np->estats.rx_runt + + np->estats.rx_frame_too_long; + np->estats.tx_errors_total = + np->estats.tx_late_collision + + np->estats.tx_fifo_errors + + np->estats.tx_carrier_errors + + np->estats.tx_excess_deferral + + np->estats.tx_retry_error; + + if (np->driver_data & DEV_HAS_STATISTICS_V2) { + np->estats.tx_deferral += readl(base + NvRegTxDef); + np->estats.tx_packets += readl(base + NvRegTxFrame); + np->estats.rx_bytes += readl(base + NvRegRxCnt); + np->estats.tx_pause += readl(base + NvRegTxPause); + np->estats.rx_pause += readl(base + NvRegRxPause); + np->estats.rx_drop_frame += readl(base + NvRegRxDropFrame); + } +} + /* * nv_get_stats: dev->get_stats function * Get latest stats value from the nic. @@ -1289,10 +1352,19 @@ static struct net_device_stats *nv_get_s { struct fe_priv *np = netdev_priv(dev); - /* It seems that the nic always generates interrupts and doesn't - * accumulate errors internally. Thus the current values in np->stats - * are already up to date. - */ + /* If the nic supports hw counters then retrieve latest values */ + if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2)) { + nv_get_hw_stats(dev); + + /* copy to net_device stats */ + np->stats.tx_bytes = np->estats.tx_bytes; + np->stats.tx_fifo_errors = np->estats.tx_fifo_errors; + np->stats.tx_carrier_errors = np->estats.tx_carrier_errors; + np->stats.rx_crc_errors = np->estats.rx_crc_errors; + np->stats.rx_over_errors = np->estats.rx_over_errors; + np->stats.rx_errors = np->estats.rx_errors_total; + np->stats.tx_errors = np->estats.tx_errors_total; + } return &np->stats; } @@ -1304,43 +1376,63 @@ static struct net_device_stats *nv_get_s static int nv_alloc_rx(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); - unsigned int refill_rx = np->refill_rx; - int nr; + struct ring_desc* less_rx; - while (np->cur_rx != refill_rx) { - struct sk_buff *skb; + less_rx = np->get_rx.orig; + if (less_rx-- == np->first_rx.orig) + less_rx = np->last_rx.orig; + + while (np->put_rx.orig != less_rx) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); + if (skb) { + skb->dev = dev; + np->put_rx_ctx->skb = skb; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, + skb->end-skb->data, PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma); + wmb(); + np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); + if (unlikely(np->put_rx.orig++ == np->last_rx.orig)) + np->put_rx.orig = np->first_rx.orig; + if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx)) + np->put_rx_ctx = np->first_rx_ctx; + } else { + return 1; + } + } + return 0; +} - nr = refill_rx % np->rx_ring_size; - if (np->rx_skbuff[nr] == NULL) { +static int nv_alloc_rx_optimized(struct net_device *dev) +{ + struct fe_priv *np = netdev_priv(dev); + struct ring_desc_ex* less_rx; - skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); - if (!skb) - break; + less_rx = np->get_rx.ex; + if (less_rx-- == np->first_rx.ex) + less_rx = np->last_rx.ex; + while (np->put_rx.ex != less_rx) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); + if (skb) { skb->dev = dev; - np->rx_skbuff[nr] = skb; - } else { - skb = np->rx_skbuff[nr]; - } - np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data, - skb->end-skb->data, PCI_DMA_FROMDEVICE); - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->rx_ring.orig[nr].buf = cpu_to_le32(np->rx_dma[nr]); + np->put_rx_ctx->skb = skb; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, + skb->end-skb->data, PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx.ex->bufhigh = cpu_to_le64(np->put_rx_ctx->dma) >> 32; + np->put_rx.ex->buflow = cpu_to_le64(np->put_rx_ctx->dma) & 0x0FFFFFFFF; wmb(); - np->rx_ring.orig[nr].flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); + np->put_rx.ex->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL); + if (unlikely(np->put_rx.ex++ == np->last_rx.ex)) + np->put_rx.ex = np->first_rx.ex; + if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx)) + np->put_rx_ctx = np->first_rx_ctx; } else { - np->rx_ring.ex[nr].bufhigh = cpu_to_le64(np->rx_dma[nr]) >> 32; - np->rx_ring.ex[nr].buflow = cpu_to_le64(np->rx_dma[nr]) & 0x0FFFFFFFF; - wmb(); - np->rx_ring.ex[nr].flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL); + return 1; } - dprintk(KERN_DEBUG "%s: nv_alloc_rx: Packet %d marked as Available\n", - dev->name, refill_rx); - refill_rx++; } - np->refill_rx = refill_rx; - if (np->cur_rx - refill_rx == np->rx_ring_size) - return 1; return 0; } @@ -1358,6 +1450,7 @@ static void nv_do_rx_refill(unsigned lon { struct net_device *dev = (struct net_device *) data; struct fe_priv *np = netdev_priv(dev); + int retcode; if (!using_multi_irqs(dev)) { if (np->msi_flags & NV_MSI_X_ENABLED) @@ -1367,7 +1460,11 @@ static void nv_do_rx_refill(unsigned lon } else { disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); } - if (nv_alloc_rx(dev)) { + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + retcode = nv_alloc_rx(dev); + else + retcode = nv_alloc_rx_optimized(dev); + if (retcode) { spin_lock_irq(&np->lock); if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL); @@ -1388,56 +1485,81 @@ static void nv_init_rx(struct net_device { struct fe_priv *np = netdev_priv(dev); int i; + np->get_rx = np->put_rx = np->first_rx = np->rx_ring; + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + np->last_rx.orig = &np->rx_ring.orig[np->rx_ring_size-1]; + else + np->last_rx.ex = &np->rx_ring.ex[np->rx_ring_size-1]; + np->get_rx_ctx = np->put_rx_ctx = np->first_rx_ctx = np->rx_skb; + np->last_rx_ctx = &np->rx_skb[np->rx_ring_size-1]; - np->cur_rx = np->rx_ring_size; - np->refill_rx = 0; - for (i = 0; i < np->rx_ring_size; i++) - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + for (i = 0; i < np->rx_ring_size; i++) { + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->rx_ring.orig[i].flaglen = 0; - else + np->rx_ring.orig[i].buf = 0; + } else { np->rx_ring.ex[i].flaglen = 0; + np->rx_ring.ex[i].txvlan = 0; + np->rx_ring.ex[i].bufhigh = 0; + np->rx_ring.ex[i].buflow = 0; + } + np->rx_skb[i].skb = NULL; + np->rx_skb[i].dma = 0; + } } static void nv_init_tx(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); int i; + np->get_tx = np->put_tx = np->first_tx = np->tx_ring; + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + np->last_tx.orig = &np->tx_ring.orig[np->tx_ring_size-1]; + else + np->last_tx.ex = &np->tx_ring.ex[np->tx_ring_size-1]; + np->get_tx_ctx = np->put_tx_ctx = np->first_tx_ctx = np->tx_skb; + np->last_tx_ctx = &np->tx_skb[np->tx_ring_size-1]; - np->next_tx = np->nic_tx = 0; for (i = 0; i < np->tx_ring_size; i++) { - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->tx_ring.orig[i].flaglen = 0; - else + np->tx_ring.orig[i].buf = 0; + } else { np->tx_ring.ex[i].flaglen = 0; - np->tx_skbuff[i] = NULL; - np->tx_dma[i] = 0; + np->tx_ring.ex[i].txvlan = 0; + np->tx_ring.ex[i].bufhigh = 0; + np->tx_ring.ex[i].buflow = 0; + } + np->tx_skb[i].skb = NULL; + np->tx_skb[i].dma = 0; } } static int nv_init_ring(struct net_device *dev) { + struct fe_priv *np = netdev_priv(dev); + nv_init_tx(dev); nv_init_rx(dev); - return nv_alloc_rx(dev); + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + return nv_alloc_rx(dev); + else + return nv_alloc_rx_optimized(dev); } -static int nv_release_txskb(struct net_device *dev, unsigned int skbnr) +static int nv_release_txskb(struct net_device *dev, struct nv_skb_map* tx_skb) { struct fe_priv *np = netdev_priv(dev); - dprintk(KERN_INFO "%s: nv_release_txskb for skbnr %d\n", - dev->name, skbnr); - - if (np->tx_dma[skbnr]) { - pci_unmap_page(np->pci_dev, np->tx_dma[skbnr], - np->tx_dma_len[skbnr], + if (tx_skb->dma) { + pci_unmap_page(np->pci_dev, tx_skb->dma, + tx_skb->dma_len, PCI_DMA_TODEVICE); - np->tx_dma[skbnr] = 0; + tx_skb->dma = 0; } - - if (np->tx_skbuff[skbnr]) { - dev_kfree_skb_any(np->tx_skbuff[skbnr]); - np->tx_skbuff[skbnr] = NULL; + if (tx_skb->skb) { + dev_kfree_skb_any(tx_skb->skb); + tx_skb->skb = NULL; return 1; } else { return 0; @@ -1450,11 +1572,16 @@ static void nv_drain_tx(struct net_devic unsigned int i; for (i = 0; i < np->tx_ring_size; i++) { - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->tx_ring.orig[i].flaglen = 0; - else + np->tx_ring.orig[i].buf = 0; + } else { np->tx_ring.ex[i].flaglen = 0; - if (nv_release_txskb(dev, i)) + np->tx_ring.ex[i].txvlan = 0; + np->tx_ring.ex[i].bufhigh = 0; + np->tx_ring.ex[i].buflow = 0; + } + if (nv_release_txskb(dev, &np->tx_skb[i])) np->stats.tx_dropped++; } } @@ -1463,18 +1590,24 @@ static void nv_drain_rx(struct net_devic { struct fe_priv *np = netdev_priv(dev); int i; + for (i = 0; i < np->rx_ring_size; i++) { - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->rx_ring.orig[i].flaglen = 0; - else + np->rx_ring.orig[i].buf = 0; + } else { np->rx_ring.ex[i].flaglen = 0; + np->rx_ring.ex[i].txvlan = 0; + np->rx_ring.ex[i].bufhigh = 0; + np->rx_ring.ex[i].buflow = 0; + } wmb(); - if (np->rx_skbuff[i]) { - pci_unmap_single(np->pci_dev, np->rx_dma[i], - np->rx_skbuff[i]->end-np->rx_skbuff[i]->data, + if (np->rx_skb[i].skb) { + pci_unmap_single(np->pci_dev, np->rx_skb[i].dma, + np->rx_skb[i].skb->end-np->rx_skb[i].skb->data, PCI_DMA_FROMDEVICE); - dev_kfree_skb(np->rx_skbuff[i]); - np->rx_skbuff[i] = NULL; + dev_kfree_skb(np->rx_skb[i].skb); + np->rx_skb[i].skb = NULL; } } } @@ -1485,6 +1618,11 @@ static void drain_ring(struct net_device nv_drain_rx(dev); } +static inline u32 nv_get_empty_tx_slots(struct fe_priv *np) +{ + return (u32)(np->tx_ring_size - ((np->tx_ring_size + (np->put_tx_ctx - np->get_tx_ctx)) % np->tx_ring_size)); +} + /* * nv_start_xmit: dev->hard_start_xmit function * Called with netif_tx_lock held. @@ -1495,14 +1633,16 @@ static int nv_start_xmit(struct sk_buff u32 tx_flags = 0; u32 tx_flags_extra = (np->desc_ver == DESC_VER_1 ? NV_TX_LASTPACKET : NV_TX2_LASTPACKET); unsigned int fragments = skb_shinfo(skb)->nr_frags; - unsigned int nr = (np->next_tx - 1) % np->tx_ring_size; - unsigned int start_nr = np->next_tx % np->tx_ring_size; unsigned int i; u32 offset = 0; u32 bcnt; u32 size = skb->len-skb->data_len; u32 entries = (size >> NV_TX2_TSO_MAX_SHIFT) + ((size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0); - u32 tx_flags_vlan = 0; + u32 empty_slots; + struct ring_desc* put_tx; + struct ring_desc* start_tx; + struct ring_desc* prev_tx; + struct nv_skb_map* prev_tx_ctx; /* add fragments to entries count */ for (i = 0; i < fragments; i++) { @@ -1510,34 +1650,35 @@ static int nv_start_xmit(struct sk_buff ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0); } - spin_lock_irq(&np->lock); - - if ((np->next_tx - np->nic_tx + entries - 1) > np->tx_limit_stop) { - spin_unlock_irq(&np->lock); + empty_slots = nv_get_empty_tx_slots(np); + if (unlikely(empty_slots <= entries)) { + spin_lock_irq(&np->lock); netif_stop_queue(dev); + np->tx_stop = 1; + spin_unlock_irq(&np->lock); return NETDEV_TX_BUSY; } + start_tx = put_tx = np->put_tx.orig; + /* setup the header buffer */ do { + prev_tx = put_tx; + prev_tx_ctx = np->put_tx_ctx; bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size; - nr = (nr + 1) % np->tx_ring_size; - - np->tx_dma[nr] = pci_map_single(np->pci_dev, skb->data + offset, bcnt, + np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt, PCI_DMA_TODEVICE); - np->tx_dma_len[nr] = bcnt; + np->put_tx_ctx->dma_len = bcnt; + put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma); + put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].buf = cpu_to_le32(np->tx_dma[nr]); - np->tx_ring.orig[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); - } else { - np->tx_ring.ex[nr].bufhigh = cpu_to_le64(np->tx_dma[nr]) >> 32; - np->tx_ring.ex[nr].buflow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; - np->tx_ring.ex[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); - } tx_flags = np->tx_flags; offset += bcnt; size -= bcnt; + if (unlikely(put_tx++ == np->last_tx.orig)) + put_tx = np->first_tx.orig; + if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx)) + np->put_tx_ctx = np->first_tx_ctx; } while (size); /* setup the fragments */ @@ -1547,58 +1688,174 @@ static int nv_start_xmit(struct sk_buff offset = 0; do { + prev_tx = put_tx; + prev_tx_ctx = np->put_tx_ctx; bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size; - nr = (nr + 1) % np->tx_ring_size; + np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt, + PCI_DMA_TODEVICE); + np->put_tx_ctx->dma_len = bcnt; + put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma); + put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); - np->tx_dma[nr] = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt, - PCI_DMA_TODEVICE); - np->tx_dma_len[nr] = bcnt; - - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].buf = cpu_to_le32(np->tx_dma[nr]); - np->tx_ring.orig[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); - } else { - np->tx_ring.ex[nr].bufhigh = cpu_to_le64(np->tx_dma[nr]) >> 32; - np->tx_ring.ex[nr].buflow = cpu_to_le64(np->tx_dma[nr]) & 0x0FFFFFFFF; - np->tx_ring.ex[nr].flaglen = cpu_to_le32((bcnt-1) | tx_flags); - } offset += bcnt; size -= bcnt; + if (unlikely(put_tx++ == np->last_tx.orig)) + put_tx = np->first_tx.orig; + if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx)) + np->put_tx_ctx = np->first_tx_ctx; } while (size); } /* set last fragment flag */ - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[nr].flaglen |= cpu_to_le32(tx_flags_extra); - } else { - np->tx_ring.ex[nr].flaglen |= cpu_to_le32(tx_flags_extra); + prev_tx->flaglen |= cpu_to_le32(tx_flags_extra); + + /* save skb in this slot's context area */ + prev_tx_ctx->skb = skb; + + if (skb_is_gso(skb)) + tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); + else + tx_flags_extra = skb->ip_summed == CHECKSUM_PARTIAL ? + NV_TX2_CHECKSUM_L3 | NV_TX2_CHECKSUM_L4 : 0; + + spin_lock_irq(&np->lock); + + /* set tx flags */ + start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); + np->put_tx.orig = put_tx; + + spin_unlock_irq(&np->lock); + + dprintk(KERN_DEBUG "%s: nv_start_xmit: entries %d queued for transmission. tx_flags_extra: %x\n", + dev->name, entries, tx_flags_extra); + { + int j; + for (j=0; j<64; j++) { + if ((j%16) == 0) + dprintk("\n%03x:", j); + dprintk(" %02x", ((unsigned char*)skb->data)[j]); + } + dprintk("\n"); + } + + dev->trans_start = jiffies; + writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl); + return NETDEV_TX_OK; +} + +static int nv_start_xmit_optimized(struct sk_buff *skb, struct net_device *dev) +{ + struct fe_priv *np = netdev_priv(dev); + u32 tx_flags = 0; + u32 tx_flags_extra; + unsigned int fragments = skb_shinfo(skb)->nr_frags; + unsigned int i; + u32 offset = 0; + u32 bcnt; + u32 size = skb->len-skb->data_len; + u32 entries = (size >> NV_TX2_TSO_MAX_SHIFT) + ((size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0); + u32 empty_slots; + struct ring_desc_ex* put_tx; + struct ring_desc_ex* start_tx; + struct ring_desc_ex* prev_tx; + struct nv_skb_map* prev_tx_ctx; + + /* add fragments to entries count */ + for (i = 0; i < fragments; i++) { + entries += (skb_shinfo(skb)->frags[i].size >> NV_TX2_TSO_MAX_SHIFT) + + ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0); + } + + empty_slots = nv_get_empty_tx_slots(np); + if (unlikely(empty_slots <= entries)) { + spin_lock_irq(&np->lock); + netif_stop_queue(dev); + np->tx_stop = 1; + spin_unlock_irq(&np->lock); + return NETDEV_TX_BUSY; } - np->tx_skbuff[nr] = skb; + start_tx = put_tx = np->put_tx.ex; + + /* setup the header buffer */ + do { + prev_tx = put_tx; + prev_tx_ctx = np->put_tx_ctx; + bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size; + np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt, + PCI_DMA_TODEVICE); + np->put_tx_ctx->dma_len = bcnt; + put_tx->bufhigh = cpu_to_le64(np->put_tx_ctx->dma) >> 32; + put_tx->buflow = cpu_to_le64(np->put_tx_ctx->dma) & 0x0FFFFFFFF; + put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); + + tx_flags = NV_TX2_VALID; + offset += bcnt; + size -= bcnt; + if (unlikely(put_tx++ == np->last_tx.ex)) + put_tx = np->first_tx.ex; + if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx)) + np->put_tx_ctx = np->first_tx_ctx; + } while (size); + + /* setup the fragments */ + for (i = 0; i < fragments; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + u32 size = frag->size; + offset = 0; + + do { + prev_tx = put_tx; + prev_tx_ctx = np->put_tx_ctx; + bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size; + np->put_tx_ctx->dma = pci_map_page(np->pci_dev, frag->page, frag->page_offset+offset, bcnt, + PCI_DMA_TODEVICE); + np->put_tx_ctx->dma_len = bcnt; + put_tx->bufhigh = cpu_to_le64(np->put_tx_ctx->dma) >> 32; + put_tx->buflow = cpu_to_le64(np->put_tx_ctx->dma) & 0x0FFFFFFFF; + put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags); + + offset += bcnt; + size -= bcnt; + if (unlikely(put_tx++ == np->last_tx.ex)) + put_tx = np->first_tx.ex; + if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx)) + np->put_tx_ctx = np->first_tx_ctx; + } while (size); + } + + /* set last fragment flag */ + prev_tx->flaglen |= cpu_to_le32(NV_TX2_LASTPACKET); + + /* save skb in this slot's context area */ + prev_tx_ctx->skb = skb; -#ifdef NETIF_F_TSO if (skb_is_gso(skb)) tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); else -#endif - tx_flags_extra = skb->ip_summed == CHECKSUM_PARTIAL ? + tx_flags_extra = skb->ip_summed == CHECKSUM_PARTIAL ? NV_TX2_CHECKSUM_L3 | NV_TX2_CHECKSUM_L4 : 0; /* vlan tag */ - if (np->vlangrp && vlan_tx_tag_present(skb)) { - tx_flags_vlan = NV_TX3_VLAN_TAG_PRESENT | vlan_tx_tag_get(skb); + if (likely(!np->vlangrp)) { + start_tx->txvlan = 0; + } else { + if (vlan_tx_tag_present(skb)) + start_tx->txvlan = cpu_to_le32(NV_TX3_VLAN_TAG_PRESENT | vlan_tx_tag_get(skb)); + else + start_tx->txvlan = 0; } + spin_lock_irq(&np->lock); + /* set tx flags */ - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - np->tx_ring.orig[start_nr].flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); - } else { - np->tx_ring.ex[start_nr].txvlan = cpu_to_le32(tx_flags_vlan); - np->tx_ring.ex[start_nr].flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); - } + start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra); + np->put_tx.ex = put_tx; + + spin_unlock_irq(&np->lock); - dprintk(KERN_DEBUG "%s: nv_start_xmit: packet %d (entries %d) queued for transmission. tx_flags_extra: %x\n", - dev->name, np->next_tx, entries, tx_flags_extra); + dprintk(KERN_DEBUG "%s: nv_start_xmit_optimized: entries %d queued for transmission. tx_flags_extra: %x\n", + dev->name, entries, tx_flags_extra); { int j; for (j=0; j<64; j++) { @@ -1609,12 +1866,8 @@ static int nv_start_xmit(struct sk_buff dprintk("\n"); } - np->next_tx += entries; - dev->trans_start = jiffies; - spin_unlock_irq(&np->lock); writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl); - pci_push(get_hwbase(dev)); return NETDEV_TX_OK; } @@ -1627,26 +1880,22 @@ static void nv_tx_done(struct net_device { struct fe_priv *np = netdev_priv(dev); u32 flags; - unsigned int i; - struct sk_buff *skb; + struct ring_desc* orig_get_tx = np->get_tx.orig; - while (np->nic_tx != np->next_tx) { - i = np->nic_tx % np->tx_ring_size; + while ((np->get_tx.orig != np->put_tx.orig) && + !((flags = le32_to_cpu(np->get_tx.orig->flaglen)) & NV_TX_VALID)) { - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) - flags = le32_to_cpu(np->tx_ring.orig[i].flaglen); - else - flags = le32_to_cpu(np->tx_ring.ex[i].flaglen); + dprintk(KERN_DEBUG "%s: nv_tx_done: flags 0x%x.\n", + dev->name, flags); + + pci_unmap_page(np->pci_dev, np->get_tx_ctx->dma, + np->get_tx_ctx->dma_len, + PCI_DMA_TODEVICE); + np->get_tx_ctx->dma = 0; - dprintk(KERN_DEBUG "%s: nv_tx_done: looking at packet %d, flags 0x%x.\n", - dev->name, np->nic_tx, flags); - if (flags & NV_TX_VALID) - break; if (np->desc_ver == DESC_VER_1) { if (flags & NV_TX_LASTPACKET) { - skb = np->tx_skbuff[i]; - if (flags & (NV_TX_RETRYERROR|NV_TX_CARRIERLOST|NV_TX_LATECOLLISION| - NV_TX_UNDERFLOW|NV_TX_ERROR)) { + if (flags & NV_TX_ERROR) { if (flags & NV_TX_UNDERFLOW) np->stats.tx_fifo_errors++; if (flags & NV_TX_CARRIERLOST) @@ -1654,14 +1903,14 @@ static void nv_tx_done(struct net_device np->stats.tx_errors++; } else { np->stats.tx_packets++; - np->stats.tx_bytes += skb->len; + np->stats.tx_bytes += np->get_tx_ctx->skb->len; } + dev_kfree_skb_any(np->get_tx_ctx->skb); + np->get_tx_ctx->skb = NULL; } } else { if (flags & NV_TX2_LASTPACKET) { - skb = np->tx_skbuff[i]; - if (flags & (NV_TX2_RETRYERROR|NV_TX2_CARRIERLOST|NV_TX2_LATECOLLISION| - NV_TX2_UNDERFLOW|NV_TX2_ERROR)) { + if (flags & NV_TX2_ERROR) { if (flags & NV_TX2_UNDERFLOW) np->stats.tx_fifo_errors++; if (flags & NV_TX2_CARRIERLOST) @@ -1669,15 +1918,56 @@ static void nv_tx_done(struct net_device np->stats.tx_errors++; } else { np->stats.tx_packets++; - np->stats.tx_bytes += skb->len; + np->stats.tx_bytes += np->get_tx_ctx->skb->len; } + dev_kfree_skb_any(np->get_tx_ctx->skb); + np->get_tx_ctx->skb = NULL; } } - nv_release_txskb(dev, i); - np->nic_tx++; + if (unlikely(np->get_tx.orig++ == np->last_tx.orig)) + np->get_tx.orig = np->first_tx.orig; + if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx)) + np->get_tx_ctx = np->first_tx_ctx; + } + if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx))) { + np->tx_stop = 0; + netif_wake_queue(dev); + } +} + +static void nv_tx_done_optimized(struct net_device *dev, int limit) +{ + struct fe_priv *np = netdev_priv(dev); + u32 flags; + struct ring_desc_ex* orig_get_tx = np->get_tx.ex; + + while ((np->get_tx.ex != np->put_tx.ex) && + !((flags = le32_to_cpu(np->get_tx.ex->flaglen)) & NV_TX_VALID) && + (limit-- > 0)) { + + dprintk(KERN_DEBUG "%s: nv_tx_done_optimized: flags 0x%x.\n", + dev->name, flags); + + pci_unmap_page(np->pci_dev, np->get_tx_ctx->dma, + np->get_tx_ctx->dma_len, + PCI_DMA_TODEVICE); + np->get_tx_ctx->dma = 0; + + if (flags & NV_TX2_LASTPACKET) { + if (!(flags & NV_TX2_ERROR)) + np->stats.tx_packets++; + dev_kfree_skb_any(np->get_tx_ctx->skb); + np->get_tx_ctx->skb = NULL; + } + if (unlikely(np->get_tx.ex++ == np->last_tx.ex)) + np->get_tx.ex = np->first_tx.ex; + if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx)) + np->get_tx_ctx = np->first_tx_ctx; } - if (np->next_tx - np->nic_tx < np->tx_limit_start) + if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx))) { + np->tx_stop = 0; netif_wake_queue(dev); + } } /* @@ -1700,9 +1990,8 @@ static void nv_tx_timeout(struct net_dev { int i; - printk(KERN_INFO "%s: Ring at %lx: next %d nic %d\n", - dev->name, (unsigned long)np->ring_addr, - np->next_tx, np->nic_tx); + printk(KERN_INFO "%s: Ring at %lx\n", + dev->name, (unsigned long)np->ring_addr); printk(KERN_INFO "%s: Dumping tx registers\n", dev->name); for (i=0;i<=np->register_size;i+= 32) { printk(KERN_INFO "%3x: %08x %08x %08x %08x %08x %08x %08x %08x\n", @@ -1750,13 +2039,16 @@ static void nv_tx_timeout(struct net_dev nv_stop_tx(dev); /* 2) check that the packets were not sent already: */ - nv_tx_done(dev); + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + nv_tx_done(dev); + else + nv_tx_done_optimized(dev, np->tx_ring_size); /* 3) if there are dead entries: clear everything */ - if (np->next_tx != np->nic_tx) { + if (np->get_tx_ctx != np->put_tx_ctx) { printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name); nv_drain_tx(dev); - np->next_tx = np->nic_tx = 0; + nv_init_tx(dev); setup_hw_rings(dev, NV_SETUP_TX_RING); netif_wake_queue(dev); } @@ -1813,50 +2105,175 @@ static int nv_getlen(struct net_device * dev->name); return -1; } - dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n", - dev->name, datalen); - return datalen; + dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n", + dev->name, datalen); + return datalen; + } +} + +static int nv_rx_process(struct net_device *dev, int limit) +{ + struct fe_priv *np = netdev_priv(dev); + u32 flags; + u32 rx_processed_cnt = 0; + struct sk_buff *skb; + int len; + + while((np->get_rx.orig != np->put_rx.orig) && + !((flags = le32_to_cpu(np->get_rx.orig->flaglen)) & NV_RX_AVAIL) && + (rx_processed_cnt++ < limit)) { + + dprintk(KERN_DEBUG "%s: nv_rx_process: flags 0x%x.\n", + dev->name, flags); + + /* + * the packet is for us - immediately tear down the pci mapping. + * TODO: check if a prefetch of the first cacheline improves + * the performance. + */ + pci_unmap_single(np->pci_dev, np->get_rx_ctx->dma, + np->get_rx_ctx->dma_len, + PCI_DMA_FROMDEVICE); + skb = np->get_rx_ctx->skb; + np->get_rx_ctx->skb = NULL; + + { + int j; + dprintk(KERN_DEBUG "Dumping packet (flags 0x%x).",flags); + for (j=0; j<64; j++) { + if ((j%16) == 0) + dprintk("\n%03x:", j); + dprintk(" %02x", ((unsigned char*)skb->data)[j]); + } + dprintk("\n"); + } + /* look at what we actually got: */ + if (np->desc_ver == DESC_VER_1) { + if (likely(flags & NV_RX_DESCRIPTORVALID)) { + len = flags & LEN_MASK_V1; + if (unlikely(flags & NV_RX_ERROR)) { + if (flags & NV_RX_ERROR4) { + len = nv_getlen(dev, skb->data, len); + if (len < 0) { + np->stats.rx_errors++; + dev_kfree_skb(skb); + goto next_pkt; + } + } + /* framing errors are soft errors */ + else if (flags & NV_RX_FRAMINGERR) { + if (flags & NV_RX_SUBSTRACT1) { + len--; + } + } + /* the rest are hard errors */ + else { + if (flags & NV_RX_MISSEDFRAME) + np->stats.rx_missed_errors++; + if (flags & NV_RX_CRCERR) + np->stats.rx_crc_errors++; + if (flags & NV_RX_OVERFLOW) + np->stats.rx_over_errors++; + np->stats.rx_errors++; + dev_kfree_skb(skb); + goto next_pkt; + } + } + } else { + dev_kfree_skb(skb); + goto next_pkt; + } + } else { + if (likely(flags & NV_RX2_DESCRIPTORVALID)) { + len = flags & LEN_MASK_V2; + if (unlikely(flags & NV_RX2_ERROR)) { + if (flags & NV_RX2_ERROR4) { + len = nv_getlen(dev, skb->data, len); + if (len < 0) { + np->stats.rx_errors++; + dev_kfree_skb(skb); + goto next_pkt; + } + } + /* framing errors are soft errors */ + else if (flags & NV_RX2_FRAMINGERR) { + if (flags & NV_RX2_SUBSTRACT1) { + len--; + } + } + /* the rest are hard errors */ + else { + if (flags & NV_RX2_CRCERR) + np->stats.rx_crc_errors++; + if (flags & NV_RX2_OVERFLOW) + np->stats.rx_over_errors++; + np->stats.rx_errors++; + dev_kfree_skb(skb); + goto next_pkt; + } + } + if ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK2)/*ip and tcp */ { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + if ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK1 || + (flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK3) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + } + } else { + dev_kfree_skb(skb); + goto next_pkt; + } + } + /* got a valid packet - forward it to the network core */ + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, dev); + dprintk(KERN_DEBUG "%s: nv_rx_process: %d bytes, proto %d accepted.\n", + dev->name, len, skb->protocol); +#ifdef CONFIG_FORCEDETH_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += len; +next_pkt: + if (unlikely(np->get_rx.orig++ == np->last_rx.orig)) + np->get_rx.orig = np->first_rx.orig; + if (unlikely(np->get_rx_ctx++ == np->last_rx_ctx)) + np->get_rx_ctx = np->first_rx_ctx; } + + return rx_processed_cnt; } -static int nv_rx_process(struct net_device *dev, int limit) +static int nv_rx_process_optimized(struct net_device *dev, int limit) { struct fe_priv *np = netdev_priv(dev); u32 flags; u32 vlanflags = 0; - int count; - - for (count = 0; count < limit; ++count) { - struct sk_buff *skb; - int len; - int i; - if (np->cur_rx - np->refill_rx >= np->rx_ring_size) - break; /* we scanned the whole ring - do not continue */ - - i = np->cur_rx % np->rx_ring_size; - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { - flags = le32_to_cpu(np->rx_ring.orig[i].flaglen); - len = nv_descr_getlength(&np->rx_ring.orig[i], np->desc_ver); - } else { - flags = le32_to_cpu(np->rx_ring.ex[i].flaglen); - len = nv_descr_getlength_ex(&np->rx_ring.ex[i], np->desc_ver); - vlanflags = le32_to_cpu(np->rx_ring.ex[i].buflow); - } + u32 rx_processed_cnt = 0; + struct sk_buff *skb; + int len; - dprintk(KERN_DEBUG "%s: nv_rx_process: looking at packet %d, flags 0x%x.\n", - dev->name, np->cur_rx, flags); + while((np->get_rx.ex != np->put_rx.ex) && + !((flags = le32_to_cpu(np->get_rx.ex->flaglen)) & NV_RX2_AVAIL) && + (rx_processed_cnt++ < limit)) { - if (flags & NV_RX_AVAIL) - break; /* still owned by hardware, */ + dprintk(KERN_DEBUG "%s: nv_rx_process_optimized: flags 0x%x.\n", + dev->name, flags); /* * the packet is for us - immediately tear down the pci mapping. * TODO: check if a prefetch of the first cacheline improves * the performance. */ - pci_unmap_single(np->pci_dev, np->rx_dma[i], - np->rx_skbuff[i]->end-np->rx_skbuff[i]->data, + pci_unmap_single(np->pci_dev, np->get_rx_ctx->dma, + np->get_rx_ctx->dma_len, PCI_DMA_FROMDEVICE); + skb = np->get_rx_ctx->skb; + np->get_rx_ctx->skb = NULL; { int j; @@ -1864,123 +2281,90 @@ static int nv_rx_process(struct net_devi for (j=0; j<64; j++) { if ((j%16) == 0) dprintk("\n%03x:", j); - dprintk(" %02x", ((unsigned char*)np->rx_skbuff[i]->data)[j]); + dprintk(" %02x", ((unsigned char*)skb->data)[j]); } dprintk("\n"); } /* look at what we actually got: */ - if (np->desc_ver == DESC_VER_1) { - if (!(flags & NV_RX_DESCRIPTORVALID)) - goto next_pkt; - - if (flags & NV_RX_ERROR) { - if (flags & NV_RX_MISSEDFRAME) { - np->stats.rx_missed_errors++; - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) { - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & NV_RX_CRCERR) { - np->stats.rx_crc_errors++; - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & NV_RX_OVERFLOW) { - np->stats.rx_over_errors++; - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & NV_RX_ERROR4) { - len = nv_getlen(dev, np->rx_skbuff[i]->data, len); - if (len < 0) { - np->stats.rx_errors++; - goto next_pkt; - } - } - /* framing errors are soft errors. */ - if (flags & NV_RX_FRAMINGERR) { - if (flags & NV_RX_SUBSTRACT1) { - len--; - } - } - } - } else { - if (!(flags & NV_RX2_DESCRIPTORVALID)) - goto next_pkt; - - if (flags & NV_RX2_ERROR) { - if (flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) { - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & NV_RX2_CRCERR) { - np->stats.rx_crc_errors++; - np->stats.rx_errors++; - goto next_pkt; - } - if (flags & NV_RX2_OVERFLOW) { - np->stats.rx_over_errors++; - np->stats.rx_errors++; - goto next_pkt; - } + if (likely(flags & NV_RX2_DESCRIPTORVALID)) { + len = flags & LEN_MASK_V2; + if (unlikely(flags & NV_RX2_ERROR)) { if (flags & NV_RX2_ERROR4) { - len = nv_getlen(dev, np->rx_skbuff[i]->data, len); + len = nv_getlen(dev, skb->data, len); if (len < 0) { - np->stats.rx_errors++; + dev_kfree_skb(skb); goto next_pkt; } } /* framing errors are soft errors */ - if (flags & NV_RX2_FRAMINGERR) { + else if (flags & NV_RX2_FRAMINGERR) { if (flags & NV_RX2_SUBSTRACT1) { len--; } } + /* the rest are hard errors */ + else { + dev_kfree_skb(skb); + goto next_pkt; + } } - if (np->rx_csum) { - flags &= NV_RX2_CHECKSUMMASK; - if (flags == NV_RX2_CHECKSUMOK1 || - flags == NV_RX2_CHECKSUMOK2 || - flags == NV_RX2_CHECKSUMOK3) { - dprintk(KERN_DEBUG "%s: hw checksum hit!.\n", dev->name); - np->rx_skbuff[i]->ip_summed = CHECKSUM_UNNECESSARY; - } else { - dprintk(KERN_DEBUG "%s: hwchecksum miss!.\n", dev->name); + + if ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK2)/*ip and tcp */ { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + if ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK1 || + (flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUMOK3) { + skb->ip_summed = CHECKSUM_UNNECESSARY; } } - } - /* got a valid packet - forward it to the network core */ - skb = np->rx_skbuff[i]; - np->rx_skbuff[i] = NULL; - skb_put(skb, len); - skb->protocol = eth_type_trans(skb, dev); - dprintk(KERN_DEBUG "%s: nv_rx_process: packet %d with %d bytes, proto %d accepted.\n", - dev->name, np->cur_rx, len, skb->protocol); + /* got a valid packet - forward it to the network core */ + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, dev); + prefetch(skb->data); + + dprintk(KERN_DEBUG "%s: nv_rx_process_optimized: %d bytes, proto %d accepted.\n", + dev->name, len, skb->protocol); + + if (likely(!np->vlangrp)) { #ifdef CONFIG_FORCEDETH_NAPI - if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) - vlan_hwaccel_receive_skb(skb, np->vlangrp, - vlanflags & NV_RX3_VLAN_TAG_MASK); - else - netif_receive_skb(skb); + netif_receive_skb(skb); #else - if (np->vlangrp && (vlanflags & NV_RX3_VLAN_TAG_PRESENT)) - vlan_hwaccel_rx(skb, np->vlangrp, - vlanflags & NV_RX3_VLAN_TAG_MASK); - else - netif_rx(skb); + netif_rx(skb); #endif - dev->last_rx = jiffies; - np->stats.rx_packets++; - np->stats.rx_bytes += len; + } else { + vlanflags = le32_to_cpu(np->get_rx.ex->buflow); + if (vlanflags & NV_RX3_VLAN_TAG_PRESENT) { +#ifdef CONFIG_FORCEDETH_NAPI + vlan_hwaccel_receive_skb(skb, np->vlangrp, + vlanflags & NV_RX3_VLAN_TAG_MASK); +#else + vlan_hwaccel_rx(skb, np->vlangrp, + vlanflags & NV_RX3_VLAN_TAG_MASK); +#endif + } else { +#ifdef CONFIG_FORCEDETH_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + } + } + + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += len; + } else { + dev_kfree_skb(skb); + } next_pkt: - np->cur_rx++; + if (unlikely(np->get_rx.ex++ == np->last_rx.ex)) + np->get_rx.ex = np->first_rx.ex; + if (unlikely(np->get_rx_ctx++ == np->last_rx_ctx)) + np->get_rx_ctx = np->first_rx_ctx; } - return count; + return rx_processed_cnt; } static void set_bufsize(struct net_device *dev) @@ -2456,7 +2840,6 @@ static irqreturn_t nv_nic_irq(int foo, v events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK; writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus); } - pci_push(base); dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events); if (!(events & np->irqmask)) break; @@ -2465,22 +2848,46 @@ static irqreturn_t nv_nic_irq(int foo, v nv_tx_done(dev); spin_unlock(&np->lock); - if (events & NVREG_IRQ_LINK) { +#ifdef CONFIG_FORCEDETH_NAPI + if (events & NVREG_IRQ_RX_ALL) { + netif_rx_schedule(dev); + + /* Disable furthur receive irq's */ + spin_lock(&np->lock); + np->irqmask &= ~NVREG_IRQ_RX_ALL; + + if (np->msi_flags & NV_MSI_X_ENABLED) + writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); + else + writel(np->irqmask, base + NvRegIrqMask); + spin_unlock(&np->lock); + } +#else + if (nv_rx_process(dev, dev->weight)) { + if (unlikely(nv_alloc_rx(dev))) { + spin_lock(&np->lock); + if (!np->in_shutdown) + mod_timer(&np->oom_kick, jiffies + OOM_REFILL); + spin_unlock(&np->lock); + } + } +#endif + if (unlikely(events & NVREG_IRQ_LINK)) { spin_lock(&np->lock); nv_link_irq(dev); spin_unlock(&np->lock); } - if (np->need_linktimer && time_after(jiffies, np->link_timeout)) { + if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) { spin_lock(&np->lock); nv_linkchange(dev); spin_unlock(&np->lock); np->link_timeout = jiffies + LINK_TIMEOUT; } - if (events & (NVREG_IRQ_TX_ERR)) { + if (unlikely(events & (NVREG_IRQ_TX_ERR))) { dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n", dev->name, events); } - if (events & (NVREG_IRQ_UNKNOWN)) { + if (unlikely(events & (NVREG_IRQ_UNKNOWN))) { printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", dev->name, events); } @@ -2501,6 +2908,63 @@ static irqreturn_t nv_nic_irq(int foo, v spin_unlock(&np->lock); break; } + if (unlikely(i > max_interrupt_work)) { + spin_lock(&np->lock); + /* disable interrupts on the nic */ + if (!(np->msi_flags & NV_MSI_X_ENABLED)) + writel(0, base + NvRegIrqMask); + else + writel(np->irqmask, base + NvRegIrqMask); + pci_push(base); + + if (!np->in_shutdown) { + np->nic_poll_irq = np->irqmask; + mod_timer(&np->nic_poll, jiffies + POLL_WAIT); + } + printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq.\n", dev->name, i); + spin_unlock(&np->lock); + break; + } + + } + dprintk(KERN_DEBUG "%s: nv_nic_irq completed\n", dev->name); + + return IRQ_RETVAL(i); +} + +#define TX_WORK_PER_LOOP 64 +#define RX_WORK_PER_LOOP 64 +/** + * All _optimized functions are used to help increase performance + * (reduce CPU and increase throughput). They use descripter version 3, + * compiler directives, and reduce memory accesses. + */ +static irqreturn_t nv_nic_irq_optimized(int foo, void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct fe_priv *np = netdev_priv(dev); + u8 __iomem *base = get_hwbase(dev); + u32 events; + int i; + + dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized\n", dev->name); + + for (i=0; ; i++) { + if (!(np->msi_flags & NV_MSI_X_ENABLED)) { + events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK; + writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); + } else { + events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK; + writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus); + } + dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events); + if (!(events & np->irqmask)) + break; + + spin_lock(&np->lock); + nv_tx_done_optimized(dev, TX_WORK_PER_LOOP); + spin_unlock(&np->lock); + #ifdef CONFIG_FORCEDETH_NAPI if (events & NVREG_IRQ_RX_ALL) { netif_rx_schedule(dev); @@ -2516,15 +2980,53 @@ static irqreturn_t nv_nic_irq(int foo, v spin_unlock(&np->lock); } #else - nv_rx_process(dev, dev->weight); - if (nv_alloc_rx(dev)) { + if (nv_rx_process_optimized(dev, dev->weight)) { + if (unlikely(nv_alloc_rx_optimized(dev))) { + spin_lock(&np->lock); + if (!np->in_shutdown) + mod_timer(&np->oom_kick, jiffies + OOM_REFILL); + spin_unlock(&np->lock); + } + } +#endif + if (unlikely(events & NVREG_IRQ_LINK)) { spin_lock(&np->lock); - if (!np->in_shutdown) - mod_timer(&np->oom_kick, jiffies + OOM_REFILL); + nv_link_irq(dev); spin_unlock(&np->lock); } -#endif - if (i > max_interrupt_work) { + if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) { + spin_lock(&np->lock); + nv_linkchange(dev); + spin_unlock(&np->lock); + np->link_timeout = jiffies + LINK_TIMEOUT; + } + if (unlikely(events & (NVREG_IRQ_TX_ERR))) { + dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n", + dev->name, events); + } + if (unlikely(events & (NVREG_IRQ_UNKNOWN))) { + printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", + dev->name, events); + } + if (unlikely(events & NVREG_IRQ_RECOVER_ERROR)) { + spin_lock(&np->lock); + /* disable interrupts on the nic */ + if (!(np->msi_flags & NV_MSI_X_ENABLED)) + writel(0, base + NvRegIrqMask); + else + writel(np->irqmask, base + NvRegIrqMask); + pci_push(base); + + if (!np->in_shutdown) { + np->nic_poll_irq = np->irqmask; + np->recover_error = 1; + mod_timer(&np->nic_poll, jiffies + POLL_WAIT); + } + spin_unlock(&np->lock); + break; + } + + if (unlikely(i > max_interrupt_work)) { spin_lock(&np->lock); /* disable interrupts on the nic */ if (!(np->msi_flags & NV_MSI_X_ENABLED)) @@ -2543,7 +3045,7 @@ static irqreturn_t nv_nic_irq(int foo, v } } - dprintk(KERN_DEBUG "%s: nv_nic_irq completed\n", dev->name); + dprintk(KERN_DEBUG "%s: nv_nic_irq_optimized completed\n", dev->name); return IRQ_RETVAL(i); } @@ -2562,20 +3064,19 @@ static irqreturn_t nv_nic_irq_tx(int foo for (i=0; ; i++) { events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL; writel(NVREG_IRQ_TX_ALL, base + NvRegMSIXIrqStatus); - pci_push(base); dprintk(KERN_DEBUG "%s: tx irq: %08x\n", dev->name, events); if (!(events & np->irqmask)) break; spin_lock_irqsave(&np->lock, flags); - nv_tx_done(dev); + nv_tx_done_optimized(dev, TX_WORK_PER_LOOP); spin_unlock_irqrestore(&np->lock, flags); - if (events & (NVREG_IRQ_TX_ERR)) { + if (unlikely(events & (NVREG_IRQ_TX_ERR))) { dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n", dev->name, events); } - if (i > max_interrupt_work) { + if (unlikely(i > max_interrupt_work)) { spin_lock_irqsave(&np->lock, flags); /* disable interrupts on the nic */ writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask); @@ -2604,7 +3105,10 @@ static int nv_napi_poll(struct net_devic u8 __iomem *base = get_hwbase(dev); unsigned long flags; - pkts = nv_rx_process(dev, limit); + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + pkts = nv_rx_process(dev, limit); + else + pkts = nv_rx_process_optimized(dev, limit); if (nv_alloc_rx(dev)) { spin_lock_irqsave(&np->lock, flags); @@ -2670,20 +3174,20 @@ static irqreturn_t nv_nic_irq_rx(int foo for (i=0; ; i++) { events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL; writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus); - pci_push(base); dprintk(KERN_DEBUG "%s: rx irq: %08x\n", dev->name, events); if (!(events & np->irqmask)) break; - nv_rx_process(dev, dev->weight); - if (nv_alloc_rx(dev)) { - spin_lock_irqsave(&np->lock, flags); - if (!np->in_shutdown) - mod_timer(&np->oom_kick, jiffies + OOM_REFILL); - spin_unlock_irqrestore(&np->lock, flags); + if (nv_rx_process_optimized(dev, dev->weight)) { + if (unlikely(nv_alloc_rx_optimized(dev))) { + spin_lock_irqsave(&np->lock, flags); + if (!np->in_shutdown) + mod_timer(&np->oom_kick, jiffies + OOM_REFILL); + spin_unlock_irqrestore(&np->lock, flags); + } } - if (i > max_interrupt_work) { + if (unlikely(i > max_interrupt_work)) { spin_lock_irqsave(&np->lock, flags); /* disable interrupts on the nic */ writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); @@ -2718,11 +3222,15 @@ static irqreturn_t nv_nic_irq_other(int for (i=0; ; i++) { events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_OTHER; writel(NVREG_IRQ_OTHER, base + NvRegMSIXIrqStatus); - pci_push(base); dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name, events); if (!(events & np->irqmask)) break; + /* check tx in case we reached max loop limit in tx isr */ + spin_lock_irqsave(&np->lock, flags); + nv_tx_done_optimized(dev, TX_WORK_PER_LOOP); + spin_unlock_irqrestore(&np->lock, flags); + if (events & NVREG_IRQ_LINK) { spin_lock_irqsave(&np->lock, flags); nv_link_irq(dev); @@ -2752,7 +3260,7 @@ static irqreturn_t nv_nic_irq_other(int printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n", dev->name, events); } - if (i > max_interrupt_work) { + if (unlikely(i > max_interrupt_work)) { spin_lock_irqsave(&np->lock, flags); /* disable interrupts on the nic */ writel(NVREG_IRQ_OTHER, base + NvRegIrqMask); @@ -2835,6 +3343,16 @@ static int nv_request_irq(struct net_dev u8 __iomem *base = get_hwbase(dev); int ret = 1; int i; + irqreturn_t (*handler)(int foo, void *data); + + if (intr_test) { + handler = nv_nic_irq_test; + } else { + if (np->desc_ver == DESC_VER_3) + handler = nv_nic_irq_optimized; + else + handler = nv_nic_irq; + } if (np->msi_flags & NV_MSI_X_CAPABLE) { for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++) { @@ -2872,10 +3390,7 @@ static int nv_request_irq(struct net_dev set_msix_vector_map(dev, NV_MSI_X_VECTOR_OTHER, NVREG_IRQ_OTHER); } else { /* Request irq for all interrupts */ - if ((!intr_test && - request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector, &nv_nic_irq, IRQF_SHARED, dev->name, dev) != 0) || - (intr_test && - request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector, &nv_nic_irq_test, IRQF_SHARED, dev->name, dev) != 0)) { + if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector, handler, IRQF_SHARED, dev->name, dev) != 0) { printk(KERN_INFO "forcedeth: request_irq failed %d\n", ret); pci_disable_msix(np->pci_dev); np->msi_flags &= ~NV_MSI_X_ENABLED; @@ -2891,8 +3406,7 @@ static int nv_request_irq(struct net_dev if (ret != 0 && np->msi_flags & NV_MSI_CAPABLE) { if ((ret = pci_enable_msi(np->pci_dev)) == 0) { np->msi_flags |= NV_MSI_ENABLED; - if ((!intr_test && request_irq(np->pci_dev->irq, &nv_nic_irq, IRQF_SHARED, dev->name, dev) != 0) || - (intr_test && request_irq(np->pci_dev->irq, &nv_nic_irq_test, IRQF_SHARED, dev->name, dev) != 0)) { + if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0) { printk(KERN_INFO "forcedeth: request_irq failed %d\n", ret); pci_disable_msi(np->pci_dev); np->msi_flags &= ~NV_MSI_ENABLED; @@ -2907,8 +3421,7 @@ static int nv_request_irq(struct net_dev } } if (ret != 0) { - if ((!intr_test && request_irq(np->pci_dev->irq, &nv_nic_irq, IRQF_SHARED, dev->name, dev) != 0) || - (intr_test && request_irq(np->pci_dev->irq, &nv_nic_irq_test, IRQF_SHARED, dev->name, dev) != 0)) + if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0) goto out_err; } @@ -3051,47 +3564,8 @@ static void nv_do_stats_poll(unsigned lo { struct net_device *dev = (struct net_device *) data; struct fe_priv *np = netdev_priv(dev); - u8 __iomem *base = get_hwbase(dev); - np->estats.tx_bytes += readl(base + NvRegTxCnt); - np->estats.tx_zero_rexmt += readl(base + NvRegTxZeroReXmt); - np->estats.tx_one_rexmt += readl(base + NvRegTxOneReXmt); - np->estats.tx_many_rexmt += readl(base + NvRegTxManyReXmt); - np->estats.tx_late_collision += readl(base + NvRegTxLateCol); - np->estats.tx_fifo_errors += readl(base + NvRegTxUnderflow); - np->estats.tx_carrier_errors += readl(base + NvRegTxLossCarrier); - np->estats.tx_excess_deferral += readl(base + NvRegTxExcessDef); - np->estats.tx_retry_error += readl(base + NvRegTxRetryErr); - np->estats.tx_deferral += readl(base + NvRegTxDef); - np->estats.tx_packets += readl(base + NvRegTxFrame); - np->estats.tx_pause += readl(base + NvRegTxPause); - np->estats.rx_frame_error += readl(base + NvRegRxFrameErr); - np->estats.rx_extra_byte += readl(base + NvRegRxExtraByte); - np->estats.rx_late_collision += readl(base + NvRegRxLateCol); - np->estats.rx_runt += readl(base + NvRegRxRunt); - np->estats.rx_frame_too_long += readl(base + NvRegRxFrameTooLong); - np->estats.rx_over_errors += readl(base + NvRegRxOverflow); - np->estats.rx_crc_errors += readl(base + NvRegRxFCSErr); - np->estats.rx_frame_align_error += readl(base + NvRegRxFrameAlignErr); - np->estats.rx_length_error += readl(base + NvRegRxLenErr); - np->estats.rx_unicast += readl(base + NvRegRxUnicast); - np->estats.rx_multicast += readl(base + NvRegRxMulticast); - np->estats.rx_broadcast += readl(base + NvRegRxBroadcast); - np->estats.rx_bytes += readl(base + NvRegRxCnt); - np->estats.rx_pause += readl(base + NvRegRxPause); - np->estats.rx_drop_frame += readl(base + NvRegRxDropFrame); - np->estats.rx_packets = - np->estats.rx_unicast + - np->estats.rx_multicast + - np->estats.rx_broadcast; - np->estats.rx_errors_total = - np->estats.rx_crc_errors + - np->estats.rx_over_errors + - np->estats.rx_frame_error + - (np->estats.rx_frame_align_error - np->estats.rx_extra_byte) + - np->estats.rx_late_collision + - np->estats.rx_runt + - np->estats.rx_frame_too_long; + nv_get_hw_stats(dev); if (!np->in_shutdown) mod_timer(&np->stats_poll, jiffies + STATS_INTERVAL); @@ -3465,7 +3939,7 @@ static int nv_set_ringparam(struct net_d { struct fe_priv *np = netdev_priv(dev); u8 __iomem *base = get_hwbase(dev); - u8 *rxtx_ring, *rx_skbuff, *tx_skbuff, *rx_dma, *tx_dma, *tx_dma_len; + u8 *rxtx_ring, *rx_skbuff, *tx_skbuff; dma_addr_t ring_addr; if (ring->rx_pending < RX_RING_MIN || @@ -3491,12 +3965,9 @@ static int nv_set_ringparam(struct net_d sizeof(struct ring_desc_ex) * (ring->rx_pending + ring->tx_pending), &ring_addr); } - rx_skbuff = kmalloc(sizeof(struct sk_buff*) * ring->rx_pending, GFP_KERNEL); - rx_dma = kmalloc(sizeof(dma_addr_t) * ring->rx_pending, GFP_KERNEL); - tx_skbuff = kmalloc(sizeof(struct sk_buff*) * ring->tx_pending, GFP_KERNEL); - tx_dma = kmalloc(sizeof(dma_addr_t) * ring->tx_pending, GFP_KERNEL); - tx_dma_len = kmalloc(sizeof(unsigned int) * ring->tx_pending, GFP_KERNEL); - if (!rxtx_ring || !rx_skbuff || !rx_dma || !tx_skbuff || !tx_dma || !tx_dma_len) { + rx_skbuff = kmalloc(sizeof(struct nv_skb_map) * ring->rx_pending, GFP_KERNEL); + tx_skbuff = kmalloc(sizeof(struct nv_skb_map) * ring->tx_pending, GFP_KERNEL); + if (!rxtx_ring || !rx_skbuff || !tx_skbuff) { /* fall back to old rings */ if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { if (rxtx_ring) @@ -3509,14 +3980,8 @@ static int nv_set_ringparam(struct net_d } if (rx_skbuff) kfree(rx_skbuff); - if (rx_dma) - kfree(rx_dma); if (tx_skbuff) kfree(tx_skbuff); - if (tx_dma) - kfree(tx_dma); - if (tx_dma_len) - kfree(tx_dma_len); goto exit; } @@ -3538,8 +4003,6 @@ static int nv_set_ringparam(struct net_d /* set new values */ np->rx_ring_size = ring->rx_pending; np->tx_ring_size = ring->tx_pending; - np->tx_limit_stop = ring->tx_pending - TX_LIMIT_DIFFERENCE; - np->tx_limit_start = ring->tx_pending - TX_LIMIT_DIFFERENCE - 1; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->rx_ring.orig = (struct ring_desc*)rxtx_ring; np->tx_ring.orig = &np->rx_ring.orig[np->rx_ring_size]; @@ -3547,18 +4010,12 @@ static int nv_set_ringparam(struct net_d np->rx_ring.ex = (struct ring_desc_ex*)rxtx_ring; np->tx_ring.ex = &np->rx_ring.ex[np->rx_ring_size]; } - np->rx_skbuff = (struct sk_buff**)rx_skbuff; - np->rx_dma = (dma_addr_t*)rx_dma; - np->tx_skbuff = (struct sk_buff**)tx_skbuff; - np->tx_dma = (dma_addr_t*)tx_dma; - np->tx_dma_len = (unsigned int*)tx_dma_len; + np->rx_skb = (struct nv_skb_map*)rx_skbuff; + np->tx_skb = (struct nv_skb_map*)tx_skbuff; np->ring_addr = ring_addr; - memset(np->rx_skbuff, 0, sizeof(struct sk_buff*) * np->rx_ring_size); - memset(np->rx_dma, 0, sizeof(dma_addr_t) * np->rx_ring_size); - memset(np->tx_skbuff, 0, sizeof(struct sk_buff*) * np->tx_ring_size); - memset(np->tx_dma, 0, sizeof(dma_addr_t) * np->tx_ring_size); - memset(np->tx_dma_len, 0, sizeof(unsigned int) * np->tx_ring_size); + memset(np->rx_skb, 0, sizeof(struct nv_skb_map) * np->rx_ring_size); + memset(np->tx_skb, 0, sizeof(struct nv_skb_map) * np->tx_ring_size); if (netif_running(dev)) { /* reinit driver view of the queues */ @@ -3727,8 +4184,10 @@ static int nv_get_stats_count(struct net { struct fe_priv *np = netdev_priv(dev); - if (np->driver_data & DEV_HAS_STATISTICS) - return sizeof(struct nv_ethtool_stats)/sizeof(u64); + if (np->driver_data & DEV_HAS_STATISTICS_V1) + return NV_DEV_STATISTICS_V1_COUNT; + else if (np->driver_data & DEV_HAS_STATISTICS_V2) + return NV_DEV_STATISTICS_V2_COUNT; else return 0; } @@ -3955,7 +4414,7 @@ static int nv_loopback_test(struct net_d dprintk(KERN_DEBUG "%s: loopback len mismatch %d vs %d\n", dev->name, len, pkt_len); } else { - rx_skb = np->rx_skbuff[0]; + rx_skb = np->rx_skb[0].skb; for (i = 0; i < pkt_len; i++) { if (rx_skb->data[i] != (u8)(i & 0xff)) { ret = 0; @@ -4315,7 +4774,7 @@ static int nv_open(struct net_device *de mod_timer(&np->oom_kick, jiffies + OOM_REFILL); /* start statistics timer */ - if (np->driver_data & DEV_HAS_STATISTICS) + if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2)) mod_timer(&np->stats_poll, jiffies + STATS_INTERVAL); spin_unlock_irq(&np->lock); @@ -4412,7 +4871,9 @@ static int __devinit nv_probe(struct pci if (err < 0) goto out_disable; - if (id->driver_data & (DEV_HAS_VLAN|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS)) + if (id->driver_data & (DEV_HAS_VLAN|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V2)) + np->register_size = NV_PCI_REGSZ_VER3; + else if (id->driver_data & DEV_HAS_STATISTICS_V1) np->register_size = NV_PCI_REGSZ_VER2; else np->register_size = NV_PCI_REGSZ_VER1; @@ -4475,10 +4936,8 @@ static int __devinit nv_probe(struct pci np->rx_csum = 1; np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK; dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; -#ifdef NETIF_F_TSO dev->features |= NETIF_F_TSO; -#endif - } + } np->vlanctl_bits = 0; if (id->driver_data & DEV_HAS_VLAN) { @@ -4512,8 +4971,6 @@ static int __devinit nv_probe(struct pci np->rx_ring_size = RX_RING_DEFAULT; np->tx_ring_size = TX_RING_DEFAULT; - np->tx_limit_stop = np->tx_ring_size - TX_LIMIT_DIFFERENCE; - np->tx_limit_start = np->tx_ring_size - TX_LIMIT_DIFFERENCE - 1; if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->rx_ring.orig = pci_alloc_consistent(pci_dev, @@ -4530,22 +4987,19 @@ static int __devinit nv_probe(struct pci goto out_unmap; np->tx_ring.ex = &np->rx_ring.ex[np->rx_ring_size]; } - np->rx_skbuff = kmalloc(sizeof(struct sk_buff*) * np->rx_ring_size, GFP_KERNEL); - np->rx_dma = kmalloc(sizeof(dma_addr_t) * np->rx_ring_size, GFP_KERNEL); - np->tx_skbuff = kmalloc(sizeof(struct sk_buff*) * np->tx_ring_size, GFP_KERNEL); - np->tx_dma = kmalloc(sizeof(dma_addr_t) * np->tx_ring_size, GFP_KERNEL); - np->tx_dma_len = kmalloc(sizeof(unsigned int) * np->tx_ring_size, GFP_KERNEL); - if (!np->rx_skbuff || !np->rx_dma || !np->tx_skbuff || !np->tx_dma || !np->tx_dma_len) + np->rx_skb = kmalloc(sizeof(struct nv_skb_map) * np->rx_ring_size, GFP_KERNEL); + np->tx_skb = kmalloc(sizeof(struct nv_skb_map) * np->tx_ring_size, GFP_KERNEL); + if (!np->rx_skb || !np->tx_skb) goto out_freering; - memset(np->rx_skbuff, 0, sizeof(struct sk_buff*) * np->rx_ring_size); - memset(np->rx_dma, 0, sizeof(dma_addr_t) * np->rx_ring_size); - memset(np->tx_skbuff, 0, sizeof(struct sk_buff*) * np->tx_ring_size); - memset(np->tx_dma, 0, sizeof(dma_addr_t) * np->tx_ring_size); - memset(np->tx_dma_len, 0, sizeof(unsigned int) * np->tx_ring_size); + memset(np->rx_skb, 0, sizeof(struct nv_skb_map) * np->rx_ring_size); + memset(np->tx_skb, 0, sizeof(struct nv_skb_map) * np->tx_ring_size); dev->open = nv_open; dev->stop = nv_close; - dev->hard_start_xmit = nv_start_xmit; + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + dev->hard_start_xmit = nv_start_xmit; + else + dev->hard_start_xmit = nv_start_xmit_optimized; dev->get_stats = nv_get_stats; dev->change_mtu = nv_change_mtu; dev->set_mac_address = nv_set_mac_address; @@ -4553,7 +5007,7 @@ static int __devinit nv_probe(struct pci #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = nv_poll_controller; #endif - dev->weight = 64; + dev->weight = RX_WORK_PER_LOOP; #ifdef CONFIG_FORCEDETH_NAPI dev->poll = nv_napi_poll; #endif @@ -4868,83 +5322,83 @@ static struct pci_device_id pci_tbl[] = }, { /* CK804 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_8), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1, }, { /* CK804 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_9), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1, }, { /* MCP04 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_10), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1, }, { /* MCP04 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_11), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1, }, { /* MCP51 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_12), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V1, }, { /* MCP51 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_13), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V1, }, { /* MCP55 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_14), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP55 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP61 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_16), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP61 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_17), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP61 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_18), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP61 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_19), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_20), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_21), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_22), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_23), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_24), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_25), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_26), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_27), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, {0,}, }; diff -puN drivers/net/ixgb/ixgb.h~git-netdev-all drivers/net/ixgb/ixgb.h --- a/drivers/net/ixgb/ixgb.h~git-netdev-all +++ a/drivers/net/ixgb/ixgb.h @@ -61,9 +61,7 @@ #include #include #include -#ifdef NETIF_F_TSO #include -#endif #include #include diff -puN drivers/net/ixgb/ixgb_ethtool.c~git-netdev-all drivers/net/ixgb/ixgb_ethtool.c --- a/drivers/net/ixgb/ixgb_ethtool.c~git-netdev-all +++ a/drivers/net/ixgb/ixgb_ethtool.c @@ -82,10 +82,8 @@ static struct ixgb_stats ixgb_gstrings_s {"tx_restart_queue", IXGB_STAT(restart_queue) }, {"rx_long_length_errors", IXGB_STAT(stats.roc)}, {"rx_short_length_errors", IXGB_STAT(stats.ruc)}, -#ifdef NETIF_F_TSO {"tx_tcp_seg_good", IXGB_STAT(stats.tsctc)}, {"tx_tcp_seg_failed", IXGB_STAT(stats.tsctfc)}, -#endif {"rx_flow_control_xon", IXGB_STAT(stats.xonrxc)}, {"rx_flow_control_xoff", IXGB_STAT(stats.xoffrxc)}, {"tx_flow_control_xon", IXGB_STAT(stats.xontxc)}, @@ -240,7 +238,6 @@ ixgb_set_tx_csum(struct net_device *netd return 0; } -#ifdef NETIF_F_TSO static int ixgb_set_tso(struct net_device *netdev, uint32_t data) { @@ -250,7 +247,6 @@ ixgb_set_tso(struct net_device *netdev, netdev->features &= ~NETIF_F_TSO; return 0; } -#endif /* NETIF_F_TSO */ static uint32_t ixgb_get_msglevel(struct net_device *netdev) @@ -722,10 +718,8 @@ static const struct ethtool_ops ixgb_eth .set_sg = ethtool_op_set_sg, .get_msglevel = ixgb_get_msglevel, .set_msglevel = ixgb_set_msglevel, -#ifdef NETIF_F_TSO .get_tso = ethtool_op_get_tso, .set_tso = ixgb_set_tso, -#endif .get_strings = ixgb_get_strings, .phys_id = ixgb_phys_id, .get_stats_count = ixgb_get_stats_count, diff -puN drivers/net/ixgb/ixgb_main.c~git-netdev-all drivers/net/ixgb/ixgb_main.c --- a/drivers/net/ixgb/ixgb_main.c~git-netdev-all +++ a/drivers/net/ixgb/ixgb_main.c @@ -456,9 +456,7 @@ ixgb_probe(struct pci_dev *pdev, NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; -#ifdef NETIF_F_TSO netdev->features |= NETIF_F_TSO; -#endif #ifdef NETIF_F_LLTX netdev->features |= NETIF_F_LLTX; #endif @@ -1176,7 +1174,6 @@ ixgb_watchdog(unsigned long data) static int ixgb_tso(struct ixgb_adapter *adapter, struct sk_buff *skb) { -#ifdef NETIF_F_TSO struct ixgb_context_desc *context_desc; unsigned int i; uint8_t ipcss, ipcso, tucss, tucso, hdr_len; @@ -1233,7 +1230,6 @@ ixgb_tso(struct ixgb_adapter *adapter, s return 1; } -#endif return 0; } diff -puN drivers/net/mace.c~git-netdev-all drivers/net/mace.c --- a/drivers/net/mace.c~git-netdev-all +++ a/drivers/net/mace.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +75,6 @@ struct mace_data { #define PRIV_BYTES (sizeof(struct mace_data) \ + (N_RX_RING + NCMDS_TX * N_TX_RING + 3) * sizeof(struct dbdma_cmd)) -static int bitrev(int); static int mace_open(struct net_device *dev); static int mace_close(struct net_device *dev); static int mace_xmit_start(struct sk_buff *skb, struct net_device *dev); @@ -96,18 +96,6 @@ static void __mace_set_address(struct ne */ static unsigned char *dummy_buf; -/* Bit-reverse one byte of an ethernet hardware address. */ -static inline int -bitrev(int b) -{ - int d = 0, i; - - for (i = 0; i < 8; ++i, b >>= 1) - d = (d << 1) | (b & 1); - return d; -} - - static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_id *match) { struct device_node *mace = macio_get_of_node(mdev); @@ -173,7 +161,7 @@ static int __devinit mace_probe(struct m rev = addr[0] == 0 && addr[1] == 0xA0; for (j = 0; j < 6; ++j) { - dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j]; + dev->dev_addr[j] = rev ? bitrev8(addr[j]): addr[j]; } mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) | in_8(&mp->mace->chipid_lo); diff -puN drivers/net/macmace.c~git-netdev-all drivers/net/macmace.c --- a/drivers/net/macmace.c~git-netdev-all +++ a/drivers/net/macmace.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -81,19 +82,6 @@ static irqreturn_t mace_interrupt(int ir static irqreturn_t mace_dma_intr(int irq, void *dev_id); static void mace_tx_timeout(struct net_device *dev); -/* Bit-reverse one byte of an ethernet hardware address. */ - -static int bitrev(int b) -{ - int d = 0, i; - - for (i = 0; i < 8; ++i, b >>= 1) { - d = (d << 1) | (b & 1); - } - - return d; -} - /* * Load a receive DMA channel with a base address and ring length */ @@ -219,12 +207,12 @@ struct net_device *mace_probe(int unit) addr = (void *)MACE_PROM; for (j = 0; j < 6; ++j) { - u8 v=bitrev(addr[j<<4]); + u8 v = bitrev8(addr[j<<4]); checksum ^= v; dev->dev_addr[j] = v; } for (; j < 8; ++j) { - checksum ^= bitrev(addr[j<<4]); + checksum ^= bitrev8(addr[j<<4]); } if (checksum != 0xFF) { diff -puN drivers/net/macsonic.c~git-netdev-all drivers/net/macsonic.c --- a/drivers/net/macsonic.c~git-netdev-all +++ a/drivers/net/macsonic.c @@ -121,16 +121,12 @@ enum macsonic_type { * For reversing the PROM address */ -static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, - 1, 9, 5, 13, 3, 11, 7, 15}; - static inline void bit_reverse_addr(unsigned char addr[6]) { int i; for(i = 0; i < 6; i++) - addr[i] = ((nibbletab[addr[i] & 0xf] << 4) | - nibbletab[(addr[i] >> 4) &0xf]); + addr[i] = bitrev8(addr[i]); } int __init macsonic_init(struct net_device* dev) diff -puN drivers/net/myri10ge/myri10ge.c~git-netdev-all drivers/net/myri10ge/myri10ge.c --- a/drivers/net/myri10ge/myri10ge.c~git-netdev-all +++ a/drivers/net/myri10ge/myri10ge.c @@ -1412,10 +1412,8 @@ static const struct ethtool_ops myri10ge .set_tx_csum = ethtool_op_set_tx_hw_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, -#ifdef NETIF_F_TSO .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso, -#endif .get_strings = myri10ge_get_strings, .get_stats_count = myri10ge_get_stats_count, .get_ethtool_stats = myri10ge_get_ethtool_stats, @@ -1975,13 +1973,11 @@ again: mss = 0; max_segments = MXGEFW_MAX_SEND_DESC; -#ifdef NETIF_F_TSO if (skb->len > (dev->mtu + ETH_HLEN)) { mss = skb_shinfo(skb)->gso_size; if (mss != 0) max_segments = MYRI10GE_MAX_SEND_DESC_TSO; } -#endif /*NETIF_F_TSO */ if ((unlikely(avail < max_segments))) { /* we are out of transmit resources */ @@ -2013,7 +2009,6 @@ again: cum_len = 0; -#ifdef NETIF_F_TSO if (mss) { /* TSO */ /* this removes any CKSUM flag from before */ flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST); @@ -2029,7 +2024,6 @@ again: * the checksum by parsing the header. */ pseudo_hdr_offset = mss; } else -#endif /*NETIF_F_TSO */ /* Mark small packets, and pad out tiny packets */ if (skb->len <= MXGEFW_SEND_SMALL_SIZE) { flags |= MXGEFW_FLAGS_SMALL; @@ -2097,7 +2091,6 @@ again: seglen = len; flags_next = flags & ~MXGEFW_FLAGS_FIRST; cum_len_next = cum_len + seglen; -#ifdef NETIF_F_TSO if (mss) { /* TSO */ (req - rdma_count)->rdma_count = rdma_count + 1; @@ -2124,7 +2117,6 @@ again: (small * MXGEFW_FLAGS_SMALL); } } -#endif /* NETIF_F_TSO */ req->addr_high = high_swapped; req->addr_low = htonl(low); req->pseudo_hdr_offset = htons(pseudo_hdr_offset); @@ -2161,14 +2153,12 @@ again: } (req - rdma_count)->rdma_count = rdma_count; -#ifdef NETIF_F_TSO if (mss) do { req--; req->flags |= MXGEFW_FLAGS_TSO_LAST; } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP | MXGEFW_FLAGS_FIRST))); -#endif idx = ((count - 1) + tx->req) & tx->mask; tx->info[idx].last = 1; if (tx->wc_fifo == NULL) diff -puN drivers/net/oaknet.c~git-netdev-all /dev/null --- a/drivers/net/oaknet.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * - * Copyright (c) 1999-2000 Grant Erickson - * - * Module name: oaknet.c - * - * Description: - * Driver for the National Semiconductor DP83902AV Ethernet controller - * on-board the IBM PowerPC "Oak" evaluation board. Adapted from the - * various other 8390 drivers written by Donald Becker and Paul Gortmaker. - * - * Additional inspiration from the "tcd8390.c" driver from TiVo, Inc. - * and "enetLib.c" from IBM. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8390.h" - - -/* Preprocessor Defines */ - -#if !defined(TRUE) || TRUE != 1 -#define TRUE 1 -#endif - -#if !defined(FALSE) || FALSE != 0 -#define FALSE 0 -#endif - -#define OAKNET_START_PG 0x20 /* First page of TX buffer */ -#define OAKNET_STOP_PG 0x40 /* Last pagge +1 of RX ring */ - -#define OAKNET_WAIT (2 * HZ / 100) /* 20 ms */ - -/* Experimenting with some fixes for a broken driver... */ - -#define OAKNET_DISINT -#define OAKNET_HEADCHECK -#define OAKNET_RWFIX - - -/* Global Variables */ - -static const char *name = "National DP83902AV"; - -static struct net_device *oaknet_devs; - - -/* Function Prototypes */ - -static int oaknet_open(struct net_device *dev); -static int oaknet_close(struct net_device *dev); - -static void oaknet_reset_8390(struct net_device *dev); -static void oaknet_get_8390_hdr(struct net_device *dev, - struct e8390_pkt_hdr *hdr, int ring_page); -static void oaknet_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void oaknet_block_output(struct net_device *dev, int count, - const unsigned char *buf, int start_page); - -static void oaknet_dma_error(struct net_device *dev, const char *name); - - -/* - * int oaknet_init() - * - * Description: - * This routine performs all the necessary platform-specific initiali- - * zation and set-up for the IBM "Oak" evaluation board's National - * Semiconductor DP83902AV "ST-NIC" Ethernet controller. - * - * Input(s): - * N/A - * - * Output(s): - * N/A - * - * Returns: - * 0 if OK, otherwise system error number on error. - * - */ -static int __init oaknet_init(void) -{ - register int i; - int reg0, regd; - int ret = -ENOMEM; - struct net_device *dev; -#if 0 - unsigned long ioaddr = OAKNET_IO_BASE; -#else - unsigned long ioaddr = ioremap(OAKNET_IO_BASE, OAKNET_IO_SIZE); -#endif - bd_t *bip = (bd_t *)__res; - - if (!ioaddr) - return -ENOMEM; - - dev = alloc_ei_netdev(); - if (!dev) - goto out_unmap; - - ret = -EBUSY; - if (!request_region(OAKNET_IO_BASE, OAKNET_IO_SIZE, name)) - goto out_dev; - - /* Quick register check to see if the device is really there. */ - - ret = -ENODEV; - if ((reg0 = ei_ibp(ioaddr)) == 0xFF) - goto out_region; - - /* - * That worked. Now a more thorough check, using the multicast - * address registers, that the device is definitely out there - * and semi-functional. - */ - - ei_obp(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr + E8390_CMD); - regd = ei_ibp(ioaddr + 0x0D); - ei_obp(0xFF, ioaddr + 0x0D); - ei_obp(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD); - ei_ibp(ioaddr + EN0_COUNTER0); - - /* It's no good. Fix things back up and leave. */ - - ret = -ENODEV; - if (ei_ibp(ioaddr + EN0_COUNTER0) != 0) { - ei_obp(reg0, ioaddr); - ei_obp(regd, ioaddr + 0x0D); - goto out_region; - } - - SET_MODULE_OWNER(dev); - - /* - * This controller is on an embedded board, so the base address - * and interrupt assignments are pre-assigned and unchageable. - */ - - dev->base_addr = ioaddr; - dev->irq = OAKNET_INT; - - /* - * Disable all chip interrupts for now and ACK all pending - * interrupts. - */ - - ei_obp(0x0, ioaddr + EN0_IMR); - ei_obp(0xFF, ioaddr + EN0_ISR); - - /* Attempt to get the interrupt line */ - - ret = -EAGAIN; - if (request_irq(dev->irq, ei_interrupt, 0, name, dev)) { - printk("%s: unable to request interrupt %d.\n", - name, dev->irq); - goto out_region; - } - - /* Tell the world about what and where we've found. */ - - printk("%s: %s at", dev->name, name); - for (i = 0; i < ETHER_ADDR_LEN; ++i) { - dev->dev_addr[i] = bip->bi_enetaddr[i]; - printk("%c%.2x", (i ? ':' : ' '), dev->dev_addr[i]); - } - printk(", found at %#lx, using IRQ %d.\n", dev->base_addr, dev->irq); - - /* Set up some required driver fields and then we're done. */ - - ei_status.name = name; - ei_status.word16 = FALSE; - ei_status.tx_start_page = OAKNET_START_PG; - ei_status.rx_start_page = OAKNET_START_PG + TX_PAGES; - ei_status.stop_page = OAKNET_STOP_PG; - - ei_status.reset_8390 = &oaknet_reset_8390; - ei_status.block_input = &oaknet_block_input; - ei_status.block_output = &oaknet_block_output; - ei_status.get_8390_hdr = &oaknet_get_8390_hdr; - - dev->open = oaknet_open; - dev->stop = oaknet_close; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = ei_poll; -#endif - - NS8390_init(dev, FALSE); - ret = register_netdev(dev); - if (ret) - goto out_irq; - - oaknet_devs = dev; - return 0; - -out_irq; - free_irq(dev->irq, dev); -out_region: - release_region(OAKNET_IO_BASE, OAKNET_IO_SIZE); -out_dev: - free_netdev(dev); -out_unmap: - iounmap(ioaddr); - return ret; -} - -/* - * static int oaknet_open() - * - * Description: - * This routine is a modest wrapper around ei_open, the 8390-generic, - * driver open routine. This just increments the module usage count - * and passes along the status from ei_open. - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * - * Output(s): - * *dev - Pointer to the device structure for this driver, potentially - * modified by ei_open. - * - * Returns: - * 0 if OK, otherwise < 0 on error. - * - */ -static int -oaknet_open(struct net_device *dev) -{ - int status = ei_open(dev); - return (status); -} - -/* - * static int oaknet_close() - * - * Description: - * This routine is a modest wrapper around ei_close, the 8390-generic, - * driver close routine. This just decrements the module usage count - * and passes along the status from ei_close. - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * - * Output(s): - * *dev - Pointer to the device structure for this driver, potentially - * modified by ei_close. - * - * Returns: - * 0 if OK, otherwise < 0 on error. - * - */ -static int -oaknet_close(struct net_device *dev) -{ - int status = ei_close(dev); - return (status); -} - -/* - * static void oaknet_reset_8390() - * - * Description: - * This routine resets the DP83902 chip. - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -static void -oaknet_reset_8390(struct net_device *dev) -{ - int base = E8390_BASE; - - /* - * We have no provision of reseting the controller as is done - * in other drivers, such as "ne.c". However, the following - * seems to work well enough in the TiVo driver. - */ - - printk("Resetting %s...\n", dev->name); - ei_obp(E8390_STOP | E8390_NODMA | E8390_PAGE0, base + E8390_CMD); - ei_status.txing = 0; - ei_status.dmaing = 0; -} - -/* - * static void oaknet_get_8390_hdr() - * - * Description: - * This routine grabs the 8390-specific header. It's similar to the - * block input routine, but we don't need to be concerned with ring wrap - * as the header will be at the start of a page, so we optimize accordingly. - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * *hdr - Pointer to storage for the 8390-specific packet header. - * ring_page - ? - * - * Output(s): - * *hdr - Pointer to the 8390-specific packet header for the just- - * received frame. - * - * Returns: - * N/A - * - */ -static void -oaknet_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page) -{ - int base = dev->base_addr; - - /* - * This should NOT happen. If it does, it is the LAST thing you'll - * see. - */ - - if (ei_status.dmaing) { - oaknet_dma_error(dev, "oaknet_get_8390_hdr"); - return; - } - - ei_status.dmaing |= 0x01; - outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, base + OAKNET_CMD); - outb_p(sizeof(struct e8390_pkt_hdr), base + EN0_RCNTLO); - outb_p(0, base + EN0_RCNTHI); - outb_p(0, base + EN0_RSARLO); /* On page boundary */ - outb_p(ring_page, base + EN0_RSARHI); - outb_p(E8390_RREAD + E8390_START, base + OAKNET_CMD); - - if (ei_status.word16) - insw(base + OAKNET_DATA, hdr, - sizeof(struct e8390_pkt_hdr) >> 1); - else - insb(base + OAKNET_DATA, hdr, - sizeof(struct e8390_pkt_hdr)); - - /* Byte-swap the packet byte count */ - - hdr->count = le16_to_cpu(hdr->count); - - outb_p(ENISR_RDC, base + EN0_ISR); /* ACK Remote DMA interrupt */ - ei_status.dmaing &= ~0x01; -} - -/* - * XXX - Document me. - */ -static void -oaknet_block_input(struct net_device *dev, int count, struct sk_buff *skb, - int ring_offset) -{ - int base = OAKNET_BASE; - char *buf = skb->data; - - /* - * This should NOT happen. If it does, it is the LAST thing you'll - * see. - */ - - if (ei_status.dmaing) { - oaknet_dma_error(dev, "oaknet_block_input"); - return; - } - -#ifdef OAKNET_DISINT - save_flags(flags); - cli(); -#endif - - ei_status.dmaing |= 0x01; - ei_obp(E8390_NODMA + E8390_PAGE0 + E8390_START, base + E8390_CMD); - ei_obp(count & 0xff, base + EN0_RCNTLO); - ei_obp(count >> 8, base + EN0_RCNTHI); - ei_obp(ring_offset & 0xff, base + EN0_RSARLO); - ei_obp(ring_offset >> 8, base + EN0_RSARHI); - ei_obp(E8390_RREAD + E8390_START, base + E8390_CMD); - if (ei_status.word16) { - ei_isw(base + E8390_DATA, buf, count >> 1); - if (count & 0x01) { - buf[count - 1] = ei_ib(base + E8390_DATA); -#ifdef OAKNET_HEADCHECK - bytes++; -#endif - } - } else { - ei_isb(base + E8390_DATA, buf, count); - } -#ifdef OAKNET_HEADCHECK - /* - * This was for the ALPHA version only, but enough people have - * been encountering problems so it is still here. If you see - * this message you either 1) have a slightly incompatible clone - * or 2) have noise/speed problems with your bus. - */ - - /* DMA termination address check... */ - { - int addr, tries = 20; - do { - /* DON'T check for 'ei_ibp(EN0_ISR) & ENISR_RDC' here - -- it's broken for Rx on some cards! */ - int high = ei_ibp(base + EN0_RSARHI); - int low = ei_ibp(base + EN0_RSARLO); - addr = (high << 8) + low; - if (((ring_offset + bytes) & 0xff) == low) - break; - } while (--tries > 0); - if (tries <= 0) - printk("%s: RX transfer address mismatch," - "%#4.4x (expected) vs. %#4.4x (actual).\n", - dev->name, ring_offset + bytes, addr); - } -#endif - ei_obp(ENISR_RDC, base + EN0_ISR); /* ACK Remote DMA interrupt */ - ei_status.dmaing &= ~0x01; - -#ifdef OAKNET_DISINT - restore_flags(flags); -#endif -} - -/* - * static void oaknet_block_output() - * - * Description: - * This routine... - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * count - Number of bytes to be transferred. - * *buf - - * start_page - - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -static void -oaknet_block_output(struct net_device *dev, int count, - const unsigned char *buf, int start_page) -{ - int base = E8390_BASE; -#if 0 - int bug; -#endif - unsigned long start; -#ifdef OAKNET_DISINT - unsigned long flags; -#endif -#ifdef OAKNET_HEADCHECK - int retries = 0; -#endif - - /* Round the count up for word writes. */ - - if (ei_status.word16 && (count & 0x1)) - count++; - - /* - * This should NOT happen. If it does, it is the LAST thing you'll - * see. - */ - - if (ei_status.dmaing) { - oaknet_dma_error(dev, "oaknet_block_output"); - return; - } - -#ifdef OAKNET_DISINT - save_flags(flags); - cli(); -#endif - - ei_status.dmaing |= 0x01; - - /* Make sure we are in page 0. */ - - ei_obp(E8390_PAGE0 + E8390_START + E8390_NODMA, base + E8390_CMD); - -#ifdef OAKNET_HEADCHECK -retry: -#endif - -#if 0 - /* - * The 83902 documentation states that the processor needs to - * do a "dummy read" before doing the remote write to work - * around a chip bug they don't feel like fixing. - */ - - bug = 0; - while (1) { - unsigned int rdhi; - unsigned int rdlo; - - /* Now the normal output. */ - ei_obp(ENISR_RDC, base + EN0_ISR); - ei_obp(count & 0xff, base + EN0_RCNTLO); - ei_obp(count >> 8, base + EN0_RCNTHI); - ei_obp(0x00, base + EN0_RSARLO); - ei_obp(start_page, base + EN0_RSARHI); - - if (bug++) - break; - - /* Perform the dummy read */ - rdhi = ei_ibp(base + EN0_CRDAHI); - rdlo = ei_ibp(base + EN0_CRDALO); - ei_obp(E8390_RREAD + E8390_START, base + E8390_CMD); - - while (1) { - unsigned int nrdhi; - unsigned int nrdlo; - nrdhi = ei_ibp(base + EN0_CRDAHI); - nrdlo = ei_ibp(base + EN0_CRDALO); - if ((rdhi != nrdhi) || (rdlo != nrdlo)) - break; - } - } -#else -#ifdef OAKNET_RWFIX - /* - * Handle the read-before-write bug the same way as the - * Crynwr packet driver -- the Nat'l Semi. method doesn't work. - * Actually this doesn't always work either, but if you have - * problems with your 83902 this is better than nothing! - */ - - ei_obp(0x42, base + EN0_RCNTLO); - ei_obp(0x00, base + EN0_RCNTHI); - ei_obp(0x42, base + EN0_RSARLO); - ei_obp(0x00, base + EN0_RSARHI); - ei_obp(E8390_RREAD + E8390_START, base + E8390_CMD); - /* Make certain that the dummy read has occurred. */ - udelay(6); -#endif - - ei_obp(ENISR_RDC, base + EN0_ISR); - - /* Now the normal output. */ - ei_obp(count & 0xff, base + EN0_RCNTLO); - ei_obp(count >> 8, base + EN0_RCNTHI); - ei_obp(0x00, base + EN0_RSARLO); - ei_obp(start_page, base + EN0_RSARHI); -#endif /* 0/1 */ - - ei_obp(E8390_RWRITE + E8390_START, base + E8390_CMD); - if (ei_status.word16) { - ei_osw(E8390_BASE + E8390_DATA, buf, count >> 1); - } else { - ei_osb(E8390_BASE + E8390_DATA, buf, count); - } - -#ifdef OAKNET_DISINT - restore_flags(flags); -#endif - - start = jiffies; - -#ifdef OAKNET_HEADCHECK - /* - * This was for the ALPHA version only, but enough people have - * been encountering problems so it is still here. - */ - - { - /* DMA termination address check... */ - int addr, tries = 20; - do { - int high = ei_ibp(base + EN0_RSARHI); - int low = ei_ibp(base + EN0_RSARLO); - addr = (high << 8) + low; - if ((start_page << 8) + count == addr) - break; - } while (--tries > 0); - - if (tries <= 0) { - printk("%s: Tx packet transfer address mismatch," - "%#4.4x (expected) vs. %#4.4x (actual).\n", - dev->name, (start_page << 8) + count, addr); - if (retries++ == 0) - goto retry; - } - } -#endif - - while ((ei_ibp(base + EN0_ISR) & ENISR_RDC) == 0) { - if (time_after(jiffies, start + OAKNET_WAIT)) { - printk("%s: timeout waiting for Tx RDC.\n", dev->name); - oaknet_reset_8390(dev); - NS8390_init(dev, TRUE); - break; - } - } - - ei_obp(ENISR_RDC, base + EN0_ISR); /* Ack intr. */ - ei_status.dmaing &= ~0x01; -} - -/* - * static void oaknet_dma_error() - * - * Description: - * This routine prints out a last-ditch informative message to the console - * indicating that a DMA error occurred. If you see this, it's the last - * thing you'll see. - * - * Input(s): - * *dev - Pointer to the device structure for this driver. - * *name - Informative text (e.g. function name) indicating where the - * DMA error occurred. - * - * Output(s): - * N/A - * - * Returns: - * N/A - * - */ -static void -oaknet_dma_error(struct net_device *dev, const char *name) -{ - printk(KERN_EMERG "%s: DMAing conflict in %s." - "[DMAstat:%d][irqlock:%d][intr:%ld]\n", - dev->name, name, ei_status.dmaing, ei_status.irqlock, - dev->interrupt); -} - -/* - * Oak Ethernet module unload interface. - */ -static void __exit oaknet_cleanup_module (void) -{ - /* Convert to loop once driver supports multiple devices. */ - unregister_netdev(oaknet_dev); - free_irq(oaknet_devs->irq, oaknet_devs); - release_region(oaknet_devs->base_addr, OAKNET_IO_SIZE); - iounmap(ioaddr); - free_netdev(oaknet_devs); -} - -module_init(oaknet_init); -module_exit(oaknet_cleanup_module); -MODULE_LICENSE("GPL"); diff -puN drivers/net/qla3xxx.c~git-netdev-all drivers/net/qla3xxx.c --- a/drivers/net/qla3xxx.c~git-netdev-all +++ a/drivers/net/qla3xxx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ MODULE_PARM_DESC(msi, "Turn on Message S static struct pci_device_id ql3xxx_pci_tbl[] __devinitdata = { {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3022_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QL3032_DEVICE_ID)}, /* required last entry */ {0,} }; @@ -1475,6 +1477,10 @@ static int ql_mii_setup(struct ql3_adapt 2) << 7)) return -1; + if (qdev->device_id == QL3032_DEVICE_ID) + ql_write_page0_reg(qdev, + &port_regs->macMIIMgmtControlReg, 0x0f00000); + /* Divide 125MHz clock by 28 to meet PHY timing requirements */ reg = MAC_MII_CONTROL_CLK_SEL_DIV28; @@ -1706,18 +1712,42 @@ static void ql_process_mac_tx_intr(struc struct ob_mac_iocb_rsp *mac_rsp) { struct ql_tx_buf_cb *tx_cb; + int i; tx_cb = &qdev->tx_buf[mac_rsp->transaction_id]; pci_unmap_single(qdev->pdev, - pci_unmap_addr(tx_cb, mapaddr), - pci_unmap_len(tx_cb, maplen), PCI_DMA_TODEVICE); - dev_kfree_skb_irq(tx_cb->skb); + pci_unmap_addr(&tx_cb->map[0], mapaddr), + pci_unmap_len(&tx_cb->map[0], maplen), + PCI_DMA_TODEVICE); + tx_cb->seg_count--; + if (tx_cb->seg_count) { + for (i = 1; i < tx_cb->seg_count; i++) { + pci_unmap_page(qdev->pdev, + pci_unmap_addr(&tx_cb->map[i], + mapaddr), + pci_unmap_len(&tx_cb->map[i], maplen), + PCI_DMA_TODEVICE); + } + } qdev->stats.tx_packets++; qdev->stats.tx_bytes += tx_cb->skb->len; + dev_kfree_skb_irq(tx_cb->skb); tx_cb->skb = NULL; atomic_inc(&qdev->tx_count); } +/* + * The difference between 3022 and 3032 for inbound completions: + * 3022 uses two buffers per completion. The first buffer contains + * (some) header info, the second the remainder of the headers plus + * the data. For this chip we reserve some space at the top of the + * receive buffer so that the header info in buffer one can be + * prepended to the buffer two. Buffer two is the sent up while + * buffer one is returned to the hardware to be reused. + * 3032 receives all of it's data and headers in one buffer for a + * simpler process. 3032 also supports checksum verification as + * can be seen in ql_process_macip_rx_intr(). + */ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, struct ib_mac_iocb_rsp *ib_mac_rsp_ptr) { @@ -1740,14 +1770,17 @@ static void ql_process_mac_rx_intr(struc qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; qdev->small_buf_release_cnt++; - /* start of first buffer */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) - qdev->lrg_buf_index = 0; - curr_ial_ptr++; /* 64-bit pointers require two incs. */ - curr_ial_ptr++; + if (qdev->device_id == QL3022_DEVICE_ID) { + /* start of first buffer (3022 only) */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) { + qdev->lrg_buf_index = 0; + } + curr_ial_ptr++; /* 64-bit pointers require two incs. */ + curr_ial_ptr++; + } /* start of second buffer */ lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); @@ -1778,7 +1811,8 @@ static void ql_process_mac_rx_intr(struc qdev->ndev->last_rx = jiffies; lrg_buf_cb2->skb = NULL; - ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); + if (qdev->device_id == QL3022_DEVICE_ID) + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2); } @@ -1790,7 +1824,7 @@ static void ql_process_macip_rx_intr(str struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL; struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL; u32 *curr_ial_ptr; - struct sk_buff *skb1, *skb2; + struct sk_buff *skb1 = NULL, *skb2; struct net_device *ndev = qdev->ndev; u16 length = le16_to_cpu(ib_ip_rsp_ptr->length); u16 size = 0; @@ -1806,16 +1840,20 @@ static void ql_process_macip_rx_intr(str qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; qdev->small_buf_release_cnt++; - /* start of first buffer */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; - - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) - qdev->lrg_buf_index = 0; - skb1 = lrg_buf_cb1->skb; - curr_ial_ptr++; /* 64-bit pointers require two incs. */ - curr_ial_ptr++; + if (qdev->device_id == QL3022_DEVICE_ID) { + /* start of first buffer on 3022 */ + lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); + lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) + qdev->lrg_buf_index = 0; + skb1 = lrg_buf_cb1->skb; + curr_ial_ptr++; /* 64-bit pointers require two incs. */ + curr_ial_ptr++; + size = ETH_HLEN; + if (*((u16 *) skb1->data) != 0xFFFF) + size += VLAN_ETH_HLEN - ETH_HLEN; + } /* start of second buffer */ lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); @@ -1825,18 +1863,6 @@ static void ql_process_macip_rx_intr(str if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) qdev->lrg_buf_index = 0; - qdev->stats.rx_packets++; - qdev->stats.rx_bytes += length; - - /* - * Copy the ethhdr from first buffer to second. This - * is necessary for IP completions. - */ - if (*((u16 *) skb1->data) != 0xFFFF) - size = VLAN_ETH_HLEN; - else - size = ETH_HLEN; - skb_put(skb2, length); /* Just the second buffer length here. */ pci_unmap_single(qdev->pdev, pci_unmap_addr(lrg_buf_cb2, mapaddr), @@ -1844,16 +1870,40 @@ static void ql_process_macip_rx_intr(str PCI_DMA_FROMDEVICE); prefetch(skb2->data); - memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size); - skb2->dev = qdev->ndev; skb2->ip_summed = CHECKSUM_NONE; + if (qdev->device_id == QL3022_DEVICE_ID) { + /* + * Copy the ethhdr from first buffer to second. This + * is necessary for 3022 IP completions. + */ + memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size); + } else { + u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum); + if (checksum & + (IB_IP_IOCB_RSP_3032_ICE | + IB_IP_IOCB_RSP_3032_CE | + IB_IP_IOCB_RSP_3032_NUC)) { + printk(KERN_ERR + "%s: Bad checksum for this %s packet, checksum = %x.\n", + __func__, + ((checksum & + IB_IP_IOCB_RSP_3032_TCP) ? "TCP" : + "UDP"),checksum); + } else if (checksum & IB_IP_IOCB_RSP_3032_TCP) { + skb2->ip_summed = CHECKSUM_UNNECESSARY; + } + } + skb2->dev = qdev->ndev; skb2->protocol = eth_type_trans(skb2, qdev->ndev); netif_receive_skb(skb2); + qdev->stats.rx_packets++; + qdev->stats.rx_bytes += length; ndev->last_rx = jiffies; lrg_buf_cb2->skb = NULL; - ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); + if (qdev->device_id == QL3022_DEVICE_ID) + ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb1); ql_release_to_lrg_buf_free_list(qdev, lrg_buf_cb2); } @@ -1880,12 +1930,14 @@ static int ql_tx_rx_clean(struct ql3_ada break; case OPCODE_IB_MAC_IOCB: + case OPCODE_IB_3032_MAC_IOCB: ql_process_mac_rx_intr(qdev, (struct ib_mac_iocb_rsp *) net_rsp); (*rx_cleaned)++; break; case OPCODE_IB_IP_IOCB: + case OPCODE_IB_3032_IP_IOCB: ql_process_macip_rx_intr(qdev, (struct ib_ip_iocb_rsp *) net_rsp); (*rx_cleaned)++; @@ -2032,13 +2084,96 @@ static irqreturn_t ql3xxx_isr(int irq, v return IRQ_RETVAL(handled); } +/* + * Get the total number of segments needed for the + * given number of fragments. This is necessary because + * outbound address lists (OAL) will be used when more than + * two frags are given. Each address list has 5 addr/len + * pairs. The 5th pair in each AOL is used to point to + * the next AOL if more frags are coming. + * That is why the frags:segment count ratio is not linear. + */ +static int ql_get_seg_count(unsigned short frags) +{ + switch(frags) { + case 0: return 1; /* just the skb->data seg */ + case 1: return 2; /* skb->data + 1 frag */ + case 2: return 3; /* skb->data + 2 frags */ + case 3: return 5; /* skb->data + 1 frag + 1 AOL containting 2 frags */ + case 4: return 6; + case 5: return 7; + case 6: return 8; + case 7: return 10; + case 8: return 11; + case 9: return 12; + case 10: return 13; + case 11: return 15; + case 12: return 16; + case 13: return 17; + case 14: return 18; + case 15: return 20; + case 16: return 21; + case 17: return 22; + case 18: return 23; + } + return -1; +} + +static void ql_hw_csum_setup(struct sk_buff *skb, + struct ob_mac_iocb_req *mac_iocb_ptr) +{ + struct ethhdr *eth; + struct iphdr *ip = NULL; + u8 offset = ETH_HLEN; + + eth = (struct ethhdr *)(skb->data); + + if (eth->h_proto == __constant_htons(ETH_P_IP)) { + ip = (struct iphdr *)&skb->data[ETH_HLEN]; + } else if (eth->h_proto == htons(ETH_P_8021Q) && + ((struct vlan_ethhdr *)skb->data)-> + h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP)) { + ip = (struct iphdr *)&skb->data[VLAN_ETH_HLEN]; + offset = VLAN_ETH_HLEN; + } + + if (ip) { + if (ip->protocol == IPPROTO_TCP) { + mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_TC; + mac_iocb_ptr->ip_hdr_off = offset; + mac_iocb_ptr->ip_hdr_len = ip->ihl; + } else if (ip->protocol == IPPROTO_UDP) { + mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_UC; + mac_iocb_ptr->ip_hdr_off = offset; + mac_iocb_ptr->ip_hdr_len = ip->ihl; + } + } +} + +/* + * The difference between 3022 and 3032 sends: + * 3022 only supports a simple single segment transmission. + * 3032 supports checksumming and scatter/gather lists (fragments). + * The 3032 supports sglists by using the 3 addr/len pairs (ALP) + * in the IOCB plus a chain of outbound address lists (OAL) that + * each contain 5 ALPs. The last ALP of the IOCB (3rd) or OAL (5th) + * will used to point to an OAL when more ALP entries are required. + * The IOCB is always the top of the chain followed by one or more + * OALs (when necessary). + */ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) { struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; struct ql_tx_buf_cb *tx_cb; + u32 tot_len = skb->len; + struct oal *oal; + struct oal_entry *oal_entry; + int len; struct ob_mac_iocb_req *mac_iocb_ptr; u64 map; + int seg_cnt, seg = 0; + int frag_cnt = (int)skb_shinfo(skb)->nr_frags; if (unlikely(atomic_read(&qdev->tx_count) < 2)) { if (!netif_queue_stopped(ndev)) @@ -2046,21 +2181,79 @@ static int ql3xxx_send(struct sk_buff *s return NETDEV_TX_BUSY; } tx_cb = &qdev->tx_buf[qdev->req_producer_index] ; + seg_cnt = tx_cb->seg_count = ql_get_seg_count((skb_shinfo(skb)->nr_frags)); + if(seg_cnt == -1) { + printk(KERN_ERR PFX"%s: invalid segment count!\n",__func__); + return NETDEV_TX_OK; + + } mac_iocb_ptr = tx_cb->queue_entry; memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req)); mac_iocb_ptr->opcode = qdev->mac_ob_opcode; mac_iocb_ptr->flags |= qdev->mb_bit_mask; mac_iocb_ptr->transaction_id = qdev->req_producer_index; - mac_iocb_ptr->data_len = cpu_to_le16((u16) skb->len); + mac_iocb_ptr->data_len = cpu_to_le16((u16) tot_len); tx_cb->skb = skb; - map = pci_map_single(qdev->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - mac_iocb_ptr->buf_addr0_low = cpu_to_le32(LS_64BITS(map)); - mac_iocb_ptr->buf_addr0_high = cpu_to_le32(MS_64BITS(map)); - mac_iocb_ptr->buf_0_len = cpu_to_le32(skb->len | OB_MAC_IOCB_REQ_E); - pci_unmap_addr_set(tx_cb, mapaddr, map); - pci_unmap_len_set(tx_cb, maplen, skb->len); - atomic_dec(&qdev->tx_count); + if (skb->ip_summed == CHECKSUM_PARTIAL) + ql_hw_csum_setup(skb, mac_iocb_ptr); + len = skb_headlen(skb); + map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); + oal_entry = (struct oal_entry *)&mac_iocb_ptr->buf_addr0_low; + oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); + oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); + oal_entry->len = cpu_to_le32(len); + pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, map); + pci_unmap_len_set(&tx_cb->map[seg], maplen, len); + seg++; + + if (!skb_shinfo(skb)->nr_frags) { + /* Terminate the last segment. */ + oal_entry->len = + cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY); + } else { + int i; + oal = tx_cb->oal; + for (i=0; ifrags[i]; + oal_entry++; + if ((seg == 2 && seg_cnt > 3) || /* Check for continuation */ + (seg == 7 && seg_cnt > 8) || /* requirements. It's strange */ + (seg == 12 && seg_cnt > 13) || /* but necessary. */ + (seg == 17 && seg_cnt > 18)) { + /* Continuation entry points to outbound address list. */ + map = pci_map_single(qdev->pdev, oal, + sizeof(struct oal), + PCI_DMA_TODEVICE); + oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); + oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); + oal_entry->len = + cpu_to_le32(sizeof(struct oal) | + OAL_CONT_ENTRY); + pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, + map); + pci_unmap_len_set(&tx_cb->map[seg], maplen, + len); + oal_entry = (struct oal_entry *)oal; + oal++; + seg++; + } + map = + pci_map_page(qdev->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); + oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); + oal_entry->len = cpu_to_le32(frag->size); + pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, map); + pci_unmap_len_set(&tx_cb->map[seg], maplen, + frag->size); + } + /* Terminate the last segment. */ + oal_entry->len = + cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY); + } + wmb(); qdev->req_producer_index++; if (qdev->req_producer_index == NUM_REQ_Q_ENTRIES) qdev->req_producer_index = 0; @@ -2074,8 +2267,10 @@ static int ql3xxx_send(struct sk_buff *s printk(KERN_DEBUG PFX "%s: tx queued, slot %d, len %d\n", ndev->name, qdev->req_producer_index, skb->len); + atomic_dec(&qdev->tx_count); return NETDEV_TX_OK; } + static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev) { qdev->req_q_size = @@ -2359,7 +2554,22 @@ static int ql_alloc_large_buffers(struct return 0; } -static void ql_create_send_free_list(struct ql3_adapter *qdev) +static void ql_free_send_free_list(struct ql3_adapter *qdev) +{ + struct ql_tx_buf_cb *tx_cb; + int i; + + tx_cb = &qdev->tx_buf[0]; + for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) { + if (tx_cb->oal) { + kfree(tx_cb->oal); + tx_cb->oal = NULL; + } + tx_cb++; + } +} + +static int ql_create_send_free_list(struct ql3_adapter *qdev) { struct ql_tx_buf_cb *tx_cb; int i; @@ -2368,11 +2578,16 @@ static void ql_create_send_free_list(str /* Create free list of transmit buffers */ for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) { + tx_cb = &qdev->tx_buf[i]; tx_cb->skb = NULL; tx_cb->queue_entry = req_q_curr; req_q_curr++; + tx_cb->oal = kmalloc(512, GFP_KERNEL); + if (tx_cb->oal == NULL) + return -1; } + return 0; } static int ql_alloc_mem_resources(struct ql3_adapter *qdev) @@ -2447,12 +2662,14 @@ static int ql_alloc_mem_resources(struct /* Initialize the large buffer queue. */ ql_init_large_buffers(qdev); - ql_create_send_free_list(qdev); + if (ql_create_send_free_list(qdev)) + goto err_free_list; qdev->rsp_current = qdev->rsp_q_virt_addr; return 0; - +err_free_list: + ql_free_send_free_list(qdev); err_small_buffers: ql_free_buffer_queues(qdev); err_buffer_queues: @@ -2468,6 +2685,7 @@ err_req_rsp: static void ql_free_mem_resources(struct ql3_adapter *qdev) { + ql_free_send_free_list(qdev); ql_free_large_buffers(qdev); ql_free_small_buffers(qdev); ql_free_buffer_queues(qdev); @@ -2766,11 +2984,20 @@ static int ql_adapter_initialize(struct } /* Enable Ethernet Function */ - value = - (PORT_CONTROL_EF | PORT_CONTROL_ET | PORT_CONTROL_EI | - PORT_CONTROL_HH); - ql_write_page0_reg(qdev, &port_regs->portControl, - ((value << 16) | value)); + if (qdev->device_id == QL3032_DEVICE_ID) { + value = + (QL3032_PORT_CONTROL_EF | QL3032_PORT_CONTROL_KIE | + QL3032_PORT_CONTROL_EIv6 | QL3032_PORT_CONTROL_EIv4); + ql_write_page0_reg(qdev, &port_regs->functionControl, + ((value << 16) | value)); + } else { + value = + (PORT_CONTROL_EF | PORT_CONTROL_ET | PORT_CONTROL_EI | + PORT_CONTROL_HH); + ql_write_page0_reg(qdev, &port_regs->portControl, + ((value << 16) | value)); + } + out: return status; @@ -2917,8 +3144,10 @@ static void ql_display_dev_info(struct n struct pci_dev *pdev = qdev->pdev; printk(KERN_INFO PFX - "\n%s Adapter %d RevisionID %d found on PCI slot %d.\n", - DRV_NAME, qdev->index, qdev->chip_rev_id, qdev->pci_slot); + "\n%s Adapter %d RevisionID %d found %s on PCI slot %d.\n", + DRV_NAME, qdev->index, qdev->chip_rev_id, + (qdev->device_id == QL3032_DEVICE_ID) ? "QLA3032" : "QLA3022", + qdev->pci_slot); printk(KERN_INFO PFX "%s Interface.\n", test_bit(QL_LINK_OPTICAL,&qdev->flags) ? "OPTICAL" : "COPPER"); @@ -3212,15 +3441,22 @@ static void ql_reset_work(struct work_st * Loop through the active list and return the skb. */ for (i = 0; i < NUM_REQ_Q_ENTRIES; i++) { + int j; tx_cb = &qdev->tx_buf[i]; if (tx_cb->skb) { - printk(KERN_DEBUG PFX "%s: Freeing lost SKB.\n", qdev->ndev->name); pci_unmap_single(qdev->pdev, - pci_unmap_addr(tx_cb, mapaddr), - pci_unmap_len(tx_cb, maplen), PCI_DMA_TODEVICE); + pci_unmap_addr(&tx_cb->map[0], mapaddr), + pci_unmap_len(&tx_cb->map[0], maplen), + PCI_DMA_TODEVICE); + for(j=1;jseg_count;j++) { + pci_unmap_page(qdev->pdev, + pci_unmap_addr(&tx_cb->map[j],mapaddr), + pci_unmap_len(&tx_cb->map[j],maplen), + PCI_DMA_TODEVICE); + } dev_kfree_skb(tx_cb->skb); tx_cb->skb = NULL; } @@ -3379,21 +3615,24 @@ static int __devinit ql3xxx_probe(struct SET_MODULE_OWNER(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); - if (pci_using_dac) - ndev->features |= NETIF_F_HIGHDMA; - pci_set_drvdata(pdev, ndev); qdev = netdev_priv(ndev); qdev->index = cards_found; qdev->ndev = ndev; qdev->pdev = pdev; + qdev->device_id = pci_entry->device; qdev->port_link_state = LS_DOWN; if (msi) qdev->msi = 1; qdev->msg_enable = netif_msg_init(debug, default_msg); + if (pci_using_dac) + ndev->features |= NETIF_F_HIGHDMA; + if (qdev->device_id == QL3032_DEVICE_ID) + ndev->features |= (NETIF_F_HW_CSUM | NETIF_F_SG); + qdev->mem_map_registers = ioremap_nocache(pci_resource_start(pdev, 1), pci_resource_len(qdev->pdev, 1)); diff -puN drivers/net/qla3xxx.h~git-netdev-all drivers/net/qla3xxx.h --- a/drivers/net/qla3xxx.h~git-netdev-all +++ a/drivers/net/qla3xxx.h @@ -21,7 +21,9 @@ #define OPCODE_UPDATE_NCB_IOCB 0xF0 #define OPCODE_IB_MAC_IOCB 0xF9 +#define OPCODE_IB_3032_MAC_IOCB 0x09 #define OPCODE_IB_IP_IOCB 0xFA +#define OPCODE_IB_3032_IP_IOCB 0x0A #define OPCODE_IB_TCP_IOCB 0xFB #define OPCODE_DUMP_PROTO_IOCB 0xFE #define OPCODE_BUFFER_ALERT_IOCB 0xFB @@ -37,18 +39,23 @@ struct ob_mac_iocb_req { u8 opcode; u8 flags; -#define OB_MAC_IOCB_REQ_MA 0xC0 -#define OB_MAC_IOCB_REQ_F 0x20 -#define OB_MAC_IOCB_REQ_X 0x10 +#define OB_MAC_IOCB_REQ_MA 0xe0 +#define OB_MAC_IOCB_REQ_F 0x10 +#define OB_MAC_IOCB_REQ_X 0x08 #define OB_MAC_IOCB_REQ_D 0x02 #define OB_MAC_IOCB_REQ_I 0x01 - __le16 reserved0; + u8 flags1; +#define OB_3032MAC_IOCB_REQ_IC 0x04 +#define OB_3032MAC_IOCB_REQ_TC 0x02 +#define OB_3032MAC_IOCB_REQ_UC 0x01 + u8 reserved0; __le32 transaction_id; __le16 data_len; - __le16 reserved1; + u8 ip_hdr_off; + u8 ip_hdr_len; + __le32 reserved1; __le32 reserved2; - __le32 reserved3; __le32 buf_addr0_low; __le32 buf_addr0_high; __le32 buf_0_len; @@ -58,8 +65,8 @@ struct ob_mac_iocb_req { __le32 buf_addr2_low; __le32 buf_addr2_high; __le32 buf_2_len; + __le32 reserved3; __le32 reserved4; - __le32 reserved5; }; /* * The following constants define control bits for buffer @@ -74,6 +81,7 @@ struct ob_mac_iocb_rsp { u8 opcode; u8 flags; #define OB_MAC_IOCB_RSP_P 0x08 +#define OB_MAC_IOCB_RSP_L 0x04 #define OB_MAC_IOCB_RSP_S 0x02 #define OB_MAC_IOCB_RSP_I 0x01 @@ -85,6 +93,7 @@ struct ob_mac_iocb_rsp { struct ib_mac_iocb_rsp { u8 opcode; +#define IB_MAC_IOCB_RSP_V 0x80 u8 flags; #define IB_MAC_IOCB_RSP_S 0x80 #define IB_MAC_IOCB_RSP_H1 0x40 @@ -138,6 +147,7 @@ struct ob_ip_iocb_req { struct ob_ip_iocb_rsp { u8 opcode; u8 flags; +#define OB_MAC_IOCB_RSP_H 0x10 #define OB_MAC_IOCB_RSP_E 0x08 #define OB_MAC_IOCB_RSP_L 0x04 #define OB_MAC_IOCB_RSP_S 0x02 @@ -220,6 +230,10 @@ struct ob_tcp_iocb_rsp { struct ib_ip_iocb_rsp { u8 opcode; +#define IB_IP_IOCB_RSP_3032_V 0x80 +#define IB_IP_IOCB_RSP_3032_O 0x40 +#define IB_IP_IOCB_RSP_3032_I 0x20 +#define IB_IP_IOCB_RSP_3032_R 0x10 u8 flags; #define IB_IP_IOCB_RSP_S 0x80 #define IB_IP_IOCB_RSP_H1 0x40 @@ -230,6 +244,12 @@ struct ib_ip_iocb_rsp { __le16 length; __le16 checksum; +#define IB_IP_IOCB_RSP_3032_ICE 0x01 +#define IB_IP_IOCB_RSP_3032_CE 0x02 +#define IB_IP_IOCB_RSP_3032_NUC 0x04 +#define IB_IP_IOCB_RSP_3032_UDP 0x08 +#define IB_IP_IOCB_RSP_3032_TCP 0x10 +#define IB_IP_IOCB_RSP_3032_IPE 0x20 __le16 reserved; #define IB_IP_IOCB_RSP_R 0x01 __le32 ial_low; @@ -524,6 +544,21 @@ enum { IP_ADDR_INDEX_REG_FUNC_2_SEC = 0x0005, IP_ADDR_INDEX_REG_FUNC_3_PRI = 0x0006, IP_ADDR_INDEX_REG_FUNC_3_SEC = 0x0007, + IP_ADDR_INDEX_REG_6 = 0x0008, + IP_ADDR_INDEX_REG_OFFSET_MASK = 0x0030, + IP_ADDR_INDEX_REG_E = 0x0040, +}; +enum { + QL3032_PORT_CONTROL_DS = 0x0001, + QL3032_PORT_CONTROL_HH = 0x0002, + QL3032_PORT_CONTROL_EIv6 = 0x0004, + QL3032_PORT_CONTROL_EIv4 = 0x0008, + QL3032_PORT_CONTROL_ET = 0x0010, + QL3032_PORT_CONTROL_EF = 0x0020, + QL3032_PORT_CONTROL_DRM = 0x0040, + QL3032_PORT_CONTROL_RLB = 0x0080, + QL3032_PORT_CONTROL_RCB = 0x0100, + QL3032_PORT_CONTROL_KIE = 0x0200, }; enum { @@ -657,7 +692,8 @@ struct ql3xxx_port_registers { u32 internalRamWDataReg; u32 reclaimedBufferAddrRegLow; u32 reclaimedBufferAddrRegHigh; - u32 reserved[2]; + u32 tcpConfiguration; + u32 functionControl; u32 fpgaRevID; u32 localRamAddr; u32 localRamDataAutoIncr; @@ -963,6 +999,7 @@ struct eeprom_data { #define QL3XXX_VENDOR_ID 0x1077 #define QL3022_DEVICE_ID 0x3022 +#define QL3032_DEVICE_ID 0x3032 /* MTU & Frame Size stuff */ #define NORMAL_MTU_SIZE ETH_DATA_LEN @@ -1038,11 +1075,41 @@ struct ql_rcv_buf_cb { int index; }; +/* + * Original IOCB has 3 sg entries: + * first points to skb-data area + * second points to first frag + * third points to next oal. + * OAL has 5 entries: + * 1 thru 4 point to frags + * fifth points to next oal. + */ +#define MAX_OAL_CNT ((MAX_SKB_FRAGS-1)/4 + 1) + +struct oal_entry { + u32 dma_lo; + u32 dma_hi; + u32 len; +#define OAL_LAST_ENTRY 0x80000000 /* Last valid buffer in list. */ +#define OAL_CONT_ENTRY 0x40000000 /* points to an OAL. (continuation) */ + u32 reserved; +}; + +struct oal { + struct oal_entry oal_entry[5]; +}; + +struct map_list { + DECLARE_PCI_UNMAP_ADDR(mapaddr); + DECLARE_PCI_UNMAP_LEN(maplen); +}; + struct ql_tx_buf_cb { struct sk_buff *skb; struct ob_mac_iocb_req *queue_entry ; - DECLARE_PCI_UNMAP_ADDR(mapaddr); - DECLARE_PCI_UNMAP_LEN(maplen); + int seg_count; + struct oal *oal; + struct map_list map[MAX_SKB_FRAGS+1]; }; /* definitions for type field */ @@ -1189,6 +1256,7 @@ struct ql3_adapter { struct delayed_work reset_work; struct delayed_work tx_timeout_work; u32 max_frame_size; + u32 device_id; }; #endif /* _QLA3XXX_H_ */ diff -puN drivers/net/s2io.c~git-netdev-all drivers/net/s2io.c --- a/drivers/net/s2io.c~git-netdev-all +++ a/drivers/net/s2io.c @@ -3887,12 +3887,10 @@ static int s2io_xmit(struct sk_buff *skb } offload_type = s2io_offload_type(skb); -#ifdef NETIF_F_TSO if (offload_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { txdp->Control_1 |= TXD_TCP_LSO_EN; txdp->Control_1 |= TXD_TCP_LSO_MSS(s2io_tcp_mss(skb)); } -#endif if (skb->ip_summed == CHECKSUM_PARTIAL) { txdp->Control_2 |= (TXD_TX_CKO_IPV4_EN | TXD_TX_CKO_TCP_EN | @@ -5750,10 +5748,8 @@ static const struct ethtool_ops netdev_e .set_tx_csum = s2io_ethtool_op_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, -#ifdef NETIF_F_TSO .get_tso = s2io_ethtool_op_get_tso, .set_tso = s2io_ethtool_op_set_tso, -#endif .get_ufo = ethtool_op_get_ufo, .set_ufo = ethtool_op_set_ufo, .self_test_count = s2io_ethtool_self_test_count, @@ -6978,12 +6974,8 @@ s2io_init_nic(struct pci_dev *pdev, cons dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; if (sp->high_dma_flag == TRUE) dev->features |= NETIF_F_HIGHDMA; -#ifdef NETIF_F_TSO dev->features |= NETIF_F_TSO; -#endif -#ifdef NETIF_F_TSO6 dev->features |= NETIF_F_TSO6; -#endif if (sp->device_type & XFRAME_II_DEVICE) { dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_HW_CSUM; diff -puN /dev/null drivers/net/sc92031.c --- /dev/null +++ a/drivers/net/sc92031.c @@ -0,0 +1,1620 @@ +/* Silan SC92031 PCI Fast Ethernet Adapter driver + * + * Based on vendor drivers: + * Silan Fast Ethernet Netcard Driver: + * MODULE_AUTHOR ("gaoyonghong"); + * MODULE_DESCRIPTION ("SILAN Fast Ethernet driver"); + * MODULE_LICENSE("GPL"); + * 8139D Fast Ethernet driver: + * (C) 2002 by gaoyonghong + * MODULE_AUTHOR ("gaoyonghong"); + * MODULE_DESCRIPTION ("Rsltek 8139D PCI Fast Ethernet Adapter driver"); + * MODULE_LICENSE("GPL"); + * Both are almost identical and seem to be based on pci-skeleton.c + * + * Rewritten for 2.6 by Cesar Eduardo Barros + */ + +/* Note about set_mac_address: I don't know how to change the hardware + * matching, so you need to enable IFF_PROMISC when using it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PCI_VENDOR_ID_SILAN 0x1904 +#define PCI_DEVICE_ID_SILAN_SC92031 0x2031 +#define PCI_DEVICE_ID_SILAN_8139D 0x8139 + +#define SC92031_NAME "sc92031" +#define SC92031_DESCRIPTION "Silan SC92031 PCI Fast Ethernet Adapter driver" +#define SC92031_VERSION "2.0c" + +/* BAR 0 is MMIO, BAR 1 is PIO */ +#ifndef SC92031_USE_BAR +#define SC92031_USE_BAR 0 +#endif + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ +static int multicast_filter_limit = 64; +module_param(multicast_filter_limit, int, 0); +MODULE_PARM_DESC(multicast_filter_limit, + "Maximum number of filtered multicast addresses"); + +static int media; +module_param(media, int, 0); +MODULE_PARM_DESC(media, "Media type (0x00 = autodetect," + " 0x01 = 10M half, 0x02 = 10M full," + " 0x04 = 100M half, 0x08 = 100M full)"); + +/* Size of the in-memory receive ring. */ +#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K ,4==128K*/ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +#define SILAN_STATS_NUM 2 /* number of ETHTOOL_GSTATS */ + +/* media options */ +#define AUTOSELECT 0x00 +#define M10_HALF 0x01 +#define M10_FULL 0x02 +#define M100_HALF 0x04 +#define M100_FULL 0x08 + + /* Symbolic offsets to registers. */ +enum silan_registers { + Config0 = 0x00, // Config0 + Config1 = 0x04, // Config1 + RxBufWPtr = 0x08, // Rx buffer writer poiter + IntrStatus = 0x0C, // Interrupt status + IntrMask = 0x10, // Interrupt mask + RxbufAddr = 0x14, // Rx buffer start address + RxBufRPtr = 0x18, // Rx buffer read pointer + Txstatusall = 0x1C, // Transmit status of all descriptors + TxStatus0 = 0x20, // Transmit status (Four 32bit registers). + TxAddr0 = 0x30, // Tx descriptors (also four 32bit). + RxConfig = 0x40, // Rx configuration + MAC0 = 0x44, // Ethernet hardware address. + MAR0 = 0x4C, // Multicast filter. + RxStatus0 = 0x54, // Rx status + TxConfig = 0x5C, // Tx configuration + PhyCtrl = 0x60, // physical control + FlowCtrlConfig = 0x64, // flow control + Miicmd0 = 0x68, // Mii command0 register + Miicmd1 = 0x6C, // Mii command1 register + Miistatus = 0x70, // Mii status register + Timercnt = 0x74, // Timer counter register + TimerIntr = 0x78, // Timer interrupt register + PMConfig = 0x7C, // Power Manager configuration + CRC0 = 0x80, // Power Manager CRC ( Two 32bit regisers) + Wakeup0 = 0x88, // power Manager wakeup( Eight 64bit regiser) + LSBCRC0 = 0xC8, // power Manager LSBCRC(Two 32bit regiser) + TestD0 = 0xD0, + TestD4 = 0xD4, + TestD8 = 0xD8, +}; + +#define MII_BMCR 0 // Basic mode control register +#define MII_BMSR 1 // Basic mode status register +#define MII_JAB 16 +#define MII_OutputStatus 24 + +#define BMCR_FULLDPLX 0x0100 // Full duplex +#define BMCR_ANRESTART 0x0200 // Auto negotiation restart +#define BMCR_ANENABLE 0x1000 // Enable auto negotiation +#define BMCR_SPEED100 0x2000 // Select 100Mbps +#define BMSR_LSTATUS 0x0004 // Link status +#define PHY_16_JAB_ENB 0x1000 +#define PHY_16_PORT_ENB 0x1 + +enum IntrStatusBits { + LinkFail = 0x80000000, + LinkOK = 0x40000000, + TimeOut = 0x20000000, + RxOverflow = 0x0040, + RxOK = 0x0020, + TxOK = 0x0001, + IntrBits = LinkFail|LinkOK|TimeOut|RxOverflow|RxOK|TxOK, +}; + +enum TxStatusBits { + TxCarrierLost = 0x20000000, + TxAborted = 0x10000000, + TxOutOfWindow = 0x08000000, + TxNccShift = 22, + EarlyTxThresShift = 16, + TxStatOK = 0x8000, + TxUnderrun = 0x4000, + TxOwn = 0x2000, +}; + +enum RxStatusBits { + RxStatesOK = 0x80000, + RxBadAlign = 0x40000, + RxHugeFrame = 0x20000, + RxSmallFrame = 0x10000, + RxCRCOK = 0x8000, + RxCrlFrame = 0x4000, + Rx_Broadcast = 0x2000, + Rx_Multicast = 0x1000, + RxAddrMatch = 0x0800, + MiiErr = 0x0400, +}; + +enum RxConfigBits { + RxFullDx = 0x80000000, + RxEnb = 0x40000000, + RxSmall = 0x20000000, + RxHuge = 0x10000000, + RxErr = 0x08000000, + RxAllphys = 0x04000000, + RxMulticast = 0x02000000, + RxBroadcast = 0x01000000, + RxLoopBack = (1 << 23) | (1 << 22), + LowThresholdShift = 12, + HighThresholdShift = 2, +}; + +enum TxConfigBits { + TxFullDx = 0x80000000, + TxEnb = 0x40000000, + TxEnbPad = 0x20000000, + TxEnbHuge = 0x10000000, + TxEnbFCS = 0x08000000, + TxNoBackOff = 0x04000000, + TxEnbPrem = 0x02000000, + TxCareLostCrs = 0x1000000, + TxExdCollNum = 0xf00000, + TxDataRate = 0x80000, +}; + +enum PhyCtrlconfigbits { + PhyCtrlAne = 0x80000000, + PhyCtrlSpd100 = 0x40000000, + PhyCtrlSpd10 = 0x20000000, + PhyCtrlPhyBaseAddr = 0x1f000000, + PhyCtrlDux = 0x800000, + PhyCtrlReset = 0x400000, +}; + +enum FlowCtrlConfigBits { + FlowCtrlFullDX = 0x80000000, + FlowCtrlEnb = 0x40000000, +}; + +enum Config0Bits { + Cfg0_Reset = 0x80000000, + Cfg0_Anaoff = 0x40000000, + Cfg0_LDPS = 0x20000000, +}; + +enum Config1Bits { + Cfg1_EarlyRx = 1 << 31, + Cfg1_EarlyTx = 1 << 30, + + //rx buffer size + Cfg1_Rcv8K = 0x0, + Cfg1_Rcv16K = 0x1, + Cfg1_Rcv32K = 0x3, + Cfg1_Rcv64K = 0x7, + Cfg1_Rcv128K = 0xf, +}; + +enum MiiCmd0Bits { + Mii_Divider = 0x20000000, + Mii_WRITE = 0x400000, + Mii_READ = 0x200000, + Mii_SCAN = 0x100000, + Mii_Tamod = 0x80000, + Mii_Drvmod = 0x40000, + Mii_mdc = 0x20000, + Mii_mdoen = 0x10000, + Mii_mdo = 0x8000, + Mii_mdi = 0x4000, +}; + +enum MiiStatusBits { + Mii_StatusBusy = 0x80000000, +}; + +enum PMConfigBits { + PM_Enable = 1 << 31, + PM_LongWF = 1 << 30, + PM_Magic = 1 << 29, + PM_LANWake = 1 << 28, + PM_LWPTN = (1 << 27 | 1<< 26), + PM_LinkUp = 1 << 25, + PM_WakeUp = 1 << 24, +}; + +/* Locking rules: + * priv->lock protects most of the fields of priv and most of the + * hardware registers. It does not have to protect against softirqs + * between sc92031_disable_interrupts and sc92031_enable_interrupts; + * it also does not need to be used in ->open and ->stop while the + * device interrupts are off. + * Not having to protect against softirqs is very useful due to heavy + * use of mdelay() at _sc92031_reset. + * Functions prefixed with _sc92031_ must be called with the lock held; + * functions prefixed with sc92031_ must be called without the lock held. + * Use mmiowb() before unlocking if the hardware was written to. + */ + +/* Locking rules for the interrupt: + * - the interrupt and the tasklet never run at the same time + * - neither run between sc92031_disable_interrupts and + * sc92031_enable_interrupt + */ + +struct sc92031_priv { + spinlock_t lock; + /* iomap.h cookie */ + void __iomem *port_base; + /* pci device structure */ + struct pci_dev *pdev; + /* tasklet */ + struct tasklet_struct tasklet; + + /* CPU address of rx ring */ + void *rx_ring; + /* PCI address of rx ring */ + dma_addr_t rx_ring_dma_addr; + /* PCI address of rx ring read pointer */ + dma_addr_t rx_ring_tail; + + /* tx ring write index */ + unsigned tx_head; + /* tx ring read index */ + unsigned tx_tail; + /* CPU address of tx bounce buffer */ + void *tx_bufs; + /* PCI address of tx bounce buffer */ + dma_addr_t tx_bufs_dma_addr; + + /* copies of some hardware registers */ + u32 intr_status; + atomic_t intr_mask; + u32 rx_config; + u32 tx_config; + u32 pm_config; + + /* copy of some flags from dev->flags */ + unsigned int mc_flags; + + /* for ETHTOOL_GSTATS */ + u64 tx_timeouts; + u64 rx_loss; + + /* for dev->get_stats */ + long rx_value; + struct net_device_stats stats; +}; + +/* I don't know which registers can be safely read; however, I can guess + * MAC0 is one of them. */ +static inline void _sc92031_dummy_read(void __iomem *port_base) +{ + ioread32(port_base + MAC0); +} + +static u32 _sc92031_mii_wait(void __iomem *port_base) +{ + u32 mii_status; + + do { + udelay(10); + mii_status = ioread32(port_base + Miistatus); + } while (mii_status & Mii_StatusBusy); + + return mii_status; +} + +static u32 _sc92031_mii_cmd(void __iomem *port_base, u32 cmd0, u32 cmd1) +{ + iowrite32(Mii_Divider, port_base + Miicmd0); + + _sc92031_mii_wait(port_base); + + iowrite32(cmd1, port_base + Miicmd1); + iowrite32(Mii_Divider | cmd0, port_base + Miicmd0); + + return _sc92031_mii_wait(port_base); +} + +static void _sc92031_mii_scan(void __iomem *port_base) +{ + _sc92031_mii_cmd(port_base, Mii_SCAN, 0x1 << 6); +} + +static u16 _sc92031_mii_read(void __iomem *port_base, unsigned reg) +{ + return _sc92031_mii_cmd(port_base, Mii_READ, reg << 6) >> 13; +} + +static void _sc92031_mii_write(void __iomem *port_base, unsigned reg, u16 val) +{ + _sc92031_mii_cmd(port_base, Mii_WRITE, (reg << 6) | ((u32)val << 11)); +} + +static void sc92031_disable_interrupts(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + /* tell the tasklet/interrupt not to enable interrupts */ + atomic_set(&priv->intr_mask, 0); + wmb(); + + /* stop interrupts */ + iowrite32(0, port_base + IntrMask); + _sc92031_dummy_read(port_base); + mmiowb(); + + /* wait for any concurrent interrupt/tasklet to finish */ + synchronize_irq(dev->irq); + tasklet_disable(&priv->tasklet); +} + +static void sc92031_enable_interrupts(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + tasklet_enable(&priv->tasklet); + + atomic_set(&priv->intr_mask, IntrBits); + wmb(); + + iowrite32(IntrBits, port_base + IntrMask); + mmiowb(); +} + +static void _sc92031_disable_tx_rx(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + priv->rx_config &= ~RxEnb; + priv->tx_config &= ~TxEnb; + iowrite32(priv->rx_config, port_base + RxConfig); + iowrite32(priv->tx_config, port_base + TxConfig); +} + +static void _sc92031_enable_tx_rx(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + priv->rx_config |= RxEnb; + priv->tx_config |= TxEnb; + iowrite32(priv->rx_config, port_base + RxConfig); + iowrite32(priv->tx_config, port_base + TxConfig); +} + +static void _sc92031_tx_clear(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + while (priv->tx_head - priv->tx_tail > 0) { + priv->tx_tail++; + priv->stats.tx_dropped++; + } + priv->tx_head = priv->tx_tail = 0; +} + +static void _sc92031_set_mar(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 mar0 = 0, mar1 = 0; + + if ((dev->flags & IFF_PROMISC) + || dev->mc_count > multicast_filter_limit + || (dev->flags & IFF_ALLMULTI)) + mar0 = mar1 = 0xffffffff; + else if (dev->flags & IFF_MULTICAST) { + struct dev_mc_list *mc_list; + + for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) { + u32 crc; + unsigned bit = 0; + + crc = ~ether_crc(ETH_ALEN, mc_list->dmi_addr); + crc >>= 24; + + if (crc & 0x01) bit |= 0x02; + if (crc & 0x02) bit |= 0x01; + if (crc & 0x10) bit |= 0x20; + if (crc & 0x20) bit |= 0x10; + if (crc & 0x40) bit |= 0x08; + if (crc & 0x80) bit |= 0x04; + + if (bit > 31) + mar0 |= 0x1 << (bit - 32); + else + mar1 |= 0x1 << bit; + } + } + + iowrite32(mar0, port_base + MAR0); + iowrite32(mar1, port_base + MAR0 + 4); +} + +static void _sc92031_set_rx_config(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + unsigned int old_mc_flags; + u32 rx_config_bits = 0; + + old_mc_flags = priv->mc_flags; + + if (dev->flags & IFF_PROMISC) + rx_config_bits |= RxSmall | RxHuge | RxErr | RxBroadcast + | RxMulticast | RxAllphys; + + if (dev->flags & (IFF_ALLMULTI | IFF_MULTICAST)) + rx_config_bits |= RxMulticast; + + if (dev->flags & IFF_BROADCAST) + rx_config_bits |= RxBroadcast; + + priv->rx_config &= ~(RxSmall | RxHuge | RxErr | RxBroadcast + | RxMulticast | RxAllphys); + priv->rx_config |= rx_config_bits; + + priv->mc_flags = dev->flags & (IFF_PROMISC | IFF_ALLMULTI + | IFF_MULTICAST | IFF_BROADCAST); + + if (netif_carrier_ok(dev) && priv->mc_flags != old_mc_flags) + iowrite32(priv->rx_config, port_base + RxConfig); +} + +static bool _sc92031_check_media(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u16 bmsr; + + bmsr = _sc92031_mii_read(port_base, MII_BMSR); + rmb(); + if (bmsr & BMSR_LSTATUS) { + bool speed_100, duplex_full; + u32 flow_ctrl_config = 0; + u16 output_status = _sc92031_mii_read(port_base, + MII_OutputStatus); + _sc92031_mii_scan(port_base); + + speed_100 = output_status & 0x2; + duplex_full = output_status & 0x4; + + /* Initial Tx/Rx configuration */ + priv->rx_config = (0x40 << LowThresholdShift) | (0x1c0 << HighThresholdShift); + priv->tx_config = 0x48800000; + + /* NOTE: vendor driver had dead code here to enable tx padding */ + + if (!speed_100) + priv->tx_config |= 0x80000; + + // configure rx mode + _sc92031_set_rx_config(dev); + + if (duplex_full) { + priv->rx_config |= RxFullDx; + priv->tx_config |= TxFullDx; + flow_ctrl_config = FlowCtrlFullDX | FlowCtrlEnb; + } else { + priv->rx_config &= ~RxFullDx; + priv->tx_config &= ~TxFullDx; + } + + _sc92031_set_mar(dev); + _sc92031_set_rx_config(dev); + _sc92031_enable_tx_rx(dev); + iowrite32(flow_ctrl_config, port_base + FlowCtrlConfig); + + netif_carrier_on(dev); + + if (printk_ratelimit()) + printk(KERN_INFO "%s: link up, %sMbps, %s-duplex\n", + dev->name, + speed_100 ? "100" : "10", + duplex_full ? "full" : "half"); + return true; + } else { + _sc92031_mii_scan(port_base); + + netif_carrier_off(dev); + + _sc92031_disable_tx_rx(dev); + + if (printk_ratelimit()) + printk(KERN_INFO "%s: link down\n", dev->name); + return false; + } +} + +static void _sc92031_phy_reset(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 phy_ctrl; + + phy_ctrl = ioread32(port_base + PhyCtrl); + phy_ctrl &= ~(PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10); + phy_ctrl |= PhyCtrlAne | PhyCtrlReset; + + switch (media) { + default: + case AUTOSELECT: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; + break; + case M10_HALF: + phy_ctrl |= PhyCtrlSpd10; + break; + case M10_FULL: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd10; + break; + case M100_HALF: + phy_ctrl |= PhyCtrlSpd100; + break; + case M100_FULL: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; + break; + } + + iowrite32(phy_ctrl, port_base + PhyCtrl); + mdelay(10); + + phy_ctrl &= ~PhyCtrlReset; + iowrite32(phy_ctrl, port_base + PhyCtrl); + mdelay(1); + + _sc92031_mii_write(port_base, MII_JAB, + PHY_16_JAB_ENB | PHY_16_PORT_ENB); + _sc92031_mii_scan(port_base); + + netif_carrier_off(dev); + netif_stop_queue(dev); +} + +static void _sc92031_reset(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + /* disable PM */ + iowrite32(0, port_base + PMConfig); + + /* soft reset the chip */ + iowrite32(Cfg0_Reset, port_base + Config0); + mdelay(200); + + iowrite32(0, port_base + Config0); + mdelay(10); + + /* disable interrupts */ + iowrite32(0, port_base + IntrMask); + + /* clear multicast address */ + iowrite32(0, port_base + MAR0); + iowrite32(0, port_base + MAR0 + 4); + + /* init rx ring */ + iowrite32(priv->rx_ring_dma_addr, port_base + RxbufAddr); + priv->rx_ring_tail = priv->rx_ring_dma_addr; + + /* init tx ring */ + _sc92031_tx_clear(dev); + + /* clear old register values */ + priv->intr_status = 0; + atomic_set(&priv->intr_mask, 0); + priv->rx_config = 0; + priv->tx_config = 0; + priv->mc_flags = 0; + + /* configure rx buffer size */ + /* NOTE: vendor driver had dead code here to enable early tx/rx */ + iowrite32(Cfg1_Rcv64K, port_base + Config1); + + _sc92031_phy_reset(dev); + _sc92031_check_media(dev); + + /* calculate rx fifo overflow */ + priv->rx_value = 0; + + /* enable PM */ + iowrite32(priv->pm_config, port_base + PMConfig); + + /* clear intr register */ + ioread32(port_base + IntrStatus); +} + +static void _sc92031_tx_tasklet(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + unsigned old_tx_tail; + unsigned entry; + u32 tx_status; + + old_tx_tail = priv->tx_tail; + while (priv->tx_head - priv->tx_tail > 0) { + entry = priv->tx_tail % NUM_TX_DESC; + tx_status = ioread32(port_base + TxStatus0 + entry * 4); + + if (!(tx_status & (TxStatOK | TxUnderrun | TxAborted))) + break; + + priv->tx_tail++; + + if (tx_status & TxStatOK) { + priv->stats.tx_bytes += tx_status & 0x1fff; + priv->stats.tx_packets++; + /* Note: TxCarrierLost is always asserted at 100mbps. */ + priv->stats.collisions += (tx_status >> 22) & 0xf; + } + + if (tx_status & (TxOutOfWindow | TxAborted)) { + priv->stats.tx_errors++; + + if (tx_status & TxAborted) + priv->stats.tx_aborted_errors++; + + if (tx_status & TxCarrierLost) + priv->stats.tx_carrier_errors++; + + if (tx_status & TxOutOfWindow) + priv->stats.tx_window_errors++; + } + + if (tx_status & TxUnderrun) + priv->stats.tx_fifo_errors++; + } + + if (priv->tx_tail != old_tx_tail) + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +static void _sc92031_rx_tasklet_error(u32 rx_status, + struct sc92031_priv *priv, unsigned rx_size) +{ + if(rx_size > (MAX_ETH_FRAME_SIZE + 4) || rx_size < 16) { + priv->stats.rx_errors++; + priv->stats.rx_length_errors++; + } + + if (!(rx_status & RxStatesOK)) { + priv->stats.rx_errors++; + + if (rx_status & (RxHugeFrame | RxSmallFrame)) + priv->stats.rx_length_errors++; + + if (rx_status & RxBadAlign) + priv->stats.rx_frame_errors++; + + if (!(rx_status & RxCRCOK)) + priv->stats.rx_crc_errors++; + } else + priv->rx_loss++; +} + +static void _sc92031_rx_tasklet(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + dma_addr_t rx_ring_head; + unsigned rx_len; + unsigned rx_ring_offset; + void *rx_ring = priv->rx_ring; + + rx_ring_head = ioread32(port_base + RxBufWPtr); + rmb(); + + /* rx_ring_head is only 17 bits in the RxBufWPtr register. + * we need to change it to 32 bits physical address + */ + rx_ring_head &= (dma_addr_t)(RX_BUF_LEN - 1); + rx_ring_head |= priv->rx_ring_dma_addr & ~(dma_addr_t)(RX_BUF_LEN - 1); + if (rx_ring_head < priv->rx_ring_dma_addr) + rx_ring_head += RX_BUF_LEN; + + if (rx_ring_head >= priv->rx_ring_tail) + rx_len = rx_ring_head - priv->rx_ring_tail; + else + rx_len = RX_BUF_LEN - (priv->rx_ring_tail - rx_ring_head); + + if (!rx_len) + return; + + if (unlikely(rx_len > RX_BUF_LEN)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: rx packets length > rx buffer\n", + dev->name); + return; + } + + rx_ring_offset = (priv->rx_ring_tail - priv->rx_ring_dma_addr) % RX_BUF_LEN; + + while (rx_len) { + u32 rx_status; + unsigned rx_size, rx_size_align, pkt_size; + struct sk_buff *skb; + + rx_status = le32_to_cpup((__le32 *)(rx_ring + rx_ring_offset)); + rmb(); + + rx_size = rx_status >> 20; + rx_size_align = (rx_size + 3) & ~3; // for 4 bytes aligned + pkt_size = rx_size - 4; // Omit the four octet CRC from the length. + + rx_ring_offset = (rx_ring_offset + 4) % RX_BUF_LEN; + + if (unlikely(rx_status == 0 + || rx_size > (MAX_ETH_FRAME_SIZE + 4) + || rx_size < 16 + || !(rx_status & RxStatesOK))) { + _sc92031_rx_tasklet_error(rx_status, priv, rx_size); + break; + } + + if (unlikely(rx_size_align + 4 > rx_len)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: rx_len is too small\n", dev->name); + break; + } + + rx_len -= rx_size_align + 4; + + skb = dev_alloc_skb(pkt_size + NET_IP_ALIGN); + if (unlikely(!skb)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: Couldn't allocate a skb_buff for a packet of size %u\n", + dev->name, pkt_size); + goto next; + } + + skb_reserve(skb, NET_IP_ALIGN); + + if ((rx_ring_offset + pkt_size) > RX_BUF_LEN) { + memcpy(skb_put(skb, RX_BUF_LEN - rx_ring_offset), + rx_ring + rx_ring_offset, RX_BUF_LEN - rx_ring_offset); + memcpy(skb_put(skb, pkt_size - (RX_BUF_LEN - rx_ring_offset)), + rx_ring, pkt_size - (RX_BUF_LEN - rx_ring_offset)); + } else { + memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size); + } + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + dev->last_rx = jiffies; + netif_rx(skb); + + priv->stats.rx_bytes += pkt_size; + priv->stats.rx_packets++; + + if (rx_status & Rx_Multicast) + priv->stats.multicast++; + + next: + rx_ring_offset = (rx_ring_offset + rx_size_align) % RX_BUF_LEN; + } + mb(); + + priv->rx_ring_tail = rx_ring_head; + iowrite32(priv->rx_ring_tail, port_base + RxBufRPtr); +} + +static void _sc92031_link_tasklet(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + if (_sc92031_check_media(dev)) + netif_wake_queue(dev); + else { + netif_stop_queue(dev); + priv->stats.tx_carrier_errors++; + } +} + +static void sc92031_tasklet(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 intr_status, intr_mask; + + intr_status = priv->intr_status; + + spin_lock(&priv->lock); + + if (unlikely(!netif_running(dev))) + goto out; + + if (intr_status & TxOK) + _sc92031_tx_tasklet(dev); + + if (intr_status & RxOK) + _sc92031_rx_tasklet(dev); + + if (intr_status & RxOverflow) + priv->stats.rx_errors++; + + if (intr_status & TimeOut) { + priv->stats.rx_errors++; + priv->stats.rx_length_errors++; + } + + if (intr_status & (LinkFail | LinkOK)) + _sc92031_link_tasklet(dev); + +out: + intr_mask = atomic_read(&priv->intr_mask); + rmb(); + + iowrite32(intr_mask, port_base + IntrMask); + mmiowb(); + + spin_unlock(&priv->lock); +} + +static irqreturn_t sc92031_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 intr_status, intr_mask; + + /* mask interrupts before clearing IntrStatus */ + iowrite32(0, port_base + IntrMask); + _sc92031_dummy_read(port_base); + + intr_status = ioread32(port_base + IntrStatus); + if (unlikely(intr_status == 0xffffffff)) + return IRQ_NONE; // hardware has gone missing + + intr_status &= IntrBits; + if (!intr_status) + goto out_none; + + priv->intr_status = intr_status; + tasklet_schedule(&priv->tasklet); + + return IRQ_HANDLED; + +out_none: + intr_mask = atomic_read(&priv->intr_mask); + rmb(); + + iowrite32(intr_mask, port_base + IntrMask); + mmiowb(); + + return IRQ_NONE; +} + +static struct net_device_stats *sc92031_get_stats(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + // FIXME I do not understand what is this trying to do. + if (netif_running(dev)) { + int temp; + + spin_lock_bh(&priv->lock); + + /* Update the error count. */ + temp = (ioread32(port_base + RxStatus0) >> 16) & 0xffff; + + if (temp == 0xffff) { + priv->rx_value += temp; + priv->stats.rx_fifo_errors = priv->rx_value; + } else { + priv->stats.rx_fifo_errors = temp + priv->rx_value; + } + + spin_unlock_bh(&priv->lock); + } + + return &priv->stats; +} + +static int sc92031_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int err = 0; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + unsigned len; + unsigned entry; + u32 tx_status; + + if (unlikely(skb->len > TX_BUF_SIZE)) { + err = -EMSGSIZE; + priv->stats.tx_dropped++; + goto out; + } + + spin_lock_bh(&priv->lock); + + if (unlikely(!netif_carrier_ok(dev))) { + err = -ENOLINK; + priv->stats.tx_dropped++; + goto out_unlock; + } + + BUG_ON(priv->tx_head - priv->tx_tail >= NUM_TX_DESC); + + entry = priv->tx_head++ % NUM_TX_DESC; + + skb_copy_and_csum_dev(skb, priv->tx_bufs + entry * TX_BUF_SIZE); + + len = skb->len; + if (unlikely(len < ETH_ZLEN)) { + memset(priv->tx_bufs + entry * TX_BUF_SIZE + len, + 0, ETH_ZLEN - len); + len = ETH_ZLEN; + } + + wmb(); + + if (len < 100) + tx_status = len; + else if (len < 300) + tx_status = 0x30000 | len; + else + tx_status = 0x50000 | len; + + iowrite32(priv->tx_bufs_dma_addr + entry * TX_BUF_SIZE, + port_base + TxAddr0 + entry * 4); + iowrite32(tx_status, port_base + TxStatus0 + entry * 4); + mmiowb(); + + dev->trans_start = jiffies; + + if (priv->tx_head - priv->tx_tail >= NUM_TX_DESC) + netif_stop_queue(dev); + +out_unlock: + spin_unlock_bh(&priv->lock); + +out: + dev_kfree_skb(skb); + + return err; +} + +static int sc92031_open(struct net_device *dev) +{ + int err; + struct sc92031_priv *priv = netdev_priv(dev); + struct pci_dev *pdev = priv->pdev; + + priv->rx_ring = pci_alloc_consistent(pdev, RX_BUF_LEN, + &priv->rx_ring_dma_addr); + if (unlikely(!priv->rx_ring)) { + err = -ENOMEM; + goto out_alloc_rx_ring; + } + + priv->tx_bufs = pci_alloc_consistent(pdev, TX_BUF_TOT_LEN, + &priv->tx_bufs_dma_addr); + if (unlikely(!priv->tx_bufs)) { + err = -ENOMEM; + goto out_alloc_tx_bufs; + } + priv->tx_head = priv->tx_tail = 0; + + err = request_irq(pdev->irq, sc92031_interrupt, + SA_SHIRQ, dev->name, dev); + if (unlikely(err < 0)) + goto out_request_irq; + + priv->pm_config = 0; + + /* Interrupts already disabled by sc92031_stop or sc92031_probe */ + spin_lock(&priv->lock); + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock(&priv->lock); + sc92031_enable_interrupts(dev); + + if (netif_carrier_ok(dev)) + netif_start_queue(dev); + else + netif_tx_disable(dev); + + return 0; + +out_request_irq: + pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, + priv->tx_bufs_dma_addr); +out_alloc_tx_bufs: + pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, + priv->rx_ring_dma_addr); +out_alloc_rx_ring: + return err; +} + +static int sc92031_stop(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + struct pci_dev *pdev = priv->pdev; + + netif_tx_disable(dev); + + /* Disable interrupts, stop Tx and Rx. */ + sc92031_disable_interrupts(dev); + + spin_lock(&priv->lock); + + _sc92031_disable_tx_rx(dev); + _sc92031_tx_clear(dev); + mmiowb(); + + spin_unlock(&priv->lock); + + free_irq(pdev->irq, dev); + pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, + priv->tx_bufs_dma_addr); + pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, + priv->rx_ring_dma_addr); + + return 0; +} + +static void sc92031_set_multicast_list(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + spin_lock_bh(&priv->lock); + + _sc92031_set_mar(dev); + _sc92031_set_rx_config(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); +} + +static void sc92031_tx_timeout(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + /* Disable interrupts by clearing the interrupt mask.*/ + sc92031_disable_interrupts(dev); + + spin_lock(&priv->lock); + + priv->tx_timeouts++; + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock(&priv->lock); + + /* enable interrupts */ + sc92031_enable_interrupts(dev); + + if (netif_carrier_ok(dev)) + netif_wake_queue(dev); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sc92031_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + if (sc92031_interrupt(dev->irq, dev) != IRQ_NONE) + sc92031_tasklet((unsigned long)dev); + enable_irq(dev->irq); +} +#endif + +static int sc92031_ethtool_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u8 phy_address; + u32 phy_ctrl; + u16 output_status; + + spin_lock_bh(&priv->lock); + + phy_address = ioread32(port_base + Miicmd1) >> 27; + phy_ctrl = ioread32(port_base + PhyCtrl); + + output_status = _sc92031_mii_read(port_base, MII_OutputStatus); + _sc92031_mii_scan(port_base); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII; + + cmd->advertising = ADVERTISED_TP | ADVERTISED_MII; + + if ((phy_ctrl & (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) + == (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) + cmd->advertising |= ADVERTISED_Autoneg; + + if ((phy_ctrl & PhyCtrlSpd10) == PhyCtrlSpd10) + cmd->advertising |= ADVERTISED_10baseT_Half; + + if ((phy_ctrl & (PhyCtrlSpd10 | PhyCtrlDux)) + == (PhyCtrlSpd10 | PhyCtrlDux)) + cmd->advertising |= ADVERTISED_10baseT_Full; + + if ((phy_ctrl & PhyCtrlSpd100) == PhyCtrlSpd100) + cmd->advertising |= ADVERTISED_100baseT_Half; + + if ((phy_ctrl & (PhyCtrlSpd100 | PhyCtrlDux)) + == (PhyCtrlSpd100 | PhyCtrlDux)) + cmd->advertising |= ADVERTISED_100baseT_Full; + + if (phy_ctrl & PhyCtrlAne) + cmd->advertising |= ADVERTISED_Autoneg; + + cmd->speed = (output_status & 0x2) ? SPEED_100 : SPEED_10; + cmd->duplex = (output_status & 0x4) ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_MII; + cmd->phy_address = phy_address; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = (phy_ctrl & PhyCtrlAne) ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int sc92031_ethtool_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 phy_ctrl; + u32 old_phy_ctrl; + + if (!(cmd->speed == SPEED_10 || cmd->speed == SPEED_100)) + return -EINVAL; + if (!(cmd->duplex == DUPLEX_HALF || cmd->duplex == DUPLEX_FULL)) + return -EINVAL; + if (!(cmd->port == PORT_MII)) + return -EINVAL; + if (!(cmd->phy_address == 0x1f)) + return -EINVAL; + if (!(cmd->transceiver == XCVR_INTERNAL)) + return -EINVAL; + if (!(cmd->autoneg == AUTONEG_DISABLE || cmd->autoneg == AUTONEG_ENABLE)) + return -EINVAL; + + if (cmd->autoneg == AUTONEG_ENABLE) { + if (!(cmd->advertising & (ADVERTISED_Autoneg + | ADVERTISED_100baseT_Full + | ADVERTISED_100baseT_Half + | ADVERTISED_10baseT_Full + | ADVERTISED_10baseT_Half))) + return -EINVAL; + + phy_ctrl = PhyCtrlAne; + + // FIXME: I'm not sure what the original code was trying to do + if (cmd->advertising & ADVERTISED_Autoneg) + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; + if (cmd->advertising & ADVERTISED_100baseT_Full) + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; + if (cmd->advertising & ADVERTISED_100baseT_Half) + phy_ctrl |= PhyCtrlSpd100; + if (cmd->advertising & ADVERTISED_10baseT_Full) + phy_ctrl |= PhyCtrlSpd10 | PhyCtrlDux; + if (cmd->advertising & ADVERTISED_10baseT_Half) + phy_ctrl |= PhyCtrlSpd10; + } else { + // FIXME: Whole branch guessed + phy_ctrl = 0; + + if (cmd->speed == SPEED_10) + phy_ctrl |= PhyCtrlSpd10; + else /* cmd->speed == SPEED_100 */ + phy_ctrl |= PhyCtrlSpd100; + + if (cmd->duplex == DUPLEX_FULL) + phy_ctrl |= PhyCtrlDux; + } + + spin_lock_bh(&priv->lock); + + old_phy_ctrl = ioread32(port_base + PhyCtrl); + phy_ctrl |= old_phy_ctrl & ~(PhyCtrlAne | PhyCtrlDux + | PhyCtrlSpd100 | PhyCtrlSpd10); + if (phy_ctrl != old_phy_ctrl) + iowrite32(phy_ctrl, port_base + PhyCtrl); + + spin_unlock_bh(&priv->lock); + + return 0; +} + +static void sc92031_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + struct sc92031_priv *priv = netdev_priv(dev); + struct pci_dev *pdev = priv->pdev; + + strcpy(drvinfo->driver, SC92031_NAME); + strcpy(drvinfo->version, SC92031_VERSION); + strcpy(drvinfo->bus_info, pci_name(pdev)); +} + +static void sc92031_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 pm_config; + + spin_lock_bh(&priv->lock); + pm_config = ioread32(port_base + PMConfig); + spin_unlock_bh(&priv->lock); + + // FIXME: Guessed + wolinfo->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + wolinfo->wolopts = 0; + + if (pm_config & PM_LinkUp) + wolinfo->wolopts |= WAKE_PHY; + + if (pm_config & PM_Magic) + wolinfo->wolopts |= WAKE_MAGIC; + + if (pm_config & PM_WakeUp) + // FIXME: Guessed + wolinfo->wolopts |= WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; +} + +static int sc92031_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 pm_config; + + spin_lock_bh(&priv->lock); + + pm_config = ioread32(port_base + PMConfig) + & ~(PM_LinkUp | PM_Magic | PM_WakeUp); + + if (wolinfo->wolopts & WAKE_PHY) + pm_config |= PM_LinkUp; + + if (wolinfo->wolopts & WAKE_MAGIC) + pm_config |= PM_Magic; + + // FIXME: Guessed + if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) + pm_config |= PM_WakeUp; + + priv->pm_config = pm_config; + iowrite32(pm_config, port_base + PMConfig); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + return 0; +} + +static int sc92031_ethtool_nway_reset(struct net_device *dev) +{ + int err = 0; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u16 bmcr; + + spin_lock_bh(&priv->lock); + + bmcr = _sc92031_mii_read(port_base, MII_BMCR); + if (!(bmcr & BMCR_ANENABLE)) { + err = -EINVAL; + goto out; + } + + _sc92031_mii_write(port_base, MII_BMCR, bmcr | BMCR_ANRESTART); + +out: + _sc92031_mii_scan(port_base); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + return err; +} + +static const char sc92031_ethtool_stats_strings[SILAN_STATS_NUM][ETH_GSTRING_LEN] = { + "tx_timeout", + "rx_loss", +}; + +static void sc92031_ethtool_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + if (stringset == ETH_SS_STATS) + memcpy(data, sc92031_ethtool_stats_strings, + SILAN_STATS_NUM * ETH_GSTRING_LEN); +} + +static int sc92031_ethtool_get_stats_count(struct net_device *dev) +{ + return SILAN_STATS_NUM; +} + +static void sc92031_ethtool_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + spin_lock_bh(&priv->lock); + data[0] = priv->tx_timeouts; + data[1] = priv->rx_loss; + spin_unlock_bh(&priv->lock); +} + +static struct ethtool_ops sc92031_ethtool_ops = { + .get_settings = sc92031_ethtool_get_settings, + .set_settings = sc92031_ethtool_set_settings, + .get_drvinfo = sc92031_ethtool_get_drvinfo, + .get_wol = sc92031_ethtool_get_wol, + .set_wol = sc92031_ethtool_set_wol, + .nway_reset = sc92031_ethtool_nway_reset, + .get_link = ethtool_op_get_link, + .get_tx_csum = ethtool_op_get_tx_csum, + .get_sg = ethtool_op_get_sg, + .get_tso = ethtool_op_get_tso, + .get_strings = sc92031_ethtool_get_strings, + .get_stats_count = sc92031_ethtool_get_stats_count, + .get_ethtool_stats = sc92031_ethtool_get_ethtool_stats, + .get_perm_addr = ethtool_op_get_perm_addr, + .get_ufo = ethtool_op_get_ufo, +}; + +static int __devinit sc92031_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err; + void __iomem* port_base; + struct net_device *dev; + struct sc92031_priv *priv; + u32 mac0, mac1; + + err = pci_enable_device(pdev); + if (unlikely(err < 0)) + goto out_enable_device; + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (unlikely(err < 0)) + goto out_set_dma_mask; + + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (unlikely(err < 0)) + goto out_set_dma_mask; + + err = pci_request_regions(pdev, SC92031_NAME); + if (unlikely(err < 0)) + goto out_request_regions; + + port_base = pci_iomap(pdev, SC92031_USE_BAR, 0); + if (unlikely(!port_base)) { + err = -EIO; + goto out_iomap; + } + + dev = alloc_etherdev(sizeof(struct sc92031_priv)); + if (unlikely(!dev)) { + err = -ENOMEM; + goto out_alloc_etherdev; + } + + pci_set_drvdata(pdev, dev); + +#if SC92031_USE_BAR == 0 + dev->mem_start = pci_resource_start(pdev, SC92031_USE_BAR); + dev->mem_end = pci_resource_end(pdev, SC92031_USE_BAR); +#elif SC92031_USE_BAR == 1 + dev->base_addr = pci_resource_start(pdev, SC92031_USE_BAR); +#endif + dev->irq = pdev->irq; + + /* faked with skb_copy_and_csum_dev */ + dev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + + dev->get_stats = sc92031_get_stats; + dev->ethtool_ops = &sc92031_ethtool_ops; + dev->hard_start_xmit = sc92031_start_xmit; + dev->watchdog_timeo = TX_TIMEOUT; + dev->open = sc92031_open; + dev->stop = sc92031_stop; + dev->set_multicast_list = sc92031_set_multicast_list; + dev->tx_timeout = sc92031_tx_timeout; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = sc92031_poll_controller; +#endif + + priv = netdev_priv(dev); + spin_lock_init(&priv->lock); + priv->port_base = port_base; + priv->pdev = pdev; + tasklet_init(&priv->tasklet, sc92031_tasklet, (unsigned long)dev); + /* Fudge tasklet count so the call to sc92031_enable_interrupts at + * sc92031_open will work correctly */ + tasklet_disable_nosync(&priv->tasklet); + + /* PCI PM Wakeup */ + iowrite32((~PM_LongWF & ~PM_LWPTN) | PM_Enable, port_base + PMConfig); + + mac0 = ioread32(port_base + MAC0); + mac1 = ioread32(port_base + MAC0 + 4); + dev->dev_addr[0] = dev->perm_addr[0] = mac0 >> 24; + dev->dev_addr[1] = dev->perm_addr[1] = mac0 >> 16; + dev->dev_addr[2] = dev->perm_addr[2] = mac0 >> 8; + dev->dev_addr[3] = dev->perm_addr[3] = mac0; + dev->dev_addr[4] = dev->perm_addr[4] = mac1 >> 8; + dev->dev_addr[5] = dev->perm_addr[5] = mac1; + + err = register_netdev(dev); + if (err < 0) + goto out_register_netdev; + + return 0; + +out_register_netdev: + free_netdev(dev); +out_alloc_etherdev: + pci_iounmap(pdev, port_base); +out_iomap: + pci_release_regions(pdev); +out_request_regions: +out_set_dma_mask: + pci_disable_device(pdev); +out_enable_device: + return err; +} + +static void __devexit sc92031_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem* port_base = priv->port_base; + + unregister_netdev(dev); + free_netdev(dev); + pci_iounmap(pdev, port_base); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int sc92031_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + + pci_save_state(pdev); + + if (!netif_running(dev)) + goto out; + + netif_device_detach(dev); + + /* Disable interrupts, stop Tx and Rx. */ + sc92031_disable_interrupts(dev); + + spin_lock(&priv->lock); + + _sc92031_disable_tx_rx(dev); + _sc92031_tx_clear(dev); + mmiowb(); + + spin_unlock(&priv->lock); + +out: + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sc92031_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + + pci_restore_state(pdev); + pci_set_power_state(pdev, PCI_D0); + + if (!netif_running(dev)) + goto out; + + /* Interrupts already disabled by sc92031_suspend */ + spin_lock(&priv->lock); + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock(&priv->lock); + sc92031_enable_interrupts(dev); + + netif_device_attach(dev); + + if (netif_carrier_ok(dev)) + netif_wake_queue(dev); + else + netif_tx_disable(dev); + +out: + return 0; +} + +static struct pci_device_id sc92031_pci_device_id_table[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, PCI_DEVICE_ID_SILAN_SC92031) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, PCI_DEVICE_ID_SILAN_8139D) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); + +static struct pci_driver sc92031_pci_driver = { + .name = SC92031_NAME, + .id_table = sc92031_pci_device_id_table, + .probe = sc92031_probe, + .remove = __devexit_p(sc92031_remove), + .suspend = sc92031_suspend, + .resume = sc92031_resume, +}; + +static int __init sc92031_init(void) +{ + printk(KERN_INFO SC92031_DESCRIPTION " " SC92031_VERSION "\n"); + return pci_register_driver(&sc92031_pci_driver); +} + +static void __exit sc92031_exit(void) +{ + pci_unregister_driver(&sc92031_pci_driver); +} + +module_init(sc92031_init); +module_exit(sc92031_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cesar Eduardo Barros "); +MODULE_DESCRIPTION(SC92031_DESCRIPTION); +MODULE_VERSION(SC92031_VERSION); diff -puN drivers/net/sk_mca.c~git-netdev-all /dev/null --- a/drivers/net/sk_mca.c +++ /dev/null @@ -1,1216 +0,0 @@ -/* -net-3-driver for the SKNET MCA-based cards - -This is an extension to the Linux operating system, and is covered by the -same GNU General Public License that covers that work. - -Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de, - alfred.arnold@lancom.de) - -This driver is based both on the 3C523 driver and the SK_G16 driver. - -paper sources: - 'PC Hardware: Aufbau, Funktionsweise, Programmierung' by - Hans-Peter Messmer for the basic Microchannel stuff - - 'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer - for help on Ethernet driver programming - - 'Ethernet/IEEE 802.3 Family 1992 World Network Data Book/Handbook' by AMD - for documentation on the AM7990 LANCE - - 'SKNET Personal Technisches Manual', Version 1.2 by Schneider&Koch - for documentation on the Junior board - - 'SK-NET MC2+ Technical Manual", Version 1.1 by Schneider&Koch for - documentation on the MC2 bord - - A big thank you to the S&K support for providing me so quickly with - documentation! - - Also see http://www.syskonnect.com/ - - Missing things: - - -> set debug level via ioctl instead of compile-time switches - -> I didn't follow the development of the 2.1.x kernels, so my - assumptions about which things changed with which kernel version - are probably nonsense - -History: - May 16th, 1999 - startup - May 22st, 1999 - added private structure, methods - begun building data structures in RAM - May 23nd, 1999 - can receive frames, send frames - May 24th, 1999 - modularized initialization of LANCE - loadable as module - still Tx problem :-( - May 26th, 1999 - MC2 works - support for multiple devices - display media type for MC2+ - May 28th, 1999 - fixed problem in GetLANCE leaving interrupts turned off - increase TX queue to 4 packets to improve send performance - May 29th, 1999 - a few corrections in statistics, caught rcvr overruns - reinitialization of LANCE/board in critical situations - MCA info implemented - implemented LANCE multicast filter - Jun 6th, 1999 - additions for Linux 2.2 - Dec 25th, 1999 - unfortunately there seem to be newer MC2+ boards that react - on IRQ 3/5/9/10 instead of 3/5/10/11, so we have to autoprobe - in questionable cases... - Dec 28th, 1999 - integrated patches from David Weinehall & Bill Wendling for 2.3 - kernels (isa_...functions). Things are defined in a way that - it still works with 2.0.x 8-) - Dec 30th, 1999 - added handling of the remaining interrupt conditions. That - should cure the spurious hangs. - Jan 30th, 2000 - newer kernels automatically probe more than one board, so the - 'startslot' as a variable is also needed here - June 1st, 2000 - added changes for recent 2.3 kernels - - *************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define _SK_MCA_DRIVER_ -#include "sk_mca.h" - -/* ------------------------------------------------------------------------ - * global static data - not more since we can handle multiple boards and - * have to pack all state info into the device struct! - * ------------------------------------------------------------------------ */ - -static char *MediaNames[Media_Count] = - { "10Base2", "10BaseT", "10Base5", "Unknown" }; - -static unsigned char poly[] = - { 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 -}; - -/* ------------------------------------------------------------------------ - * private subfunctions - * ------------------------------------------------------------------------ */ - -/* dump parts of shared memory - only needed during debugging */ - -#ifdef DEBUG -static void dumpmem(struct net_device *dev, u32 start, u32 len) -{ - skmca_priv *priv = netdev_priv(dev); - int z; - - for (z = 0; z < len; z++) { - if ((z & 15) == 0) - printk("%04x:", z); - printk(" %02x", readb(priv->base + start + z)); - if ((z & 15) == 15) - printk("\n"); - } -} - -/* print exact time - ditto */ - -static void PrTime(void) -{ - struct timeval tv; - - do_gettimeofday(&tv); - printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec); -} -#endif - -/* deduce resources out of POS registers */ - -static void __init getaddrs(int slot, int junior, int *base, int *irq, - skmca_medium * medium) -{ - u_char pos0, pos1, pos2; - - if (junior) { - pos0 = mca_read_stored_pos(slot, 2); - *base = ((pos0 & 0x0e) << 13) + 0xc0000; - *irq = ((pos0 & 0x10) >> 4) + 10; - *medium = Media_Unknown; - } else { - /* reset POS 104 Bits 0+1 so the shared memory region goes to the - configured area between 640K and 1M. Afterwards, enable the MC2. - I really don't know what rode SK to do this... */ - - mca_write_pos(slot, 4, - mca_read_stored_pos(slot, 4) & 0xfc); - mca_write_pos(slot, 2, - mca_read_stored_pos(slot, 2) | 0x01); - - pos1 = mca_read_stored_pos(slot, 3); - pos2 = mca_read_stored_pos(slot, 4); - *base = ((pos1 & 0x07) << 14) + 0xc0000; - switch (pos2 & 0x0c) { - case 0: - *irq = 3; - break; - case 4: - *irq = 5; - break; - case 8: - *irq = -10; - break; - case 12: - *irq = -11; - break; - } - *medium = (pos2 >> 6) & 3; - } -} - -/* check for both cards: - When the MC2 is turned off, it was configured for more than 15MB RAM, - is disabled and won't get detected using the standard probe. We - therefore have to scan the slots manually :-( */ - -static int __init dofind(int *junior, int firstslot) -{ - int slot; - unsigned int id; - - for (slot = firstslot; slot < MCA_MAX_SLOT_NR; slot++) { - id = mca_read_stored_pos(slot, 0) - + (((unsigned int) mca_read_stored_pos(slot, 1)) << 8); - - *junior = 0; - if (id == SKNET_MCA_ID) - return slot; - *junior = 1; - if (id == SKNET_JUNIOR_MCA_ID) - return slot; - } - return MCA_NOTFOUND; -} - -/* reset the whole board */ - -static void ResetBoard(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - - writeb(CTRL_RESET_ON, priv->ctrladdr); - udelay(10); - writeb(CTRL_RESET_OFF, priv->ctrladdr); -} - -/* wait for LANCE interface to become not busy */ - -static int WaitLANCE(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - int t = 0; - - while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == - STAT_IO_BUSY) { - udelay(1); - if (++t > 1000) { - printk("%s: LANCE access timeout", dev->name); - return 0; - } - } - - return 1; -} - -/* set LANCE register - must be atomic */ - -static void SetLANCE(struct net_device *dev, u16 addr, u16 value) -{ - skmca_priv *priv = netdev_priv(dev); - unsigned long flags; - - /* disable interrupts */ - - spin_lock_irqsave(&priv->lock, flags); - - /* wait until no transfer is pending */ - - WaitLANCE(dev); - - /* transfer register address to RAP */ - - writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr); - writew(addr, priv->ioregaddr); - writeb(IOCMD_GO, priv->cmdaddr); - udelay(1); - WaitLANCE(dev); - - /* transfer data to register */ - - writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA, priv->ctrladdr); - writew(value, priv->ioregaddr); - writeb(IOCMD_GO, priv->cmdaddr); - udelay(1); - WaitLANCE(dev); - - /* reenable interrupts */ - - spin_unlock_irqrestore(&priv->lock, flags); -} - -/* get LANCE register */ - -static u16 GetLANCE(struct net_device *dev, u16 addr) -{ - skmca_priv *priv = netdev_priv(dev); - unsigned long flags; - unsigned int res; - - /* disable interrupts */ - - spin_lock_irqsave(&priv->lock, flags); - - /* wait until no transfer is pending */ - - WaitLANCE(dev); - - /* transfer register address to RAP */ - - writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr); - writew(addr, priv->ioregaddr); - writeb(IOCMD_GO, priv->cmdaddr); - udelay(1); - WaitLANCE(dev); - - /* transfer data from register */ - - writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA, priv->ctrladdr); - writeb(IOCMD_GO, priv->cmdaddr); - udelay(1); - WaitLANCE(dev); - res = readw(priv->ioregaddr); - - /* reenable interrupts */ - - spin_unlock_irqrestore(&priv->lock, flags); - - return res; -} - -/* build up descriptors in shared RAM */ - -static void InitDscrs(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - u32 bufaddr; - - /* Set up Tx descriptors. The board has only 16K RAM so bits 16..23 - are always 0. */ - - bufaddr = RAM_DATABASE; - { - LANCE_TxDescr descr; - int z; - - for (z = 0; z < TXCOUNT; z++) { - descr.LowAddr = bufaddr; - descr.Flags = 0; - descr.Len = 0xf000; - descr.Status = 0; - memcpy_toio(priv->base + RAM_TXBASE + - (z * sizeof(LANCE_TxDescr)), &descr, - sizeof(LANCE_TxDescr)); - memset_io(priv->base + bufaddr, 0, RAM_BUFSIZE); - bufaddr += RAM_BUFSIZE; - } - } - - /* do the same for the Rx descriptors */ - - { - LANCE_RxDescr descr; - int z; - - for (z = 0; z < RXCOUNT; z++) { - descr.LowAddr = bufaddr; - descr.Flags = RXDSCR_FLAGS_OWN; - descr.MaxLen = -RAM_BUFSIZE; - descr.Len = 0; - memcpy_toio(priv->base + RAM_RXBASE + - (z * sizeof(LANCE_RxDescr)), &descr, - sizeof(LANCE_RxDescr)); - memset_io(priv->base + bufaddr, 0, RAM_BUFSIZE); - bufaddr += RAM_BUFSIZE; - } - } -} - -/* calculate the hash bit position for a given multicast address - taken more or less directly from the AMD datasheet... */ - -static void UpdateCRC(unsigned char *CRC, int bit) -{ - int j; - - /* shift CRC one bit */ - - memmove(CRC + 1, CRC, 32 * sizeof(unsigned char)); - CRC[0] = 0; - - /* if bit XOR controlbit = 1, set CRC = CRC XOR polynomial */ - - if (bit ^ CRC[32]) - for (j = 0; j < 32; j++) - CRC[j] ^= poly[j]; -} - -static unsigned int GetHash(char *address) -{ - unsigned char CRC[33]; - int i, byte, hashcode; - - /* a multicast address has bit 0 in the first byte set */ - - if ((address[0] & 1) == 0) - return -1; - - /* initialize CRC */ - - memset(CRC, 1, sizeof(CRC)); - - /* loop through address bits */ - - for (byte = 0; byte < 6; byte++) - for (i = 0; i < 8; i++) - UpdateCRC(CRC, (address[byte] >> i) & 1); - - /* hashcode is the 6 least significant bits of the CRC */ - - hashcode = 0; - for (i = 0; i < 6; i++) - hashcode = (hashcode << 1) + CRC[i]; - return hashcode; -} - -/* feed ready-built initialization block into LANCE */ - -static void InitLANCE(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - - /* build up descriptors. */ - - InitDscrs(dev); - - /* next RX descriptor to be read is the first one. Since the LANCE - will start from the beginning after initialization, we have to - reset out pointers too. */ - - priv->nextrx = 0; - - /* no TX descriptors active */ - - priv->nexttxput = priv->nexttxdone = priv->txbusy = 0; - - /* set up the LANCE bus control register - constant for SKnet boards */ - - SetLANCE(dev, LANCE_CSR3, - CSR3_BSWAP_OFF | CSR3_ALE_LOW | CSR3_BCON_HOLD); - - /* write address of initialization block into LANCE */ - - SetLANCE(dev, LANCE_CSR1, RAM_INITBASE & 0xffff); - SetLANCE(dev, LANCE_CSR2, (RAM_INITBASE >> 16) & 0xff); - - /* we don't get ready until the LANCE has read the init block */ - - netif_stop_queue(dev); - - /* let LANCE read the initialization block. LANCE is ready - when we receive the corresponding interrupt. */ - - SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_INIT); -} - -/* stop the LANCE so we can reinitialize it */ - -static void StopLANCE(struct net_device *dev) -{ - /* can't take frames any more */ - - netif_stop_queue(dev); - - /* disable interrupts, stop it */ - - SetLANCE(dev, LANCE_CSR0, CSR0_STOP); -} - -/* initialize card and LANCE for proper operation */ - -static void InitBoard(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - LANCE_InitBlock block; - - /* Lay out the shared RAM - first we create the init block for the LANCE. - We do not overwrite it later because we need it again when we switch - promiscous mode on/off. */ - - block.Mode = 0; - if (dev->flags & IFF_PROMISC) - block.Mode |= LANCE_INIT_PROM; - memcpy(block.PAdr, dev->dev_addr, 6); - memset(block.LAdrF, 0, sizeof(block.LAdrF)); - block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29); - block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29); - - memcpy_toio(priv->base + RAM_INITBASE, &block, sizeof(block)); - - /* initialize LANCE. Implicitly sets up other structures in RAM. */ - - InitLANCE(dev); -} - -/* deinitialize card and LANCE */ - -static void DeinitBoard(struct net_device *dev) -{ - /* stop LANCE */ - - StopLANCE(dev); - - /* reset board */ - - ResetBoard(dev); -} - -/* probe for device's irq */ - -static int __init ProbeIRQ(struct net_device *dev) -{ - unsigned long imaskval, njiffies, irq; - u16 csr0val; - - /* enable all interrupts */ - - imaskval = probe_irq_on(); - - /* initialize the board. Wait for interrupt 'Initialization done'. */ - - ResetBoard(dev); - InitBoard(dev); - - njiffies = jiffies + HZ; - do { - csr0val = GetLANCE(dev, LANCE_CSR0); - } - while (((csr0val & CSR0_IDON) == 0) && (jiffies != njiffies)); - - /* turn of interrupts again */ - - irq = probe_irq_off(imaskval); - - /* if we found something, ack the interrupt */ - - if (irq) - SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_IDON); - - /* back to idle state */ - - DeinitBoard(dev); - - return irq; -} - -/* ------------------------------------------------------------------------ - * interrupt handler(s) - * ------------------------------------------------------------------------ */ - -/* LANCE has read initialization block -> start it */ - -static u16 irqstart_handler(struct net_device *dev, u16 oldcsr0) -{ - /* now we're ready to transmit */ - - netif_wake_queue(dev); - - /* reset IDON bit, start LANCE */ - - SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT); - return GetLANCE(dev, LANCE_CSR0); -} - -/* did we lose blocks due to a FIFO overrun ? */ - -static u16 irqmiss_handler(struct net_device *dev, u16 oldcsr0) -{ - skmca_priv *priv = netdev_priv(dev); - - /* update statistics */ - - priv->stat.rx_fifo_errors++; - - /* reset MISS bit */ - - SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_MISS); - return GetLANCE(dev, LANCE_CSR0); -} - -/* receive interrupt */ - -static u16 irqrx_handler(struct net_device *dev, u16 oldcsr0) -{ - skmca_priv *priv = netdev_priv(dev); - LANCE_RxDescr descr; - unsigned int descraddr; - - /* run through queue until we reach a descriptor we do not own */ - - descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr)); - while (1) { - /* read descriptor */ - memcpy_fromio(&descr, priv->base + descraddr, - sizeof(LANCE_RxDescr)); - - /* if we reach a descriptor we do not own, we're done */ - if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0) - break; - -#ifdef DEBUG - PrTime(); - printk("Receive packet on descr %d len %d\n", priv->nextrx, - descr.Len); -#endif - - /* erroneous packet ? */ - if ((descr.Flags & RXDSCR_FLAGS_ERR) != 0) { - priv->stat.rx_errors++; - if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0) - priv->stat.rx_crc_errors++; - else if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0) - priv->stat.rx_frame_errors++; - else if ((descr.Flags & RXDSCR_FLAGS_OFLO) != 0) - priv->stat.rx_fifo_errors++; - } - - /* good packet ? */ - else { - struct sk_buff *skb; - - skb = dev_alloc_skb(descr.Len + 2); - if (skb == NULL) - priv->stat.rx_dropped++; - else { - memcpy_fromio(skb_put(skb, descr.Len), - priv->base + - descr.LowAddr, descr.Len); - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - skb->ip_summed = CHECKSUM_NONE; - priv->stat.rx_packets++; - priv->stat.rx_bytes += descr.Len; - netif_rx(skb); - dev->last_rx = jiffies; - } - } - - /* give descriptor back to LANCE */ - descr.Len = 0; - descr.Flags |= RXDSCR_FLAGS_OWN; - - /* update descriptor in shared RAM */ - memcpy_toio(priv->base + descraddr, &descr, - sizeof(LANCE_RxDescr)); - - /* go to next descriptor */ - priv->nextrx++; - descraddr += sizeof(LANCE_RxDescr); - if (priv->nextrx >= RXCOUNT) { - priv->nextrx = 0; - descraddr = RAM_RXBASE; - } - } - - /* reset RINT bit */ - - SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_RINT); - return GetLANCE(dev, LANCE_CSR0); -} - -/* transmit interrupt */ - -static u16 irqtx_handler(struct net_device *dev, u16 oldcsr0) -{ - skmca_priv *priv = netdev_priv(dev); - LANCE_TxDescr descr; - unsigned int descraddr; - - /* check descriptors at most until no busy one is left */ - - descraddr = - RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr)); - while (priv->txbusy > 0) { - /* read descriptor */ - memcpy_fromio(&descr, priv->base + descraddr, - sizeof(LANCE_TxDescr)); - - /* if the LANCE still owns this one, we've worked out all sent packets */ - if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0) - break; - -#ifdef DEBUG - PrTime(); - printk("Send packet done on descr %d\n", priv->nexttxdone); -#endif - - /* update statistics */ - if ((descr.Flags & TXDSCR_FLAGS_ERR) == 0) { - priv->stat.tx_packets++; - priv->stat.tx_bytes++; - } else { - priv->stat.tx_errors++; - if ((descr.Status & TXDSCR_STATUS_UFLO) != 0) { - priv->stat.tx_fifo_errors++; - InitLANCE(dev); - } - else - if ((descr.Status & TXDSCR_STATUS_LCOL) != - 0) priv->stat.tx_window_errors++; - else if ((descr.Status & TXDSCR_STATUS_LCAR) != 0) - priv->stat.tx_carrier_errors++; - else if ((descr.Status & TXDSCR_STATUS_RTRY) != 0) - priv->stat.tx_aborted_errors++; - } - - /* go to next descriptor */ - priv->nexttxdone++; - descraddr += sizeof(LANCE_TxDescr); - if (priv->nexttxdone >= TXCOUNT) { - priv->nexttxdone = 0; - descraddr = RAM_TXBASE; - } - priv->txbusy--; - } - - /* reset TX interrupt bit */ - - SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_TINT); - oldcsr0 = GetLANCE(dev, LANCE_CSR0); - - /* at least one descriptor is freed. Therefore we can accept - a new one */ - /* inform upper layers we're in business again */ - - netif_wake_queue(dev); - - return oldcsr0; -} - -/* general interrupt entry */ - -static irqreturn_t irq_handler(int irq, void *device) -{ - struct net_device *dev = (struct net_device *) device; - u16 csr0val; - - /* read CSR0 to get interrupt cause */ - - csr0val = GetLANCE(dev, LANCE_CSR0); - - /* in case we're not meant... */ - - if ((csr0val & CSR0_INTR) == 0) - return IRQ_NONE; - -#if 0 - set_bit(LINK_STATE_RXSEM, &dev->state); -#endif - - /* loop through the interrupt bits until everything is clear */ - - do { - if ((csr0val & CSR0_IDON) != 0) - csr0val = irqstart_handler(dev, csr0val); - if ((csr0val & CSR0_RINT) != 0) - csr0val = irqrx_handler(dev, csr0val); - if ((csr0val & CSR0_MISS) != 0) - csr0val = irqmiss_handler(dev, csr0val); - if ((csr0val & CSR0_TINT) != 0) - csr0val = irqtx_handler(dev, csr0val); - if ((csr0val & CSR0_MERR) != 0) { - SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_MERR); - csr0val = GetLANCE(dev, LANCE_CSR0); - } - if ((csr0val & CSR0_BABL) != 0) { - SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_BABL); - csr0val = GetLANCE(dev, LANCE_CSR0); - } - } - while ((csr0val & CSR0_INTR) != 0); - -#if 0 - clear_bit(LINK_STATE_RXSEM, &dev->state); -#endif - return IRQ_HANDLED; -} - -/* ------------------------------------------------------------------------ - * driver methods - * ------------------------------------------------------------------------ */ - -/* MCA info */ - -static int skmca_getinfo(char *buf, int slot, void *d) -{ - int len = 0, i; - struct net_device *dev = (struct net_device *) d; - skmca_priv *priv; - - /* can't say anything about an uninitialized device... */ - - if (dev == NULL) - return len; - priv = netdev_priv(dev); - - /* print info */ - - len += sprintf(buf + len, "IRQ: %d\n", priv->realirq); - len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, - dev->mem_end - 1); - len += - sprintf(buf + len, "Transceiver: %s\n", - MediaNames[priv->medium]); - len += sprintf(buf + len, "Device: %s\n", dev->name); - len += sprintf(buf + len, "MAC address:"); - for (i = 0; i < 6; i++) - len += sprintf(buf + len, " %02x", dev->dev_addr[i]); - buf[len++] = '\n'; - buf[len] = 0; - - return len; -} - -/* open driver. Means also initialization and start of LANCE */ - -static int skmca_open(struct net_device *dev) -{ - int result; - skmca_priv *priv = netdev_priv(dev); - - /* register resources - only necessary for IRQ */ - result = - request_irq(priv->realirq, irq_handler, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, "sk_mca", dev); - if (result != 0) { - printk("%s: failed to register irq %d\n", dev->name, - dev->irq); - return result; - } - dev->irq = priv->realirq; - - /* set up the card and LANCE */ - - InitBoard(dev); - - /* set up flags */ - - netif_start_queue(dev); - - return 0; -} - -/* close driver. Shut down board and free allocated resources */ - -static int skmca_close(struct net_device *dev) -{ - /* turn off board */ - DeinitBoard(dev); - - /* release resources */ - if (dev->irq != 0) - free_irq(dev->irq, dev); - dev->irq = 0; - - return 0; -} - -/* transmit a block. */ - -static int skmca_tx(struct sk_buff *skb, struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - LANCE_TxDescr descr; - unsigned int address; - int tmplen, retval = 0; - unsigned long flags; - - /* if we get called with a NULL descriptor, the Ethernet layer thinks - our card is stuck an we should reset it. We'll do this completely: */ - - if (skb == NULL) { - DeinitBoard(dev); - InitBoard(dev); - return 0; /* don't try to free the block here ;-) */ - } - - /* is there space in the Tx queue ? If no, the upper layer gave us a - packet in spite of us not being ready and is really in trouble. - We'll do the dropping for him: */ - if (priv->txbusy >= TXCOUNT) { - priv->stat.tx_dropped++; - retval = -EIO; - goto tx_done; - } - - /* get TX descriptor */ - address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr)); - memcpy_fromio(&descr, priv->base + address, sizeof(LANCE_TxDescr)); - - /* enter packet length as 2s complement - assure minimum length */ - tmplen = skb->len; - if (tmplen < 60) - tmplen = 60; - descr.Len = 65536 - tmplen; - - /* copy filler into RAM - in case we're filling up... - we're filling a bit more than necessary, but that doesn't harm - since the buffer is far larger... */ - if (tmplen > skb->len) { - char *fill = "NetBSD is a nice OS too! "; - unsigned int destoffs = 0, l = strlen(fill); - - while (destoffs < tmplen) { - memcpy_toio(priv->base + descr.LowAddr + - destoffs, fill, l); - destoffs += l; - } - } - - /* do the real data copying */ - memcpy_toio(priv->base + descr.LowAddr, skb->data, skb->len); - - /* hand descriptor over to LANCE - this is the first and last chunk */ - descr.Flags = - TXDSCR_FLAGS_OWN | TXDSCR_FLAGS_STP | TXDSCR_FLAGS_ENP; - -#ifdef DEBUG - PrTime(); - printk("Send packet on descr %d len %d\n", priv->nexttxput, - skb->len); -#endif - - /* one more descriptor busy */ - - spin_lock_irqsave(&priv->lock, flags); - - priv->nexttxput++; - if (priv->nexttxput >= TXCOUNT) - priv->nexttxput = 0; - priv->txbusy++; - - /* are we saturated ? */ - - if (priv->txbusy >= TXCOUNT) - netif_stop_queue(dev); - - /* write descriptor back to RAM */ - memcpy_toio(priv->base + address, &descr, sizeof(LANCE_TxDescr)); - - /* if no descriptors were active, give the LANCE a hint to read it - immediately */ - - if (priv->txbusy == 0) - SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD); - - spin_unlock_irqrestore(&priv->lock, flags); - - tx_done: - - dev_kfree_skb(skb); - - return retval; -} - -/* return pointer to Ethernet statistics */ - -static struct net_device_stats *skmca_stats(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - - return &(priv->stat); -} - -/* switch receiver mode. We use the LANCE's multicast filter to prefilter - multicast addresses. */ - -static void skmca_set_multicast_list(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - LANCE_InitBlock block; - - /* first stop the LANCE... */ - StopLANCE(dev); - - /* ...then modify the initialization block... */ - memcpy_fromio(&block, priv->base + RAM_INITBASE, sizeof(block)); - if (dev->flags & IFF_PROMISC) - block.Mode |= LANCE_INIT_PROM; - else - block.Mode &= ~LANCE_INIT_PROM; - - if (dev->flags & IFF_ALLMULTI) { /* get all multicasts */ - memset(block.LAdrF, 0xff, sizeof(block.LAdrF)); - } else { /* get selected/no multicasts */ - - struct dev_mc_list *mptr; - int code; - - memset(block.LAdrF, 0, sizeof(block.LAdrF)); - for (mptr = dev->mc_list; mptr != NULL; mptr = mptr->next) { - code = GetHash(mptr->dmi_addr); - block.LAdrF[(code >> 3) & 7] |= 1 << (code & 7); - } - } - - memcpy_toio(priv->base + RAM_INITBASE, &block, sizeof(block)); - - /* ...then reinit LANCE with the correct flags */ - InitLANCE(dev); -} - -/* ------------------------------------------------------------------------ - * hardware check - * ------------------------------------------------------------------------ */ - -static int startslot; /* counts through slots when probing multiple devices */ - -static void cleanup_card(struct net_device *dev) -{ - skmca_priv *priv = netdev_priv(dev); - DeinitBoard(dev); - if (dev->irq != 0) - free_irq(dev->irq, dev); - iounmap(priv->base); - mca_mark_as_unused(priv->slot); - mca_set_adapter_procfn(priv->slot, NULL, NULL); -} - -struct net_device * __init skmca_probe(int unit) -{ - struct net_device *dev; - int force_detect = 0; - int junior, slot, i; - int base = 0, irq = 0; - skmca_priv *priv; - skmca_medium medium; - int err; - - /* can't work without an MCA bus ;-) */ - - if (MCA_bus == 0) - return ERR_PTR(-ENODEV); - - dev = alloc_etherdev(sizeof(skmca_priv)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - - SET_MODULE_OWNER(dev); - - /* start address of 1 --> forced detection */ - - if (dev->mem_start == 1) - force_detect = 1; - - /* search through slots */ - - base = dev->mem_start; - irq = dev->base_addr; - for (slot = startslot; (slot = dofind(&junior, slot)) != -1; slot++) { - /* deduce card addresses */ - - getaddrs(slot, junior, &base, &irq, &medium); - - /* slot already in use ? */ - - if (mca_is_adapter_used(slot)) - continue; - - /* were we looking for something different ? */ - - if (dev->irq && dev->irq != irq) - continue; - if (dev->mem_start && dev->mem_start != base) - continue; - - /* found something that matches */ - - break; - } - - /* nothing found ? */ - - if (slot == -1) { - free_netdev(dev); - return (base || irq) ? ERR_PTR(-ENXIO) : ERR_PTR(-ENODEV); - } - - /* make procfs entries */ - - if (junior) - mca_set_adapter_name(slot, - "SKNET junior MC2 Ethernet Adapter"); - else - mca_set_adapter_name(slot, "SKNET MC2+ Ethernet Adapter"); - mca_set_adapter_procfn(slot, (MCA_ProcFn) skmca_getinfo, dev); - - mca_mark_as_used(slot); - - /* announce success */ - printk("%s: SKNet %s adapter found in slot %d\n", dev->name, - junior ? "Junior MC2" : "MC2+", slot + 1); - - priv = netdev_priv(dev); - priv->base = ioremap(base, 0x4000); - if (!priv->base) { - mca_set_adapter_procfn(slot, NULL, NULL); - mca_mark_as_unused(slot); - free_netdev(dev); - return ERR_PTR(-ENOMEM); - } - - priv->slot = slot; - priv->macbase = priv->base + 0x3fc0; - priv->ioregaddr = priv->base + 0x3ff0; - priv->ctrladdr = priv->base + 0x3ff2; - priv->cmdaddr = priv->base + 0x3ff3; - priv->medium = medium; - memset(&priv->stat, 0, sizeof(struct net_device_stats)); - spin_lock_init(&priv->lock); - - /* set base + irq for this device (irq not allocated so far) */ - dev->irq = 0; - dev->mem_start = base; - dev->mem_end = base + 0x4000; - - /* autoprobe ? */ - if (irq < 0) { - int nirq; - - printk - ("%s: ambigous POS bit combination, must probe for IRQ...\n", - dev->name); - nirq = ProbeIRQ(dev); - if (nirq <= 0) - printk("%s: IRQ probe failed, assuming IRQ %d", - dev->name, priv->realirq = -irq); - else - priv->realirq = nirq; - } else - priv->realirq = irq; - - /* set methods */ - dev->open = skmca_open; - dev->stop = skmca_close; - dev->hard_start_xmit = skmca_tx; - dev->do_ioctl = NULL; - dev->get_stats = skmca_stats; - dev->set_multicast_list = skmca_set_multicast_list; - dev->flags |= IFF_MULTICAST; - - /* copy out MAC address */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(priv->macbase + (i << 1)); - - /* print config */ - printk("%s: IRQ %d, memory %#lx-%#lx, " - "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n", - dev->name, priv->realirq, dev->mem_start, dev->mem_end - 1, - dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], - dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); - printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]); - - /* reset board */ - - ResetBoard(dev); - - startslot = slot + 1; - - err = register_netdev(dev); - if (err) { - cleanup_card(dev); - free_netdev(dev); - dev = ERR_PTR(err); - } - return dev; -} - -/* ------------------------------------------------------------------------ - * modularization support - * ------------------------------------------------------------------------ */ - -#ifdef MODULE -MODULE_LICENSE("GPL"); - -#define DEVMAX 5 - -static struct net_device *moddevs[DEVMAX]; - -int init_module(void) -{ - int z; - - startslot = 0; - for (z = 0; z < DEVMAX; z++) { - struct net_device *dev = skmca_probe(-1); - if (IS_ERR(dev)) - break; - moddevs[z] = dev; - } - if (!z) - return -EIO; - return 0; -} - -void cleanup_module(void) -{ - int z; - - for (z = 0; z < DEVMAX; z++) { - struct net_device *dev = moddevs[z]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -#endif /* MODULE */ diff -puN drivers/net/sk_mca.h~git-netdev-all /dev/null --- a/drivers/net/sk_mca.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef _SK_MCA_INCLUDE_ -#define _SK_MCA_INCLUDE_ - -#ifdef _SK_MCA_DRIVER_ - -/* Adapter ID's */ -#define SKNET_MCA_ID 0x6afd -#define SKNET_JUNIOR_MCA_ID 0x6be9 - -/* media enumeration - defined in a way that it fits onto the MC2+'s - POS registers... */ - -typedef enum { Media_10Base2, Media_10BaseT, - Media_10Base5, Media_Unknown, Media_Count -} skmca_medium; - -/* private structure */ -typedef struct { - unsigned int slot; /* MCA-Slot-# */ - void __iomem *base; - void __iomem *macbase; /* base address of MAC address PROM */ - void __iomem *ioregaddr;/* address of I/O-register (Lo) */ - void __iomem *ctrladdr; /* address of control/stat register */ - void __iomem *cmdaddr; /* address of I/O-command register */ - int nextrx; /* index of next RX descriptor to - be read */ - int nexttxput; /* index of next free TX descriptor */ - int nexttxdone; /* index of next TX descriptor to - be finished */ - int txbusy; /* # of busy TX descriptors */ - struct net_device_stats stat; /* packet statistics */ - int realirq; /* memorizes actual IRQ, even when - currently not allocated */ - skmca_medium medium; /* physical cannector */ - spinlock_t lock; -} skmca_priv; - -/* card registers: control/status register bits */ - -#define CTRL_ADR_DATA 0 /* Bit 0 = 0 ->access data register */ -#define CTRL_ADR_RAP 1 /* Bit 0 = 1 ->access RAP register */ -#define CTRL_RW_WRITE 0 /* Bit 1 = 0 ->write register */ -#define CTRL_RW_READ 2 /* Bit 1 = 1 ->read register */ -#define CTRL_RESET_ON 0 /* Bit 3 = 0 ->reset board */ -#define CTRL_RESET_OFF 8 /* Bit 3 = 1 ->no reset of board */ - -#define STAT_ADR_DATA 0 /* Bit 0 of ctrl register read back */ -#define STAT_ADR_RAP 1 -#define STAT_RW_WRITE 0 /* Bit 1 of ctrl register read back */ -#define STAT_RW_READ 2 -#define STAT_RESET_ON 0 /* Bit 3 of ctrl register read back */ -#define STAT_RESET_OFF 8 -#define STAT_IRQ_ACT 0 /* interrupt pending */ -#define STAT_IRQ_NOACT 16 /* no interrupt pending */ -#define STAT_IO_NOBUSY 0 /* no transfer busy */ -#define STAT_IO_BUSY 32 /* transfer busy */ - -/* I/O command register bits */ - -#define IOCMD_GO 128 /* Bit 7 = 1 -> start register xfer */ - -/* LANCE registers */ - -#define LANCE_CSR0 0 /* Status/Control */ - -#define CSR0_ERR 0x8000 /* general error flag */ -#define CSR0_BABL 0x4000 /* transmitter timeout */ -#define CSR0_CERR 0x2000 /* collision error */ -#define CSR0_MISS 0x1000 /* lost Rx block */ -#define CSR0_MERR 0x0800 /* memory access error */ -#define CSR0_RINT 0x0400 /* receiver interrupt */ -#define CSR0_TINT 0x0200 /* transmitter interrupt */ -#define CSR0_IDON 0x0100 /* initialization done */ -#define CSR0_INTR 0x0080 /* general interrupt flag */ -#define CSR0_INEA 0x0040 /* interrupt enable */ -#define CSR0_RXON 0x0020 /* receiver enabled */ -#define CSR0_TXON 0x0010 /* transmitter enabled */ -#define CSR0_TDMD 0x0008 /* force transmission now */ -#define CSR0_STOP 0x0004 /* stop LANCE */ -#define CSR0_STRT 0x0002 /* start LANCE */ -#define CSR0_INIT 0x0001 /* read initialization block */ - -#define LANCE_CSR1 1 /* addr bit 0..15 of initialization */ -#define LANCE_CSR2 2 /* 16..23 block */ - -#define LANCE_CSR3 3 /* Bus control */ -#define CSR3_BCON_HOLD 0 /* Bit 0 = 0 -> BM1,BM0,HOLD */ -#define CSR3_BCON_BUSRQ 1 /* Bit 0 = 1 -> BUSAK0,BYTE,BUSRQ */ -#define CSR3_ALE_HIGH 0 /* Bit 1 = 0 -> ALE asserted high */ -#define CSR3_ALE_LOW 2 /* Bit 1 = 1 -> ALE asserted low */ -#define CSR3_BSWAP_OFF 0 /* Bit 2 = 0 -> no byte swap */ -#define CSR3_BSWAP_ON 4 /* Bit 2 = 1 -> byte swap */ - -/* LANCE structures */ - -typedef struct { /* LANCE initialization block */ - u16 Mode; /* mode flags */ - u8 PAdr[6]; /* MAC address */ - u8 LAdrF[8]; /* Multicast filter */ - u32 RdrP; /* Receive descriptor */ - u32 TdrP; /* Transmit descriptor */ -} LANCE_InitBlock; - -/* Mode flags init block */ - -#define LANCE_INIT_PROM 0x8000 /* enable promiscous mode */ -#define LANCE_INIT_INTL 0x0040 /* internal loopback */ -#define LANCE_INIT_DRTY 0x0020 /* disable retry */ -#define LANCE_INIT_COLL 0x0010 /* force collision */ -#define LANCE_INIT_DTCR 0x0008 /* disable transmit CRC */ -#define LANCE_INIT_LOOP 0x0004 /* loopback */ -#define LANCE_INIT_DTX 0x0002 /* disable transmitter */ -#define LANCE_INIT_DRX 0x0001 /* disable receiver */ - -typedef struct { /* LANCE Tx descriptor */ - u16 LowAddr; /* bit 0..15 of address */ - u16 Flags; /* bit 16..23 of address + Flags */ - u16 Len; /* 2s complement of packet length */ - u16 Status; /* Result of transmission */ -} LANCE_TxDescr; - -#define TXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */ -#define TXDSCR_FLAGS_ERR 0x4000 /* summary error flag */ -#define TXDSCR_FLAGS_MORE 0x1000 /* more than one retry needed? */ -#define TXDSCR_FLAGS_ONE 0x0800 /* one retry? */ -#define TXDSCR_FLAGS_DEF 0x0400 /* transmission deferred? */ -#define TXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */ -#define TXDSCR_FLAGS_ENP 0x0100 /* last packet in chain? */ - -#define TXDSCR_STATUS_BUFF 0x8000 /* buffer error? */ -#define TXDSCR_STATUS_UFLO 0x4000 /* silo underflow during transmit? */ -#define TXDSCR_STATUS_LCOL 0x1000 /* late collision? */ -#define TXDSCR_STATUS_LCAR 0x0800 /* loss of carrier? */ -#define TXDSCR_STATUS_RTRY 0x0400 /* retry error? */ - -typedef struct { /* LANCE Rx descriptor */ - u16 LowAddr; /* bit 0..15 of address */ - u16 Flags; /* bit 16..23 of address + Flags */ - u16 MaxLen; /* 2s complement of buffer length */ - u16 Len; /* packet length */ -} LANCE_RxDescr; - -#define RXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */ -#define RXDSCR_FLAGS_ERR 0x4000 /* summary error flag */ -#define RXDSCR_FLAGS_FRAM 0x2000 /* framing error flag */ -#define RXDSCR_FLAGS_OFLO 0x1000 /* FIFO overflow? */ -#define RXDSCR_FLAGS_CRC 0x0800 /* CRC error? */ -#define RXDSCR_FLAGS_BUFF 0x0400 /* buffer error? */ -#define RXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */ -#define RXDCSR_FLAGS_ENP 0x0100 /* last packet in chain? */ - -/* RAM layout */ - -#define TXCOUNT 4 /* length of TX descriptor queue */ -#define LTXCOUNT 2 /* log2 of it */ -#define RXCOUNT 4 /* length of RX descriptor queue */ -#define LRXCOUNT 2 /* log2 of it */ - -#define RAM_INITBASE 0 /* LANCE init block */ -#define RAM_TXBASE 24 /* Start of TX descriptor queue */ -#define RAM_RXBASE \ -(RAM_TXBASE + (TXCOUNT * 8)) /* Start of RX descriptor queue */ -#define RAM_DATABASE \ -(RAM_RXBASE + (RXCOUNT * 8)) /* Start of data area for frames */ -#define RAM_BUFSIZE 1580 /* max. frame size - should never be - reached */ - -#endif /* _SK_MCA_DRIVER_ */ - -#endif /* _SK_MCA_INCLUDE_ */ diff -puN drivers/net/skfp/can.c~git-netdev-all /dev/null --- a/drivers/net/skfp/can.c +++ /dev/null @@ -1,83 +0,0 @@ -/****************************************************************************** - * - * (C)Copyright 1998,1999 SysKonnect, - * a business unit of Schneider & Koch & Co. Datensysteme GmbH. - * - * See the file "skfddi.c" for further information. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The information in this file is provided "AS IS" without warranty. - * - ******************************************************************************/ - -#ifndef lint -static const char xID_sccs[] = "@(#)can.c 1.5 97/04/07 (C) SK " ; -#endif - -/* - * canonical bit order - */ -const u_char canonical[256] = { - 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, - 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, - 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, - 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, - 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, - 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, - 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, - 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, - 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, - 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, - 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, - 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, - 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, - 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, - 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, - 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, - 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, - 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, - 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, - 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, - 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, - 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, - 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, - 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, - 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, - 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, - 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, - 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, - 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, - 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, - 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, - 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff -} ; - -#ifdef MAKE_TABLE -int byte_reverse(x) -int x ; -{ - int y = 0 ; - - if (x & 0x01) - y |= 0x80 ; - if (x & 0x02) - y |= 0x40 ; - if (x & 0x04) - y |= 0x20 ; - if (x & 0x08) - y |= 0x10 ; - if (x & 0x10) - y |= 0x08 ; - if (x & 0x20) - y |= 0x04 ; - if (x & 0x40) - y |= 0x02 ; - if (x & 0x80) - y |= 0x01 ; - return(y) ; -} -#endif diff -puN drivers/net/skfp/drvfbi.c~git-netdev-all drivers/net/skfp/drvfbi.c --- a/drivers/net/skfp/drvfbi.c~git-netdev-all +++ a/drivers/net/skfp/drvfbi.c @@ -23,6 +23,7 @@ #include "h/smc.h" #include "h/supern_2.h" #include "h/skfbiinc.h" +#include #ifndef lint static const char ID_sccs[] = "@(#)drvfbi.c 1.63 99/02/11 (C) SK " ; @@ -445,16 +446,14 @@ void read_address(struct s_smc *smc, u_c char PmdType ; int i ; - extern const u_char canonical[256] ; - #if (defined(ISA) || defined(MCA)) for (i = 0; i < 4 ;i++) { /* read mac address from board */ smc->hw.fddi_phys_addr.a[i] = - canonical[(inpw(PR_A(i+SA_MAC))&0xff)] ; + bitrev8(inpw(PR_A(i+SA_MAC))); } for (i = 4; i < 6; i++) { smc->hw.fddi_phys_addr.a[i] = - canonical[(inpw(PR_A(i+SA_MAC+PRA_OFF))&0xff)] ; + bitrev8(inpw(PR_A(i+SA_MAC+PRA_OFF))); } #endif #ifdef EISA @@ -464,17 +463,17 @@ void read_address(struct s_smc *smc, u_c */ for (i = 0; i < 4 ;i++) { /* read mac address from board */ smc->hw.fddi_phys_addr.a[i] = - canonical[inp(PR_A(i+SA_MAC))] ; + bitrev8(inp(PR_A(i+SA_MAC))); } for (i = 4; i < 6; i++) { smc->hw.fddi_phys_addr.a[i] = - canonical[inp(PR_A(i+SA_MAC+PRA_OFF))] ; + bitrev8(inp(PR_A(i+SA_MAC+PRA_OFF))); } #endif #ifdef PCI for (i = 0; i < 6; i++) { /* read mac address from board */ smc->hw.fddi_phys_addr.a[i] = - canonical[inp(ADDR(B2_MAC_0+i))] ; + bitrev8(inp(ADDR(B2_MAC_0+i))); } #endif #ifndef PCI @@ -493,7 +492,7 @@ void read_address(struct s_smc *smc, u_c if (mac_addr) { for (i = 0; i < 6 ;i++) { smc->hw.fddi_canon_addr.a[i] = mac_addr[i] ; - smc->hw.fddi_home_addr.a[i] = canonical[mac_addr[i]] ; + smc->hw.fddi_home_addr.a[i] = bitrev8(mac_addr[i]); } return ; } @@ -501,7 +500,7 @@ void read_address(struct s_smc *smc, u_c for (i = 0; i < 6 ;i++) { smc->hw.fddi_canon_addr.a[i] = - canonical[smc->hw.fddi_phys_addr.a[i]] ; + bitrev8(smc->hw.fddi_phys_addr.a[i]); } } @@ -1269,11 +1268,8 @@ void driver_get_bia(struct s_smc *smc, s { int i ; - extern const u_char canonical[256] ; - - for (i = 0 ; i < 6 ; i++) { - bia_addr->a[i] = canonical[smc->hw.fddi_phys_addr.a[i]] ; - } + for (i = 0 ; i < 6 ; i++) + bia_addr->a[i] = bitrev8(smc->hw.fddi_phys_addr.a[i]); } void smt_start_watchdog(struct s_smc *smc) diff -puN drivers/net/skfp/fplustm.c~git-netdev-all drivers/net/skfp/fplustm.c --- a/drivers/net/skfp/fplustm.c~git-netdev-all +++ a/drivers/net/skfp/fplustm.c @@ -22,7 +22,7 @@ #include "h/fddi.h" #include "h/smc.h" #include "h/supern_2.h" -#include "can.c" +#include #ifndef lint static const char ID_sccs[] = "@(#)fplustm.c 1.32 99/02/23 (C) SK " ; @@ -1073,7 +1073,7 @@ static struct s_fpmc* mac_get_mc_table(s if (can) { p = own->a ; for (i = 0 ; i < 6 ; i++, p++) - *p = canonical[*p] ; + *p = bitrev8(*p); } slot = NULL; for (i = 0, tb = smc->hw.fp.mc.table ; i < FPMAX_MULTICAST ; i++, tb++){ diff -puN drivers/net/skfp/smt.c~git-netdev-all drivers/net/skfp/smt.c --- a/drivers/net/skfp/smt.c~git-netdev-all +++ a/drivers/net/skfp/smt.c @@ -18,6 +18,7 @@ #include "h/fddi.h" #include "h/smc.h" #include "h/smt_p.h" +#include #define KERNEL #include "h/smtstate.h" @@ -26,8 +27,6 @@ static const char ID_sccs[] = "@(#)smt.c 2.43 98/11/23 (C) SK " ; #endif -extern const u_char canonical[256] ; - /* * FC in SMbuf */ @@ -180,7 +179,7 @@ void smt_agent_init(struct s_smc *smc) driver_get_bia(smc,&smc->mib.fddiSMTStationId.sid_node) ; for (i = 0 ; i < 6 ; i ++) { smc->mib.fddiSMTStationId.sid_node.a[i] = - canonical[smc->mib.fddiSMTStationId.sid_node.a[i]] ; + bitrev8(smc->mib.fddiSMTStationId.sid_node.a[i]); } smc->mib.fddiSMTManufacturerData[0] = smc->mib.fddiSMTStationId.sid_node.a[0] ; @@ -2049,9 +2048,8 @@ static void hwm_conv_can(struct s_smc *s SK_UNUSED(smc) ; - for (i = len; i ; i--, data++) { - *data = canonical[*(u_char *)data] ; - } + for (i = len; i ; i--, data++) + *data = bitrev8(*data); } #endif diff -puN drivers/net/sky2.c~git-netdev-all drivers/net/sky2.c --- a/drivers/net/sky2.c~git-netdev-all +++ a/drivers/net/sky2.c @@ -192,76 +192,52 @@ static u16 gm_phy_read(struct sky2_hw *h return v; } -static void sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) -{ - u16 power_control; - int vaux; - - pr_debug("sky2_set_power_state %d\n", state); - sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - - power_control = sky2_pci_read16(hw, hw->pm_cap + PCI_PM_PMC); - vaux = (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) && - (power_control & PCI_PM_CAP_PME_D3cold); - - power_control = sky2_pci_read16(hw, hw->pm_cap + PCI_PM_CTRL); - - power_control |= PCI_PM_CTRL_PME_STATUS; - power_control &= ~(PCI_PM_CTRL_STATE_MASK); - - switch (state) { - case PCI_D0: - /* switch power to VCC (WA for VAUX problem) */ - sky2_write8(hw, B0_POWER_CTRL, - PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); - /* disable Core Clock Division, */ - sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); - - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) - /* enable bits are inverted */ - sky2_write8(hw, B2_Y2_CLK_GATE, - Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | - Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | - Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); - else - sky2_write8(hw, B2_Y2_CLK_GATE, 0); +static void sky2_power_on(struct sky2_hw *hw) +{ + /* switch power to VCC (WA for VAUX problem) */ + sky2_write8(hw, B0_POWER_CTRL, + PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); - if (hw->chip_id == CHIP_ID_YUKON_EC_U) { - u32 reg1; + /* disable Core Clock Division, */ + sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS); - sky2_pci_write32(hw, PCI_DEV_REG3, 0); - reg1 = sky2_pci_read32(hw, PCI_DEV_REG4); - reg1 &= P_ASPM_CONTROL_MSK; - sky2_pci_write32(hw, PCI_DEV_REG4, reg1); - sky2_pci_write32(hw, PCI_DEV_REG5, 0); - } + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + /* enable bits are inverted */ + sky2_write8(hw, B2_Y2_CLK_GATE, + Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | + Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | + Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); + else + sky2_write8(hw, B2_Y2_CLK_GATE, 0); - break; + if (hw->chip_id == CHIP_ID_YUKON_EC_U) { + u32 reg1; - case PCI_D3hot: - case PCI_D3cold: - if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) - sky2_write8(hw, B2_Y2_CLK_GATE, 0); - else - /* enable bits are inverted */ - sky2_write8(hw, B2_Y2_CLK_GATE, - Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | - Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | - Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); - - /* switch power to VAUX */ - if (vaux && state != PCI_D3cold) - sky2_write8(hw, B0_POWER_CTRL, - (PC_VAUX_ENA | PC_VCC_ENA | - PC_VAUX_ON | PC_VCC_OFF)); - break; - default: - printk(KERN_ERR PFX "Unknown power state %d\n", state); + sky2_pci_write32(hw, PCI_DEV_REG3, 0); + reg1 = sky2_pci_read32(hw, PCI_DEV_REG4); + reg1 &= P_ASPM_CONTROL_MSK; + sky2_pci_write32(hw, PCI_DEV_REG4, reg1); + sky2_pci_write32(hw, PCI_DEV_REG5, 0); } +} - sky2_pci_write16(hw, hw->pm_cap + PCI_PM_CTRL, power_control); - sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); +static void sky2_power_aux(struct sky2_hw *hw) +{ + if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) + sky2_write8(hw, B2_Y2_CLK_GATE, 0); + else + /* enable bits are inverted */ + sky2_write8(hw, B2_Y2_CLK_GATE, + Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS | + Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS | + Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS); + + /* switch power to VAUX */ + if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) + sky2_write8(hw, B0_POWER_CTRL, + (PC_VAUX_ENA | PC_VCC_ENA | + PC_VAUX_ON | PC_VCC_OFF)); } static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port) @@ -2480,7 +2456,7 @@ static int sky2_reset(struct sky2_hw *hw ++hw->ports; } - sky2_set_power_state(hw, PCI_D0); + sky2_power_on(hw); for (i = 0; i < hw->ports; i++) { sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); @@ -3376,7 +3352,7 @@ static int __devinit sky2_probe(struct p { struct net_device *dev, *dev1 = NULL; struct sky2_hw *hw; - int err, pm_cap, using_dac = 0; + int err, using_dac = 0; err = pci_enable_device(pdev); if (err) { @@ -3394,15 +3370,6 @@ static int __devinit sky2_probe(struct p pci_set_master(pdev); - /* Find power-management capability. */ - pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (pm_cap == 0) { - printk(KERN_ERR PFX "Cannot find PowerManagement capability, " - "aborting.\n"); - err = -EIO; - goto err_out_free_regions; - } - if (sizeof(dma_addr_t) > sizeof(u32) && !(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) { using_dac = 1; @@ -3438,7 +3405,6 @@ static int __devinit sky2_probe(struct p pci_name(pdev)); goto err_out_free_hw; } - hw->pm_cap = pm_cap; #ifdef __BIG_ENDIAN /* The sk98lin vendor driver uses hardware byte swapping but @@ -3555,7 +3521,8 @@ static void __devexit sky2_remove(struct unregister_netdev(dev1); unregister_netdev(dev0); - sky2_set_power_state(hw, PCI_D3hot); + sky2_power_aux(hw); + sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); sky2_write8(hw, B0_CTST, CS_RST_SET); sky2_read8(hw, B0_CTST); @@ -3581,10 +3548,6 @@ static int sky2_suspend(struct pci_dev * { struct sky2_hw *hw = pci_get_drvdata(pdev); int i; - pci_power_t pstate = pci_choose_state(pdev, state); - - if (!(pstate == PCI_D3hot || pstate == PCI_D3cold)) - return -EINVAL; del_timer_sync(&hw->idle_timer); netif_poll_disable(hw->dev[0]); @@ -3599,8 +3562,10 @@ static int sky2_suspend(struct pci_dev * } sky2_write32(hw, B0_IMSK, 0); + sky2_power_aux(hw); pci_save_state(pdev); - sky2_set_power_state(hw, pstate); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; } @@ -3609,9 +3574,15 @@ static int sky2_resume(struct pci_dev *p struct sky2_hw *hw = pci_get_drvdata(pdev); int i, err; - pci_restore_state(pdev); + err = pci_set_power_state(pdev, PCI_D0); + if (err) + goto out; + + err = pci_restore_state(pdev); + if (err) + goto out; + pci_enable_wake(pdev, PCI_D0, 0); - sky2_set_power_state(hw, PCI_D0); err = sky2_reset(hw); if (err) @@ -3636,7 +3607,10 @@ static int sky2_resume(struct pci_dev *p netif_poll_enable(hw->dev[0]); sky2_idle_start(hw); + return 0; out: + printk(KERN_ERR PFX "%s: resume failed (%d)\n", pci_name(pdev), err); + pci_disable_device(pdev); return err; } diff -puN drivers/net/sky2.h~git-netdev-all drivers/net/sky2.h --- a/drivers/net/sky2.h~git-netdev-all +++ a/drivers/net/sky2.h @@ -1887,7 +1887,6 @@ struct sky2_hw { struct pci_dev *pdev; struct net_device *dev[2]; - int pm_cap; u8 chip_id; u8 chip_rev; u8 pmd_type; diff -puN drivers/net/spider_net.c~git-netdev-all drivers/net/spider_net.c --- a/drivers/net/spider_net.c~git-netdev-all +++ a/drivers/net/spider_net.c @@ -280,72 +280,67 @@ spider_net_free_chain(struct spider_net_ { struct spider_net_descr *descr; - for (descr = chain->tail; !descr->bus_addr; descr = descr->next) { - pci_unmap_single(card->pdev, descr->bus_addr, - SPIDER_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL); + descr = chain->ring; + do { descr->bus_addr = 0; - } + descr->next_descr_addr = 0; + descr = descr->next; + } while (descr != chain->ring); + + dma_free_coherent(&card->pdev->dev, chain->num_desc, + chain->ring, chain->dma_addr); } /** - * spider_net_init_chain - links descriptor chain + * spider_net_init_chain - alloc and link descriptor chain * @card: card structure * @chain: address of chain - * @start_descr: address of descriptor array - * @no: number of descriptors * - * we manage a circular list that mirrors the hardware structure, + * We manage a circular list that mirrors the hardware structure, * except that the hardware uses bus addresses. * - * returns 0 on success, <0 on failure + * Returns 0 on success, <0 on failure */ static int spider_net_init_chain(struct spider_net_card *card, - struct spider_net_descr_chain *chain, - struct spider_net_descr *start_descr, - int no) + struct spider_net_descr_chain *chain) { int i; struct spider_net_descr *descr; dma_addr_t buf; + size_t alloc_size; - descr = start_descr; - memset(descr, 0, sizeof(*descr) * no); + alloc_size = chain->num_desc * sizeof (struct spider_net_descr); - /* set up the hardware pointers in each descriptor */ - for (i=0; idmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + chain->ring = dma_alloc_coherent(&card->pdev->dev, alloc_size, + &chain->dma_addr, GFP_KERNEL); - buf = pci_map_single(card->pdev, descr, - SPIDER_NET_DESCR_SIZE, - PCI_DMA_BIDIRECTIONAL); + if (!chain->ring) + return -ENOMEM; - if (pci_dma_mapping_error(buf)) - goto iommu_error; + descr = chain->ring; + memset(descr, 0, alloc_size); + + /* Set up the hardware pointers in each descriptor */ + buf = chain->dma_addr; + for (i=0; i < chain->num_desc; i++, descr++) { + descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; descr->bus_addr = buf; + descr->next_descr_addr = 0; descr->next = descr + 1; descr->prev = descr - 1; + buf += sizeof(struct spider_net_descr); } /* do actual circular list */ - (descr-1)->next = start_descr; - start_descr->prev = descr-1; + (descr-1)->next = chain->ring; + chain->ring->prev = descr-1; spin_lock_init(&chain->lock); - chain->head = start_descr; - chain->tail = start_descr; - + chain->head = chain->ring; + chain->tail = chain->ring; return 0; - -iommu_error: - descr = start_descr; - for (i=0; i < no; i++, descr++) - if (descr->bus_addr) - pci_unmap_single(card->pdev, descr->bus_addr, - SPIDER_NET_DESCR_SIZE, - PCI_DMA_BIDIRECTIONAL); - return -ENOMEM; } /** @@ -372,21 +367,20 @@ spider_net_free_rx_chain_contents(struct } /** - * spider_net_prepare_rx_descr - reinitializes a rx descriptor + * spider_net_prepare_rx_descr - Reinitialize RX descriptor * @card: card structure * @descr: descriptor to re-init * - * return 0 on succes, <0 on failure + * Return 0 on succes, <0 on failure. * - * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. - * Activate the descriptor state-wise + * Allocates a new rx skb, iommu-maps it and attaches it to the + * descriptor. Mark the descriptor as activated, ready-to-use. */ static int spider_net_prepare_rx_descr(struct spider_net_card *card, struct spider_net_descr *descr) { dma_addr_t buf; - int error = 0; int offset; int bufsize; @@ -414,7 +408,7 @@ spider_net_prepare_rx_descr(struct spide (SPIDER_NET_RXBUF_ALIGN - 1); if (offset) skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); - /* io-mmu-map the skb */ + /* iommu-map the skb */ buf = pci_map_single(card->pdev, descr->skb->data, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); descr->buf_addr = buf; @@ -425,11 +419,12 @@ spider_net_prepare_rx_descr(struct spide card->spider_stats.rx_iommu_map_error++; descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; } else { + wmb(); descr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOINTR_COMPLETE; } - return error; + return 0; } /** @@ -493,10 +488,10 @@ spider_net_refill_rx_chain(struct spider } /** - * spider_net_alloc_rx_skbs - allocates rx skbs in rx descriptor chains + * spider_net_alloc_rx_skbs - Allocates rx skbs in rx descriptor chains * @card: card structure * - * returns 0 on success, <0 on failure + * Returns 0 on success, <0 on failure. */ static int spider_net_alloc_rx_skbs(struct spider_net_card *card) @@ -507,16 +502,16 @@ spider_net_alloc_rx_skbs(struct spider_n result = -ENOMEM; chain = &card->rx_chain; - /* put at least one buffer into the chain. if this fails, - * we've got a problem. if not, spider_net_refill_rx_chain - * will do the rest at the end of this function */ + /* Put at least one buffer into the chain. if this fails, + * we've got a problem. If not, spider_net_refill_rx_chain + * will do the rest at the end of this function. */ if (spider_net_prepare_rx_descr(card, chain->head)) goto error; else chain->head = chain->head->next; - /* this will allocate the rest of the rx buffers; if not, it's - * business as usual later on */ + /* This will allocate the rest of the rx buffers; + * if not, it's business as usual later on. */ spider_net_refill_rx_chain(card); spider_net_enable_rxdmac(card); return 0; @@ -707,7 +702,7 @@ spider_net_set_low_watermark(struct spid } /* If TX queue is short, don't even bother with interrupts */ - if (cnt < card->num_tx_desc/4) + if (cnt < card->tx_chain.num_desc/4) return cnt; /* Set low-watermark 3/4th's of the way into the queue. */ @@ -915,16 +910,13 @@ spider_net_do_ioctl(struct net_device *n * spider_net_pass_skb_up - takes an skb from a descriptor and passes it on * @descr: descriptor to process * @card: card structure - * @napi: whether caller is in NAPI context - * - * returns 1 on success, 0 if no packet was passed to the stack * - * iommu-unmaps the skb, fills out skb structure and passes the data to the - * stack. The descriptor state is not changed. + * Fills out skb structure and passes the data to the stack. + * The descriptor state is not changed. */ -static int +static void spider_net_pass_skb_up(struct spider_net_descr *descr, - struct spider_net_card *card, int napi) + struct spider_net_card *card) { struct sk_buff *skb; struct net_device *netdev; @@ -932,23 +924,8 @@ spider_net_pass_skb_up(struct spider_net data_status = descr->data_status; data_error = descr->data_error; - netdev = card->netdev; - /* unmap descriptor */ - pci_unmap_single(card->pdev, descr->buf_addr, SPIDER_NET_MAX_FRAME, - PCI_DMA_FROMDEVICE); - - /* the cases we'll throw away the packet immediately */ - if (data_error & SPIDER_NET_DESTROY_RX_FLAGS) { - if (netif_msg_rx_err(card)) - pr_err("error in received descriptor found, " - "data_status=x%08x, data_error=x%08x\n", - data_status, data_error); - card->spider_stats.rx_desc_error++; - return 0; - } - skb = descr->skb; skb->dev = netdev; skb_put(skb, descr->valid_size); @@ -977,57 +954,72 @@ spider_net_pass_skb_up(struct spider_net } /* pass skb up to stack */ - if (napi) - netif_receive_skb(skb); - else - netif_rx_ni(skb); + netif_receive_skb(skb); /* update netdevice statistics */ card->netdev_stats.rx_packets++; card->netdev_stats.rx_bytes += skb->len; +} - return 1; +#ifdef DEBUG +static void show_rx_chain(struct spider_net_card *card) +{ + struct spider_net_descr_chain *chain = &card->rx_chain; + struct spider_net_descr *start= chain->tail; + struct spider_net_descr *descr= start; + int status; + + int cnt = 0; + int cstat = spider_net_get_descr_status(descr); + printk(KERN_INFO "RX chain tail at descr=%ld\n", + (start - card->descr) - card->tx_chain.num_desc); + status = cstat; + do + { + status = spider_net_get_descr_status(descr); + if (cstat != status) { + printk(KERN_INFO "Have %d descrs with stat=x%08x\n", cnt, cstat); + cstat = status; + cnt = 0; + } + cnt ++; + descr = descr->next; + } while (descr != start); + printk(KERN_INFO "Last %d descrs with stat=x%08x\n", cnt, cstat); } +#endif /** * spider_net_decode_one_descr - processes an rx descriptor * @card: card structure - * @napi: whether caller is in NAPI context * - * returns 1 if a packet has been sent to the stack, otherwise 0 + * Returns 1 if a packet has been sent to the stack, otherwise 0 * - * processes an rx descriptor by iommu-unmapping the data buffer and passing + * Processes an rx descriptor by iommu-unmapping the data buffer and passing * the packet up to the stack. This function is called in softirq * context, e.g. either bottom half from interrupt or NAPI polling context */ static int -spider_net_decode_one_descr(struct spider_net_card *card, int napi) +spider_net_decode_one_descr(struct spider_net_card *card) { struct spider_net_descr_chain *chain = &card->rx_chain; struct spider_net_descr *descr = chain->tail; int status; - int result; status = spider_net_get_descr_status(descr); - if (status == SPIDER_NET_DESCR_CARDOWNED) { - /* nothing in the descriptor yet */ - result=0; - goto out; - } - - if (status == SPIDER_NET_DESCR_NOT_IN_USE) { - /* not initialized yet, the ring must be empty */ - spider_net_refill_rx_chain(card); - spider_net_enable_rxdmac(card); - result=0; - goto out; - } + /* Nothing in the descriptor, or ring must be empty */ + if ((status == SPIDER_NET_DESCR_CARDOWNED) || + (status == SPIDER_NET_DESCR_NOT_IN_USE)) + return 0; /* descriptor definitively used -- move on tail */ chain->tail = descr->next; - result = 0; + /* unmap descriptor */ + pci_unmap_single(card->pdev, descr->buf_addr, + SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); + if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) || (status == SPIDER_NET_DESCR_PROTECTION_ERROR) || (status == SPIDER_NET_DESCR_FORCE_END) ) { @@ -1035,31 +1027,55 @@ spider_net_decode_one_descr(struct spide pr_err("%s: dropping RX descriptor with state %d\n", card->netdev->name, status); card->netdev_stats.rx_dropped++; - pci_unmap_single(card->pdev, descr->buf_addr, - SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - dev_kfree_skb_irq(descr->skb); - goto refill; + goto bad_desc; } if ( (status != SPIDER_NET_DESCR_COMPLETE) && (status != SPIDER_NET_DESCR_FRAME_END) ) { - if (netif_msg_rx_err(card)) { - pr_err("%s: RX descriptor with state %d\n", + if (netif_msg_rx_err(card)) + pr_err("%s: RX descriptor with unkown state %d\n", card->netdev->name, status); - card->spider_stats.rx_desc_unk_state++; - } - goto refill; + card->spider_stats.rx_desc_unk_state++; + goto bad_desc; + } + + /* The cases we'll throw away the packet immediately */ + if (descr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { + if (netif_msg_rx_err(card)) + pr_err("%s: error in received descriptor found, " + "data_status=x%08x, data_error=x%08x\n", + card->netdev->name, + descr->data_status, descr->data_error); + goto bad_desc; + } + + if (descr->dmac_cmd_status & 0xfefe) { + pr_err("%s: bad status, cmd_status=x%08x\n", + card->netdev->name, + descr->dmac_cmd_status); + pr_err("buf_addr=x%08x\n", descr->buf_addr); + pr_err("buf_size=x%08x\n", descr->buf_size); + pr_err("next_descr_addr=x%08x\n", descr->next_descr_addr); + pr_err("result_size=x%08x\n", descr->result_size); + pr_err("valid_size=x%08x\n", descr->valid_size); + pr_err("data_status=x%08x\n", descr->data_status); + pr_err("data_error=x%08x\n", descr->data_error); + pr_err("bus_addr=x%08x\n", descr->bus_addr); + pr_err("which=%ld\n", descr - card->rx_chain.ring); + + card->spider_stats.rx_desc_error++; + goto bad_desc; } - /* ok, we've got a packet in descr */ - result = spider_net_pass_skb_up(descr, card, napi); -refill: + /* Ok, we've got a packet in descr */ + spider_net_pass_skb_up(descr, card); descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - /* change the descriptor state: */ - if (!napi) - spider_net_refill_rx_chain(card); -out: - return result; + return 1; + +bad_desc: + dev_kfree_skb_irq(descr->skb); + descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + return 0; } /** @@ -1085,7 +1101,7 @@ spider_net_poll(struct net_device *netde packets_to_do = min(*budget, netdev->quota); while (packets_to_do) { - if (spider_net_decode_one_descr(card, 1)) { + if (spider_net_decode_one_descr(card)) { packets_done++; packets_to_do--; } else { @@ -1098,6 +1114,7 @@ spider_net_poll(struct net_device *netde netdev->quota -= packets_done; *budget -= packets_done; spider_net_refill_rx_chain(card); + spider_net_enable_rxdmac(card); /* if all packets are in the stack, enable interrupts and return 0 */ /* if not, return 1 */ @@ -1227,24 +1244,6 @@ spider_net_set_mac(struct net_device *ne } /** - * spider_net_handle_rxram_full - cleans up RX ring upon RX RAM full interrupt - * @card: card structure - * - * spider_net_handle_rxram_full empties the RX ring so that spider can put - * more packets in it and empty its RX RAM. This is called in bottom half - * context - */ -static void -spider_net_handle_rxram_full(struct spider_net_card *card) -{ - while (spider_net_decode_one_descr(card, 0)) - ; - spider_net_enable_rxchtails(card); - spider_net_enable_rxdmac(card); - netif_rx_schedule(card->netdev); -} - -/** * spider_net_handle_error_irq - handles errors raised by an interrupt * @card: card structure * @status_reg: interrupt status register 0 (GHIINT0STS) @@ -1366,10 +1365,10 @@ spider_net_handle_error_irq(struct spide case SPIDER_NET_GRFAFLLINT: /* fallthrough */ case SPIDER_NET_GRMFLLINT: if (netif_msg_intr(card) && net_ratelimit()) - pr_debug("Spider RX RAM full, incoming packets " + pr_err("Spider RX RAM full, incoming packets " "might be discarded!\n"); spider_net_rx_irq_off(card); - tasklet_schedule(&card->rxram_full_tl); + netif_rx_schedule(card->netdev); show_error = 0; break; @@ -1384,7 +1383,7 @@ spider_net_handle_error_irq(struct spide case SPIDER_NET_GDCDCEINT: /* fallthrough */ case SPIDER_NET_GDBDCEINT: /* fallthrough */ case SPIDER_NET_GDADCEINT: - if (netif_msg_intr(card)) + if (netif_msg_intr(card) && net_ratelimit()) pr_err("got descriptor chain end interrupt, " "restarting DMAC %c.\n", 'D'-(i-SPIDER_NET_GDDDCEINT)/3); @@ -1455,7 +1454,7 @@ spider_net_handle_error_irq(struct spide break; } - if ((show_error) && (netif_msg_intr(card))) + if ((show_error) && (netif_msg_intr(card)) && net_ratelimit()) pr_err("Got error interrupt on %s, GHIINT0STS = 0x%08x, " "GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n", card->netdev->name, @@ -1652,26 +1651,25 @@ spider_net_open(struct net_device *netde { struct spider_net_card *card = netdev_priv(netdev); struct spider_net_descr *descr; - int i, result; + int result; - result = -ENOMEM; - if (spider_net_init_chain(card, &card->tx_chain, card->descr, - card->num_tx_desc)) + result = spider_net_init_chain(card, &card->tx_chain); + if (result) goto alloc_tx_failed; - card->low_watermark = NULL; - /* rx_chain is after tx_chain, so offset is descr + tx_count */ - if (spider_net_init_chain(card, &card->rx_chain, - card->descr + card->num_tx_desc, - card->num_rx_desc)) + result = spider_net_init_chain(card, &card->rx_chain); + if (result) goto alloc_rx_failed; - descr = card->rx_chain.head; - for (i=0; i < card->num_rx_desc; i++, descr++) + /* Make a ring of of bus addresses */ + descr = card->rx_chain.ring; + do { descr->next_descr_addr = descr->next->bus_addr; + descr = descr->next; + } while (descr != card->rx_chain.ring); - /* allocate rx skbs */ + /* Allocate rx skbs */ if (spider_net_alloc_rx_skbs(card)) goto alloc_skbs_failed; @@ -1902,7 +1900,6 @@ spider_net_stop(struct net_device *netde { struct spider_net_card *card = netdev_priv(netdev); - tasklet_kill(&card->rxram_full_tl); netif_poll_disable(netdev); netif_carrier_off(netdev); netif_stop_queue(netdev); @@ -1924,6 +1921,7 @@ spider_net_stop(struct net_device *netde /* release chains */ spider_net_release_tx_chain(card, 1); + spider_net_free_rx_chain_contents(card); spider_net_free_chain(card, &card->tx_chain); spider_net_free_chain(card, &card->rx_chain); @@ -2044,9 +2042,6 @@ spider_net_setup_netdev(struct spider_ne pci_set_drvdata(card->pdev, netdev); - card->rxram_full_tl.data = (unsigned long) card; - card->rxram_full_tl.func = - (void (*)(unsigned long)) spider_net_handle_rxram_full; init_timer(&card->tx_timer); card->tx_timer.function = (void (*)(unsigned long)) spider_net_cleanup_tx_ring; @@ -2055,8 +2050,8 @@ spider_net_setup_netdev(struct spider_ne card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; - card->num_tx_desc = tx_descriptors; - card->num_rx_desc = rx_descriptors; + card->tx_chain.num_desc = tx_descriptors; + card->rx_chain.num_desc = rx_descriptors; spider_net_setup_netdev_ops(netdev); @@ -2105,12 +2100,8 @@ spider_net_alloc_card(void) { struct net_device *netdev; struct spider_net_card *card; - size_t alloc_size; - alloc_size = sizeof (*card) + - sizeof (struct spider_net_descr) * rx_descriptors + - sizeof (struct spider_net_descr) * tx_descriptors; - netdev = alloc_etherdev(alloc_size); + netdev = alloc_etherdev(sizeof(struct spider_net_card)); if (!netdev) return NULL; diff -puN drivers/net/spider_net.h~git-netdev-all drivers/net/spider_net.h --- a/drivers/net/spider_net.h~git-netdev-all +++ a/drivers/net/spider_net.h @@ -24,7 +24,7 @@ #ifndef _SPIDER_NET_H #define _SPIDER_NET_H -#define VERSION "1.6 A" +#define VERSION "1.6 B" #include "sungem_phy.h" @@ -378,6 +378,9 @@ struct spider_net_descr_chain { spinlock_t lock; struct spider_net_descr *head; struct spider_net_descr *tail; + struct spider_net_descr *ring; + int num_desc; + dma_addr_t dma_addr; }; /* descriptor data_status bits */ @@ -397,8 +400,6 @@ struct spider_net_descr_chain { * 701b8000 would be correct, but every packets gets that flag */ #define SPIDER_NET_DESTROY_RX_FLAGS 0x700b8000 -#define SPIDER_NET_DESCR_SIZE 32 - /* this will be bigger some time */ struct spider_net_options { int rx_csum; /* for rx: if 0 ip_summed=NONE, @@ -441,25 +442,16 @@ struct spider_net_card { struct spider_net_descr_chain rx_chain; struct spider_net_descr *low_watermark; - struct net_device_stats netdev_stats; - - struct spider_net_options options; - - spinlock_t intmask_lock; - struct tasklet_struct rxram_full_tl; struct timer_list tx_timer; - struct work_struct tx_timeout_task; atomic_t tx_timeout_task_counter; wait_queue_head_t waitq; /* for ethtool */ int msg_enable; - int num_rx_desc; - int num_tx_desc; + struct net_device_stats netdev_stats; struct spider_net_extra_stats spider_stats; - - struct spider_net_descr descr[0]; + struct spider_net_options options; }; #define pr_err(fmt,arg...) \ diff -puN drivers/net/spider_net_ethtool.c~git-netdev-all drivers/net/spider_net_ethtool.c --- a/drivers/net/spider_net_ethtool.c~git-netdev-all +++ a/drivers/net/spider_net_ethtool.c @@ -158,9 +158,9 @@ spider_net_ethtool_get_ringparam(struct struct spider_net_card *card = netdev->priv; ering->tx_max_pending = SPIDER_NET_TX_DESCRIPTORS_MAX; - ering->tx_pending = card->num_tx_desc; + ering->tx_pending = card->tx_chain.num_desc; ering->rx_max_pending = SPIDER_NET_RX_DESCRIPTORS_MAX; - ering->rx_pending = card->num_rx_desc; + ering->rx_pending = card->rx_chain.num_desc; } static int spider_net_get_stats_count(struct net_device *netdev) diff -puN drivers/net/tg3.c~git-netdev-all drivers/net/tg3.c --- a/drivers/net/tg3.c~git-netdev-all +++ a/drivers/net/tg3.c @@ -58,11 +58,7 @@ #define TG3_VLAN_TAG_USED 0 #endif -#ifdef NETIF_F_TSO #define TG3_TSO_SUPPORT 1 -#else -#define TG3_TSO_SUPPORT 0 -#endif #include "tg3.h" @@ -3873,7 +3869,6 @@ static int tg3_start_xmit(struct sk_buff entry = tp->tx_prod; base_flags = 0; -#if TG3_TSO_SUPPORT != 0 mss = 0; if (skb->len > (tp->dev->mtu + ETH_HLEN) && (mss = skb_shinfo(skb)->gso_size) != 0) { @@ -3906,11 +3901,6 @@ static int tg3_start_xmit(struct sk_buff } else if (skb->ip_summed == CHECKSUM_PARTIAL) base_flags |= TXD_FLAG_TCPUDP_CSUM; -#else - mss = 0; - if (skb->ip_summed == CHECKSUM_PARTIAL) - base_flags |= TXD_FLAG_TCPUDP_CSUM; -#endif #if TG3_VLAN_TAG_USED if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) base_flags |= (TXD_FLAG_VLAN | @@ -3970,7 +3960,6 @@ out_unlock: return NETDEV_TX_OK; } -#if TG3_TSO_SUPPORT != 0 static int tg3_start_xmit_dma_bug(struct sk_buff *, struct net_device *); /* Use GSO to workaround a rare TSO bug that may be triggered when the @@ -4002,7 +3991,6 @@ tg3_tso_bug_end: return NETDEV_TX_OK; } -#endif /* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and * support TG3_FLG2_HW_TSO_1 or firmware TSO only. @@ -4036,7 +4024,6 @@ static int tg3_start_xmit_dma_bug(struct base_flags = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) base_flags |= TXD_FLAG_TCPUDP_CSUM; -#if TG3_TSO_SUPPORT != 0 mss = 0; if (skb->len > (tp->dev->mtu + ETH_HLEN) && (mss = skb_shinfo(skb)->gso_size) != 0) { @@ -4091,9 +4078,6 @@ static int tg3_start_xmit_dma_bug(struct } } } -#else - mss = 0; -#endif #if TG3_VLAN_TAG_USED if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) base_flags |= (TXD_FLAG_VLAN | @@ -5329,7 +5313,6 @@ static int tg3_load_5701_a0_firmware_fix return 0; } -#if TG3_TSO_SUPPORT != 0 #define TG3_TSO_FW_RELEASE_MAJOR 0x1 #define TG3_TSO_FW_RELASE_MINOR 0x6 @@ -5906,7 +5889,6 @@ static int tg3_load_tso_firmware(struct return 0; } -#endif /* TG3_TSO_SUPPORT != 0 */ /* tp->lock is held. */ static void __tg3_set_mac_addr(struct tg3 *tp) @@ -6120,7 +6102,6 @@ static int tg3_reset_hw(struct tg3 *tp, tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE); tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE); } -#if TG3_TSO_SUPPORT != 0 else if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { int fw_len; @@ -6135,7 +6116,6 @@ static int tg3_reset_hw(struct tg3 *tp, tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE5705 - fw_len - 0xa00); } -#endif if (tp->dev->mtu <= ETH_DATA_LEN) { tw32(BUFMGR_MB_RDMA_LOW_WATER, @@ -6337,10 +6317,8 @@ static int tg3_reset_hw(struct tg3 *tp, if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST; -#if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) rdmac_mode |= (1 << 27); -#endif /* Receive/send statistics. */ if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) { @@ -6511,10 +6489,8 @@ static int tg3_reset_hw(struct tg3 *tp, tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB); tw32(RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ); tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); -#if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE | 0x8); -#endif tw32(SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE); tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); @@ -6524,13 +6500,11 @@ static int tg3_reset_hw(struct tg3 *tp, return err; } -#if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { err = tg3_load_tso_firmware(tp); if (err) return err; } -#endif tp->tx_mode = TX_MODE_ENABLE; tw32_f(MAC_TX_MODE, tp->tx_mode); @@ -8062,7 +8036,6 @@ static void tg3_set_msglevel(struct net_ tp->msg_enable = value; } -#if TG3_TSO_SUPPORT != 0 static int tg3_set_tso(struct net_device *dev, u32 value) { struct tg3 *tp = netdev_priv(dev); @@ -8081,7 +8054,6 @@ static int tg3_set_tso(struct net_device } return ethtool_op_set_tso(dev, value); } -#endif static int tg3_nway_reset(struct net_device *dev) { @@ -9212,10 +9184,8 @@ static const struct ethtool_ops tg3_etht .set_tx_csum = tg3_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, -#if TG3_TSO_SUPPORT != 0 .get_tso = ethtool_op_get_tso, .set_tso = tg3_set_tso, -#endif .self_test_count = tg3_get_test_count, .self_test = tg3_self_test, .get_strings = tg3_get_strings, @@ -11856,7 +11826,6 @@ static int __devinit tg3_init_one(struct tg3_init_bufmgr_config(tp); -#if TG3_TSO_SUPPORT != 0 if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; } @@ -11881,7 +11850,6 @@ static int __devinit tg3_init_one(struct dev->features |= NETIF_F_TSO6; } -#endif if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 && !(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) && diff -puN drivers/net/wan/hdlc.c~git-netdev-all drivers/net/wan/hdlc.c --- a/drivers/net/wan/hdlc.c~git-netdev-all +++ a/drivers/net/wan/hdlc.c @@ -222,7 +222,7 @@ int hdlc_ioctl(struct net_device *dev, s return -EINVAL; } -void hdlc_setup(struct net_device *dev) +static void hdlc_setup(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); @@ -325,7 +325,6 @@ MODULE_LICENSE("GPL v2"); EXPORT_SYMBOL(hdlc_open); EXPORT_SYMBOL(hdlc_close); EXPORT_SYMBOL(hdlc_ioctl); -EXPORT_SYMBOL(hdlc_setup); EXPORT_SYMBOL(alloc_hdlcdev); EXPORT_SYMBOL(unregister_hdlc_device); EXPORT_SYMBOL(register_hdlc_protocol); diff -puN drivers/net/wireless/bcm43xx/bcm43xx.h~git-netdev-all drivers/net/wireless/bcm43xx/bcm43xx.h --- a/drivers/net/wireless/bcm43xx/bcm43xx.h~git-netdev-all +++ a/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -352,6 +352,10 @@ #define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040 #define BCM43xx_UCODEFLAG_JAPAN 0x0080 +/* Hardware Radio Enable masks */ +#define BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) +#define BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) + /* Generic-Interrupt reasons. */ #define BCM43xx_IRQ_READY (1 << 0) #define BCM43xx_IRQ_BEACON (1 << 1) @@ -758,7 +762,8 @@ struct bcm43xx_private { bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */ reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */ short_preamble:1, /* TRUE, if short preamble is enabled. */ - firmware_norelease:1; /* Do not release the firmware. Used on suspend. */ + firmware_norelease:1, /* Do not release the firmware. Used on suspend. */ + radio_hw_enable:1; /* TRUE if radio is hardware enabled */ struct bcm43xx_stats stats; diff -puN drivers/net/wireless/bcm43xx/bcm43xx_leds.c~git-netdev-all drivers/net/wireless/bcm43xx/bcm43xx_leds.c --- a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c~git-netdev-all +++ a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c @@ -26,6 +26,7 @@ */ #include "bcm43xx_leds.h" +#include "bcm43xx_radio.h" #include "bcm43xx.h" #include @@ -108,6 +109,7 @@ static void bcm43xx_led_init_hardcoded(s switch (led_index) { case 0: led->behaviour = BCM43xx_LED_ACTIVITY; + led->activelow = 1; if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ) led->behaviour = BCM43xx_LED_RADIO_ALL; break; @@ -199,20 +201,21 @@ void bcm43xx_leds_update(struct bcm43xx_ turn_on = activity; break; case BCM43xx_LED_RADIO_ALL: - turn_on = radio->enabled; + turn_on = radio->enabled && bcm43xx_is_hw_radio_enabled(bcm); break; case BCM43xx_LED_RADIO_A: case BCM43xx_LED_BCM4303_2: - turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A); + turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) && + phy->type == BCM43xx_PHYTYPE_A); break; case BCM43xx_LED_RADIO_B: case BCM43xx_LED_BCM4303_1: - turn_on = (radio->enabled && + turn_on = (radio->enabled && bcm43xx_is_hw_radio_enabled(bcm) && (phy->type == BCM43xx_PHYTYPE_B || phy->type == BCM43xx_PHYTYPE_G)); break; case BCM43xx_LED_MODE_BG: - if (phy->type == BCM43xx_PHYTYPE_G && + if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(bcm) && 1/*FIXME: using G rates.*/) turn_on = 1; break; diff -puN drivers/net/wireless/bcm43xx/bcm43xx_main.c~git-netdev-all drivers/net/wireless/bcm43xx/bcm43xx_main.c --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c~git-netdev-all +++ a/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -2441,6 +2441,9 @@ static int bcm43xx_chip_init(struct bcm4 if (err) goto err_gpio_cleanup; bcm43xx_radio_turn_on(bcm); + bcm->radio_hw_enable = bcm43xx_is_hw_radio_enabled(bcm); + dprintk(KERN_INFO PFX "Radio %s by hardware\n", + (bcm->radio_hw_enable == 0) ? "disabled" : "enabled"); bcm43xx_write16(bcm, 0x03E6, 0x0000); err = bcm43xx_phy_init(bcm); @@ -3175,9 +3178,24 @@ static void bcm43xx_periodic_every30sec( static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm) { + bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning? + //TODO for APHY (temperature?) +} + +static void bcm43xx_periodic_every1sec(struct bcm43xx_private *bcm) +{ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + int radio_hw_enable; + /* check if radio hardware enabled status changed */ + radio_hw_enable = bcm43xx_is_hw_radio_enabled(bcm); + if (unlikely(bcm->radio_hw_enable != radio_hw_enable)) { + bcm->radio_hw_enable = radio_hw_enable; + dprintk(KERN_INFO PFX "Radio hardware status changed to %s\n", + (radio_hw_enable == 0) ? "disabled" : "enabled"); + bcm43xx_leds_update(bcm, 0); + } if (phy->type == BCM43xx_PHYTYPE_G) { //TODO: update_aci_moving_average if (radio->aci_enable && radio->aci_wlan_automatic) { @@ -3201,21 +3219,21 @@ static void bcm43xx_periodic_every15sec( //TODO: implement rev1 workaround } } - bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning? - //TODO for APHY (temperature?) } static void do_periodic_work(struct bcm43xx_private *bcm) { - if (bcm->periodic_state % 8 == 0) + if (bcm->periodic_state % 120 == 0) bcm43xx_periodic_every120sec(bcm); - if (bcm->periodic_state % 4 == 0) + if (bcm->periodic_state % 60 == 0) bcm43xx_periodic_every60sec(bcm); - if (bcm->periodic_state % 2 == 0) + if (bcm->periodic_state % 30 == 0) bcm43xx_periodic_every30sec(bcm); - bcm43xx_periodic_every15sec(bcm); + if (bcm->periodic_state % 15 == 0) + bcm43xx_periodic_every15sec(bcm); + bcm43xx_periodic_every1sec(bcm); - schedule_delayed_work(&bcm->periodic_work, HZ * 15); + schedule_delayed_work(&bcm->periodic_work, HZ); } static void bcm43xx_periodic_work_handler(struct work_struct *work) @@ -3228,7 +3246,7 @@ static void bcm43xx_periodic_work_handle unsigned long orig_trans_start = 0; mutex_lock(&bcm->mutex); - if (unlikely(bcm->periodic_state % 4 == 0)) { + if (unlikely(bcm->periodic_state % 60 == 0)) { /* Periodic work will take a long time, so we want it to * be preemtible. */ @@ -3260,7 +3278,7 @@ static void bcm43xx_periodic_work_handle do_periodic_work(bcm); - if (unlikely(bcm->periodic_state % 4 == 0)) { + if (unlikely(bcm->periodic_state % 60 == 0)) { spin_lock_irqsave(&bcm->irq_lock, flags); tasklet_enable(&bcm->isr_tasklet); bcm43xx_interrupt_enable(bcm, savedirqs); diff -puN drivers/net/wireless/bcm43xx/bcm43xx_radio.c~git-netdev-all drivers/net/wireless/bcm43xx/bcm43xx_radio.c --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c~git-netdev-all +++ a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -1981,6 +1981,7 @@ void bcm43xx_radio_turn_on(struct bcm43x } radio->enabled = 1; dprintk(KERN_INFO PFX "Radio turned on\n"); + bcm43xx_leds_update(bcm, 0); } void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm) @@ -2001,6 +2002,7 @@ void bcm43xx_radio_turn_off(struct bcm43 bcm43xx_phy_write(bcm, 0x0015, 0xAA00); radio->enabled = 0; dprintk(KERN_INFO PFX "Radio turned off\n"); + bcm43xx_leds_update(bcm, 0); } void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm) diff -puN drivers/net/wireless/bcm43xx/bcm43xx_radio.h~git-netdev-all drivers/net/wireless/bcm43xx/bcm43xx_radio.h --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.h~git-netdev-all +++ a/drivers/net/wireless/bcm43xx/bcm43xx_radio.h @@ -65,6 +65,22 @@ void bcm43xx_radio_init2060(struct bcm43 void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm); void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm); +static inline +int bcm43xx_is_hw_radio_enabled(struct bcm43xx_private *bcm) +{ + /* function to return state of hardware enable of radio + * returns 0 if radio disabled, 1 if radio enabled + */ + if (bcm->current_core->rev >= 3) + return ((bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) + & BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK) + == 0) ? 1 : 0; + else + return ((bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) + & BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK) + == 0) ? 0 : 1; +} + int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel, int synthetic_pu_workaround); diff -puN drivers/net/wireless/ipw2200.c~git-netdev-all drivers/net/wireless/ipw2200.c --- a/drivers/net/wireless/ipw2200.c~git-netdev-all +++ a/drivers/net/wireless/ipw2200.c @@ -9166,7 +9166,7 @@ static int ipw_wx_set_rts(struct net_dev { struct ipw_priv *priv = ieee80211_priv(dev); mutex_lock(&priv->mutex); - if (wrqu->rts.disabled) + if (wrqu->rts.disabled || !wrqu->rts.fixed) priv->rts_threshold = DEFAULT_RTS_THRESHOLD; else { if (wrqu->rts.value < MIN_RTS_THRESHOLD || @@ -9255,7 +9255,7 @@ static int ipw_wx_set_frag(struct net_de { struct ipw_priv *priv = ieee80211_priv(dev); mutex_lock(&priv->mutex); - if (wrqu->frag.disabled) + if (wrqu->frag.disabled || !wrqu->frag.fixed) priv->ieee->fts = DEFAULT_FTS; else { if (wrqu->frag.value < MIN_FRAG_THRESHOLD || diff -puN drivers/net/wireless/prism54/islpci_dev.c~git-netdev-all drivers/net/wireless/prism54/islpci_dev.c --- a/drivers/net/wireless/prism54/islpci_dev.c~git-netdev-all +++ a/drivers/net/wireless/prism54/islpci_dev.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -787,6 +788,17 @@ islpci_set_multicast_list(struct net_dev } #endif +static void islpci_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); +} + +static struct ethtool_ops islpci_ethtool_ops = { + .get_drvinfo = islpci_ethtool_get_drvinfo, +}; + struct net_device * islpci_setup(struct pci_dev *pdev) { @@ -813,6 +825,7 @@ islpci_setup(struct pci_dev *pdev) ndev->do_ioctl = &prism54_ioctl; ndev->wireless_handlers = (struct iw_handler_def *) &prism54_handler_def; + ndev->ethtool_ops = &islpci_ethtool_ops; ndev->hard_start_xmit = &islpci_eth_transmit; /* ndev->set_multicast_list = &islpci_set_multicast_list; */ diff -puN drivers/net/wireless/prism54/islpci_dev.h~git-netdev-all drivers/net/wireless/prism54/islpci_dev.h --- a/drivers/net/wireless/prism54/islpci_dev.h~git-netdev-all +++ a/drivers/net/wireless/prism54/islpci_dev.h @@ -211,4 +211,8 @@ islpci_trigger(islpci_private *priv) int islpci_free_memory(islpci_private *); struct net_device *islpci_setup(struct pci_dev *); + +#define DRV_NAME "prism54" +#define DRV_VERSION "1.2" + #endif /* _ISLPCI_DEV_H */ diff -puN drivers/net/wireless/prism54/islpci_hotplug.c~git-netdev-all drivers/net/wireless/prism54/islpci_hotplug.c --- a/drivers/net/wireless/prism54/islpci_hotplug.c~git-netdev-all +++ a/drivers/net/wireless/prism54/islpci_hotplug.c @@ -28,9 +28,6 @@ #include "islpci_mgt.h" /* for pc_debug */ #include "isl_oid.h" -#define DRV_NAME "prism54" -#define DRV_VERSION "1.2" - MODULE_AUTHOR("[Intersil] R.Bastings and W.Termorshuizen, The prism54.org Development Team "); MODULE_DESCRIPTION("The Prism54 802.11 Wireless LAN adapter"); MODULE_LICENSE("GPL"); diff -puN drivers/net/wireless/zd1211rw/zd_chip.c~git-netdev-all drivers/net/wireless/zd1211rw/zd_chip.c --- a/drivers/net/wireless/zd1211rw/zd_chip.c~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_chip.c @@ -84,6 +84,18 @@ static void print_id(struct zd_chip *chi dev_info(zd_chip_dev(chip), "%s\n", buffer); } +static zd_addr_t inc_addr(zd_addr_t addr) +{ + u16 a = (u16)addr; + /* Control registers use byte addressing, but everything else uses word + * addressing. */ + if ((a & 0xf000) == CR_START) + a += 2; + else + a += 1; + return (zd_addr_t)a; +} + /* Read a variable number of 32-bit values. Parameter count is not allowed to * exceed USB_MAX_IOREAD32_COUNT. */ @@ -114,7 +126,7 @@ int zd_ioread32v_locked(struct zd_chip * for (i = 0; i < count; i++) { int j = 2*i; /* We read the high word always first. */ - a16[j] = zd_inc_word(addr[i]); + a16[j] = inc_addr(addr[i]); a16[j+1] = addr[i]; } @@ -163,7 +175,7 @@ int _zd_iowrite32v_locked(struct zd_chip j = 2*i; /* We write the high word always first. */ ioreqs16[j].value = ioreqs[i].value >> 16; - ioreqs16[j].addr = zd_inc_word(ioreqs[i].addr); + ioreqs16[j].addr = inc_addr(ioreqs[i].addr); ioreqs16[j+1].value = ioreqs[i].value; ioreqs16[j+1].addr = ioreqs[i].addr; } @@ -466,7 +478,8 @@ static int read_values(struct zd_chip *c ZD_ASSERT(mutex_is_locked(&chip->mutex)); for (i = 0;;) { - r = zd_ioread32_locked(chip, &v, e2p_addr+i/2); + r = zd_ioread32_locked(chip, &v, + (zd_addr_t)((u16)e2p_addr+i/2)); if (r) return r; v -= guard; @@ -798,47 +811,18 @@ static int hw_reset_phy(struct zd_chip * static int zd1211_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { - { CR_ACK_TIMEOUT_EXT, 0x20 }, - { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, { CR_ZD1211_RETRY_MAX, 0x2 }, - { CR_SNIFFER_ON, 0 }, - { CR_RX_FILTER, STA_RX_FILTER }, - { CR_GROUP_HASH_P1, 0x00 }, - { CR_GROUP_HASH_P2, 0x80000000 }, - { CR_REG1, 0xa4 }, - { CR_ADDA_PWR_DWN, 0x7f }, - { CR_BCN_PLCP_CFG, 0x00f00401 }, - { CR_PHY_DELAY, 0x00 }, - { CR_ACK_TIMEOUT_EXT, 0x80 }, - { CR_ADDA_PWR_DWN, 0x00 }, - { CR_ACK_TIME_80211, 0x100 }, - { CR_RX_PE_DELAY, 0x70 }, - { CR_PS_CTRL, 0x10000000 }, - { CR_RTS_CTS_RATE, 0x02030203 }, { CR_RX_THRESHOLD, 0x000c0640 }, - { CR_AFTER_PNP, 0x1 }, - { CR_WEP_PROTECT, 0x114 }, }; - int r; - dev_dbg_f(zd_chip_dev(chip), "\n"); ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -#ifdef DEBUG - if (r) { - dev_err(zd_chip_dev(chip), - "error in zd_iowrite32a_locked. Error number %d\n", r); - } -#endif /* DEBUG */ - return r; + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } static int zd1211b_hw_init_hmac(struct zd_chip *chip) { static const struct zd_ioreq32 ioreqs[] = { - { CR_ACK_TIMEOUT_EXT, 0x20 }, - { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, { CR_ZD1211B_RETRY_MAX, 0x02020202 }, { CR_ZD1211B_TX_PWR_CTL4, 0x007f003f }, { CR_ZD1211B_TX_PWR_CTL3, 0x007f003f }, @@ -847,6 +831,20 @@ static int zd1211b_hw_init_hmac(struct z { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, { CR_ZD1211B_TXOP, 0x01800824 }, + { CR_RX_THRESHOLD, 0x000c0eff, }, + }; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int hw_init_hmac(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, { CR_SNIFFER_ON, 0 }, { CR_RX_FILTER, STA_RX_FILTER }, { CR_GROUP_HASH_P1, 0x00 }, @@ -861,25 +859,16 @@ static int zd1211b_hw_init_hmac(struct z { CR_RX_PE_DELAY, 0x70 }, { CR_PS_CTRL, 0x10000000 }, { CR_RTS_CTS_RATE, 0x02030203 }, - { CR_RX_THRESHOLD, 0x000c0eff, }, { CR_AFTER_PNP, 0x1 }, { CR_WEP_PROTECT, 0x114 }, + { CR_IFS_VALUE, IFS_VALUE_DEFAULT }, }; - int r; - - dev_dbg_f(zd_chip_dev(chip), "\n"); ZD_ASSERT(mutex_is_locked(&chip->mutex)); r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) { - dev_dbg_f(zd_chip_dev(chip), - "error in zd_iowrite32a_locked. Error number %d\n", r); - } - return r; -} + if (r) + return r; -static int hw_init_hmac(struct zd_chip *chip) -{ return chip->is_zd1211b ? zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); } @@ -974,16 +963,14 @@ static int hw_init(struct zd_chip *chip) if (r) return r; - /* Although the vendor driver defaults to a different value during - * init, it overwrites the IFS value with the following every time - * the channel changes. We should aim to be more intelligent... */ - r = zd_iowrite32_locked(chip, IFS_VALUE_DEFAULT, CR_IFS_VALUE); - if (r) - return r; - return set_beacon_interval(chip, 100); } +static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset) +{ + return (zd_addr_t)((u16)chip->fw_regs_base + offset); +} + #ifdef DEBUG static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, const char *addr_string) @@ -1018,9 +1005,11 @@ static int test_init(struct zd_chip *chi static void dump_fw_registers(struct zd_chip *chip) { - static const zd_addr_t addr[4] = { - FW_FIRMWARE_VER, FW_USB_SPEED, FW_FIX_TX_RATE, - FW_LINK_STATUS + const zd_addr_t addr[4] = { + fw_reg_addr(chip, FW_REG_FIRMWARE_VER), + fw_reg_addr(chip, FW_REG_USB_SPEED), + fw_reg_addr(chip, FW_REG_FIX_TX_RATE), + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), }; int r; @@ -1046,7 +1035,8 @@ static int print_fw_version(struct zd_ch int r; u16 version; - r = zd_ioread16_locked(chip, &version, FW_FIRMWARE_VER); + r = zd_ioread16_locked(chip, &version, + fw_reg_addr(chip, FW_REG_FIRMWARE_VER)); if (r) return r; @@ -1126,6 +1116,22 @@ int zd_chip_disable_hwint(struct zd_chip return r; } +static int read_fw_regs_offset(struct zd_chip *chip) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base, + FWRAW_REGS_ADDR); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n", + (u16)chip->fw_regs_base); + + return 0; +} + + int zd_chip_init_hw(struct zd_chip *chip, u8 device_type) { int r; @@ -1145,7 +1151,7 @@ int zd_chip_init_hw(struct zd_chip *chip if (r) goto out; - r = zd_usb_init_hw(&chip->usb); + r = read_fw_regs_offset(chip); if (r) goto out; @@ -1325,15 +1331,15 @@ u8 zd_chip_get_channel(struct zd_chip *c int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) { - static const zd_addr_t a[] = { - FW_LINK_STATUS, + const zd_addr_t a[] = { + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), CR_LED, }; int r; u16 v[ARRAY_SIZE(a)]; struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = { - [0] = { FW_LINK_STATUS }, + [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) }, [1] = { CR_LED }, }; u16 other_led; diff -puN drivers/net/wireless/zd1211rw/zd_chip.h~git-netdev-all drivers/net/wireless/zd1211rw/zd_chip.h --- a/drivers/net/wireless/zd1211rw/zd_chip.h~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_chip.h @@ -18,7 +18,6 @@ #ifndef _ZD_CHIP_H #define _ZD_CHIP_H -#include "zd_types.h" #include "zd_rf.h" #include "zd_usb.h" @@ -27,6 +26,37 @@ * adds a processor for handling the USB protocol. */ +/* Address space */ +enum { + /* CONTROL REGISTERS */ + CR_START = 0x9000, + + + /* FIRMWARE */ + FW_START = 0xee00, + + + /* EEPROM */ + E2P_START = 0xf800, + E2P_LEN = 0x800, + + /* EEPROM layout */ + E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */ + E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */ + /* E2P_DATA indexes into this */ + E2P_DATA_LEN = 0x7e, /* base 0xf817 */ + E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */ + E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */ + + /* Some precomputed offsets into the EEPROM */ + E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN, + E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN, +}; + +#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset))) +#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset))) +#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset))) + /* 8-bit hardware registers */ #define CR0 CTL_REG(0x0000) #define CR1 CTL_REG(0x0004) @@ -302,7 +332,7 @@ #define CR_MAX_PHY_REG 255 -/* Taken from the ZYDAS driver, not all of them are relevant for the ZSD1211 +/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211 * driver. */ @@ -594,79 +624,69 @@ /* * Upper 16 bit contains the regulatory domain. */ -#define E2P_SUBID E2P_REG(0x00) -#define E2P_POD E2P_REG(0x02) -#define E2P_MAC_ADDR_P1 E2P_REG(0x04) -#define E2P_MAC_ADDR_P2 E2P_REG(0x06) -#define E2P_PWR_CAL_VALUE1 E2P_REG(0x08) -#define E2P_PWR_CAL_VALUE2 E2P_REG(0x0a) -#define E2P_PWR_CAL_VALUE3 E2P_REG(0x0c) -#define E2P_PWR_CAL_VALUE4 E2P_REG(0x0e) -#define E2P_PWR_INT_VALUE1 E2P_REG(0x10) -#define E2P_PWR_INT_VALUE2 E2P_REG(0x12) -#define E2P_PWR_INT_VALUE3 E2P_REG(0x14) -#define E2P_PWR_INT_VALUE4 E2P_REG(0x16) +#define E2P_SUBID E2P_DATA(0x00) +#define E2P_POD E2P_DATA(0x02) +#define E2P_MAC_ADDR_P1 E2P_DATA(0x04) +#define E2P_MAC_ADDR_P2 E2P_DATA(0x06) +#define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08) +#define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a) +#define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c) +#define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e) +#define E2P_PWR_INT_VALUE1 E2P_DATA(0x10) +#define E2P_PWR_INT_VALUE2 E2P_DATA(0x12) +#define E2P_PWR_INT_VALUE3 E2P_DATA(0x14) +#define E2P_PWR_INT_VALUE4 E2P_DATA(0x16) /* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) * also only 11 channels. */ -#define E2P_ALLOWED_CHANNEL E2P_REG(0x18) +#define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) -#define E2P_PHY_REG E2P_REG(0x1a) -#define E2P_DEVICE_VER E2P_REG(0x20) -#define E2P_36M_CAL_VALUE1 E2P_REG(0x28) -#define E2P_36M_CAL_VALUE2 E2P_REG(0x2a) -#define E2P_36M_CAL_VALUE3 E2P_REG(0x2c) -#define E2P_36M_CAL_VALUE4 E2P_REG(0x2e) -#define E2P_11A_INT_VALUE1 E2P_REG(0x30) -#define E2P_11A_INT_VALUE2 E2P_REG(0x32) -#define E2P_11A_INT_VALUE3 E2P_REG(0x34) -#define E2P_11A_INT_VALUE4 E2P_REG(0x36) -#define E2P_48M_CAL_VALUE1 E2P_REG(0x38) -#define E2P_48M_CAL_VALUE2 E2P_REG(0x3a) -#define E2P_48M_CAL_VALUE3 E2P_REG(0x3c) -#define E2P_48M_CAL_VALUE4 E2P_REG(0x3e) -#define E2P_48M_INT_VALUE1 E2P_REG(0x40) -#define E2P_48M_INT_VALUE2 E2P_REG(0x42) -#define E2P_48M_INT_VALUE3 E2P_REG(0x44) -#define E2P_48M_INT_VALUE4 E2P_REG(0x46) -#define E2P_54M_CAL_VALUE1 E2P_REG(0x48) /* ??? */ -#define E2P_54M_CAL_VALUE2 E2P_REG(0x4a) -#define E2P_54M_CAL_VALUE3 E2P_REG(0x4c) -#define E2P_54M_CAL_VALUE4 E2P_REG(0x4e) -#define E2P_54M_INT_VALUE1 E2P_REG(0x50) -#define E2P_54M_INT_VALUE2 E2P_REG(0x52) -#define E2P_54M_INT_VALUE3 E2P_REG(0x54) -#define E2P_54M_INT_VALUE4 E2P_REG(0x56) - -/* All 16 bit values */ -#define FW_FIRMWARE_VER FW_REG(0) -/* non-zero if USB high speed connection */ -#define FW_USB_SPEED FW_REG(1) -#define FW_FIX_TX_RATE FW_REG(2) -/* Seems to be able to control LEDs over the firmware */ -#define FW_LINK_STATUS FW_REG(3) -#define FW_SOFT_RESET FW_REG(4) -#define FW_FLASH_CHK FW_REG(5) +#define E2P_PHY_REG E2P_DATA(0x1a) +#define E2P_DEVICE_VER E2P_DATA(0x20) +#define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) +#define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) +#define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) +#define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e) +#define E2P_11A_INT_VALUE1 E2P_DATA(0x30) +#define E2P_11A_INT_VALUE2 E2P_DATA(0x32) +#define E2P_11A_INT_VALUE3 E2P_DATA(0x34) +#define E2P_11A_INT_VALUE4 E2P_DATA(0x36) +#define E2P_48M_CAL_VALUE1 E2P_DATA(0x38) +#define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a) +#define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c) +#define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e) +#define E2P_48M_INT_VALUE1 E2P_DATA(0x40) +#define E2P_48M_INT_VALUE2 E2P_DATA(0x42) +#define E2P_48M_INT_VALUE3 E2P_DATA(0x44) +#define E2P_48M_INT_VALUE4 E2P_DATA(0x46) +#define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */ +#define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a) +#define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c) +#define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e) +#define E2P_54M_INT_VALUE1 E2P_DATA(0x50) +#define E2P_54M_INT_VALUE2 E2P_DATA(0x52) +#define E2P_54M_INT_VALUE3 E2P_DATA(0x54) +#define E2P_54M_INT_VALUE4 E2P_DATA(0x56) -#define FW_LINK_OFF 0x0 -#define FW_LINK_TX 0x1 -/* 0x2 - link led on? */ +/* This word contains the base address of the FW_REG_ registers below */ +#define FWRAW_REGS_ADDR FWRAW_DATA(0x1d) +/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */ enum { - CR_BASE_OFFSET = 0x9000, - FW_START_OFFSET = 0xee00, - FW_BASE_ADDR_OFFSET = FW_START_OFFSET + 0x1d, - EEPROM_START_OFFSET = 0xf800, - EEPROM_SIZE = 0x800, /* words */ - LOAD_CODE_SIZE = 0xe, /* words */ - LOAD_VECT_SIZE = 0x10000 - 0xfff7, /* words */ - EEPROM_REGS_OFFSET = LOAD_CODE_SIZE + LOAD_VECT_SIZE, - EEPROM_REGS_SIZE = 0x7e, /* words */ - E2P_BASE_OFFSET = EEPROM_START_OFFSET + - EEPROM_REGS_OFFSET, + FW_REG_FIRMWARE_VER = 0, + /* non-zero if USB high speed connection */ + FW_REG_USB_SPEED = 1, + FW_REG_FIX_TX_RATE = 2, + /* Seems to be able to control LEDs over the firmware */ + FW_REG_LED_LINK_STATUS = 3, + FW_REG_SOFT_RESET = 4, + FW_REG_FLASH_CHK = 5, }; -#define FW_REG_TABLE_ADDR USB_ADDR(FW_START_OFFSET + 0x1d) +/* Values for FW_LINK_STATUS */ +#define FW_LINK_OFF 0x0 +#define FW_LINK_TX 0x1 +/* 0x2 - link led on? */ enum { /* indices for ofdm_cal_values */ @@ -679,6 +699,8 @@ struct zd_chip { struct zd_usb usb; struct zd_rf rf; struct mutex mutex; + /* Base address of FW_REG_ registers */ + zd_addr_t fw_regs_base; u8 e2p_mac[ETH_ALEN]; /* EepSetPoint in the vendor driver */ u8 pwr_cal_values[E2P_CHANNEL_COUNT]; diff -puN drivers/net/wireless/zd1211rw/zd_def.h~git-netdev-all drivers/net/wireless/zd1211rw/zd_def.h --- a/drivers/net/wireless/zd1211rw/zd_def.h~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_def.h @@ -23,6 +23,8 @@ #include #include +typedef u16 __nocast zd_addr_t; + #define dev_printk_f(level, dev, fmt, args...) \ dev_printk(level, dev, "%s() " fmt, __func__, ##args) diff -puN drivers/net/wireless/zd1211rw/zd_ieee80211.h~git-netdev-all drivers/net/wireless/zd1211rw/zd_ieee80211.h --- a/drivers/net/wireless/zd1211rw/zd_ieee80211.h~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_ieee80211.h @@ -2,7 +2,6 @@ #define _ZD_IEEE80211_H #include -#include "zd_types.h" /* Additional definitions from the standards. */ diff -puN drivers/net/wireless/zd1211rw/zd_rf.h~git-netdev-all drivers/net/wireless/zd1211rw/zd_rf.h --- a/drivers/net/wireless/zd1211rw/zd_rf.h~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_rf.h @@ -18,8 +18,6 @@ #ifndef _ZD_RF_H #define _ZD_RF_H -#include "zd_types.h" - #define UW2451_RF 0x2 #define UCHIP_RF 0x3 #define AL2230_RF 0x4 diff -puN drivers/net/wireless/zd1211rw/zd_types.h~git-netdev-all /dev/null --- a/drivers/net/wireless/zd1211rw/zd_types.h +++ /dev/null @@ -1,71 +0,0 @@ -/* zd_types.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _ZD_TYPES_H -#define _ZD_TYPES_H - -#include - -/* We have three register spaces mapped into the overall USB address space of - * 64K words (16-bit values). There is the control register space of - * double-word registers, the eeprom register space and the firmware register - * space. The control register space is byte mapped, the others are word - * mapped. - * - * For that reason, we are using byte offsets for control registers and word - * offsets for everything else. - */ - -typedef u32 __nocast zd_addr_t; - -enum { - ADDR_BASE_MASK = 0xff000000, - ADDR_OFFSET_MASK = 0x0000ffff, - ADDR_ZERO_MASK = 0x00ff0000, - NULL_BASE = 0x00000000, - USB_BASE = 0x01000000, - CR_BASE = 0x02000000, - CR_MAX_OFFSET = 0x0b30, - E2P_BASE = 0x03000000, - E2P_MAX_OFFSET = 0x007e, - FW_BASE = 0x04000000, - FW_MAX_OFFSET = 0x0005, -}; - -#define ZD_ADDR_BASE(addr) ((u32)(addr) & ADDR_BASE_MASK) -#define ZD_OFFSET(addr) ((u32)(addr) & ADDR_OFFSET_MASK) - -#define ZD_ADDR(base, offset) \ - ((zd_addr_t)(((base) & ADDR_BASE_MASK) | ((offset) & ADDR_OFFSET_MASK))) - -#define ZD_NULL_ADDR ((zd_addr_t)0) -#define USB_REG(offset) ZD_ADDR(USB_BASE, offset) /* word addressing */ -#define CTL_REG(offset) ZD_ADDR(CR_BASE, offset) /* byte addressing */ -#define E2P_REG(offset) ZD_ADDR(E2P_BASE, offset) /* word addressing */ -#define FW_REG(offset) ZD_ADDR(FW_BASE, offset) /* word addressing */ - -static inline zd_addr_t zd_inc_word(zd_addr_t addr) -{ - u32 base = ZD_ADDR_BASE(addr); - u32 offset = ZD_OFFSET(addr); - - offset += base == CR_BASE ? 2 : 1; - - return base | offset; -} - -#endif /* _ZD_TYPES_H */ diff -puN drivers/net/wireless/zd1211rw/zd_usb.c~git-netdev-all drivers/net/wireless/zd1211rw/zd_usb.c --- a/drivers/net/wireless/zd1211rw/zd_usb.c~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_usb.c @@ -58,6 +58,10 @@ static struct usb_device_id usb_ids[] = { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} @@ -73,96 +77,6 @@ MODULE_DEVICE_TABLE(usb, usb_ids); #define FW_ZD1211_PREFIX "zd1211/zd1211_" #define FW_ZD1211B_PREFIX "zd1211/zd1211b_" -/* register address handling */ - -#ifdef DEBUG -static int check_addr(struct zd_usb *usb, zd_addr_t addr) -{ - u32 base = ZD_ADDR_BASE(addr); - u32 offset = ZD_OFFSET(addr); - - if ((u32)addr & ADDR_ZERO_MASK) - goto invalid_address; - switch (base) { - case USB_BASE: - break; - case CR_BASE: - if (offset > CR_MAX_OFFSET) { - dev_dbg(zd_usb_dev(usb), - "CR offset %#010x larger than" - " CR_MAX_OFFSET %#10x\n", - offset, CR_MAX_OFFSET); - goto invalid_address; - } - if (offset & 1) { - dev_dbg(zd_usb_dev(usb), - "CR offset %#010x is not a multiple of 2\n", - offset); - goto invalid_address; - } - break; - case E2P_BASE: - if (offset > E2P_MAX_OFFSET) { - dev_dbg(zd_usb_dev(usb), - "E2P offset %#010x larger than" - " E2P_MAX_OFFSET %#010x\n", - offset, E2P_MAX_OFFSET); - goto invalid_address; - } - break; - case FW_BASE: - if (!usb->fw_base_offset) { - dev_dbg(zd_usb_dev(usb), - "ERROR: fw base offset has not been set\n"); - return -EAGAIN; - } - if (offset > FW_MAX_OFFSET) { - dev_dbg(zd_usb_dev(usb), - "FW offset %#10x is larger than" - " FW_MAX_OFFSET %#010x\n", - offset, FW_MAX_OFFSET); - goto invalid_address; - } - break; - default: - dev_dbg(zd_usb_dev(usb), - "address has unsupported base %#010x\n", addr); - goto invalid_address; - } - - return 0; -invalid_address: - dev_dbg(zd_usb_dev(usb), - "ERROR: invalid address: %#010x\n", addr); - return -EINVAL; -} -#endif /* DEBUG */ - -static u16 usb_addr(struct zd_usb *usb, zd_addr_t addr) -{ - u32 base; - u16 offset; - - base = ZD_ADDR_BASE(addr); - offset = ZD_OFFSET(addr); - - ZD_ASSERT(check_addr(usb, addr) == 0); - - switch (base) { - case CR_BASE: - offset += CR_BASE_OFFSET; - break; - case E2P_BASE: - offset += E2P_BASE_OFFSET; - break; - case FW_BASE: - offset += usb->fw_base_offset; - break; - } - - return offset; -} - /* USB device initialization */ static int request_fw_file( @@ -295,14 +209,13 @@ static int handle_version_mismatch(struc if (r) goto error; - r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START_OFFSET, - REBOOT); + r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT); if (r) goto error; - offset = ((EEPROM_REGS_OFFSET + EEPROM_REGS_SIZE) * sizeof(u16)); + offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16)); r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset, - E2P_BASE_OFFSET + EEPROM_REGS_SIZE, REBOOT); + E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT); /* At this point, the vendor driver downloads the whole firmware * image, hacks around with version IDs, and uploads it again, @@ -331,7 +244,7 @@ static int upload_firmware(struct usb_de if (r) goto error; - fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET); + fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET); if (fw_bcdDevice != bcdDevice) { dev_info(&udev->dev, @@ -357,8 +270,7 @@ static int upload_firmware(struct usb_de if (r) goto error; - r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START_OFFSET, - REBOOT); + r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT); if (r) { dev_err(&udev->dev, "Could not upload firmware code uph. Error number %d\n", @@ -858,7 +770,7 @@ static inline void init_usb_interrupt(st spin_lock_init(&intr->lock); intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); init_completion(&intr->read_regs.completion); - intr->read_regs.cr_int_addr = cpu_to_le16(usb_addr(usb, CR_INTERRUPT)); + intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); } static inline void init_usb_rx(struct zd_usb *usb) @@ -890,22 +802,6 @@ void zd_usb_init(struct zd_usb *usb, str init_usb_rx(usb); } -int zd_usb_init_hw(struct zd_usb *usb) -{ - int r; - struct zd_chip *chip = zd_usb_to_chip(usb); - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread16_locked(chip, &usb->fw_base_offset, - USB_REG((u16)FW_BASE_ADDR_OFFSET)); - if (r) - return r; - dev_dbg_f(zd_usb_dev(usb), "fw_base_offset: %#06hx\n", - usb->fw_base_offset); - - return 0; -} - void zd_usb_clear(struct zd_usb *usb) { usb_set_intfdata(usb->intf, NULL); @@ -1253,7 +1149,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, return -ENOMEM; req->id = cpu_to_le16(USB_REQ_READ_REGS); for (i = 0; i < count; i++) - req->addr[i] = cpu_to_le16(usb_addr(usb, addresses[i])); + req->addr[i] = cpu_to_le16((u16)addresses[i]); udev = zd_usb_to_usbdev(usb); prepare_read_regs_int(usb); @@ -1318,7 +1214,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb req->id = cpu_to_le16(USB_REQ_WRITE_REGS); for (i = 0; i < count; i++) { struct reg_data *rw = &req->reg_writes[i]; - rw->addr = cpu_to_le16(usb_addr(usb, ioreqs[i].addr)); + rw->addr = cpu_to_le16((u16)ioreqs[i].addr); rw->value = cpu_to_le16(ioreqs[i].value); } diff -puN drivers/net/wireless/zd1211rw/zd_usb.h~git-netdev-all drivers/net/wireless/zd1211rw/zd_usb.h --- a/drivers/net/wireless/zd1211rw/zd_usb.h~git-netdev-all +++ a/drivers/net/wireless/zd1211rw/zd_usb.h @@ -25,7 +25,6 @@ #include #include "zd_def.h" -#include "zd_types.h" enum devicetype { DEVICE_ZD1211 = 0, @@ -181,15 +180,14 @@ struct zd_usb_tx { spinlock_t lock; }; -/* Contains the usb parts. The structure doesn't require a lock, because intf - * and fw_base_offset, will not be changed after initialization. +/* Contains the usb parts. The structure doesn't require a lock because intf + * will not be changed after initialization. */ struct zd_usb { struct zd_usb_interrupt intr; struct zd_usb_rx rx; struct zd_usb_tx tx; struct usb_interface *intf; - u16 fw_base_offset; }; #define zd_usb_dev(usb) (&usb->intf->dev) diff -puN net/ieee80211/softmac/ieee80211softmac_wx.c~git-netdev-all net/ieee80211/softmac/ieee80211softmac_wx.c --- a/net/ieee80211/softmac/ieee80211softmac_wx.c~git-netdev-all +++ a/net/ieee80211/softmac/ieee80211softmac_wx.c @@ -265,6 +265,12 @@ ieee80211softmac_wx_get_rate(struct net_ int err = -EINVAL; spin_lock_irqsave(&mac->lock, flags); + + if (unlikely(!mac->running)) { + err = -ENODEV; + goto out_unlock; + } + switch (mac->txrates.default_rate) { case IEEE80211_CCK_RATE_1MB: data->bitrate.value = 1000000; _