bk://gkernel.bkbits.net/netdev-2.6 jgarzik@pobox.com|ChangeSet|20040619215718|45457 jgarzik # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/06/19 15:26:48-07:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # drivers/net/yellowfin.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/19 15:26:45-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ibmlana.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/acenic.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/19 15:26:44-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/19 17:57:18-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/via-rhine # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/via-rhine.c # 2004/06/19 17:57:15-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/19 17:53:34-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/natsemi # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/natsemi.c # 2004/06/19 17:53:31-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/19 17:51:46-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/mv64340 # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/Kconfig # 2004/06/19 17:51:43-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/19 17:49:44-04:00 ralf@linux-mips.org # [PATCH] New driver for MV64340 GigE # # The Marvell MV64340 is a system controller with PCI and 3 integrated GigE # interfaces. # # Signed-off-by: Ralf Baechle # # drivers/net/mv64340_eth.h # 2004/06/19 17:14:57-04:00 ralf@linux-mips.org +601 -0 # New driver for MV64340 GigE # # drivers/net/mv64340_eth.h # 2004/06/19 17:14:57-04:00 ralf@linux-mips.org +0 -0 # BitKeeper file /spare/repo/netdev-2.6/mv64340/drivers/net/mv64340_eth.h # # drivers/net/mv64340_eth.c # 2004/06/19 17:14:53-04:00 ralf@linux-mips.org +2702 -0 # New driver for MV64340 GigE # # drivers/net/Makefile # 2004/06/19 17:14:12-04:00 ralf@linux-mips.org +2 -0 # New driver for MV64340 GigE # # drivers/net/Kconfig # 2004/06/19 17:12:58-04:00 ralf@linux-mips.org +28 -0 # New driver for MV64340 GigE # # drivers/net/mv64340_eth.c # 2004/06/19 17:14:53-04:00 ralf@linux-mips.org +0 -0 # BitKeeper file /spare/repo/netdev-2.6/mv64340/drivers/net/mv64340_eth.c # # ChangeSet # 2004/06/19 17:40:00-04:00 akpm@osdl.org # [PATCH] sis900-fix-phy-transceiver-detection.patch # # From: Daniele Venzano # # Fix PHY transceiver detection code to fall back to known PHY and not to the # last detected. # # The code checks every transceiver detected for link status and type, but fails # when ghost transceivers are detected, deciding to use the last one detected. # # With this patch the driver should choose the correct transceiver even when # some ghosts are detected by checking for the type of the tranceiver it is # going to use. # # drivers/net/sis900.c # 2004/05/19 15:40:44-04:00 akpm@osdl.org +14 -7 # sis900-fix-phy-transceiver-detection.patch # # ChangeSet # 2004/06/19 17:19:33-04:00 rl@hellgate.ch # [PATCH] Add WOL support # # Add rhine_shutdown callback to prepare Rhine power registers for # shutdown. # # Add rhine_get_wol/rhine_set_wol for ethtool ioctl. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/15 12:36:12-04:00 rl@hellgate.ch +119 -4 # Add WOL support # # ChangeSet # 2004/06/19 17:19:25-04:00 rl@hellgate.ch # [PATCH] Small fixes and clean-up # # Bump driver version to mark recent major changes in driver code. # # Remove backoff parameter. The reason it was once introduced is gone. # Continue to go with EEPROM default for now, will hard-wire IEEE backoff # algorithm instead (later). # # Rhine-I needs extra time to recuperate from chip reset before EEPROM # reload. # # Add Rhine model identification. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +61 -49 # Small fixes and clean-up # # ChangeSet # 2004/06/19 17:19:16-04:00 rl@hellgate.ch # [PATCH] Media mode rewrite # # Remove rhine_check_duplex, rhine_timer and related data structures # # Add rhine_check_media: wrapper for generic mii_check_media, sets duplex # bit in MAC # # Add rhine_enable_linkmon, rhine_disable_linkmon to enable hardware link # status monitoring # # Update mdio_read, mdio_write accordingly # # Remove get_intr_status check in rhine_start_tx because we are not racing # anymore # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +70 -124 # Media mode rewrite # # ChangeSet # 2004/06/19 17:19:08-04:00 rl@hellgate.ch # [PATCH] Fix Tx engine race for good # # It finally dawned on me how to eliminate the race I've been narrowing # down with earlier patches: Instead of writing the command registers as # one word, write them one at a time (as bytes). The race was for settings # bits in ChipCmd and ChipCmd1 (0x09) against the chip clearing CmdTxOn # which is in ChipCmd. # # In addition to writing single bytes, the fix requires a switch from # using bit 5 in ChipCmd0 to bit 5 in ChipCmd1 (which is equivalent) # to signal Tx demand. # # Also, don't restart Rx engine "pre-emptively" in rhine_rx, that's a # sure way to race with the chip. # # Introduce RHINE_WAIT_FOR, a macro for small busy loops with primitive # completion checking. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +45 -42 # Fix Tx engine race for good # # ChangeSet # 2004/06/19 17:18:59-04:00 rl@hellgate.ch # [PATCH] Remove options, full_duplex parameters # # Nobody complained although media locking parameters were broken # forever. They were sort of fixed recently, but the code is still in a # bad shape. # # More seriously, the options/full_duplex stuff has fundamental design # problems that have been discussed in-depth on the list (e.g. effect of # hotplugging, nameif, suspend/resume). # # For those needing media locking with Linux 2.6+ via-rhine, ethtool(8) # is the replacement. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +5 -52 # Remove options, full_duplex parameters # # ChangeSet # 2004/06/19 17:18:51-04:00 rl@hellgate.ch # [PATCH] Rewrite PHY detection # # Instead of probing, set phy_id to 1 for Rhine III and read phy_id from # EEPROM-controlled register for Rhine I/II. # # Remove code for handling anything other than 1 MII PHY. If it wasn't # unnecessary code to begin with, it would need to be fixed because it # wouldn't work. # # Use mii_if_info.phy_id as the only container of phy_id. Not particulary # happy about those names (phy_id vs. MII_PHYSIDx), but being consequent # about it mitigates confusion. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +25 -27 # Rewrite PHY detection # # ChangeSet # 2004/06/19 17:18:42-04:00 rl@hellgate.ch # [PATCH] Remove lingering PHY special casing # # All this code is broken (e.g. unconditionally programs all PHYs as if # they were the same model) and/or unused (IntrLinkChange never occurs # with driver as is). # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +2 -13 # Remove lingering PHY special casing # # ChangeSet # 2004/06/19 17:18:33-04:00 rl@hellgate.ch # [PATCH] fix mc_filter on big-endian arch # # A.J. from VIA Networking Technologies noticed that via-rhine is using # cpu_to_le32() when preparing mc_filter hashes. This breaks Rhine hardware # multicast filters on big-endian architectures. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/06 12:05:22-04:00 rl@hellgate.ch +1 -1 # fix mc_filter on big-endian arch # # ChangeSet # 2004/06/19 17:18:25-04:00 rl@hellgate.ch # [PATCH] Restructure reset code # # Restructure code to make it easier to maintain. # # rhine_hw_init: resets chip, reloads eeprom # rhine_chip_reset: chip reset + what used to be wait_for_reset # rhine_reload_eeprom: reload eeprom, re-enable MMIO, disable EEPROM-controlled # WOL # # Note: Chip reset clears a bunch of registers that should be reloaded # from EEPROM (which turns off MMIO). Deal with that later. # # Signed-off-by: Roger Luethi # # drivers/net/via-rhine.c # 2004/06/18 20:00:00-04:00 rl@hellgate.ch +58 -57 # Restructure reset code # # ChangeSet # 2004/06/19 17:06:39-04:00 akpm@osdl.org # [PATCH] fix via-velocity oopses # # - Don't register the inet_addr notifier if the hardware is absent. It # oopses when other interfaces are being upped. # # - Mark velocity_remove1() as __devexit_p in the pci_driver table. # # - c99ification. # # # Signed-off-by: Andrew Morton # # drivers/net/via-velocity.c # 2004/06/03 02:14:38-04:00 akpm@osdl.org +18 -11 # fix via-velocity oopses # # ChangeSet # 2004/06/19 17:02:30-04:00 romieu@fr.zoreil.com # [PATCH] via-velocity: velocity_receive_frame diets # # Weight loss in velocity_receive_frame(): # - isolate the ip header alignment tsk from velocity_receive_frame(); # - following p.30 of the datasheet, rdesc0.len includes the CRC length: # the amount of data copied during the ip alignment can be shortened. # # drivers/net/via-velocity.c # 2004/06/18 15:58:58-04:00 romieu@fr.zoreil.com +26 -10 # via-velocity: velocity_receive_frame diets # # ChangeSet # 2004/06/19 17:02:22-04:00 romieu@fr.zoreil.com # [PATCH] via-velocity: uniformize use of OWNED_BY_NIC # # Introduce velocity_give_rx_desc() to uniformize the use of OWNED_BY_NIC # through the driver. # # drivers/net/via-velocity.c # 2004/06/18 15:34:14-04:00 romieu@fr.zoreil.com +9 -4 # via-velocity: uniformize use of OWNED_BY_NIC # # ChangeSet # 2004/06/19 17:02:13-04:00 romieu@fr.zoreil.com # [PATCH] via-velocity: PCI ID move # # (Note: this serie requires a -mm based kernel as the via-velocity patches # are not included in Jeff's -netdev patches). # # # # PCI ID moved from the driver to the kernel database. A change for the # pci.ids file project at souceforge has been submitted as well. # # include/linux/pci_ids.h # 2004/06/18 15:24:53-04:00 romieu@fr.zoreil.com +1 -0 # via-velocity: PCI ID move # # drivers/net/via-velocity.h # 2004/06/18 15:32:00-04:00 romieu@fr.zoreil.com +0 -1 # via-velocity: PCI ID move # # drivers/net/via-velocity.c # 2004/06/18 15:32:01-04:00 romieu@fr.zoreil.com +3 -2 # via-velocity: PCI ID move # # ChangeSet # 2004/06/19 16:45:06-04:00 manfred@colorfullife.com # [PATCH] natsemi updates # # - support for external phys, both fibre and twisted pair, added: # * remove the "phy" parameter from mdio_{read,write}: the # function accesses the current phy. # * new functions to access external phys: miiport_{read,write} # * scan for external phys on _probe. # * ethtool support for switching between internal # and external phys. # * introduce an init_phy_fixup helper: a few settings must be # reapplied after reenabling the internal phy. # - move register_netdev to the end of _probe. The current position # could cause races with hotplug. # - do not wait for autonegotiation completed after initialization. # - use pci_name() instead of dev->name until register_netdev has # initialized dev->name. # - read the BMSR register in the link beat interrupt twice: # The link status field is latched, without reading twice a link up # event will be missed (and only noticed a few seconds later in the # media timer) # - restart the autonegotiation after modifying the capabilities. # # drivers/net/natsemi.c # 2004/06/19 13:03:58-04:00 manfred@colorfullife.com +678 -183 # natsemi updates # # ChangeSet # 2004/06/19 16:35:00-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6 # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/yellowfin.c # 2004/06/19 16:34:57-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/19 16:34:57-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/19 16:34:57-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/19 16:34:57-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/19 16:34:57-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ibmlana.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/acenic.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/19 16:34:56-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/18 12:19:25-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/yellowfin.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ibmlana.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/18 12:19:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/acenic.c # 2004/06/18 12:19:20-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/18 12:19:20-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/18 12:19:20-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/17 20:14:42-07:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # drivers/net/Kconfig # 2004/06/17 20:14:39-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/16 18:28:43-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/via-gbit # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/Kconfig # 2004/06/16 18:28:40-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/15 21:51:52-07:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # drivers/net/Kconfig # 2004/06/15 21:51:49-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/acenic.c # 2004/06/14 18:35:11-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/14 18:36:36-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/misc # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/Kconfig # 2004/06/14 18:36:33-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/14 18:35:15-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/acenic # into pobox.com:/spare/repo/netdev-2.6/ALL # # ChangeSet # 2004/06/13 11:23:12-07:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # drivers/net/epic100.c # 2004/06/13 11:23:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/13 11:23:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/13 11:23:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/13 11:23:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/10 20:53:18-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/acenic # into pobox.com:/spare/repo/netdev-2.6/ALL # # ChangeSet # 2004/06/10 20:14:00-04:00 jgarzik@pobox.com # Merge # # ChangeSet # 2004/06/10 20:03:05-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/netdev-2.6/misc # into pobox.com:/spare/repo/netdev-2.6/ALL # # ChangeSet # 2004/06/10 20:12:44-04:00 scott.feldman@intel.com # [PATCH] e100: use NAPI mode all the time # # I see no reason to keep the non-NAPI option for e100. This patch removes # the CONFIG_E100_NAPI option and puts the driver in NAPI mode all the time. # Matches the way tg3 works. # # Unless someone has a really good reason to keep the non-NAPI mode, this # should go in for 2.6.7. # # -scott # # ChangeSet # 2004/06/10 19:27:16-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6 # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/acenic.c # 2004/06/10 20:53:15-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/10 20:13:57-04:00 jgarzik@pobox.com +0 -0 # SCCS merged # # drivers/net/e100.c # 2004/06/04 19:02:04-04:00 scott.feldman@intel.com +5 -24 # e100: use NAPI mode all the time # # drivers/net/Kconfig # 2004/06/04 19:02:34-04:00 scott.feldman@intel.com +0 -4 # e100: use NAPI mode all the time # # drivers/net/Kconfig # 2004/06/10 20:03:02-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/10 19:27:12-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/10 19:27:12-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/10 19:27:12-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/08 12:31:22-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/epic100.c # 2004/06/08 12:31:18-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/at1700.c # 2004/06/08 12:31:18-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/3c527.c # 2004/06/08 12:31:18-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/07 19:56:33-07:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # drivers/net/yellowfin.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/07 19:56:30-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/07 17:41:39-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6 # into pobox.com:/spare/repo/netdev-2.6/ALL # # drivers/net/yellowfin.c # 2004/06/07 17:41:35-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/07 17:41:35-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/07 17:41:34-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/04 22:52:13-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/yellowfin.c # 2004/06/04 22:52:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2004/06/04 22:52:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tulip/winbond-840.c # 2004/06/04 22:52:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sungem.c # 2004/06/04 22:52:10-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/04 22:52:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/04 22:52:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/04 22:52:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/04 22:52:09-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/04 02:29:25-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/yellowfin.c # 2004/06/04 02:29:22-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/starfire.c # 2004/06/04 02:29:22-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ns83820.c # 2004/06/04 02:29:22-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/04 02:29:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/forcedeth.c # 2004/06/04 02:29:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2004/06/04 02:29:21-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/02 23:45:50-07:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/Kconfig # 2004/06/02 23:45:47-07:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2004/06/02 20:05:09-04:00 jgarzik@redhat.com # Merge redhat.com:/spare/repo/netdev-2.6/r8169 # into redhat.com:/spare/repo/netdev-2.6/ALL # # drivers/net/r8169.c # 2004/06/02 20:05:05-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # ChangeSet # 2004/06/02 20:03:26-04:00 romieu@fr.zoreil.com # [PATCH] r8169: tx lock removal # # spinlock removal crusade. # # The patch rephrases the spinlock_irq() in rtl8169_start_xmit() and its # companion in the Tx irq handling patch in terms of ordered operations. # # drivers/net/r8169.c # 2004/06/02 18:15:37-04:00 romieu@fr.zoreil.com +10 -9 # r8169: tx lock removal # # ChangeSet # 2004/06/02 20:03:18-04:00 romieu@fr.zoreil.com # [PATCH] r8169: gcc bug workaround # # Add a temporary variable to workaround gcc 2.95.3 bug. # # drivers/net/r8169.c # 2004/06/02 18:15:34-04:00 romieu@fr.zoreil.com +5 -2 # r8169: gcc bug workaround # # ChangeSet # 2004/06/02 20:03:10-04:00 romieu@fr.zoreil.com # [PATCH] r8169: initial link setup rework # # Use rtl8169_set_speed() for link setup in rtl8169_init_one(): # - the code which handles the option checking is isolated; # - display (once) a notice message about the deprecated interface; # - rtl8169_open() enables the phy timer if the link is not up; # - rtl8169_set_speed() checks that the netdevice is actually ready # in order to activate the timer. # # drivers/net/r8169.c # 2004/06/02 17:18:02-04:00 romieu@fr.zoreil.com +43 -93 # r8169: initial link setup rework # # ChangeSet # 2004/06/02 20:03:02-04:00 romieu@fr.zoreil.com # [PATCH] r8169: link handling and phy reset rework # # Link handling changes (Andy Lutomirski ): # - removed rtl8169_hw_phy_reset() and its busy loop; # - RTL8169_PHY_TIMEOUT is x10 to account for the removal of the # phy_link_down_cnt loop in rtl8169_phy_timer(); # - added spinlocking in timer context for rtl8169_phy_timer to avoid # messing with the {set/get}_settings commands issued via ethtool; # - more TBI stuff. # # This patch differs from the former version on the following points: # - the LinkChg irq does not enable the phy timer when the link goes # down any more; # - the phy timer is not enabled in rtl8169_set_speed(); # - removal of the initial renegotiation hack. # # drivers/net/r8169.c # 2004/06/02 17:17:50-04:00 romieu@fr.zoreil.com +94 -50 # r8169: link handling and phy reset rework # # ChangeSet # 2004/06/02 09:39:39-04:00 jgarzik@redhat.com # Merge redhat.com:/spare/repo/netdev-2.6/r8169 # into redhat.com:/spare/repo/netdev-2.6/ALL # # drivers/net/r8169.c # 2004/06/02 09:39:36-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2004/06/02 09:39:36-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # ChangeSet # 2004/06/02 09:38:31-04:00 jgarzik@redhat.com # Merge redhat.com:/spare/repo/netdev-2.6/misc-herbert # into redhat.com:/spare/repo/netdev-2.6/ALL # # drivers/net/yellowfin.c # 2004/06/02 09:38:27-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # drivers/net/natsemi.c # 2004/06/02 09:38:27-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # drivers/net/ibmlana.c # 2004/06/02 09:38:27-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # drivers/net/acenic.c # 2004/06/02 09:38:27-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # ChangeSet # 2004/06/02 09:37:12-04:00 jgarzik@redhat.com # Merge redhat.com:/spare/repo/netdev-2.6/ip-copysum # into redhat.com:/spare/repo/netdev-2.6/ALL # # drivers/net/tulip/winbond-840.c # 2004/06/02 09:37:08-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # drivers/net/epic100.c # 2004/06/02 09:37:08-04:00 jgarzik@redhat.com +0 -0 # Auto merged # # ChangeSet # 2004/05/27 13:58:44-04:00 romieu@fr.zoreil.com # [PATCH] r8169: ethtool .get_{settings/link} # # - ethtool get_settings() for r8169 (Andy Lutomirski ); # - rtl8169_ethtool_ops.get_drvinfo() is set as well; # - added some bits to handle the TBI status. # # The locking does not need to be specially clever. # # drivers/net/r8169.c # 2004/05/20 07:15:59-04:00 romieu@fr.zoreil.com +87 -0 # r8169: ethtool .get_{settings/link} # # ChangeSet # 2004/05/27 13:58:35-04:00 romieu@fr.zoreil.com # [PATCH] r8169: ethtool .set_settings # # ethtool set_settings support (Andy Lutomirski ). # The initial code has been modified so that the settings of parameters # for TBI and normal mode do not step on each others shoes. # # drivers/net/r8169.c # 2004/05/20 07:03:25-04:00 romieu@fr.zoreil.com +97 -0 # r8169: ethtool .set_settings # # ChangeSet # 2004/05/17 14:07:14-04:00 romieu@fr.zoreil.com # [PATCH] r8169: janitoring # # Spring cleanup # - unsigned int (u32) should be slightly faster on ppc64 (Jon D Mason); # - misc minor de-uglyfication. # # drivers/net/r8169.c # 2004/05/14 17:08:45-04:00 romieu@fr.zoreil.com +47 -47 # 2.6.6-mm2 - r8169 janitoring # # ChangeSet # 2004/05/17 14:07:06-04:00 romieu@fr.zoreil.com # [PATCH] r8169: cosmetic renaming of a register # # RxUnderrun status bit renamed to LinkChg (identical to the 8139cp driver). # # drivers/net/r8169.c # 2004/05/14 17:08:47-04:00 romieu@fr.zoreil.com +5 -5 # 2.6.6-mm2 - r8169 register rename # # ChangeSet # 2004/05/17 14:06:58-04:00 romieu@fr.zoreil.com # [PATCH] r8169: napi support # # Napi for r8169 (Jon D Mason ). # Both Tx and Rx processing are moved to the ->poll() function. # # drivers/net/r8169.c # 2004/05/14 17:07:18-04:00 romieu@fr.zoreil.com +78 -11 # 2.6.6-mm2 - r8169 napi # # drivers/net/Kconfig # 2004/05/14 17:07:18-04:00 romieu@fr.zoreil.com +5 -0 # 2.6.6-mm2 - r8169 napi # # ChangeSet # 2004/04/21 23:30:47-04:00 romieu@fr.zoreil.com # [PATCH] epic100: code removal in irq handler # # The loop in the irq handler is not needed any more as the high frequency # events have been deferred due to napi usage. # # drivers/net/epic100.c # 2004/04/19 18:37:16-04:00 romieu@fr.zoreil.com +52 -64 # 2.6.6-rc1-mm1 - code removal in the epic100 irq handler # # ChangeSet # 2004/04/21 23:30:40-04:00 romieu@fr.zoreil.com # [PATCH] epic100: spin_unlock_irqrestore avoidance # # This patch avoids to duplicate a spin_unlock: # - it is mostly an artifact due to wild goto; # - it makes the generated code smaller. # # drivers/net/epic100.c # 2004/04/19 18:37:05-04:00 romieu@fr.zoreil.com +16 -10 # 2.6.6-rc1-mm1 - spin_unlock_irqrestore avoidance in epic100 # # ChangeSet # 2004/03/25 23:52:21-05:00 romieu@fr.zoreil.com # [netdrvr epic100] napi fixes # # Multiple invocation of __netif_rx_schedule() in epic_interrupt() while # epic_poll loops over __netif_rx_complete() leads to serious device # refcount leak. # # drivers/net/epic100.c # 2004/03/25 23:52:16-05:00 romieu@fr.zoreil.com +18 -15 # [netdrvr epic100] napi fixes # # Multiple invocation of __netif_rx_schedule() in epic_interrupt() while # epic_poll loops over __netif_rx_complete() leads to serious device # refcount leak. # # ChangeSet # 2004/03/22 19:07:18-05:00 romieu@fr.zoreil.com # [netdrvr epic100] napi 3/3 - transmit path # # drivers/net/epic100.c # 2004/03/22 18:18:40-05:00 romieu@fr.zoreil.com +9 -11 # 2.6.5-rc2 - epic100 napi # # ChangeSet # 2004/03/22 19:07:11-05:00 romieu@fr.zoreil.com # [netdrvr epic100] napi 2/3 - receive path # # drivers/net/epic100.c # 2004/03/22 18:18:33-05:00 romieu@fr.zoreil.com +116 -21 # 2.6.5-rc2 - epic100 napi # # ChangeSet # 2004/03/22 19:07:03-05:00 romieu@fr.zoreil.com # [netdrvr epic100] napi 1/3 - just shuffle some code around # # Isolate the classical TX part of epic_interrupt. Innocent code shuffling. # # drivers/net/epic100.c # 2004/03/22 16:53:18-05:00 romieu@fr.zoreil.com +76 -61 # 2.6.5-rc2 - epic100 napi # # ChangeSet # 2004/03/22 19:06:56-05:00 romieu@fr.zoreil.com # [netdrvr epic100] minor cleanups # # - extra pci_disable_device() to balance invocation of pci_enable_device() # in epic_init_one() (-> error path + epic_remove_one()); # - lazy return status in epic_init_one(), tsss...; # - memory dedicated to Rx descriptors was not freed after failure of # register_netdev() in epic_init_one(); # - use of epic_pause() in epic_close() offers a small window for a late # interruption just before the final free_irq(). Let's close the window to # avoid two epic_rx() threads racing with each other. # # drivers/net/epic100.c # 2004/03/22 16:53:16-05:00 romieu@fr.zoreil.com +40 -19 # 2.6.5-rc2 - epic100 fixup # diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig --- a/drivers/net/Kconfig 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/Kconfig 2004-06-20 13:15:18 -07:00 @@ -1466,23 +1466,6 @@ . The module will be called e100. -config E100_NAPI - bool "Use Rx Polling (NAPI)" - depends on E100 - help - NAPI is a new driver API designed to reduce CPU and interrupt load - when the driver is receiving lots of packets from the card. It is - still somewhat experimental and thus not yet enabled by default. - - If your estimated Rx load is 10kpps or more, or if the card will be - deployed on potentially unfriendly networks (e.g. in a firewall), - then say Y here. - - See for more - information. - - If in doubt, say N. - config LNE390 tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)" depends on NET_PCI && EISA && EXPERIMENTAL @@ -2023,6 +2006,11 @@ To compile this driver as a module, choose M here: the module will be called r8169. This is recommended. +config R8169_NAPI + bool "Use Rx and Tx Polling (NAPI) (EXPERIMENTAL)" + depends on R8169 && EXPERIMENTAL + + config SK98LIN tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support" depends on PCI @@ -2110,6 +2098,34 @@ To compile this driver as a module, choose M here: the module will be called tg3. This is recommended. + +config MV64340_ETH + bool "MV-64340 Ethernet support" + depends on MOMENCO_OCELOT_C || MOMENCO_JAGUAR_ATX + help + This driver supports the gigabit Ethernet on the Marvell MV64340 + chipset which is used in the Momenco Ocelot C and Jaguar ATX. + +config MV64340_ETH_0 + bool "MV-64340 Port 0" + depends on MV64340_ETH + help + This enables support for Port 0 of the Marvell MV64340 Gigabit + Ethernet. + +config MV64340_ETH_1 + bool "MV-64340 Port 1" + depends on MV64340_ETH + help + This enables support for Port 1 of the Marvell MV64340 Gigabit + Ethernet. + +config MV64340_ETH_2 + bool "MV-64340 Port 2" + depends on MV64340_ETH + help + This enables support for Port 2 of the Marvell MV64340 Gigabit + Ethernet. endmenu diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/Makefile 2004-06-20 13:15:18 -07:00 @@ -95,6 +95,8 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o +obj-$(CONFIG_MV64340_ETH) += mv64340_eth.o + obj-$(CONFIG_PPP) += ppp_generic.o slhc.o obj-$(CONFIG_PPP_ASYNC) += ppp_async.o obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o diff -Nru a/drivers/net/e100.c b/drivers/net/e100.c --- a/drivers/net/e100.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/e100.c 2004-06-20 13:15:18 -07:00 @@ -87,9 +87,8 @@ * cb_to_use is the next CB to use for queuing a command; cb_to_clean * is the next CB to check for completion; cb_to_send is the first * CB to start on in case of a previous failure to resume. CB clean - * up happens in interrupt context in response to a CU interrupt, or - * in dev->poll in the case where NAPI is enabled. cbs_avail keeps - * track of number of free CB resources available. + * up happens in interrupt context in response to a CU interrupt. + * cbs_avail keeps track of number of free CB resources available. * * Hardware padding of short packets to minimum packet size is * enabled. 82557 pads with 7Eh, while the later controllers pad @@ -112,9 +111,8 @@ * replacement RFDs cannot be allocated, or the RU goes non-active, * the RU must be restarted. Frame arrival generates an interrupt, * and Rx indication and re-allocation happen in the same context, - * therefore no locking is required. If NAPI is enabled, this work - * happens in dev->poll. A software-generated interrupt is gen- - * erated from the watchdog to recover from a failed allocation + * therefore no locking is required. A software-generated interrupt + * is generated from the watchdog to recover from a failed allocation * senario where all Rx resources have been indicated and none re- * placed. * @@ -126,8 +124,6 @@ * supported. Tx Scatter/Gather is not supported. Jumbo Frames is * not supported (hardware limitation). * - * NAPI support is enabled with CONFIG_E100_NAPI. - * * MagicPacket(tm) WoL support is enabled/disabled via ethtool. * * Thanks to JC (jchapman@katalix.com) for helping with @@ -158,7 +154,7 @@ #define DRV_NAME "e100" -#define DRV_VERSION "3.0.18" +#define DRV_VERSION "3.0.22-NAPI" #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2004 Intel Corporation" #define PFX DRV_NAME ": " @@ -1463,11 +1459,7 @@ nic->net_stats.rx_packets++; nic->net_stats.rx_bytes += actual_size; nic->netdev->last_rx = jiffies; -#ifdef CONFIG_E100_NAPI netif_receive_skb(skb); -#else - netif_rx(skb); -#endif if(work_done) (*work_done)++; } @@ -1562,20 +1554,12 @@ if(stat_ack & stat_ack_rnr) nic->ru_running = 0; -#ifdef CONFIG_E100_NAPI e100_disable_irq(nic); netif_rx_schedule(netdev); -#else - if(stat_ack & stat_ack_rx) - e100_rx_clean(nic, NULL, 0); - if(stat_ack & stat_ack_tx) - e100_tx_clean(nic); -#endif return IRQ_HANDLED; } -#ifdef CONFIG_E100_NAPI static int e100_poll(struct net_device *netdev, int *budget) { struct nic *nic = netdev_priv(netdev); @@ -1598,7 +1582,6 @@ return 1; } -#endif #ifdef CONFIG_NET_POLL_CONTROLLER static void e100_netpoll(struct net_device *netdev) @@ -2135,10 +2118,8 @@ SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops); netdev->tx_timeout = e100_tx_timeout; netdev->watchdog_timeo = E100_WATCHDOG_PERIOD; -#ifdef CONFIG_E100_NAPI netdev->poll = e100_poll; netdev->weight = E100_NAPI_WEIGHT; -#endif #ifdef CONFIG_NET_POLL_CONTROLLER netdev->poll_controller = e100_netpoll; #endif diff -Nru a/drivers/net/epic100.c b/drivers/net/epic100.c --- a/drivers/net/epic100.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/epic100.c 2004-06-20 13:15:18 -07:00 @@ -80,8 +80,6 @@ These may be modified when a driver module is loaded.*/ static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 32; /* Used to pass the full-duplex flag, etc. */ #define MAX_UNITS 8 /* More are supported, limit only on options */ @@ -99,9 +97,9 @@ Making the Tx ring too large decreases the effectiveness of channel bonding and packet priority. There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ -#define RX_RING_SIZE 32 +#define TX_RING_SIZE 256 +#define TX_QUEUE_LEN 240 /* Limit ring entries actually used. */ +#define RX_RING_SIZE 256 #define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct epic_tx_desc) #define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct epic_rx_desc) @@ -158,12 +156,10 @@ MODULE_LICENSE("GPL"); MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM_DESC(debug, "EPIC/100 debug level (0-5)"); -MODULE_PARM_DESC(max_interrupt_work, "EPIC/100 maximum events handled per interrupt"); MODULE_PARM_DESC(options, "EPIC/100: Bits 0-3: media type, bit 4: full duplex"); MODULE_PARM_DESC(rx_copybreak, "EPIC/100 copy breakpoint for copy-only-tiny-frames"); MODULE_PARM_DESC(full_duplex, "EPIC/100 full duplex setting(s) (1)"); @@ -295,6 +291,12 @@ StopTxDMA=0x20, StopRxDMA=0x40, RestartTx=0x80, }; +#define EpicRemoved 0xffffffff /* Chip failed or removed (CardBus) */ + +#define EpicNapiEvent (TxEmpty | TxDone | \ + RxDone | RxStarted | RxEarlyWarn | RxOverflow | RxFull) +#define EpicNormalEvent (0x0000ffff & ~EpicNapiEvent) + static u16 media2miictl[16] = { 0, 0x0C00, 0x0C00, 0x2000, 0x0100, 0x2100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -333,9 +335,12 @@ /* Ring pointers. */ spinlock_t lock; /* Group with Tx control cache line. */ + spinlock_t napi_lock; + unsigned int reschedule_in_poll; unsigned int cur_tx, dirty_tx; unsigned int cur_rx, dirty_rx; + u32 irq_mask; unsigned int rx_buf_sz; /* Based on MTU+slack. */ struct pci_dev *pci_dev; /* PCI bus location. */ @@ -362,7 +367,8 @@ static void epic_tx_timeout(struct net_device *dev); static void epic_init_ring(struct net_device *dev); static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int epic_rx(struct net_device *dev); +static int epic_rx(struct net_device *dev, int budget); +static int epic_poll(struct net_device *dev, int *budget); static irqreturn_t epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static struct ethtool_ops netdev_ethtool_ops; @@ -381,7 +387,7 @@ int irq; struct net_device *dev; struct epic_private *ep; - int i, option = 0, duplex = 0; + int i, ret, option = 0, duplex = 0; void *ring_space; dma_addr_t ring_dma; @@ -395,29 +401,33 @@ card_idx++; - i = pci_enable_device(pdev); - if (i) - return i; + ret = pci_enable_device(pdev); + if (ret) + goto out; irq = pdev->irq; if (pci_resource_len(pdev, 0) < pci_id_tbl[chip_idx].io_size) { printk (KERN_ERR "card %d: no PCI region space\n", card_idx); - return -ENODEV; + ret = -ENODEV; + goto err_out_disable; } pci_set_master(pdev); + ret = pci_request_regions(pdev, DRV_NAME); + if (ret < 0) + goto err_out_disable; + + ret = -ENOMEM; + dev = alloc_etherdev(sizeof (*ep)); if (!dev) { printk (KERN_ERR "card %d: no memory for eth device\n", card_idx); - return -ENOMEM; + goto err_out_free_res; } SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - if (pci_request_regions(pdev, DRV_NAME)) - goto err_out_free_netdev; - #ifdef USE_IO_OPS ioaddr = pci_resource_start (pdev, 0); #else @@ -425,7 +435,7 @@ ioaddr = (long) ioremap (ioaddr, pci_resource_len (pdev, 1)); if (!ioaddr) { printk (KERN_ERR DRV_NAME " %d: ioremap failed\n", card_idx); - goto err_out_free_res; + goto err_out_free_netdev; } #endif @@ -462,7 +472,9 @@ dev->base_addr = ioaddr; dev->irq = irq; - spin_lock_init (&ep->lock); + spin_lock_init(&ep->lock); + spin_lock_init(&ep->napi_lock); + ep->reschedule_in_poll = 0; /* Bring the chip out of low-power mode. */ outl(0x4200, ioaddr + GENCTL); @@ -492,6 +504,9 @@ ep->pci_dev = pdev; ep->chip_id = chip_idx; ep->chip_flags = pci_id_tbl[chip_idx].drv_flags; + ep->irq_mask = + (ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) + | CntFull | TxUnderrun | EpicNapiEvent; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but @@ -546,10 +561,12 @@ dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; dev->tx_timeout = &epic_tx_timeout; + dev->poll = epic_poll; + dev->weight = 64; - i = register_netdev(dev); - if (i) - goto err_out_unmap_tx; + ret = register_netdev(dev); + if (ret < 0) + goto err_out_unmap_rx; printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", dev->name, pci_id_tbl[chip_idx].name, ioaddr, dev->irq); @@ -557,19 +574,24 @@ printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x.\n", dev->dev_addr[i]); - return 0; +out: + return ret; +err_out_unmap_rx: + pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma); err_out_unmap_tx: pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma); err_out_iounmap: #ifndef USE_IO_OPS iounmap(ioaddr); -err_out_free_res: -#endif - pci_release_regions(pdev); err_out_free_netdev: +#endif free_netdev(dev); - return -ENODEV; +err_out_free_res: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); + goto out; } /* Serial EEPROM section. */ @@ -595,6 +617,38 @@ #define EE_READ256_CMD (6 << 8) #define EE_ERASE_CMD (7 << 6) +static void epic_disable_int(struct net_device *dev, struct epic_private *ep) +{ + long ioaddr = dev->base_addr; + + outl(0x00000000, ioaddr + INTMASK); +} + +static inline void __epic_pci_commit(long ioaddr) +{ +#ifndef USE_IO_OPS + inl(ioaddr + INTMASK); +#endif +} + +static inline void epic_napi_irq_off(struct net_device *dev, + struct epic_private *ep) +{ + long ioaddr = dev->base_addr; + + outl(ep->irq_mask & ~EpicNapiEvent, ioaddr + INTMASK); + __epic_pci_commit(ioaddr); +} + +static inline void epic_napi_irq_on(struct net_device *dev, + struct epic_private *ep) +{ + long ioaddr = dev->base_addr; + + /* No need to commit possible posted write */ + outl(ep->irq_mask | EpicNapiEvent, ioaddr + INTMASK); +} + static int __devinit read_eeprom(long ioaddr, int location) { int i; @@ -755,9 +809,8 @@ /* Enable interrupts by setting the interrupt mask. */ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) - | CntFull | TxUnderrun | TxDone | TxEmpty - | RxError | RxOverflow | RxFull | RxHeader | RxDone, - ioaddr + INTMASK); + | CntFull | TxUnderrun + | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK); if (debug > 1) printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x " @@ -798,7 +851,7 @@ } /* Remove the packets on the Rx queue. */ - epic_rx(dev); + epic_rx(dev, RX_RING_SIZE); } static void epic_restart(struct net_device *dev) @@ -844,9 +897,9 @@ /* Enable interrupts by setting the interrupt mask. */ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) - | CntFull | TxUnderrun | TxDone | TxEmpty - | RxError | RxOverflow | RxFull | RxHeader | RxDone, - ioaddr + INTMASK); + | CntFull | TxUnderrun + | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK); + printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x" " interrupt %4.4x.\n", dev->name, (int)inl(ioaddr + COMMAND), (int)inl(ioaddr + GENCTL), @@ -932,7 +985,6 @@ int i; ep->tx_full = 0; - ep->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; ep->dirty_tx = ep->cur_tx = 0; ep->cur_rx = ep->dirty_rx = 0; ep->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); @@ -1032,6 +1084,76 @@ return 0; } +static void epic_tx_error(struct net_device *dev, struct epic_private *ep, + int status) +{ + struct net_device_stats *stats = &ep->stats; + +#ifndef final_version + /* There was an major error, log it. */ + if (debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif + stats->tx_errors++; + if (status & 0x1050) + stats->tx_aborted_errors++; + if (status & 0x0008) + stats->tx_carrier_errors++; + if (status & 0x0040) + stats->tx_window_errors++; + if (status & 0x0010) + stats->tx_fifo_errors++; +} + +static void epic_tx(struct net_device *dev, struct epic_private *ep) +{ + unsigned int dirty_tx, cur_tx; + + /* + * Note: if this lock becomes a problem we can narrow the locked + * region at the cost of occasionally grabbing the lock more times. + */ + cur_tx = ep->cur_tx; + for (dirty_tx = ep->dirty_tx; cur_tx - dirty_tx > 0; dirty_tx++) { + struct sk_buff *skb; + int entry = dirty_tx % TX_RING_SIZE; + int txstatus = le32_to_cpu(ep->tx_ring[entry].txstatus); + + if (txstatus & DescOwn) + break; /* It still hasn't been Txed */ + + if (likely(txstatus & 0x0001)) { + ep->stats.collisions += (txstatus >> 8) & 15; + ep->stats.tx_packets++; + ep->stats.tx_bytes += ep->tx_skbuff[entry]->len; + } else + epic_tx_error(dev, ep, txstatus); + + /* Free the original skb. */ + skb = ep->tx_skbuff[entry]; + pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr, + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb_irq(skb); + ep->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_WARNING + "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, cur_tx, ep->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + ep->dirty_tx = dirty_tx; + if (ep->tx_full && cur_tx - dirty_tx < TX_QUEUE_LEN - 4) { + /* The ring is no longer full, allow new TX entries. */ + ep->tx_full = 0; + netif_wake_queue(dev); + } +} + /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static irqreturn_t epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs) @@ -1039,135 +1161,71 @@ struct net_device *dev = dev_instance; struct epic_private *ep = dev->priv; long ioaddr = dev->base_addr; - int status, boguscnt = max_interrupt_work; unsigned int handled = 0; + int status; - do { - status = inl(ioaddr + INTSTAT); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(status & 0x00007fff, ioaddr + INTSTAT); - - if (debug > 4) - printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new " - "intstat=%#8.8x.\n", - dev->name, status, (int)inl(ioaddr + INTSTAT)); - - if ((status & IntrSummary) == 0) - break; - handled = 1; + status = inl(ioaddr + INTSTAT); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(status & EpicNormalEvent, ioaddr + INTSTAT); - if (status & (RxDone | RxStarted | RxEarlyWarn | RxOverflow)) - epic_rx(dev); + if (debug > 4) { + printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new " + "intstat=%#8.8x.\n", dev->name, status, + (int)inl(ioaddr + INTSTAT)); + } - if (status & (TxEmpty | TxDone)) { - unsigned int dirty_tx, cur_tx; + if ((status & IntrSummary) == 0) + goto out; - /* Note: if this lock becomes a problem we can narrow the locked - region at the cost of occasionally grabbing the lock more - times. */ - spin_lock(&ep->lock); - cur_tx = ep->cur_tx; - dirty_tx = ep->dirty_tx; - for (; cur_tx - dirty_tx > 0; dirty_tx++) { - struct sk_buff *skb; - int entry = dirty_tx % TX_RING_SIZE; - int txstatus = le32_to_cpu(ep->tx_ring[entry].txstatus); + handled = 1; - if (txstatus & DescOwn) - break; /* It still hasn't been Txed */ + if ((status & EpicNapiEvent) && !ep->reschedule_in_poll) { + spin_lock(&ep->napi_lock); + if (netif_rx_schedule_prep(dev)) { + epic_napi_irq_off(dev, ep); + __netif_rx_schedule(dev); + } else + ep->reschedule_in_poll++; + spin_unlock(&ep->napi_lock); + } + status &= ~EpicNapiEvent; - if ( ! (txstatus & 0x0001)) { - /* There was an major error, log it. */ -#ifndef final_version - if (debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, txstatus); -#endif - ep->stats.tx_errors++; - if (txstatus & 0x1050) ep->stats.tx_aborted_errors++; - if (txstatus & 0x0008) ep->stats.tx_carrier_errors++; - if (txstatus & 0x0040) ep->stats.tx_window_errors++; - if (txstatus & 0x0010) ep->stats.tx_fifo_errors++; - } else { - ep->stats.collisions += (txstatus >> 8) & 15; - ep->stats.tx_packets++; - ep->stats.tx_bytes += ep->tx_skbuff[entry]->len; - } - - /* Free the original skb. */ - skb = ep->tx_skbuff[entry]; - pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr, - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb_irq(skb); - ep->tx_skbuff[entry] = 0; - } + /* Check uncommon events all at once. */ + if (status & (CntFull | TxUnderrun | PCIBusErr170 | PCIBusErr175)) { + if (status == EpicRemoved) + goto out; -#ifndef final_version - if (cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_WARNING "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, cur_tx, ep->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - ep->dirty_tx = dirty_tx; - if (ep->tx_full - && cur_tx - dirty_tx < TX_QUEUE_LEN - 4) { - /* The ring is no longer full, allow new TX entries. */ - ep->tx_full = 0; - spin_unlock(&ep->lock); - netif_wake_queue(dev); - } else - spin_unlock(&ep->lock); - } + /* Always update the error counts to avoid overhead later. */ + ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); + ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); + ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); - /* Check uncommon events all at once. */ - if (status & (CntFull | TxUnderrun | RxOverflow | RxFull | - PCIBusErr170 | PCIBusErr175)) { - if (status == 0xffffffff) /* Chip failed or removed (CardBus). */ - break; - /* Always update the error counts to avoid overhead later. */ - ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); - ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); - ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); - - if (status & TxUnderrun) { /* Tx FIFO underflow. */ - ep->stats.tx_fifo_errors++; - outl(ep->tx_threshold += 128, ioaddr + TxThresh); - /* Restart the transmit process. */ - outl(RestartTx, ioaddr + COMMAND); - } - if (status & RxOverflow) { /* Missed a Rx frame. */ - ep->stats.rx_errors++; - } - if (status & (RxOverflow | RxFull)) - outw(RxQueued, ioaddr + COMMAND); - if (status & PCIBusErr170) { - printk(KERN_ERR "%s: PCI Bus Error! EPIC status %4.4x.\n", - dev->name, status); - epic_pause(dev); - epic_restart(dev); - } - /* Clear all error sources. */ - outl(status & 0x7f18, ioaddr + INTSTAT); + if (status & TxUnderrun) { /* Tx FIFO underflow. */ + ep->stats.tx_fifo_errors++; + outl(ep->tx_threshold += 128, ioaddr + TxThresh); + /* Restart the transmit process. */ + outl(RestartTx, ioaddr + COMMAND); } - if (--boguscnt < 0) { - printk(KERN_ERR "%s: Too much work at interrupt, " - "IntrStatus=0x%8.8x.\n", - dev->name, status); - /* Clear all interrupt sources. */ - outl(0x0001ffff, ioaddr + INTSTAT); - break; + if (status & PCIBusErr170) { + printk(KERN_ERR "%s: PCI Bus Error! status %4.4x.\n", + dev->name, status); + epic_pause(dev); + epic_restart(dev); } - } while (1); + /* Clear all error sources. */ + outl(status & 0x7f18, ioaddr + INTSTAT); + } - if (debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, status); +out: + if (debug > 3) { + printk(KERN_DEBUG "%s: exit interrupt, intr_status=%#4.4x.\n", + dev->name, status); + } return IRQ_RETVAL(handled); } -static int epic_rx(struct net_device *dev) +static int epic_rx(struct net_device *dev, int budget) { struct epic_private *ep = dev->priv; int entry = ep->cur_rx % RX_RING_SIZE; @@ -1177,6 +1235,10 @@ if (debug > 4) printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry, ep->rx_ring[entry].rxstatus); + + if (rx_work_limit > budget) + rx_work_limit = budget; + /* If we own the next entry, it's a new packet. Send it up. */ while ((ep->rx_ring[entry].rxstatus & cpu_to_le32(DescOwn)) == 0) { int status = le32_to_cpu(ep->rx_ring[entry].rxstatus); @@ -1232,7 +1294,7 @@ ep->rx_skbuff[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); + netif_receive_skb(skb); dev->last_rx = jiffies; ep->stats.rx_packets++; ep->stats.rx_bytes += pkt_len; @@ -1260,6 +1322,65 @@ return work_done; } +static void epic_rx_err(struct net_device *dev, struct epic_private *ep) +{ + long ioaddr = dev->base_addr; + int status; + + status = inl(ioaddr + INTSTAT); + + if (status == EpicRemoved) + return; + if (status & RxOverflow) /* Missed a Rx frame. */ + ep->stats.rx_errors++; + if (status & (RxOverflow | RxFull)) + outw(RxQueued, ioaddr + COMMAND); +} + +static int epic_poll(struct net_device *dev, int *budget) +{ + struct epic_private *ep = dev->priv; + int work_done, orig_budget; + long ioaddr = dev->base_addr; + + orig_budget = (*budget > dev->quota) ? dev->quota : *budget; + +rx_action: + + epic_tx(dev, ep); + + work_done = epic_rx(dev, *budget); + + epic_rx_err(dev, ep); + + *budget -= work_done; + dev->quota -= work_done; + + if (netif_running(dev) && (work_done < orig_budget)) { + unsigned long flags; + int more; + + /* A bit baroque but it avoids a (space hungry) spin_unlock */ + + spin_lock_irqsave(&ep->napi_lock, flags); + + more = ep->reschedule_in_poll; + if (!more) { + __netif_rx_complete(dev); + outl(EpicNapiEvent, ioaddr + INTSTAT); + epic_napi_irq_on(dev, ep); + } else + ep->reschedule_in_poll--; + + spin_unlock_irqrestore(&ep->napi_lock, flags); + + if (more) + goto rx_action; + } + + return (work_done >= orig_budget); +} + static int epic_close(struct net_device *dev) { long ioaddr = dev->base_addr; @@ -1274,9 +1395,13 @@ dev->name, (int)inl(ioaddr + INTSTAT)); del_timer_sync(&ep->timer); - epic_pause(dev); + + epic_disable_int(dev, ep); + free_irq(dev->irq, dev); + epic_pause(dev); + /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { skb = ep->rx_skbuff[i]; @@ -1497,6 +1622,7 @@ #endif pci_release_regions(pdev); free_netdev(dev); + pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); /* pci_power_off(pdev, -1); */ } diff -Nru a/drivers/net/mv64340_eth.c b/drivers/net/mv64340_eth.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/mv64340_eth.c 2004-06-20 13:15:18 -07:00 @@ -0,0 +1,2702 @@ +/* + * drivers/net/mv64340_eth.c - Driver for MV64340X ethernet ports + * Copyright (C) 2002 Matthew Dharm + * + * Based on the 64360 driver from: + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * Copyright (C) 2003 PMC-Sierra, Inc., + * written by Manish Lachwani (lachwani@pmc-sierra.com) + * + * Copyright (C) 2003 Ralf Baechle + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "mv64340_eth.h" + +/* + * The first part is the high level driver of the gigE ethernet ports. + */ + +/* Definition for configuring driver */ +#undef MV64340_RX_QUEUE_FILL_ON_TASK + +/* Constants */ +#define EXTRA_BYTES 32 +#define WRAP ETH_HLEN + 2 + 4 + 16 +#define BUFFER_MTU dev->mtu + WRAP +#define INT_CAUSE_UNMASK_ALL 0x0007ffff +#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff +#ifdef MV64340_RX_FILL_ON_TASK +#define INT_CAUSE_MASK_ALL 0x00000000 +#define INT_CAUSE_CHECK_BITS INT_CAUSE_UNMASK_ALL +#define INT_CAUSE_CHECK_BITS_EXT INT_CAUSE_UNMASK_ALL_EXT +#endif + +/* Static function declarations */ +static int mv64340_eth_real_open(struct net_device *); +static int mv64340_eth_real_stop(struct net_device *); +static int mv64340_eth_change_mtu(struct net_device *, int); +static struct net_device_stats *mv64340_eth_get_stats(struct net_device *); +static void eth_port_init_mac_tables(unsigned int eth_port_num); +#ifdef MV64340_NAPI +static int mv64340_poll(struct net_device *dev, int *budget); +#endif + +unsigned char prom_mac_addr_base[6]; +unsigned long mv64340_sram_base; + +/* + * Changes MTU (maximum transfer unit) of the gigabit ethenret port + * + * Input : pointer to ethernet interface network device structure + * new mtu size + * Output : 0 upon success, -EINVAL upon failure + */ +static int mv64340_eth_change_mtu(struct net_device *dev, int new_mtu) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&mp->lock, flags); + + if ((new_mtu > 9500) || (new_mtu < 64)) { + spin_unlock_irqrestore(&mp->lock, flags); + return -EINVAL; + } + + dev->mtu = new_mtu; + /* + * Stop then re-open the interface. This will allocate RX skb's with + * the new MTU. + * There is a possible danger that the open will not successed, due + * to memory is full, which might fail the open function. + */ + if (netif_running(dev)) { + if (mv64340_eth_real_stop(dev)) + printk(KERN_ERR + "%s: Fatal error on stopping device\n", + dev->name); + if (mv64340_eth_real_open(dev)) + printk(KERN_ERR + "%s: Fatal error on opening device\n", + dev->name); + } + + spin_unlock_irqrestore(&mp->lock, flags); + return 0; +} + +/* + * mv64340_eth_rx_task + * + * Fills / refills RX queue on a certain gigabit ethernet port + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_rx_task(void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct mv64340_private *mp = netdev_priv(dev); + struct pkt_info pkt_info; + struct sk_buff *skb; + + if (test_and_set_bit(0, &mp->rx_task_busy)) + panic("%s: Error in test_set_bit / clear_bit", dev->name); + + while (mp->rx_ring_skbs < (mp->rx_ring_size - 5)) { + /* The +8 for buffer allignment and another 32 byte extra */ + + skb = dev_alloc_skb(BUFFER_MTU + 8 + EXTRA_BYTES); + if (!skb) + /* Better luck next time */ + break; + mp->rx_ring_skbs++; + pkt_info.cmd_sts = ETH_RX_ENABLE_INTERRUPT; + pkt_info.byte_cnt = dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES; + /* Allign buffer to 8 bytes */ + if (pkt_info.byte_cnt & ~0x7) { + pkt_info.byte_cnt &= ~0x7; + pkt_info.byte_cnt += 8; + } + pkt_info.buf_ptr = + pci_map_single(0, skb->data, + dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES, + PCI_DMA_FROMDEVICE); + pkt_info.return_info = skb; + if (eth_rx_return_buff(mp, &pkt_info) != ETH_OK) { + printk(KERN_ERR + "%s: Error allocating RX Ring\n", dev->name); + break; + } + skb_reserve(skb, 2); + } + clear_bit(0, &mp->rx_task_busy); + /* + * If RX ring is empty of SKB, set a timer to try allocating + * again in a later time . + */ + if ((mp->rx_ring_skbs == 0) && (mp->rx_timer_flag == 0)) { + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name); + /* After 100mSec */ + mp->timeout.expires = jiffies + (HZ / 10); + add_timer(&mp->timeout); + mp->rx_timer_flag = 1; + } +#if MV64340_RX_QUEUE_FILL_ON_TASK + else { + /* Return interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(mp->port_num), + INT_CAUSE_UNMASK_ALL); + } +#endif +} + +/* + * mv64340_eth_rx_task_timer_wrapper + * + * Timer routine to wake up RX queue filling task. This function is + * used only in case the RX queue is empty, and all alloc_skb has + * failed (due to out of memory event). + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_rx_task_timer_wrapper(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct mv64340_private *mp = netdev_priv(dev); + + mp->rx_timer_flag = 0; + mv64340_eth_rx_task((void *) data); +} + + +/* + * mv64340_eth_update_mac_address + * + * Update the MAC address of the port in the address table + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_update_mac_address(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + + eth_port_init_mac_tables(port_num); + memcpy(mp->port_mac_addr, dev->dev_addr, 6); + eth_port_uc_addr_set(port_num, mp->port_mac_addr); +} + +/* + * mv64340_eth_set_rx_mode + * + * Change from promiscuos to regular rx mode + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_set_rx_mode(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + if (dev->flags & IFF_PROMISC) { + ethernet_set_config_reg + (mp->port_num, + ethernet_get_config_reg(mp->port_num) | + ETH_UNICAST_PROMISCUOUS_MODE); + } else { + ethernet_set_config_reg + (mp->port_num, + ethernet_get_config_reg(mp->port_num) & + ~(unsigned int) ETH_UNICAST_PROMISCUOUS_MODE); + } +} + + +/* + * mv64340_eth_set_mac_address + * + * Change the interface's mac address. + * No special hardware thing should be done because interface is always + * put in promiscuous mode. + * + * Input : pointer to ethernet interface network device structure and + * a pointer to the designated entry to be added to the cache. + * Output : zero upon success, negative upon failure + */ +static int mv64340_eth_set_mac_address(struct net_device *dev, void *addr) +{ + int i; + + for (i = 0; i < 6; i++) + /* +2 is for the offset of the HW addr type */ + dev->dev_addr[i] = ((unsigned char *) addr)[i + 2]; + mv64340_eth_update_mac_address(dev); + return 0; +} + +/* + * mv64340_eth_tx_timeout + * + * Called upon a timeout on transmitting a packet + * + * Input : pointer to ethernet interface network device structure. + * Output : N/A + */ +static void mv64340_eth_tx_timeout(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + printk(KERN_INFO "%s: TX timeout ", dev->name); + + /* Do the reset outside of interrupt context */ + schedule_work(&mp->tx_timeout_task); +} + +/* + * mv64340_eth_tx_timeout_task + * + * Actual routine to reset the adapter when a timeout on Tx has occurred + */ +static void mv64340_eth_tx_timeout_task(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + netif_device_detach(dev); + eth_port_reset(mp->port_num); + eth_port_start(mp); + netif_device_attach(dev); +} + +/* + * mv64340_eth_free_tx_queue + * + * Input : dev - a pointer to the required interface + * + * Output : 0 if was able to release skb , nonzero otherwise + */ +static int mv64340_eth_free_tx_queue(struct net_device *dev, + unsigned int eth_int_cause_ext) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + struct pkt_info pkt_info; + int released = 1; + + if (!(eth_int_cause_ext & (BIT0 | BIT8))) + return released; + + spin_lock(&mp->lock); + + /* Check only queue 0 */ + while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { + if (pkt_info.cmd_sts & BIT0) { + printk("%s: Error in TX\n", dev->name); + stats->tx_errors++; + } + + /* + * If return_info is different than 0, release the skb. + * The case where return_info is not 0 is only in case + * when transmitted a scatter/gather packet, where only + * last skb releases the whole chain. + */ + if (pkt_info.return_info) { + dev_kfree_skb_irq((struct sk_buff *) + pkt_info.return_info); + released = 0; + if (skb_shinfo(pkt_info.return_info)->nr_frags) + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, PCI_DMA_TODEVICE); + + if (mp->tx_ring_skbs != 1) + mp->tx_ring_skbs--; + } else + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, PCI_DMA_TODEVICE); + + /* + * Decrement the number of outstanding skbs counter on + * the TX queue. + */ + if (mp->tx_ring_skbs == 0) + panic("ERROR - TX outstanding SKBs counter is corrupted"); + + } + + spin_unlock(&mp->lock); + + return released; +} + +/* + * mv64340_eth_receive + * + * This function is forward packets that are received from the port's + * queues toward kernel core or FastRoute them to another interface. + * + * Input : dev - a pointer to the required interface + * max - maximum number to receive (0 means unlimted) + * + * Output : number of served packets + */ +#ifdef MV64340_NAPI +static int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max, + int budget) +#else +static int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max) +#endif +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + unsigned int received_packets = 0; + struct sk_buff *skb; + struct pkt_info pkt_info; + +#ifdef MV64340_NAPI + while (eth_port_receive(mp, &pkt_info) == ETH_OK && budget > 0) { +#else + while ((--max) && eth_port_receive(mp, &pkt_info) == ETH_OK) { +#endif + mp->rx_ring_skbs--; + received_packets++; +#ifdef MV64340_NAPI + budget--; +#endif + /* Update statistics. Note byte count includes 4 byte CRC count */ + stats->rx_packets++; + stats->rx_bytes += pkt_info.byte_cnt; + skb = (struct sk_buff *) pkt_info.return_info; + /* + * In case received a packet without first / last bits on OR + * the error summary bit is on, the packets needs to be dropeed. + */ + if (((pkt_info.cmd_sts + & (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) != + (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) + || (pkt_info.cmd_sts & ETH_ERROR_SUMMARY)) { + stats->rx_dropped++; + if ((pkt_info.cmd_sts & (ETH_RX_FIRST_DESC | + ETH_RX_LAST_DESC)) != + (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Received packet spread on multiple" + " descriptors\n", + dev->name); + } + if (pkt_info.cmd_sts & ETH_ERROR_SUMMARY) + stats->rx_errors++; + + dev_kfree_skb_irq(skb); + } else { + /* + * The -4 is for the CRC in the trailer of the + * received packet + */ + skb_put(skb, pkt_info.byte_cnt - 4); + skb->dev = dev; + + if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->csum = htons((pkt_info.cmd_sts + & 0x0007fff8) >> 3); + } + skb->protocol = eth_type_trans(skb, dev); +#ifdef MV64340_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + } + } + + return received_packets; +} + +/* + * mv64340_eth_int_handler + * + * Main interrupt handler for the gigbit ethernet ports + * + * Input : irq - irq number (not used) + * dev_id - a pointer to the required interface's data structure + * regs - not used + * Output : N/A + */ + +static irqreturn_t mv64340_eth_int_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mv64340_private *mp = netdev_priv(dev); + u32 eth_int_cause, eth_int_cause_ext = 0; + unsigned int port_num = mp->port_num; + + /* Read interrupt cause registers */ + eth_int_cause = MV_READ(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num)) & + INT_CAUSE_UNMASK_ALL; + + if (eth_int_cause & BIT1) + eth_int_cause_ext = + MV_READ(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num)) & + INT_CAUSE_UNMASK_ALL_EXT; + +#ifdef MV64340_NAPI + if (!(eth_int_cause & 0x0007fffd)) { + /* Dont ack the Rx interrupt */ +#endif + /* + * Clear specific ethernet port intrerrupt registers by + * acknowleding relevant bits. + */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), + ~eth_int_cause); + if (eth_int_cause_ext != 0x0) + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), + ~eth_int_cause_ext); + + /* UDP change : We may need this */ + if ((eth_int_cause_ext & 0x0000ffff) && + (mv64340_eth_free_tx_queue(dev, eth_int_cause_ext) == 0) && + (MV64340_TX_QUEUE_SIZE > mp->tx_ring_skbs + 1)) + netif_wake_queue(dev); +#ifdef MV64340_NAPI + } else { + if (netif_rx_schedule_prep(dev)) { + /* Mask all the interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); + __netif_rx_schedule(dev); + } +#else + { + if (eth_int_cause & (BIT2 | BIT11)) + mv64340_eth_receive_queue(dev, 0); + + /* + * After forwarded received packets to upper layer, add a task + * in an interrupts enabled context that refills the RX ring + * with skb's. + */ +#if MV64340_RX_QUEUE_FILL_ON_TASK + /* Unmask all interrupts on ethernet port */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_MASK_ALL); + queue_task(&mp->rx_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + mp->rx_task.func(dev); +#endif +#endif + } + /* PHY status changed */ + if (eth_int_cause_ext & (BIT16 | BIT20)) { + unsigned int phy_reg_data; + + /* Check Link status on ethernet port */ + eth_port_read_smi_reg(port_num, 1, &phy_reg_data); + if (!(phy_reg_data & 0x20)) { + netif_stop_queue(dev); + } else { + netif_wake_queue(dev); + + /* + * Start all TX queues on ethernet port. This is good in + * case of previous packets where not transmitted, due + * to link down and this command re-enables all TX + * queues. + * Note that it is possible to get a TX resource error + * interrupt after issuing this, since not all TX queues + * are enabled, or has anything to send. + */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), 1); + } + } + + /* + * If no real interrupt occured, exit. + * This can happen when using gigE interrupt coalescing mechanism. + */ + if ((eth_int_cause == 0x0) && (eth_int_cause_ext == 0x0)) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +#ifdef MV64340_COAL + +/* + * eth_port_set_rx_coal - Sets coalescing interrupt mechanism on RX path + * + * DESCRIPTION: + * This routine sets the RX coalescing interrupt mechanism parameter. + * This parameter is a timeout counter, that counts in 64 t_clk + * chunks ; that when timeout event occurs a maskable interrupt + * occurs. + * The parameter is calculated using the tClk of the MV-643xx chip + * , and the required delay of the interrupt in usec. + * + * INPUT: + * unsigned int eth_port_num Ethernet port number + * unsigned int t_clk t_clk of the MV-643xx chip in HZ units + * unsigned int delay Delay in usec + * + * OUTPUT: + * Interrupt coalescing mechanism value is set in MV-643xx chip. + * + * RETURN: + * The interrupt coalescing value set in the gigE port. + * + */ +static unsigned int eth_port_set_rx_coal(unsigned int eth_port_num, + unsigned int t_clk, unsigned int delay) +{ + unsigned int coal = ((t_clk / 1000000) * delay) / 64; + + /* Set RX Coalescing mechanism */ + MV_WRITE(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num), + ((coal & 0x3fff) << 8) | + (MV_READ(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num)) + & 0xffc000ff)); + + return coal; +} +#endif + +/* + * eth_port_set_tx_coal - Sets coalescing interrupt mechanism on TX path + * + * DESCRIPTION: + * This routine sets the TX coalescing interrupt mechanism parameter. + * This parameter is a timeout counter, that counts in 64 t_clk + * chunks ; that when timeout event occurs a maskable interrupt + * occurs. + * The parameter is calculated using the t_cLK frequency of the + * MV-643xx chip and the required delay in the interrupt in uSec + * + * INPUT: + * unsigned int eth_port_num Ethernet port number + * unsigned int t_clk t_clk of the MV-643xx chip in HZ units + * unsigned int delay Delay in uSeconds + * + * OUTPUT: + * Interrupt coalescing mechanism value is set in MV-643xx chip. + * + * RETURN: + * The interrupt coalescing value set in the gigE port. + * + */ +static unsigned int eth_port_set_tx_coal(unsigned int eth_port_num, + unsigned int t_clk, unsigned int delay) +{ + unsigned int coal; + coal = ((t_clk / 1000000) * delay) / 64; + /* Set TX Coalescing mechanism */ + MV_WRITE(MV64340_ETH_TX_FIFO_URGENT_THRESHOLD_REG(eth_port_num), + coal << 4); + return coal; +} + +/* + * mv64340_eth_open + * + * This function is called when openning the network device. The function + * should initialize all the hardware, initialize cyclic Rx/Tx + * descriptors chain and buffers and allocate an IRQ to the network + * device. + * + * Input : a pointer to the network device structure + * + * Output : zero of success , nonzero if fails. + */ + +static int mv64340_eth_open(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + int err = err; + + spin_lock_irq(&mp->lock); + + err = request_irq(dev->irq, mv64340_eth_int_handler, + SA_INTERRUPT | SA_SAMPLE_RANDOM, dev->name, dev); + + if (err) { + printk(KERN_ERR "Can not assign IRQ number to MV64340_eth%d\n", + port_num); + err = -EAGAIN; + goto out; + } + + if (mv64340_eth_real_open(dev)) { + printk("%s: Error opening interface\n", dev->name); + err = -EBUSY; + goto out_free; + } + + spin_unlock_irq(&mp->lock); + + return 0; + +out_free: + free_irq(dev->irq, dev); + +out: + spin_unlock_irq(&mp->lock); + + return err; +} + +/* + * ether_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory. + * + * DESCRIPTION: + * This function prepares a Rx chained list of descriptors and packet + * buffers in a form of a ring. The routine must be called after port + * initialization routine and before port start routine. + * The Ethernet SDMA engine uses CPU bus addresses to access the various + * devices in the system (i.e. DRAM). This function uses the ethernet + * struct 'virtual to physical' routine (set by the user) to set the ring + * with physical addresses. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * int rx_desc_num Number of Rx descriptors + * int rx_buff_size Size of Rx buffer + * unsigned int rx_desc_base_addr Rx descriptors memory area base addr. + * unsigned int rx_buff_base_addr Rx buffer memory area base addr. + * + * OUTPUT: + * The routine updates the Ethernet port control struct with information + * regarding the Rx descriptors and buffers. + * + * RETURN: + * false if the given descriptors memory area is not aligned according to + * Ethernet SDMA specifications. + * true otherwise. + */ +static int ether_init_rx_desc_ring(struct mv64340_private * mp, + unsigned long rx_buff_base_addr) +{ + unsigned long buffer_addr = rx_buff_base_addr; + volatile struct eth_rx_desc *p_rx_desc; + int rx_desc_num = mp->rx_ring_size; + unsigned long rx_desc_base_addr = (unsigned long) mp->p_rx_desc_area; + int rx_buff_size = 1536; /* Dummy, will be replaced later */ + int i; + + p_rx_desc = (struct eth_rx_desc *) rx_desc_base_addr; + + /* Rx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */ + if (rx_buff_base_addr & 0xf) + return 0; + + /* Rx buffers are limited to 64K bytes and Minimum size is 8 bytes */ + if ((rx_buff_size < 8) || (rx_buff_size > RX_BUFFER_MAX_SIZE)) + return 0; + + /* Rx buffers must be 64-bit aligned. */ + if ((rx_buff_base_addr + rx_buff_size) & 0x7) + return 0; + + /* initialize the Rx descriptors ring */ + for (i = 0; i < rx_desc_num; i++) { + p_rx_desc[i].buf_size = rx_buff_size; + p_rx_desc[i].byte_cnt = 0x0000; + p_rx_desc[i].cmd_sts = + ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT; + p_rx_desc[i].next_desc_ptr = + (struct eth_rx_desc *) mp->rx_desc_dma + + (i + 1) % rx_desc_num; + p_rx_desc[i].buf_ptr = buffer_addr; + + mp->rx_skb[i] = NULL; + buffer_addr += rx_buff_size; + } + + /* Save Rx desc pointer to driver struct. */ + mp->rx_curr_desc_q = 0; + mp->rx_used_desc_q = 0; + + mp->rx_desc_area_size = rx_desc_num * sizeof(struct eth_rx_desc); + + mp->port_rx_queue_command |= 1; + + return 1; +} + +/* + * ether_init_tx_desc_ring - Curve a Tx chain desc list and buffer in memory. + * + * DESCRIPTION: + * This function prepares a Tx chained list of descriptors and packet + * buffers in a form of a ring. The routine must be called after port + * initialization routine and before port start routine. + * The Ethernet SDMA engine uses CPU bus addresses to access the various + * devices in the system (i.e. DRAM). This function uses the ethernet + * struct 'virtual to physical' routine (set by the user) to set the ring + * with physical addresses. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * int tx_desc_num Number of Tx descriptors + * int tx_buff_size Size of Tx buffer + * unsigned int tx_desc_base_addr Tx descriptors memory area base addr. + * + * OUTPUT: + * The routine updates the Ethernet port control struct with information + * regarding the Tx descriptors and buffers. + * + * RETURN: + * false if the given descriptors memory area is not aligned according to + * Ethernet SDMA specifications. + * true otherwise. + */ +static int ether_init_tx_desc_ring(struct mv64340_private *mp) +{ + unsigned long tx_desc_base_addr = (unsigned long) mp->p_tx_desc_area; + int tx_desc_num = mp->tx_ring_size; + struct eth_tx_desc *p_tx_desc; + int i; + + /* Tx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */ + if (tx_desc_base_addr & 0xf) + return 0; + + /* save the first desc pointer to link with the last descriptor */ + p_tx_desc = (struct eth_tx_desc *) tx_desc_base_addr; + + /* Initialize the Tx descriptors ring */ + for (i = 0; i < tx_desc_num; i++) { + p_tx_desc[i].byte_cnt = 0x0000; + p_tx_desc[i].l4i_chk = 0x0000; + p_tx_desc[i].cmd_sts = 0x00000000; + p_tx_desc[i].next_desc_ptr = + (struct eth_tx_desc *) mp->tx_desc_dma + + (i + 1) % tx_desc_num; + p_tx_desc[i].buf_ptr = 0x00000000; + mp->tx_skb[i] = NULL; + } + + /* Set Tx desc pointer in driver struct. */ + mp->tx_curr_desc_q = 0; + mp->tx_used_desc_q = 0; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + mp->tx_first_desc_q = 0; +#endif + /* Init Tx ring base and size parameters */ + mp->tx_desc_area_size = tx_desc_num * sizeof(struct eth_tx_desc); + + /* Add the queue to the list of Tx queues of this port */ + mp->port_tx_queue_command |= 1; + + return 1; +} + +/* Helper function for mv64340_eth_open */ +static int mv64340_eth_real_open(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + u32 phy_reg_data; + unsigned int size; + + /* Stop RX Queues */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Clear the ethernet port interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + + /* Unmask RX buffer and TX end interrupt */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL); + + /* Unmask phy and link status changes interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL_EXT); + + /* Set the MAC Address */ + memcpy(mp->port_mac_addr, dev->dev_addr, 6); + + eth_port_init(mp); + + INIT_WORK(&mp->rx_task, (void (*)(void *)) mv64340_eth_rx_task, dev); + + memset(&mp->timeout, 0, sizeof(struct timer_list)); + mp->timeout.function = mv64340_eth_rx_task_timer_wrapper; + mp->timeout.data = (unsigned long) dev; + + mp->rx_task_busy = 0; + mp->rx_timer_flag = 0; + + /* Allocate TX ring */ + mp->tx_ring_skbs = 0; + mp->tx_ring_size = MV64340_TX_QUEUE_SIZE; + size = mp->tx_ring_size * sizeof(struct eth_tx_desc); + mp->tx_desc_area_size = size; + + /* Assumes allocated ring is 16 bytes alligned */ + mp->p_tx_desc_area = pci_alloc_consistent(NULL, size, &mp->tx_desc_dma); + if (!mp->p_tx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n", + dev->name, size); + return -ENOMEM; + } + memset((void *) mp->p_tx_desc_area, 0, mp->tx_desc_area_size); + + /* Dummy will be replaced upon real tx */ + ether_init_tx_desc_ring(mp); + + /* Allocate RX ring */ + /* Meantime RX Ring are fixed - but must be configurable by user */ + mp->rx_ring_size = MV64340_RX_QUEUE_SIZE; + mp->rx_ring_skbs = 0; + size = mp->rx_ring_size * sizeof(struct eth_rx_desc); + mp->rx_desc_area_size = size; + + /* Assumes allocated ring is 16 bytes aligned */ + + mp->p_rx_desc_area = pci_alloc_consistent(NULL, size, &mp->rx_desc_dma); + + if (!mp->p_rx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Rx ring (size %d bytes)\n", + dev->name, size); + printk(KERN_ERR "%s: Freeing previously allocated TX queues...", + dev->name); + pci_free_consistent(0, mp->tx_desc_area_size, + (void *) mp->p_tx_desc_area, + mp->tx_desc_dma); + return -ENOMEM; + } + memset(mp->p_rx_desc_area, 0, size); + + if (!(ether_init_rx_desc_ring(mp, 0))) + panic("%s: Error initializing RX Ring", dev->name); + + mv64340_eth_rx_task(dev); /* Fill RX ring with skb's */ + + eth_port_start(mp); + + /* Interrupt Coalescing */ + +#ifdef MV64340_COAL + mp->rx_int_coal = + eth_port_set_rx_coal(port_num, 133000000, MV64340_RX_COAL); +#endif + + mp->tx_int_coal = + eth_port_set_tx_coal (port_num, 133000000, MV64340_TX_COAL); + + /* Increase the Rx side buffer size */ + + MV_WRITE (MV64340_ETH_PORT_SERIAL_CONTROL_REG(port_num), (0x5 << 17) | + (MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG(port_num)) + & 0xfff1ffff)); + + /* Check Link status on phy */ + eth_port_read_smi_reg(port_num, 1, &phy_reg_data); + if (!(phy_reg_data & 0x20)) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + return 0; +} + +static void mv64340_eth_free_tx_rings(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + unsigned int curr; + + /* Stop Tx Queues */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Free TX rings */ + /* Free outstanding skb's on TX rings */ + for (curr = 0; + (mp->tx_ring_skbs) && (curr < MV64340_TX_QUEUE_SIZE); + curr++) { + if (mp->tx_skb[curr]) { + dev_kfree_skb(mp->tx_skb[curr]); + mp->tx_ring_skbs--; + } + } + if (mp->tx_ring_skbs != 0) + printk("%s: Error on Tx descriptor free - could not free %d" + " descriptors\n", dev->name, + mp->tx_ring_skbs); + pci_free_consistent(0, mp->tx_desc_area_size, + (void *) mp->p_tx_desc_area, mp->tx_desc_dma); +} + +static void mv64340_eth_free_rx_rings(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + int curr; + + /* Stop RX Queues */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Free RX rings */ + /* Free preallocated skb's on RX rings */ + for (curr = 0; + mp->rx_ring_skbs && (curr < MV64340_RX_QUEUE_SIZE); + curr++) { + if (mp->rx_skb[curr]) { + dev_kfree_skb(mp->rx_skb[curr]); + mp->rx_ring_skbs--; + } + } + + if (mp->rx_ring_skbs != 0) + printk(KERN_ERR + "%s: Error in freeing Rx Ring. %d skb's still" + " stuck in RX Ring - ignoring them\n", dev->name, + mp->rx_ring_skbs); + pci_free_consistent(0, mp->rx_desc_area_size, + (void *) mp->p_rx_desc_area, + mp->rx_desc_dma); +} + +/* + * mv64340_eth_stop + * + * This function is used when closing the network device. + * It updates the hardware, + * release all memory that holds buffers and descriptors and release the IRQ. + * Input : a pointer to the device structure + * Output : zero if success , nonzero if fails + */ + +/* Helper function for mv64340_eth_stop */ + +static int mv64340_eth_real_stop(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + + netif_stop_queue(dev); + + mv64340_eth_free_tx_rings(dev); + mv64340_eth_free_rx_rings(dev); + + eth_port_reset(mp->port_num); + + /* Disable ethernet port interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + + /* Mask RX buffer and TX end interrupt */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), 0); + + /* Mask phy and link status changes interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); + + return 0; +} + +static int mv64340_eth_stop(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + spin_lock_irq(&mp->lock); + + mv64340_eth_real_stop(dev); + + free_irq(dev->irq, dev); + spin_unlock_irq(&mp->lock); + + return 0; +} + +#ifdef MV64340_NAPI +static void mv64340_tx(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct pkt_info pkt_info; + + while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { + if (pkt_info.return_info) { + dev_kfree_skb_irq((struct sk_buff *) + pkt_info.return_info); + if (skb_shinfo(pkt_info.return_info)->nr_frags) + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, + PCI_DMA_TODEVICE); + + if (mp->tx_ring_skbs != 1) + mp->tx_ring_skbs--; + } else + pci_unmap_page(NULL, pkt_info.buf_ptr, pkt_info.byte_cnt, + PCI_DMA_TODEVICE); + } + + if (netif_queue_stopped(dev) && + MV64340_TX_QUEUE_SIZE > mp->tx_ring_skbs + 1) + netif_wake_queue(dev); +} + +/* + * mv64340_poll + * + * This function is used in case of NAPI + */ +static int mv64340_poll(struct net_device *dev, int *budget) +{ + struct mv64340_private *mp = netdev_priv(dev); + int done = 1, orig_budget, work_done; + unsigned int port_num = mp->port_num; + unsigned long flags; + +#ifdef MV64340_TX_FAST_REFILL + if (++mp->tx_clean_threshold > 5) { + spin_lock_irqsave(&mp->lock, flags); + mv64340_tx(dev); + mp->tx_clean_threshold = 0; + spin_unlock_irqrestore(&mp->lock, flags); + } +#endif + + if ((u32)(MV_READ(MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(port_num))) != (u32)mp->rx_used_desc_q) { + orig_budget = *budget; + if (orig_budget > dev->quota) + orig_budget = dev->quota; + work_done = mv64340_eth_receive_queue(dev, 0, orig_budget); + mp->rx_task.func(dev); + *budget -= work_done; + dev->quota -= work_done; + if (work_done >= orig_budget) + done = 0; + } + + if (done) { + spin_lock_irqsave(&mp->lock, flags); + __netif_rx_complete(dev); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL); + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL_EXT); + spin_unlock_irqrestore(&mp->lock, flags); + } + + return done ? 0 : 1; +} +#endif + +/* + * mv64340_eth_start_xmit + * + * This function is queues a packet in the Tx descriptor for + * required port. + * + * Input : skb - a pointer to socket buffer + * dev - a pointer to the required port + * + * Output : zero upon success + */ +static int mv64340_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + ETH_FUNC_RET_STATUS status; + unsigned long flags; + struct pkt_info pkt_info; + + if (netif_queue_stopped(dev)) { + printk(KERN_ERR + "%s: Tried sending packet when interface is stopped\n", + dev->name); + return 1; + } + + /* This is a hard error, log it. */ + if ((MV64340_TX_QUEUE_SIZE - mp->tx_ring_skbs) <= + (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + printk(KERN_ERR + "%s: Bug in mv64340_eth - Trying to transmit when" + " queue full !\n", dev->name); + return 1; + } + + /* Paranoid check - this shouldn't happen */ + if (skb == NULL) { + stats->tx_dropped++; + return 1; + } + + spin_lock_irqsave(&mp->lock, flags); + + /* Update packet info data structure -- DMA owned, first last */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + if (!skb_shinfo(skb)->nr_frags || (skb_shinfo(skb)->nr_frags > 3)) { +#endif + pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | + ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC; + + pkt_info.byte_cnt = skb->len; + pkt_info.buf_ptr = pci_map_single(0, skb->data, skb->len, + PCI_DMA_TODEVICE); + + + pkt_info.return_info = skb; + status = eth_port_send(mp, &pkt_info); + if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL)) + printk(KERN_ERR "%s: Error on transmitting packet\n", + dev->name); + mp->tx_ring_skbs++; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + } else { + unsigned int frag; + u32 ipheader; + + /* first frag which is skb header */ + pkt_info.byte_cnt = skb_headlen(skb); + pkt_info.buf_ptr = pci_map_single(0, skb->data, + skb_headlen(skb), PCI_DMA_TODEVICE); + pkt_info.return_info = 0; + ipheader = skb->nh.iph->ihl << 11; + pkt_info.cmd_sts = ETH_TX_FIRST_DESC | + ETH_GEN_TCP_UDP_CHECKSUM | + ETH_GEN_IP_V_4_CHECKSUM | + ipheader; + /* CPU already calculated pseudo header checksum. So, use it */ + pkt_info.l4i_chk = skb->h.th->check; + status = eth_port_send(mp, &pkt_info); + if (status != ETH_OK) { + if ((status == ETH_ERROR)) + printk(KERN_ERR "%s: Error on transmitting packet\n", dev->name); + if (status == ETH_QUEUE_FULL) + printk("Error on Queue Full \n"); + if (status == ETH_QUEUE_LAST_RESOURCE) + printk("Tx resource error \n"); + } + + /* Check for the remaining frags */ + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; + pkt_info.l4i_chk = 0x0000; + pkt_info.cmd_sts = 0x00000000; + + /* Last Frag enables interrupt and frees the skb */ + if (frag == (skb_shinfo(skb)->nr_frags - 1)) { + pkt_info.cmd_sts |= ETH_TX_ENABLE_INTERRUPT | + ETH_TX_LAST_DESC; + pkt_info.return_info = skb; + mp->tx_ring_skbs++; + } + else { + pkt_info.return_info = 0; + } + pkt_info.byte_cnt = this_frag->size; + if (this_frag->size < 8) + printk("%d : \n", skb_shinfo(skb)->nr_frags); + + pkt_info.buf_ptr = pci_map_page(NULL, this_frag->page, + this_frag->page_offset, + this_frag->size, PCI_DMA_TODEVICE); + + status = eth_port_send(mp, &pkt_info); + + if (status != ETH_OK) { + if ((status == ETH_ERROR)) + printk(KERN_ERR "%s: Error on transmitting packet\n", dev->name); + + if (status == ETH_QUEUE_LAST_RESOURCE) + printk("Tx resource error \n"); + + if (status == ETH_QUEUE_FULL) + printk("Queue is full \n"); + } + } + } +#endif + + /* Check if TX queue can handle another skb. If not, then + * signal higher layers to stop requesting TX + */ + if (MV64340_TX_QUEUE_SIZE <= (mp->tx_ring_skbs + 1)) + /* + * Stop getting skb's from upper layers. + * Getting skb's from upper layers will be enabled again after + * packets are released. + */ + netif_stop_queue(dev); + + /* Update statistics and start of transmittion time */ + stats->tx_bytes += skb->len; + stats->tx_packets++; + dev->trans_start = jiffies; + + spin_unlock_irqrestore(&mp->lock, flags); + + return 0; /* success */ +} + +/* + * mv64340_eth_get_stats + * + * Returns a pointer to the interface statistics. + * + * Input : dev - a pointer to the required interface + * + * Output : a pointer to the interface's statistics + */ + +static struct net_device_stats *mv64340_eth_get_stats(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + return &mp->stats; +} + +/*/ + * mv64340_eth_init + * + * First function called after registering the network device. + * It's purpose is to initialize the device as an ethernet device, + * fill the structure that was given in registration with pointers + * to functions, and setting the MAC address of the interface + * + * Input : number of port to initialize + * Output : -ENONMEM if failed , 0 if success + */ +static int mv64340_eth_init(int port_num) +{ + struct mv64340_private *mp; + struct net_device *dev; + int err; + + dev = alloc_etherdev(sizeof(struct mv64340_private)); + if (!dev) + return -ENOMEM; + + mp = netdev_priv(dev); + + dev->irq = ETH_PORT0_IRQ_NUM + port_num; + + dev->open = mv64340_eth_open; + dev->stop = mv64340_eth_stop; + dev->hard_start_xmit = mv64340_eth_start_xmit; + dev->get_stats = mv64340_eth_get_stats; + dev->set_mac_address = mv64340_eth_set_mac_address; + dev->set_multicast_list = mv64340_eth_set_rx_mode; + + /* No need to Tx Timeout */ + dev->tx_timeout = mv64340_eth_tx_timeout; +#ifdef MV64340_NAPI + dev->poll = mv64340_poll; + dev->weight = 64; +#endif + + dev->watchdog_timeo = 2 * HZ; + dev->tx_queue_len = MV64340_TX_QUEUE_SIZE; + dev->base_addr = 0; + dev->change_mtu = mv64340_eth_change_mtu; + +#ifdef MV64340_CHECKSUM_OFFLOAD_TX +#ifdef MAX_SKB_FRAGS +#ifndef CONFIG_JAGUAR_DMALOW + /* + * Zero copy can only work if we use Discovery II memory. Else, we will + * have to map the buffers to ISA memory which is only 16 MB + */ + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HW_CSUM; +#endif +#endif +#endif + + mp->port_num = port_num; + + /* Configure the timeout task */ + INIT_WORK(&mp->tx_timeout_task, + (void (*)(void *))mv64340_eth_tx_timeout_task, dev); + + spin_lock_init(&mp->lock); + + /* set MAC addresses */ + memcpy(dev->dev_addr, prom_mac_addr_base, 6); + dev->dev_addr[5] += port_num; + + err = register_netdev(dev); + if (err) + goto out_free_dev; + + printk(KERN_NOTICE "%s: port %d with MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, port_num, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + + if (dev->features & NETIF_F_SG) + printk("Scatter Gather Enabled "); + + if (dev->features & NETIF_F_IP_CSUM) + printk("TX TCP/IP Checksumming Supported \n"); + + printk("RX TCP/UDP Checksum Offload ON, \n"); + printk("TX and RX Interrupt Coalescing ON \n"); + +#ifdef MV64340_NAPI + printk("RX NAPI Enabled \n"); +#endif + + return 0; + +out_free_dev: + free_netdev(dev); + + return err; +} + +/* + * mv64340_init_module + * + * Registers the network drivers into the Linux kernel + * + * Input : N/A + * + * Output : N/A + */ +static int __init mv64340_init_module(void) +{ + printk(KERN_NOTICE "MV-64340 10/100/1000 Ethernet Driver\n"); +#ifdef CONFIG_MV64340_ETH_0 + if (mv64340_eth_init(0)) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 0\n"); + } +#endif +#ifdef CONFIG_MV64340_ETH_1 + if (mv64340_eth_init(1)) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 1\n"); + } +#endif +#ifdef CONFIG_MV64340_ETH_2 + if (mv64340_eth_init(2)) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 2\n"); + } +#endif + return 0; +} + +/* + * mv64340_cleanup_module + * + * Registers the network drivers into the Linux kernel + * + * Input : N/A + * + * Output : N/A + */ +static void __init mv64340_cleanup_module(void) +{ + /* Nothing to do here ! it's not a removable module */ +} + +module_init(mv64340_init_module); +module_exit(mv64340_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm and Manish Lachwani"); +MODULE_DESCRIPTION("Ethernet driver for Marvell MV64340"); + +/* + * The second part is the low level driver of the gigE ethernet ports. + */ + +/* + * Marvell's Gigabit Ethernet controller low level driver + * + * DESCRIPTION: + * This file introduce low level API to Marvell's Gigabit Ethernet + * controller. This Gigabit Ethernet Controller driver API controls + * 1) Operations (i.e. port init, start, reset etc'). + * 2) Data flow (i.e. port send, receive etc'). + * Each Gigabit Ethernet port is controlled via + * struct mv64340_private. + * This struct includes user configuration information as well as + * driver internal data needed for its operations. + * + * Supported Features: + * - This low level driver is OS independent. Allocating memory for + * the descriptor rings and buffers are not within the scope of + * this driver. + * - The user is free from Rx/Tx queue managing. + * - This low level driver introduce functionality API that enable + * the to operate Marvell's Gigabit Ethernet Controller in a + * convenient way. + * - Simple Gigabit Ethernet port operation API. + * - Simple Gigabit Ethernet port data flow API. + * - Data flow and operation API support per queue functionality. + * - Support cached descriptors for better performance. + * - Enable access to all four DRAM banks and internal SRAM memory + * spaces. + * - PHY access and control API. + * - Port control register configuration API. + * - Full control over Unicast and Multicast MAC configurations. + * + * Operation flow: + * + * Initialization phase + * This phase complete the initialization of the the mv64340_private + * struct. + * User information regarding port configuration has to be set + * prior to calling the port initialization routine. + * + * In this phase any port Tx/Rx activity is halted, MIB counters + * are cleared, PHY address is set according to user parameter and + * access to DRAM and internal SRAM memory spaces. + * + * Driver ring initialization + * Allocating memory for the descriptor rings and buffers is not + * within the scope of this driver. Thus, the user is required to + * allocate memory for the descriptors ring and buffers. Those + * memory parameters are used by the Rx and Tx ring initialization + * routines in order to curve the descriptor linked list in a form + * of a ring. + * Note: Pay special attention to alignment issues when using + * cached descriptors/buffers. In this phase the driver store + * information in the mv64340_private struct regarding each queue + * ring. + * + * Driver start + * This phase prepares the Ethernet port for Rx and Tx activity. + * It uses the information stored in the mv64340_private struct to + * initialize the various port registers. + * + * Data flow: + * All packet references to/from the driver are done using + * struct pkt_info. + * This struct is a unified struct used with Rx and Tx operations. + * This way the user is not required to be familiar with neither + * Tx nor Rx descriptors structures. + * The driver's descriptors rings are management by indexes. + * Those indexes controls the ring resources and used to indicate + * a SW resource error: + * 'current' + * This index points to the current available resource for use. For + * example in Rx process this index will point to the descriptor + * that will be passed to the user upon calling the receive routine. + * In Tx process, this index will point to the descriptor + * that will be assigned with the user packet info and transmitted. + * 'used' + * This index points to the descriptor that need to restore its + * resources. For example in Rx process, using the Rx buffer return + * API will attach the buffer returned in packet info to the + * descriptor pointed by 'used'. In Tx process, using the Tx + * descriptor return will merely return the user packet info with + * the command status of the transmitted buffer pointed by the + * 'used' index. Nevertheless, it is essential to use this routine + * to update the 'used' index. + * 'first' + * This index supports Tx Scatter-Gather. It points to the first + * descriptor of a packet assembled of multiple buffers. For example + * when in middle of Such packet we have a Tx resource error the + * 'curr' index get the value of 'first' to indicate that the ring + * returned to its state before trying to transmit this packet. + * + * Receive operation: + * The eth_port_receive API set the packet information struct, + * passed by the caller, with received information from the + * 'current' SDMA descriptor. + * It is the user responsibility to return this resource back + * to the Rx descriptor ring to enable the reuse of this source. + * Return Rx resource is done using the eth_rx_return_buff API. + * + * Transmit operation: + * The eth_port_send API supports Scatter-Gather which enables to + * send a packet spanned over multiple buffers. This means that + * for each packet info structure given by the user and put into + * the Tx descriptors ring, will be transmitted only if the 'LAST' + * bit will be set in the packet info command status field. This + * API also consider restriction regarding buffer alignments and + * sizes. + * The user must return a Tx resource after ensuring the buffer + * has been transmitted to enable the Tx ring indexes to update. + * + * BOARD LAYOUT + * This device is on-board. No jumper diagram is necessary. + * + * EXTERNAL INTERFACE + * + * Prior to calling the initialization routine eth_port_init() the user + * must set the following fields under mv64340_private struct: + * port_num User Ethernet port number. + * port_mac_addr[6] User defined port MAC address. + * port_config User port configuration value. + * port_config_extend User port config extend value. + * port_sdma_config User port SDMA config value. + * port_serial_control User port serial control value. + * + * This driver introduce a set of default values: + * PORT_CONFIG_VALUE Default port configuration value + * PORT_CONFIG_EXTEND_VALUE Default port extend configuration value + * PORT_SDMA_CONFIG_VALUE Default sdma control value + * PORT_SERIAL_CONTROL_VALUE Default port serial control value + * + * This driver data flow is done using the struct pkt_info which + * is a unified struct for Rx and Tx operations: + * + * byte_cnt Tx/Rx descriptor buffer byte count. + * l4i_chk CPU provided TCP Checksum. For Tx operation + * only. + * cmd_sts Tx/Rx descriptor command status. + * buf_ptr Tx/Rx descriptor buffer pointer. + * return_info Tx/Rx user resource return information. + */ + +/* defines */ +/* SDMA command macros */ +#define ETH_ENABLE_TX_QUEUE(eth_port) \ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), 1) + +#define ETH_DISABLE_TX_QUEUE(eth_port) \ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), \ + (1 << 8)) + +#define ETH_ENABLE_RX_QUEUE(rx_queue, eth_port) \ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), \ + (1 << rx_queue)) + +#define ETH_DISABLE_RX_QUEUE(rx_queue, eth_port) \ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), \ + (1 << (8 + rx_queue))) + +#define LINK_UP_TIMEOUT 100000 +#define PHY_BUSY_TIMEOUT 10000000 + +/* locals */ + +/* PHY routines */ +#ifdef MDD_CUT +static void ethernet_phy_set(unsigned int eth_port_num, int phy_addr); +#endif +static int ethernet_phy_get(unsigned int eth_port_num); + +/* Ethernet Port routines */ +static int eth_port_uc_addr(unsigned int eth_port_num, unsigned char uc_nibble, + int option); + +#ifdef MDD_CUT +static void eth_b_copy(unsigned int src_addr, unsigned int dst_addr, + int byte_count); +#endif + +/* + * eth_port_init - Initialize the Ethernet port driver + * + * DESCRIPTION: + * This function prepares the ethernet port to start its activity: + * 1) Completes the ethernet port driver struct initialization toward port + * start routine. + * 2) Resets the device to a quiescent state in case of warm reboot. + * 3) Enable SDMA access to all four DRAM banks as well as internal SRAM. + * 4) Clean MAC tables. The reset status of those tables is unknown. + * 5) Set PHY address. + * Note: Call this routine prior to eth_port_start routine and after + * setting user values in the user fields of Ethernet port control + * struct. + * + * INPUT: + * struct mv64340_private *mp Ethernet port control struct + * + * OUTPUT: + * See description. + * + * RETURN: + * None. + */ +static void eth_port_init(struct mv64340_private * mp) +{ + mp->port_config = PORT_CONFIG_VALUE; + mp->port_config_extend = PORT_CONFIG_EXTEND_VALUE; +#if defined(__BIG_ENDIAN) + mp->port_sdma_config = PORT_SDMA_CONFIG_VALUE; +#elif defined(__LITTLE_ENDIAN) + mp->port_sdma_config = PORT_SDMA_CONFIG_VALUE | + ETH_BLM_RX_NO_SWAP | ETH_BLM_TX_NO_SWAP; +#else +#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be defined! +#endif + mp->port_serial_control = PORT_SERIAL_CONTROL_VALUE; + + mp->port_rx_queue_command = 0; + mp->port_tx_queue_command = 0; + + mp->rx_resource_err = 0; + mp->tx_resource_err = 0; + + eth_port_reset(mp->port_num); + + eth_port_init_mac_tables(mp->port_num); + + ethernet_phy_reset(mp->port_num); +} + +/* + * eth_port_start - Start the Ethernet port activity. + * + * DESCRIPTION: + * This routine prepares the Ethernet port for Rx and Tx activity: + * 1. Initialize Tx and Rx Current Descriptor Pointer for each queue that + * has been initialized a descriptor's ring (using + * ether_init_tx_desc_ring for Tx and ether_init_rx_desc_ring for Rx) + * 2. Initialize and enable the Ethernet configuration port by writing to + * the port's configuration and command registers. + * 3. Initialize and enable the SDMA by writing to the SDMA's + * configuration and command registers. After completing these steps, + * the ethernet port SDMA can starts to perform Rx and Tx activities. + * + * Note: Each Rx and Tx queue descriptor's list must be initialized prior + * to calling this function (use ether_init_tx_desc_ring for Tx queues + * and ether_init_rx_desc_ring for Rx queues). + * + * INPUT: + * struct mv64340_private *mp Ethernet port control struct + * + * OUTPUT: + * Ethernet port is ready to receive and transmit. + * + * RETURN: + * false if the port PHY is not up. + * true otherwise. + */ +static int eth_port_start(struct mv64340_private *mp) +{ + unsigned int eth_port_num = mp->port_num; + int tx_curr_desc, rx_curr_desc; + unsigned int phy_reg_data; + + /* Assignment of Tx CTRP of given queue */ + tx_curr_desc = mp->tx_curr_desc_q; + MV_WRITE(MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num), + (struct eth_tx_desc *) mp->tx_desc_dma + tx_curr_desc); + + /* Assignment of Rx CRDP of given queue */ + rx_curr_desc = mp->rx_curr_desc_q; + MV_WRITE(MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num), + (struct eth_rx_desc *) mp->rx_desc_dma + rx_curr_desc); + + /* Add the assigned Ethernet address to the port's address table */ + eth_port_uc_addr_set(mp->port_num, mp->port_mac_addr); + + /* Assign port configuration and command. */ + MV_WRITE(MV64340_ETH_PORT_CONFIG_REG(eth_port_num), + mp->port_config); + + MV_WRITE(MV64340_ETH_PORT_CONFIG_EXTEND_REG(eth_port_num), + mp->port_config_extend); + + MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), + mp->port_serial_control); + + MV_SET_REG_BITS(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), + ETH_SERIAL_PORT_ENABLE); + + /* Assign port SDMA configuration */ + MV_WRITE(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num), + mp->port_sdma_config); + + /* Enable port Rx. */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port_num), + mp->port_rx_queue_command); + + /* Check if link is up */ + eth_port_read_smi_reg(eth_port_num, 1, &phy_reg_data); + + if (!(phy_reg_data & 0x20)) + return 0; + + return 1; +} + +/* + * eth_port_uc_addr_set - This function Set the port Unicast address. + * + * DESCRIPTION: + * This function Set the port Ethernet MAC address. + * + * INPUT: + * unsigned int eth_port_num Port number. + * char * p_addr Address to be set + * + * OUTPUT: + * Set MAC address low and high registers. also calls eth_port_uc_addr() + * To set the unicast table with the proper information. + * + * RETURN: + * N/A. + * + */ +static void eth_port_uc_addr_set(unsigned int eth_port_num, + unsigned char *p_addr) +{ + unsigned int mac_h; + unsigned int mac_l; + + mac_l = (p_addr[4] << 8) | (p_addr[5]); + mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | + (p_addr[2] << 8) | (p_addr[3] << 0); + + MV_WRITE(MV64340_ETH_MAC_ADDR_LOW(eth_port_num), mac_l); + MV_WRITE(MV64340_ETH_MAC_ADDR_HIGH(eth_port_num), mac_h); + + /* Accept frames of this address */ + eth_port_uc_addr(eth_port_num, p_addr[5], ACCEPT_MAC_ADDR); + + return; +} + +/* + * eth_port_uc_addr - This function Set the port unicast address table + * + * DESCRIPTION: + * This function locates the proper entry in the Unicast table for the + * specified MAC nibble and sets its properties according to function + * parameters. + * + * INPUT: + * unsigned int eth_port_num Port number. + * unsigned char uc_nibble Unicast MAC Address last nibble. + * int option 0 = Add, 1 = remove address. + * + * OUTPUT: + * This function add/removes MAC addresses from the port unicast address + * table. + * + * RETURN: + * true is output succeeded. + * false if option parameter is invalid. + * + */ +static int eth_port_uc_addr(unsigned int eth_port_num, + unsigned char uc_nibble, int option) +{ + unsigned int unicast_reg; + unsigned int tbl_offset; + unsigned int reg_offset; + + /* Locate the Unicast table entry */ + uc_nibble = (0xf & uc_nibble); + tbl_offset = (uc_nibble / 4) * 4; /* Register offset from unicast table base */ + reg_offset = uc_nibble % 4; /* Entry offset within the above register */ + + switch (option) { + case REJECT_MAC_ADDR: + /* Clear accepts frame bit at specified unicast DA table entry */ + unicast_reg = MV_READ((MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset)); + + unicast_reg &= (0x0E << (8 * reg_offset)); + + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset), unicast_reg); + break; + + case ACCEPT_MAC_ADDR: + /* Set accepts frame bit at unicast DA filter table entry */ + unicast_reg = + MV_READ( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset)); + + unicast_reg |= (0x01 << (8 * reg_offset)); + + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset), unicast_reg); + + break; + + default: + return 0; + } + + return 1; +} + +/* + * eth_port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables + * + * DESCRIPTION: + * Go through all the DA filter tables (Unicast, Special Multicast & + * Other Multicast) and set each entry to 0. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * Multicast and Unicast packets are rejected. + * + * RETURN: + * None. + */ +static void eth_port_init_mac_tables(unsigned int eth_port_num) +{ + int table_index; + + /* Clear DA filter unicast table (Ex_dFUT) */ + for (table_index = 0; table_index <= 0xC; table_index += 4) + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + + for (table_index = 0; table_index <= 0xFC; table_index += 4) { + /* Clear DA filter special multicast table (Ex_dFSMT) */ + MV_WRITE( + (MV64340_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + /* Clear DA filter other multicast table (Ex_dFOMT) */ + MV_WRITE((MV64340_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + } +} + +/* + * eth_clear_mib_counters - Clear all MIB counters + * + * DESCRIPTION: + * This function clears all MIB counters of a specific ethernet port. + * A read from the MIB counter will reset the counter. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * After reading all MIB counters, the counters resets. + * + * RETURN: + * MIB counter value. + * + */ +static void eth_clear_mib_counters(unsigned int eth_port_num) +{ + int i; + + /* Perform dummy reads from MIB counters */ + for (i = ETH_MIB_GOOD_OCTETS_RECEIVED_LOW; i < ETH_MIB_LATE_COLLISION; i += 4) + MV_READ(MV64340_ETH_MIB_COUNTERS_BASE(eth_port_num) + i); +} + + +#ifdef MDD_CUT +/* + * ethernet_phy_set - Set the ethernet port PHY address. + * + * DESCRIPTION: + * This routine set the ethernet port PHY address according to given + * parameter. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * Set PHY Address Register with given PHY address parameter. + * + * RETURN: + * None. + */ +static void ethernet_phy_set(unsigned int eth_port_num, int phy_addr) +{ + unsigned int reg_data; + + reg_data = MV_READ(MV64340_ETH_PHY_ADDR_REG); + + reg_data &= ~(0x1F << (5 * eth_port_num)); + reg_data |= (phy_addr << (5 * eth_port_num)); + + MV_WRITE(MV64340_ETH_PHY_ADDR_REG, reg_data); + + return; +} +#endif + +/* + * ethernet_phy_get - Get the ethernet port PHY address. + * + * DESCRIPTION: + * This routine returns the given ethernet port PHY address. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * None. + * + * RETURN: + * PHY address. + * + */ +static int ethernet_phy_get(unsigned int eth_port_num) +{ + unsigned int reg_data; + + reg_data = MV_READ(MV64340_ETH_PHY_ADDR_REG); + + return ((reg_data >> (5 * eth_port_num)) & 0x1f); +} + +/* + * ethernet_phy_reset - Reset Ethernet port PHY. + * + * DESCRIPTION: + * This routine utilize the SMI interface to reset the ethernet port PHY. + * The routine waits until the link is up again or link up is timeout. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * The ethernet port PHY renew its link. + * + * RETURN: + * None. + * + */ +static int ethernet_phy_reset(unsigned int eth_port_num) +{ + unsigned int time_out = 50; + unsigned int phy_reg_data; + + /* Reset the PHY */ + eth_port_read_smi_reg(eth_port_num, 0, &phy_reg_data); + phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */ + eth_port_write_smi_reg(eth_port_num, 0, phy_reg_data); + + /* Poll on the PHY LINK */ + do { + eth_port_read_smi_reg(eth_port_num, 1, &phy_reg_data); + + if (time_out-- == 0) + return 0; + } while (!(phy_reg_data & 0x20)); + + return 1; +} + +/* + * eth_port_reset - Reset Ethernet port + * + * DESCRIPTION: + * This routine resets the chip by aborting any SDMA engine activity and + * clearing the MIB counters. The Receiver and the Transmit unit are in + * idle state after this command is performed and the port is disabled. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * Channel activity is halted. + * + * RETURN: + * None. + * + */ +static void eth_port_reset(unsigned int eth_port_num) +{ + unsigned int reg_data; + + /* Stop Tx port activity. Check port Tx activity. */ + reg_data = + MV_READ(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG + (eth_port_num), (reg_data << 8)); + + /* Wait for all Tx activity to terminate. */ + do { + /* Check port cause register that all Tx queues are stopped */ + reg_data = + MV_READ + (MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG + (eth_port_num)); + } + while (reg_data & 0xFF); + } + + /* Stop Rx port activity. Check port Rx activity. */ + reg_data = + MV_READ(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num), (reg_data << 8)); + + /* Wait for all Rx activity to terminate. */ + do { + /* Check port cause register that all Rx queues are stopped */ + reg_data = + MV_READ + (MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num)); + } + while (reg_data & 0xFF); + } + + + /* Clear all MIB counters */ + eth_clear_mib_counters(eth_port_num); + + /* Reset the Enable bit in the Configuration Register */ + reg_data = + MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG (eth_port_num)); + reg_data &= ~ETH_SERIAL_PORT_ENABLE; + MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), reg_data); + + return; +} + +/* + * ethernet_set_config_reg - Set specified bits in configuration register. + * + * DESCRIPTION: + * This function sets specified bits in the given ethernet + * configuration register. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int value 32 bit value. + * + * OUTPUT: + * The set bits in the value parameter are set in the configuration + * register. + * + * RETURN: + * None. + * + */ +static void ethernet_set_config_reg(unsigned int eth_port_num, + unsigned int value) +{ + unsigned int eth_config_reg; + + eth_config_reg = + MV_READ(MV64340_ETH_PORT_CONFIG_REG(eth_port_num)); + eth_config_reg |= value; + MV_WRITE(MV64340_ETH_PORT_CONFIG_REG(eth_port_num), + eth_config_reg); +} + +/* + * ethernet_get_config_reg - Get the port configuration register + * + * DESCRIPTION: + * This function returns the configuration register value of the given + * ethernet port. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * None. + * + * RETURN: + * Port configuration register value. + */ +static unsigned int ethernet_get_config_reg(unsigned int eth_port_num) +{ + unsigned int eth_config_reg; + + eth_config_reg = MV_READ(MV64340_ETH_PORT_CONFIG_EXTEND_REG + (eth_port_num)); + return eth_config_reg; +} + + +/* + * eth_port_read_smi_reg - Read PHY registers + * + * DESCRIPTION: + * This routine utilize the SMI interface to interact with the PHY in + * order to perform PHY register read. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int phy_reg PHY register address offset. + * unsigned int *value Register value buffer. + * + * OUTPUT: + * Write the value of a specified PHY register into given buffer. + * + * RETURN: + * false if the PHY is busy or read data is not in valid state. + * true otherwise. + * + */ +static int eth_port_read_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, unsigned int *value) +{ + int phy_addr = ethernet_phy_get(eth_port_num); + unsigned int time_out = PHY_BUSY_TIMEOUT; + unsigned int reg_value; + + /* first check that it is not busy */ + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_BUSY); + + /* not busy */ + + MV_WRITE(MV64340_ETH_SMI_REG, + (phy_addr << 16) | (phy_reg << 21) | ETH_SMI_OPCODE_READ); + + time_out = PHY_BUSY_TIMEOUT; /* initialize the time out var again */ + + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_READ_VALID); + + /* Wait for the data to update in the SMI register */ + for (time_out = 0; time_out < PHY_BUSY_TIMEOUT; time_out++); + + reg_value = MV_READ(MV64340_ETH_SMI_REG); + + *value = reg_value & 0xffff; + + return 1; +} + +/* + * eth_port_write_smi_reg - Write to PHY registers + * + * DESCRIPTION: + * This routine utilize the SMI interface to interact with the PHY in + * order to perform writes to PHY registers. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int phy_reg PHY register address offset. + * unsigned int value Register value. + * + * OUTPUT: + * Write the given value to the specified PHY register. + * + * RETURN: + * false if the PHY is busy. + * true otherwise. + * + */ +static int eth_port_write_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, unsigned int value) +{ + unsigned int time_out = PHY_BUSY_TIMEOUT; + unsigned int reg_value; + int phy_addr; + + phy_addr = ethernet_phy_get(eth_port_num); + + /* first check that it is not busy */ + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_BUSY); + + /* not busy */ + MV_WRITE(MV64340_ETH_SMI_REG, (phy_addr << 16) | (phy_reg << 21) | + ETH_SMI_OPCODE_WRITE | (value & 0xffff)); + + return 1; +} + +/* + * eth_port_send - Send an Ethernet packet + * + * DESCRIPTION: + * This routine send a given packet described by p_pktinfo parameter. It + * supports transmitting of a packet spaned over multiple buffers. The + * routine updates 'curr' and 'first' indexes according to the packet + * segment passed to the routine. In case the packet segment is first, + * the 'first' index is update. In any case, the 'curr' index is updated. + * If the routine get into Tx resource error it assigns 'curr' index as + * 'first'. This way the function can abort Tx process of multiple + * descriptors per packet. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Tx ring 'curr' and 'first' indexes are updated. + * + * RETURN: + * ETH_QUEUE_FULL in case of Tx resource error. + * ETH_ERROR in case the routine can not access Tx desc ring. + * ETH_QUEUE_LAST_RESOURCE if the routine uses the last Tx resource. + * ETH_OK otherwise. + * + */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX +/* + * Modified to include the first descriptor pointer in case of SG + */ +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_curr, tx_desc_used, tx_first_desc, tx_next_desc; + volatile struct eth_tx_desc *current_descriptor; + volatile struct eth_tx_desc *first_descriptor; + u32 command_status, first_chip_ptr; + + /* Do not process Tx ring in case of Tx ring resource error */ + if (mp->tx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; + + current_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + if (current_descriptor == NULL) + return ETH_ERROR; + + tx_next_desc = (tx_desc_curr + 1) % MV64340_TX_QUEUE_SIZE; + command_status = p_pkt_info->cmd_sts | ETH_ZERO_PADDING | ETH_GEN_CRC; + + if (command_status & ETH_TX_FIRST_DESC) { + tx_first_desc = tx_desc_curr; + mp->tx_first_desc_q = tx_first_desc; + + /* fill first descriptor */ + first_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + first_descriptor->l4i_chk = p_pkt_info->l4i_chk; + first_descriptor->cmd_sts = command_status; + first_descriptor->byte_cnt = p_pkt_info->byte_cnt; + first_descriptor->buf_ptr = p_pkt_info->buf_ptr; + first_descriptor->next_desc_ptr = + (struct eth_tx_desc *) mp->tx_desc_dma + tx_next_desc; + wmb(); + } else { + tx_first_desc = mp->tx_first_desc_q; + first_descriptor = &mp->p_tx_desc_area[tx_first_desc]; + if (first_descriptor == NULL) { + printk("First desc is NULL !!\n"); + return ETH_ERROR; + } + if (command_status & ETH_TX_LAST_DESC) + current_descriptor->next_desc_ptr = 0x00000000; + else { + command_status |= ETH_BUFFER_OWNED_BY_DMA; + current_descriptor->next_desc_ptr = + (struct eth_tx_desc *) mp->tx_desc_dma + tx_next_desc; + } + } + + if (p_pkt_info->byte_cnt < 8) { + printk(" < 8 problem \n"); + return ETH_ERROR; + } + + current_descriptor->buf_ptr = p_pkt_info->buf_ptr; + current_descriptor->byte_cnt = p_pkt_info->byte_cnt; + current_descriptor->l4i_chk = p_pkt_info->l4i_chk; + current_descriptor->cmd_sts = command_status; + + mp->tx_skb[tx_desc_curr] = (struct sk_buff*) p_pkt_info->return_info; + + wmb(); + + /* Set last desc with DMA ownership and interrupt enable. */ + if (command_status & ETH_TX_LAST_DESC) { + current_descriptor->cmd_sts = command_status | + ETH_TX_ENABLE_INTERRUPT | + ETH_BUFFER_OWNED_BY_DMA; + + if (!(command_status & ETH_TX_FIRST_DESC)) + first_descriptor->cmd_sts |= ETH_BUFFER_OWNED_BY_DMA; + wmb(); + + first_chip_ptr = MV_READ(MV64340_ETH_CURRENT_SERVED_TX_DESC_PTR(mp->port_num)); + + /* Apply send command */ + if (first_chip_ptr == 0x00000000) + MV_WRITE(MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(mp->port_num), (struct eth_tx_desc *) mp->tx_desc_dma + tx_first_desc); + + ETH_ENABLE_TX_QUEUE(mp->port_num); + + /* + * Finish Tx packet. Update first desc in case of Tx resource + * error */ + tx_first_desc = tx_next_desc; + mp->tx_first_desc_q = tx_first_desc; + } else { + if (! (command_status & ETH_TX_FIRST_DESC) ) { + current_descriptor->cmd_sts = command_status; + wmb(); + } + } + + /* Check for ring index overlap in the Tx desc ring */ + if (tx_next_desc == tx_desc_used) { + mp->tx_resource_err = 1; + mp->tx_curr_desc_q = tx_first_desc; + + return ETH_QUEUE_LAST_RESOURCE; + } + + mp->tx_curr_desc_q = tx_next_desc; + wmb(); + + return ETH_OK; +} +#else +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_curr; + int tx_desc_used; + volatile struct eth_tx_desc* current_descriptor; + unsigned int command_status; + + /* Do not process Tx ring in case of Tx ring resource error */ + if (mp->tx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; + current_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + + if (current_descriptor == NULL) + return ETH_ERROR; + + command_status = p_pkt_info->cmd_sts | ETH_ZERO_PADDING | ETH_GEN_CRC; + +/* XXX Is this for real ?!?!? */ + /* Buffers with a payload smaller than 8 bytes must be aligned to a + * 64-bit boundary. We use the memory allocated for Tx descriptor. + * This memory is located in TX_BUF_OFFSET_IN_DESC offset within the + * Tx descriptor. */ + if (p_pkt_info->byte_cnt <= 8) { + printk(KERN_ERR + "You have failed in the < 8 bytes errata - fixme\n"); + return ETH_ERROR; + } + current_descriptor->buf_ptr = p_pkt_info->buf_ptr; + current_descriptor->byte_cnt = p_pkt_info->byte_cnt; + mp->tx_skb[tx_desc_curr] = (struct sk_buff *) p_pkt_info->return_info; + + mb(); + + /* Set last desc with DMA ownership and interrupt enable. */ + current_descriptor->cmd_sts = command_status | + ETH_BUFFER_OWNED_BY_DMA | ETH_TX_ENABLE_INTERRUPT; + + /* Apply send command */ + ETH_ENABLE_TX_QUEUE(mp->port_num); + + /* Finish Tx packet. Update first desc in case of Tx resource error */ + tx_desc_curr = (tx_desc_curr + 1) % MV64340_TX_QUEUE_SIZE; + + /* Update the current descriptor */ + mp->tx_curr_desc_q = tx_desc_curr; + + /* Check for ring index overlap in the Tx desc ring */ + if (tx_desc_curr == tx_desc_used) { + mp->tx_resource_err = 1; + return ETH_QUEUE_LAST_RESOURCE; + } + + return ETH_OK; +} +#endif + +/* + * eth_tx_return_desc - Free all used Tx descriptors + * + * DESCRIPTION: + * This routine returns the transmitted packet information to the caller. + * It uses the 'first' index to support Tx desc return in case a transmit + * of a packet spanned over multiple buffer still in process. + * In case the Tx queue was in "resource error" condition, where there are + * no available Tx resources, the function resets the resource error flag. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Tx ring 'first' and 'used' indexes are updated. + * + * RETURN: + * ETH_ERROR in case the routine can not access Tx desc ring. + * ETH_RETRY in case there is transmission in process. + * ETH_END_OF_JOB if the routine has nothing to release. + * ETH_OK otherwise. + * + */ +static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_used, tx_desc_curr; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + int tx_first_desc; +#endif + volatile struct eth_tx_desc *p_tx_desc_used; + unsigned int command_status; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + tx_first_desc = mp->tx_first_desc_q; +#endif + p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used]; + + /* XXX Sanity check */ + if (p_tx_desc_used == NULL) + return ETH_ERROR; + + command_status = p_tx_desc_used->cmd_sts; + + /* Still transmitting... */ +#ifndef MV64340_CHECKSUM_OFFLOAD_TX + if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) + return ETH_RETRY; +#endif + /* Stop release. About to overlap the current available Tx descriptor */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + if (tx_desc_used == tx_first_desc && !mp->tx_resource_err) + return ETH_END_OF_JOB; +#else + if (tx_desc_used == tx_desc_curr && !mp->tx_resource_err) + return ETH_END_OF_JOB; +#endif + + /* Pass the packet information to the caller */ + p_pkt_info->cmd_sts = command_status; + p_pkt_info->return_info = mp->tx_skb[tx_desc_used]; + mp->tx_skb[tx_desc_used] = NULL; + + /* Update the next descriptor to release. */ + mp->tx_used_desc_q = (tx_desc_used + 1) % MV64340_TX_QUEUE_SIZE; + + /* Any Tx return cancels the Tx resource error status */ + mp->tx_resource_err = 0; + + return ETH_OK; +} + +/* + * eth_port_receive - Get received information from Rx ring. + * + * DESCRIPTION: + * This routine returns the received data to the caller. There is no + * data copying during routine operation. All information is returned + * using pointer to packet information struct passed from the caller. + * If the routine exhausts Rx ring resources then the resource error flag + * is set. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Rx ring current and used indexes are updated. + * + * RETURN: + * ETH_ERROR in case the routine can not access Rx desc ring. + * ETH_QUEUE_FULL if Rx ring resources are exhausted. + * ETH_END_OF_JOB if there is no received data. + * ETH_OK otherwise. + */ +static ETH_FUNC_RET_STATUS eth_port_receive(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int rx_next_curr_desc, rx_curr_desc, rx_used_desc; + volatile struct eth_rx_desc * p_rx_desc; + unsigned int command_status; + + /* Do not process Rx ring in case of Rx ring resource error */ + if (mp->rx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Rx Desc ring 'curr and 'used' indexes */ + rx_curr_desc = mp->rx_curr_desc_q; + rx_used_desc = mp->rx_used_desc_q; + + p_rx_desc = &mp->p_rx_desc_area[rx_curr_desc]; + + /* The following parameters are used to save readings from memory */ + command_status = p_rx_desc->cmd_sts; + + /* Nothing to receive... */ + if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) + return ETH_END_OF_JOB; + + p_pkt_info->byte_cnt = (p_rx_desc->byte_cnt) - RX_BUF_OFFSET; + p_pkt_info->cmd_sts = command_status; + p_pkt_info->buf_ptr = (p_rx_desc->buf_ptr) + RX_BUF_OFFSET; + p_pkt_info->return_info = mp->rx_skb[rx_curr_desc]; + p_pkt_info->l4i_chk = p_rx_desc->buf_size; + + /* Clean the return info field to indicate that the packet has been */ + /* moved to the upper layers */ + mp->rx_skb[rx_curr_desc] = NULL; + + /* Update current index in data structure */ + rx_next_curr_desc = (rx_curr_desc + 1) % MV64340_RX_QUEUE_SIZE; + mp->rx_curr_desc_q = rx_next_curr_desc; + + /* Rx descriptors exhausted. Set the Rx ring resource error flag */ + if (rx_next_curr_desc == rx_used_desc) + mp->rx_resource_err = 1; + + mb(); + return ETH_OK; +} + +/* + * eth_rx_return_buff - Returns a Rx buffer back to the Rx ring. + * + * DESCRIPTION: + * This routine returns a Rx buffer back to the Rx ring. It retrieves the + * next 'used' descriptor and attached the returned buffer to it. + * In case the Rx ring was in "resource error" condition, where there are + * no available Rx resources, the function resets the resource error flag. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info Information on the returned buffer. + * + * OUTPUT: + * New available Rx resource in Rx descriptor ring. + * + * RETURN: + * ETH_ERROR in case the routine can not access Rx desc ring. + * ETH_OK otherwise. + */ +static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int used_rx_desc; /* Where to return Rx resource */ + volatile struct eth_rx_desc* p_used_rx_desc; + + /* Get 'used' Rx descriptor */ + used_rx_desc = mp->rx_used_desc_q; + p_used_rx_desc = &mp->p_rx_desc_area[used_rx_desc]; + + p_used_rx_desc->buf_ptr = p_pkt_info->buf_ptr; + p_used_rx_desc->buf_size = p_pkt_info->byte_cnt; + mp->rx_skb[used_rx_desc] = p_pkt_info->return_info; + + /* Flush the write pipe */ + mb(); + + /* Return the descriptor to DMA ownership */ + p_used_rx_desc->cmd_sts = + ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT; + + /* Flush descriptor and CPU pipe */ + mb(); + + /* Move the used descriptor pointer to the next descriptor */ + mp->rx_used_desc_q = (used_rx_desc + 1) % MV64340_RX_QUEUE_SIZE; + + /* Any Rx return cancels the Rx resource error status */ + mp->rx_resource_err = 0; + + return ETH_OK; +} + +#ifdef MDD_CUT +/* + * eth_b_copy - Copy bytes from source to destination + * + * DESCRIPTION: + * This function supports the eight bytes limitation on Tx buffer size. + * The routine will zero eight bytes starting from the destination address + * followed by copying bytes from the source address to the destination. + * + * INPUT: + * unsigned int src_addr 32 bit source address. + * unsigned int dst_addr 32 bit destination address. + * int byte_count Number of bytes to copy. + * + * OUTPUT: + * See description. + * + * RETURN: + * None. + * + */ +static void eth_b_copy(unsigned int src_addr, unsigned int dst_addr, + int byte_count) +{ + /* Zero the dst_addr area */ + *(unsigned int *) dst_addr = 0x0; + + while (byte_count != 0) { + *(char *) dst_addr = *(char *) src_addr; + dst_addr++; + src_addr++; + byte_count--; + } +} +#endif diff -Nru a/drivers/net/mv64340_eth.h b/drivers/net/mv64340_eth.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/mv64340_eth.h 2004-06-20 13:15:18 -07:00 @@ -0,0 +1,601 @@ +#ifndef __MV64340_ETH_H__ +#define __MV64340_ETH_H__ + +#include +#include +#include +#include +#include + +#include + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +/* + * The first part is the high level driver of the gigE ethernet ports. + */ + +#define ETH_PORT0_IRQ_NUM 48 /* main high register, bit0 */ +#define ETH_PORT1_IRQ_NUM ETH_PORT0_IRQ_NUM+1 /* main high register, bit1 */ +#define ETH_PORT2_IRQ_NUM ETH_PORT0_IRQ_NUM+2 /* main high register, bit1 */ + +/* Checksum offload for Tx works */ +#define MV64340_CHECKSUM_OFFLOAD_TX +#define MV64340_NAPI +#define MV64340_TX_FAST_REFILL +#undef MV64340_COAL + +/* + * Number of RX / TX descriptors on RX / TX rings. + * Note that allocating RX descriptors is done by allocating the RX + * ring AND a preallocated RX buffers (skb's) for each descriptor. + * The TX descriptors only allocates the TX descriptors ring, + * with no pre allocated TX buffers (skb's are allocated by higher layers. + */ + +/* Default TX ring size is 1000 descriptors */ +#define MV64340_TX_QUEUE_SIZE 1000 + +/* Default RX ring size is 400 descriptors */ +#define MV64340_RX_QUEUE_SIZE 400 + +#define MV64340_TX_COAL 100 +#ifdef MV64340_COAL +#define MV64340_RX_COAL 100 +#endif + + +/* + * The second part is the low level driver of the gigE ethernet ports. * + */ + + +/* + * Header File for : MV-643xx network interface header + * + * DESCRIPTION: + * This header file contains macros typedefs and function declaration for + * the Marvell Gig Bit Ethernet Controller. + * + * DEPENDENCIES: + * None. + * + */ + +/* Default port configuration value */ +#define PORT_CONFIG_VALUE \ + ETH_UNICAST_NORMAL_MODE | \ + ETH_DEFAULT_RX_QUEUE_0 | \ + ETH_DEFAULT_RX_ARP_QUEUE_0 | \ + ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP | \ + ETH_RECEIVE_BC_IF_IP | \ + ETH_RECEIVE_BC_IF_ARP | \ + ETH_CAPTURE_TCP_FRAMES_DIS | \ + ETH_CAPTURE_UDP_FRAMES_DIS | \ + ETH_DEFAULT_RX_TCP_QUEUE_0 | \ + ETH_DEFAULT_RX_UDP_QUEUE_0 | \ + ETH_DEFAULT_RX_BPDU_QUEUE_0 + +/* Default port extend configuration value */ +#define PORT_CONFIG_EXTEND_VALUE \ + ETH_SPAN_BPDU_PACKETS_AS_NORMAL | \ + ETH_PARTITION_DISABLE + + +/* Default sdma control value */ +#define PORT_SDMA_CONFIG_VALUE \ + ETH_RX_BURST_SIZE_16_64BIT | \ + GT_ETH_IPG_INT_RX(0) | \ + ETH_TX_BURST_SIZE_16_64BIT; + +#define GT_ETH_IPG_INT_RX(value) \ + ((value & 0x3fff) << 8) + +/* Default port serial control value */ +#define PORT_SERIAL_CONTROL_VALUE \ + ETH_FORCE_LINK_PASS | \ + ETH_ENABLE_AUTO_NEG_FOR_DUPLX | \ + ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL | \ + ETH_ADV_SYMMETRIC_FLOW_CTRL | \ + ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + ETH_FORCE_BP_MODE_NO_JAM | \ + BIT9 | \ + ETH_DO_NOT_FORCE_LINK_FAIL | \ + ETH_RETRANSMIT_16_ATTEMPTS | \ + ETH_ENABLE_AUTO_NEG_SPEED_GMII | \ + ETH_DTE_ADV_0 | \ + ETH_DISABLE_AUTO_NEG_BYPASS | \ + ETH_AUTO_NEG_NO_CHANGE | \ + ETH_MAX_RX_PACKET_9700BYTE | \ + ETH_CLR_EXT_LOOPBACK | \ + ETH_SET_FULL_DUPLEX_MODE | \ + ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX + +#define RX_BUFFER_MAX_SIZE 0x4000000 +#define TX_BUFFER_MAX_SIZE 0x4000000 + +/* MAC accepet/reject macros */ +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 + +/* Buffer offset from buffer pointer */ +#define RX_BUF_OFFSET 0x2 + +/* Gigabit Ethernet Unit Global Registers */ + +/* MIB Counters register definitions */ +#define ETH_MIB_GOOD_OCTETS_RECEIVED_LOW 0x0 +#define ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH 0x4 +#define ETH_MIB_BAD_OCTETS_RECEIVED 0x8 +#define ETH_MIB_INTERNAL_MAC_TRANSMIT_ERR 0xc +#define ETH_MIB_GOOD_FRAMES_RECEIVED 0x10 +#define ETH_MIB_BAD_FRAMES_RECEIVED 0x14 +#define ETH_MIB_BROADCAST_FRAMES_RECEIVED 0x18 +#define ETH_MIB_MULTICAST_FRAMES_RECEIVED 0x1c +#define ETH_MIB_FRAMES_64_OCTETS 0x20 +#define ETH_MIB_FRAMES_65_TO_127_OCTETS 0x24 +#define ETH_MIB_FRAMES_128_TO_255_OCTETS 0x28 +#define ETH_MIB_FRAMES_256_TO_511_OCTETS 0x2c +#define ETH_MIB_FRAMES_512_TO_1023_OCTETS 0x30 +#define ETH_MIB_FRAMES_1024_TO_MAX_OCTETS 0x34 +#define ETH_MIB_GOOD_OCTETS_SENT_LOW 0x38 +#define ETH_MIB_GOOD_OCTETS_SENT_HIGH 0x3c +#define ETH_MIB_GOOD_FRAMES_SENT 0x40 +#define ETH_MIB_EXCESSIVE_COLLISION 0x44 +#define ETH_MIB_MULTICAST_FRAMES_SENT 0x48 +#define ETH_MIB_BROADCAST_FRAMES_SENT 0x4c +#define ETH_MIB_UNREC_MAC_CONTROL_RECEIVED 0x50 +#define ETH_MIB_FC_SENT 0x54 +#define ETH_MIB_GOOD_FC_RECEIVED 0x58 +#define ETH_MIB_BAD_FC_RECEIVED 0x5c +#define ETH_MIB_UNDERSIZE_RECEIVED 0x60 +#define ETH_MIB_FRAGMENTS_RECEIVED 0x64 +#define ETH_MIB_OVERSIZE_RECEIVED 0x68 +#define ETH_MIB_JABBER_RECEIVED 0x6c +#define ETH_MIB_MAC_RECEIVE_ERROR 0x70 +#define ETH_MIB_BAD_CRC_EVENT 0x74 +#define ETH_MIB_COLLISION 0x78 +#define ETH_MIB_LATE_COLLISION 0x7c + +/* Port serial status reg (PSR) */ +#define ETH_INTERFACE_GMII_MII 0 +#define ETH_INTERFACE_PCM BIT0 +#define ETH_LINK_IS_DOWN 0 +#define ETH_LINK_IS_UP BIT1 +#define ETH_PORT_AT_HALF_DUPLEX 0 +#define ETH_PORT_AT_FULL_DUPLEX BIT2 +#define ETH_RX_FLOW_CTRL_DISABLED 0 +#define ETH_RX_FLOW_CTRL_ENBALED BIT3 +#define ETH_GMII_SPEED_100_10 0 +#define ETH_GMII_SPEED_1000 BIT4 +#define ETH_MII_SPEED_10 0 +#define ETH_MII_SPEED_100 BIT5 +#define ETH_NO_TX 0 +#define ETH_TX_IN_PROGRESS BIT7 +#define ETH_BYPASS_NO_ACTIVE 0 +#define ETH_BYPASS_ACTIVE BIT8 +#define ETH_PORT_NOT_AT_PARTITION_STATE 0 +#define ETH_PORT_AT_PARTITION_STATE BIT9 +#define ETH_PORT_TX_FIFO_NOT_EMPTY 0 +#define ETH_PORT_TX_FIFO_EMPTY BIT10 + + +/* These macros describes the Port configuration reg (Px_cR) bits */ +#define ETH_UNICAST_NORMAL_MODE 0 +#define ETH_UNICAST_PROMISCUOUS_MODE BIT0 +#define ETH_DEFAULT_RX_QUEUE_0 0 +#define ETH_DEFAULT_RX_QUEUE_1 BIT1 +#define ETH_DEFAULT_RX_QUEUE_2 BIT2 +#define ETH_DEFAULT_RX_QUEUE_3 (BIT2 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_4 BIT3 +#define ETH_DEFAULT_RX_QUEUE_5 (BIT3 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_6 (BIT3 | BIT2) +#define ETH_DEFAULT_RX_QUEUE_7 (BIT3 | BIT2 | BIT1) +#define ETH_DEFAULT_RX_ARP_QUEUE_0 0 +#define ETH_DEFAULT_RX_ARP_QUEUE_1 BIT4 +#define ETH_DEFAULT_RX_ARP_QUEUE_2 BIT5 +#define ETH_DEFAULT_RX_ARP_QUEUE_3 (BIT5 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_4 BIT6 +#define ETH_DEFAULT_RX_ARP_QUEUE_5 (BIT6 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_6 (BIT6 | BIT5) +#define ETH_DEFAULT_RX_ARP_QUEUE_7 (BIT6 | BIT5 | BIT4) +#define ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP 0 +#define ETH_REJECT_BC_IF_NOT_IP_OR_ARP BIT7 +#define ETH_RECEIVE_BC_IF_IP 0 +#define ETH_REJECT_BC_IF_IP BIT8 +#define ETH_RECEIVE_BC_IF_ARP 0 +#define ETH_REJECT_BC_IF_ARP BIT9 +#define ETH_TX_AM_NO_UPDATE_ERROR_SUMMARY BIT12 +#define ETH_CAPTURE_TCP_FRAMES_DIS 0 +#define ETH_CAPTURE_TCP_FRAMES_EN BIT14 +#define ETH_CAPTURE_UDP_FRAMES_DIS 0 +#define ETH_CAPTURE_UDP_FRAMES_EN BIT15 +#define ETH_DEFAULT_RX_TCP_QUEUE_0 0 +#define ETH_DEFAULT_RX_TCP_QUEUE_1 BIT16 +#define ETH_DEFAULT_RX_TCP_QUEUE_2 BIT17 +#define ETH_DEFAULT_RX_TCP_QUEUE_3 (BIT17 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_4 BIT18 +#define ETH_DEFAULT_RX_TCP_QUEUE_5 (BIT18 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_6 (BIT18 | BIT17) +#define ETH_DEFAULT_RX_TCP_QUEUE_7 (BIT18 | BIT17 | BIT16) +#define ETH_DEFAULT_RX_UDP_QUEUE_0 0 +#define ETH_DEFAULT_RX_UDP_QUEUE_1 BIT19 +#define ETH_DEFAULT_RX_UDP_QUEUE_2 BIT20 +#define ETH_DEFAULT_RX_UDP_QUEUE_3 (BIT20 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_4 (BIT21 +#define ETH_DEFAULT_RX_UDP_QUEUE_5 (BIT21 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_6 (BIT21 | BIT20) +#define ETH_DEFAULT_RX_UDP_QUEUE_7 (BIT21 | BIT20 | BIT19) +#define ETH_DEFAULT_RX_BPDU_QUEUE_0 0 +#define ETH_DEFAULT_RX_BPDU_QUEUE_1 BIT22 +#define ETH_DEFAULT_RX_BPDU_QUEUE_2 BIT23 +#define ETH_DEFAULT_RX_BPDU_QUEUE_3 (BIT23 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_4 BIT24 +#define ETH_DEFAULT_RX_BPDU_QUEUE_5 (BIT24 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_6 (BIT24 | BIT23) +#define ETH_DEFAULT_RX_BPDU_QUEUE_7 (BIT24 | BIT23 | BIT22) + + +/* These macros describes the Port configuration extend reg (Px_cXR) bits*/ +#define ETH_CLASSIFY_EN BIT0 +#define ETH_SPAN_BPDU_PACKETS_AS_NORMAL 0 +#define ETH_SPAN_BPDU_PACKETS_TO_RX_QUEUE_7 BIT1 +#define ETH_PARTITION_DISABLE 0 +#define ETH_PARTITION_ENABLE BIT2 + + +/* Tx/Rx queue command reg (RQCR/TQCR)*/ +#define ETH_QUEUE_0_ENABLE BIT0 +#define ETH_QUEUE_1_ENABLE BIT1 +#define ETH_QUEUE_2_ENABLE BIT2 +#define ETH_QUEUE_3_ENABLE BIT3 +#define ETH_QUEUE_4_ENABLE BIT4 +#define ETH_QUEUE_5_ENABLE BIT5 +#define ETH_QUEUE_6_ENABLE BIT6 +#define ETH_QUEUE_7_ENABLE BIT7 +#define ETH_QUEUE_0_DISABLE BIT8 +#define ETH_QUEUE_1_DISABLE BIT9 +#define ETH_QUEUE_2_DISABLE BIT10 +#define ETH_QUEUE_3_DISABLE BIT11 +#define ETH_QUEUE_4_DISABLE BIT12 +#define ETH_QUEUE_5_DISABLE BIT13 +#define ETH_QUEUE_6_DISABLE BIT14 +#define ETH_QUEUE_7_DISABLE BIT15 + + +/* These macros describes the Port Sdma configuration reg (SDCR) bits */ +#define ETH_RIFB BIT0 +#define ETH_RX_BURST_SIZE_1_64BIT 0 +#define ETH_RX_BURST_SIZE_2_64BIT BIT1 +#define ETH_RX_BURST_SIZE_4_64BIT BIT2 +#define ETH_RX_BURST_SIZE_8_64BIT (BIT2 | BIT1) +#define ETH_RX_BURST_SIZE_16_64BIT BIT3 +#define ETH_BLM_RX_NO_SWAP BIT4 +#define ETH_BLM_RX_BYTE_SWAP 0 +#define ETH_BLM_TX_NO_SWAP BIT5 +#define ETH_BLM_TX_BYTE_SWAP 0 +#define ETH_DESCRIPTORS_BYTE_SWAP BIT6 +#define ETH_DESCRIPTORS_NO_SWAP 0 +#define ETH_TX_BURST_SIZE_1_64BIT 0 +#define ETH_TX_BURST_SIZE_2_64BIT BIT22 +#define ETH_TX_BURST_SIZE_4_64BIT BIT23 +#define ETH_TX_BURST_SIZE_8_64BIT (BIT23 | BIT22) +#define ETH_TX_BURST_SIZE_16_64BIT BIT24 + + + +/* These macros describes the Port serial control reg (PSCR) bits */ +#define ETH_SERIAL_PORT_DISABLE 0 +#define ETH_SERIAL_PORT_ENABLE BIT0 +#define ETH_FORCE_LINK_PASS BIT1 +#define ETH_DO_NOT_FORCE_LINK_PASS 0 +#define ETH_ENABLE_AUTO_NEG_FOR_DUPLX 0 +#define ETH_DISABLE_AUTO_NEG_FOR_DUPLX BIT2 +#define ETH_ENABLE_AUTO_NEG_FOR_FLOW_CTRL 0 +#define ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL BIT3 +#define ETH_ADV_NO_FLOW_CTRL 0 +#define ETH_ADV_SYMMETRIC_FLOW_CTRL BIT4 +#define ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX 0 +#define ETH_FORCE_FC_MODE_TX_PAUSE_DIS BIT5 +#define ETH_FORCE_BP_MODE_NO_JAM 0 +#define ETH_FORCE_BP_MODE_JAM_TX BIT7 +#define ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR BIT8 +#define ETH_FORCE_LINK_FAIL 0 +#define ETH_DO_NOT_FORCE_LINK_FAIL BIT10 +#define ETH_RETRANSMIT_16_ATTEMPTS 0 +#define ETH_RETRANSMIT_FOREVER BIT11 +#define ETH_DISABLE_AUTO_NEG_SPEED_GMII BIT13 +#define ETH_ENABLE_AUTO_NEG_SPEED_GMII 0 +#define ETH_DTE_ADV_0 0 +#define ETH_DTE_ADV_1 BIT14 +#define ETH_DISABLE_AUTO_NEG_BYPASS 0 +#define ETH_ENABLE_AUTO_NEG_BYPASS BIT15 +#define ETH_AUTO_NEG_NO_CHANGE 0 +#define ETH_RESTART_AUTO_NEG BIT16 +#define ETH_MAX_RX_PACKET_1518BYTE 0 +#define ETH_MAX_RX_PACKET_1522BYTE BIT17 +#define ETH_MAX_RX_PACKET_1552BYTE BIT18 +#define ETH_MAX_RX_PACKET_9022BYTE (BIT18 | BIT17) +#define ETH_MAX_RX_PACKET_9192BYTE BIT19 +#define ETH_MAX_RX_PACKET_9700BYTE (BIT19 | BIT17) +#define ETH_SET_EXT_LOOPBACK BIT20 +#define ETH_CLR_EXT_LOOPBACK 0 +#define ETH_SET_FULL_DUPLEX_MODE BIT21 +#define ETH_SET_HALF_DUPLEX_MODE 0 +#define ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX BIT22 +#define ETH_DISABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX 0 +#define ETH_SET_GMII_SPEED_TO_10_100 0 +#define ETH_SET_GMII_SPEED_TO_1000 BIT23 +#define ETH_SET_MII_SPEED_TO_10 0 +#define ETH_SET_MII_SPEED_TO_100 BIT24 + + +/* SMI reg */ +#define ETH_SMI_BUSY BIT28 /* 0 - Write, 1 - Read */ +#define ETH_SMI_READ_VALID BIT27 /* 0 - Write, 1 - Read */ +#define ETH_SMI_OPCODE_WRITE 0 /* Completion of Read operation */ +#define ETH_SMI_OPCODE_READ BIT26 /* Operation is in progress */ + +/* SDMA command status fields macros */ + +/* Tx & Rx descriptors status */ +#define ETH_ERROR_SUMMARY (BIT0) + +/* Tx & Rx descriptors command */ +#define ETH_BUFFER_OWNED_BY_DMA (BIT31) + +/* Tx descriptors status */ +#define ETH_LC_ERROR (0 ) +#define ETH_UR_ERROR (BIT1 ) +#define ETH_RL_ERROR (BIT2 ) +#define ETH_LLC_SNAP_FORMAT (BIT9 ) + +/* Rx descriptors status */ +#define ETH_CRC_ERROR (0 ) +#define ETH_OVERRUN_ERROR (BIT1 ) +#define ETH_MAX_FRAME_LENGTH_ERROR (BIT2 ) +#define ETH_RESOURCE_ERROR ((BIT2 | BIT1)) +#define ETH_VLAN_TAGGED (BIT19) +#define ETH_BPDU_FRAME (BIT20) +#define ETH_TCP_FRAME_OVER_IP_V_4 (0 ) +#define ETH_UDP_FRAME_OVER_IP_V_4 (BIT21) +#define ETH_OTHER_FRAME_TYPE (BIT22) +#define ETH_LAYER_2_IS_ETH_V_2 (BIT23) +#define ETH_FRAME_TYPE_IP_V_4 (BIT24) +#define ETH_FRAME_HEADER_OK (BIT25) +#define ETH_RX_LAST_DESC (BIT26) +#define ETH_RX_FIRST_DESC (BIT27) +#define ETH_UNKNOWN_DESTINATION_ADDR (BIT28) +#define ETH_RX_ENABLE_INTERRUPT (BIT29) +#define ETH_LAYER_4_CHECKSUM_OK (BIT30) + +/* Rx descriptors byte count */ +#define ETH_FRAME_FRAGMENTED (BIT2) + +/* Tx descriptors command */ +#define ETH_LAYER_4_CHECKSUM_FIRST_DESC (BIT10) +#define ETH_FRAME_SET_TO_VLAN (BIT15) +#define ETH_TCP_FRAME (0 ) +#define ETH_UDP_FRAME (BIT16) +#define ETH_GEN_TCP_UDP_CHECKSUM (BIT17) +#define ETH_GEN_IP_V_4_CHECKSUM (BIT18) +#define ETH_ZERO_PADDING (BIT19) +#define ETH_TX_LAST_DESC (BIT20) +#define ETH_TX_FIRST_DESC (BIT21) +#define ETH_GEN_CRC (BIT22) +#define ETH_TX_ENABLE_INTERRUPT (BIT23) +#define ETH_AUTO_MODE (BIT30) + +/* typedefs */ + +typedef enum _eth_func_ret_status { + ETH_OK, /* Returned as expected. */ + ETH_ERROR, /* Fundamental error. */ + ETH_RETRY, /* Could not process request. Try later. */ + ETH_END_OF_JOB, /* Ring has nothing to process. */ + ETH_QUEUE_FULL, /* Ring resource error. */ + ETH_QUEUE_LAST_RESOURCE /* Ring resources about to exhaust. */ +} ETH_FUNC_RET_STATUS; + +typedef enum _eth_target { + ETH_TARGET_DRAM, + ETH_TARGET_DEVICE, + ETH_TARGET_CBS, + ETH_TARGET_PCI0, + ETH_TARGET_PCI1 +} ETH_TARGET; + +/* These are for big-endian machines. Little endian needs different + * definitions. + */ +#if defined(__BIG_ENDIAN) +struct eth_rx_desc { + u16 byte_cnt; /* Descriptor buffer byte count */ + u16 buf_size; /* Buffer size */ + u32 cmd_sts; /* Descriptor command status */ + u32 next_desc_ptr; /* Next descriptor pointer */ + u32 buf_ptr; /* Descriptor buffer pointer */ +}; + +struct eth_tx_desc { + u16 byte_cnt; /* buffer byte count */ + u16 l4i_chk; /* CPU provided TCP checksum */ + u32 cmd_sts; /* Command/status field */ + u32 next_desc_ptr; /* Pointer to next descriptor */ + u32 buf_ptr; /* pointer to buffer for this descriptor */ +}; + +#elif defined(__LITTLE_ENDIAN) +struct eth_rx_desc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u32 buf_ptr; /* Descriptor buffer pointer */ + u32 next_desc_ptr; /* Next descriptor pointer */ +}; + +struct eth_tx_desc { + u32 cmd_sts; /* Command/status field */ + u16 l4i_chk; /* CPU provided TCP checksum */ + u16 byte_cnt; /* buffer byte count */ + u32 buf_ptr; /* pointer to buffer for this descriptor */ + u32 next_desc_ptr; /* Pointer to next descriptor */ +}; +#else +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined +#endif + +/* Unified struct for Rx and Tx operations. The user is not required to */ +/* be familier with neither Tx nor Rx descriptors. */ +struct pkt_info { + unsigned short byte_cnt; /* Descriptor buffer byte count */ + unsigned short l4i_chk; /* Tx CPU provided TCP Checksum */ + unsigned int cmd_sts; /* Descriptor command status */ + dma_addr_t buf_ptr; /* Descriptor buffer pointer */ + struct sk_buff * return_info; /* User resource return information */ +}; + + +/* Ethernet port specific infomation */ + +struct mv64340_private { + int port_num; /* User Ethernet port number */ + u8 port_mac_addr[6]; /* User defined port MAC address. */ + u32 port_config; /* User port configuration value */ + u32 port_config_extend; /* User port config extend value */ + u32 port_sdma_config; /* User port SDMA config value */ + u32 port_serial_control; /* User port serial control value */ + u32 port_tx_queue_command; /* Port active Tx queues summary */ + u32 port_rx_queue_command; /* Port active Rx queues summary */ + + int rx_resource_err; /* Rx ring resource error flag */ + int tx_resource_err; /* Tx ring resource error flag */ + + /* Tx/Rx rings managment indexes fields. For driver use */ + + /* Next available and first returning Rx resource */ + int rx_curr_desc_q, rx_used_desc_q; + + /* Next available and first returning Tx resource */ + int tx_curr_desc_q, tx_used_desc_q; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + int tx_first_desc_q; +#endif + +#ifdef MV64340_TX_FAST_REFILL + u32 tx_clean_threshold; +#endif + + volatile struct eth_rx_desc * p_rx_desc_area; + dma_addr_t rx_desc_dma; + unsigned int rx_desc_area_size; + struct sk_buff * rx_skb[MV64340_RX_QUEUE_SIZE]; + + volatile struct eth_tx_desc * p_tx_desc_area; + dma_addr_t tx_desc_dma; + unsigned int tx_desc_area_size; + struct sk_buff * tx_skb[MV64340_TX_QUEUE_SIZE]; + + struct work_struct tx_timeout_task; + + /* + * Former struct mv64340_eth_priv members start here + */ + struct net_device_stats stats; + spinlock_t lock; + /* Size of Tx Ring per queue */ + unsigned int tx_ring_size; + /* Ammont of SKBs outstanding on Tx queue */ + unsigned int tx_ring_skbs; + /* Size of Rx Ring per queue */ + unsigned int rx_ring_size; + /* Ammount of SKBs allocated to Rx Ring per queue */ + unsigned int rx_ring_skbs; + + /* + * rx_task used to fill RX ring out of bottom half context + */ + struct work_struct rx_task; + + /* + * Used in case RX Ring is empty, which can be caused when + * system does not have resources (skb's) + */ + struct timer_list timeout; + long rx_task_busy __attribute__ ((aligned(SMP_CACHE_BYTES))); + unsigned rx_timer_flag; + + u32 rx_int_coal; + u32 tx_int_coal; +}; + +/* ethernet.h API list */ + +/* Port operation control routines */ +static void eth_port_init(struct mv64340_private *mp); +static void eth_port_reset(unsigned int eth_port_num); +static int eth_port_start(struct mv64340_private *mp); + +static void ethernet_set_config_reg(unsigned int eth_port_num, + unsigned int value); +static unsigned int ethernet_get_config_reg(unsigned int eth_port_num); + +/* Port MAC address routines */ +static void eth_port_uc_addr_set(unsigned int eth_port_num, + unsigned char *p_addr); + +/* PHY and MIB routines */ +static int ethernet_phy_reset(unsigned int eth_port_num); + +static int eth_port_write_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, + unsigned int value); + +static int eth_port_read_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, + unsigned int *value); + +static void eth_clear_mib_counters(unsigned int eth_port_num); + +/* Port data flow control routines */ +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_port_receive(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); + +#endif /* __MV64340_ETH_H__ */ diff -Nru a/drivers/net/natsemi.c b/drivers/net/natsemi.c --- a/drivers/net/natsemi.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/natsemi.c 2004-06-20 13:15:18 -07:00 @@ -360,6 +360,18 @@ #define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +/* + * Support for fibre connections on Am79C874: + * This phy needs a special setup when connected to a fibre cable. + * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf + */ +#define PHYID_AM79C874 0x0022561b + +#define MII_MCTRL 0x15 /* mode control register */ +#define MII_FX_SEL 0x0001 /* 100BASE-FX (fiber) */ +#define MII_EN_SCRM 0x0004 /* enable scrambler (tp) */ + + /* array of board data directly indexed by pci_tbl[x].driver_data */ static struct { const char *name; @@ -463,6 +475,9 @@ EE_DataIn = 0x01, EE_ChipSelect = 0x08, EE_DataOut = 0x02, + MII_Data = 0x10, + MII_Write = 0x20, + MII_ShiftClk = 0x40, }; enum PCIBusCfg_bits { @@ -594,9 +609,12 @@ }; enum PhyCtrl_bits { - PhyAddrMask = 0xf, + PhyAddrMask = 0x1f, }; +#define PHY_ADDR_NONE 32 +#define PHY_ADDR_INTERNAL 1 + /* values we might find in the silicon revision register */ #define SRR_DP83815_C 0x0302 #define SRR_DP83815_D 0x0403 @@ -656,7 +674,9 @@ int oom; /* Do not touch the nic registers */ int hands_off; - /* These values are keep track of the transceiver/media in use */ + /* external phy that is used: only valid if dev->if_port != PORT_TP */ + int mii; + int phy_addr_external; unsigned int full_duplex; /* Rx filter */ u32 cur_rx_mode; @@ -669,6 +689,10 @@ u32 srr; /* expected DSPCFG value */ u16 dspcfg; + /* parms saved in ethtool format */ + u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ + u8 duplex; /* Duplex, half or full */ + u8 autoneg; /* Autonegotiation enabled */ /* MII transceiver section */ u16 advertising; unsigned int iosize; @@ -676,9 +700,14 @@ u32 msg_enable; }; +static void move_int_phy(struct net_device *dev, int addr); static int eeprom_read(long ioaddr, int location); -static int mdio_read(struct net_device *dev, int phy_id, int reg); -static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data); +static int mdio_read(struct net_device *dev, int reg); +static void mdio_write(struct net_device *dev, int reg, u16 data); +static void init_phy_fixup(struct net_device *dev); +static int miiport_read(struct net_device *dev, int phy_id, int reg); +static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data); +static int find_mii(struct net_device *dev); static void natsemi_reset(struct net_device *dev); static void natsemi_reload_eeprom(struct net_device *dev); static void natsemi_stop_rxtx(struct net_device *dev); @@ -718,6 +747,29 @@ static int netdev_get_regs(struct net_device *dev, u8 *buf); static int netdev_get_eeprom(struct net_device *dev, u8 *buf); +static void move_int_phy(struct net_device *dev, int addr) +{ + struct netdev_private *np = dev->priv; + int target = 31; + + /* + * The internal phy is visible on the external mii bus. Therefore we must + * move it away before we can send commands to an external phy. + * There are two addresses we must avoid: + * - the address on the external phy that is used for transmission. + * - the address that we want to access. User space can access phys + * on the mii bus with SIOCGMIIREG/SIOCSMIIREG, independant from the + * phy that is used for transmission. + */ + + if (target == addr) + target--; + if (target == np->phy_addr_external) + target--; + writew(target, dev->base_addr + PhyCtrl); + readw(dev->base_addr + PhyCtrl); + udelay(1); +} static int __devinit natsemi_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -797,10 +849,32 @@ np->msg_enable = (debug >= 0) ? (1<hands_off = 0; + /* Initial port: + * - If the nic was configured to use an external phy and if find_mii + * finds a phy: use external port, first phy that replies. + * - Otherwise: internal port. + * Note that the phy address for the internal phy doesn't matter: + * The address would be used to access a phy over the mii bus, but + * the internal phy is accessed through mapped registers. + */ + if (readl(dev->base_addr + ChipConfig) & CfgExtPhy) + dev->if_port = PORT_MII; + else + dev->if_port = PORT_TP; /* Reset the chip to erase previous misconfiguration. */ natsemi_reload_eeprom(dev); natsemi_reset(dev); + if (dev->if_port != PORT_TP) { + np->phy_addr_external = find_mii(dev); + if (np->phy_addr_external == PHY_ADDR_NONE) { + dev->if_port = PORT_TP; + np->phy_addr_external = PHY_ADDR_INTERNAL; + } + } else { + np->phy_addr_external = PHY_ADDR_INTERNAL; + } + option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; if (dev->mem_start) option = dev->mem_start; @@ -811,8 +885,8 @@ np->full_duplex = 1; if (option & 15) printk(KERN_INFO - "%s: ignoring user supplied media type %d", - dev->name, option & 15); + "natsemi %s: ignoring user supplied media type %d", + pci_name(np->pci_dev), option & 15); } if (find_cnt < MAX_UNITS && full_duplex[find_cnt]) np->full_duplex = 1; @@ -830,45 +904,57 @@ if (mtu) dev->mtu = mtu; - i = register_netdev(dev); - if (i) - goto err_register_netdev; - netif_carrier_off(dev); - if (netif_msg_drv(np)) { - printk(KERN_INFO "%s: %s at %#08lx, ", - dev->name, natsemi_pci_info[chip_idx].name, ioaddr); - for (i = 0; i < ETH_ALEN-1; i++) - printk("%02x:", dev->dev_addr[i]); - printk("%02x, IRQ %d.\n", dev->dev_addr[i], irq); - } + /* get the initial settings from hardware */ + tmp = mdio_read(dev, MII_BMCR); + np->speed = (tmp & BMCR_SPEED100)? SPEED_100 : SPEED_10; + np->duplex = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL : DUPLEX_HALF; + np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE; + np->advertising= mdio_read(dev, MII_ADVERTISE); - np->advertising = mdio_read(dev, 1, MII_ADVERTISE); - if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000 + if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL && netif_msg_probe(np)) { - u32 chip_config = readl(ioaddr + ChipConfig); - printk(KERN_INFO "%s: Transceiver default autonegotiation %s " + printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s " "10%s %s duplex.\n", - dev->name, - chip_config & CfgAnegEnable ? + pci_name(np->pci_dev), + (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)? "enabled, advertise" : "disabled, force", - chip_config & CfgAneg100 ? "0" : "", - chip_config & CfgAnegFull ? "full" : "half"); + (np->advertising & + (ADVERTISE_100FULL|ADVERTISE_100HALF))? + "0" : "", + (np->advertising & + (ADVERTISE_100FULL|ADVERTISE_10FULL))? + "full" : "half"); } if (netif_msg_probe(np)) printk(KERN_INFO - "%s: Transceiver status %#04x advertising %#04x.\n", - dev->name, mdio_read(dev, 1, MII_BMSR), + "natsemi %s: Transceiver status %#04x advertising %#04x.\n", + pci_name(np->pci_dev), mdio_read(dev, MII_BMSR), np->advertising); /* save the silicon revision for later querying */ np->srr = readl(ioaddr + SiliconRev); if (netif_msg_hw(np)) - printk(KERN_INFO "%s: silicon revision %#04x.\n", - dev->name, np->srr); + printk(KERN_INFO "natsemi %s: silicon revision %#04x.\n", + pci_name(np->pci_dev), np->srr); + i = register_netdev(dev); + if (i) + goto err_register_netdev; + if (netif_msg_drv(np)) { + printk(KERN_INFO "natsemi %s: %s at %#08lx (%s), ", + dev->name, natsemi_pci_info[chip_idx].name, ioaddr, + pci_name(np->pci_dev)); + for (i = 0; i < ETH_ALEN-1; i++) + printk("%02x:", dev->dev_addr[i]); + printk("%02x, IRQ %d", dev->dev_addr[i], irq); + if (dev->if_port == PORT_TP) + printk(", port TP.\n"); + else + printk(", port MII, phy ad %d.\n", np->phy_addr_external); + } return 0; err_register_netdev: @@ -939,25 +1025,335 @@ /* MII transceiver control section. * The 83815 series has an internal transceiver, and we present the - * management registers as if they were MII connected. */ + * internal management registers as if they were MII connected. + * External Phy registers are referenced through the MII interface. + */ + +/* clock transitions >= 20ns (25MHz) + * One readl should be good to PCI @ 100MHz + */ +#define mii_delay(dev) readl(dev->base_addr + EECtrl) + +static int mii_getbit (struct net_device *dev) +{ + int data; + + writel(MII_ShiftClk, dev->base_addr + EECtrl); + data = readl(dev->base_addr + EECtrl); + writel(0, dev->base_addr + EECtrl); + mii_delay(dev); + return (data & MII_Data)? 1 : 0; +} + +static void mii_send_bits (struct net_device *dev, u32 data, int len) +{ + u32 i; + + for (i = (1 << (len-1)); i; i >>= 1) + { + u32 mdio_val = MII_Write | ((data & i)? MII_Data : 0); + writel(mdio_val, dev->base_addr + EECtrl); + mii_delay(dev); + writel(mdio_val | MII_ShiftClk, dev->base_addr + EECtrl); + mii_delay(dev); + } + writel(0, dev->base_addr + EECtrl); + mii_delay(dev); +} -static int mdio_read(struct net_device *dev, int phy_id, int reg) +static int miiport_read(struct net_device *dev, int phy_id, int reg) { - if (phy_id == 1 && reg < 32) - return readl(dev->base_addr+BasicControl+(reg<<2))&0xffff; + u32 cmd; + int i; + u32 retval = 0; + + /* Ensure sync */ + mii_send_bits (dev, 0xffffffff, 32); + /* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */ + /* ST,OP = 0110'b for read operation */ + cmd = (0x06 << 10) | (phy_id << 5) | reg; + mii_send_bits (dev, cmd, 14); + /* Turnaround */ + if (mii_getbit (dev)) + return 0; + /* Read data */ + for (i = 0; i < 16; i++) { + retval <<= 1; + retval |= mii_getbit (dev); + } + /* End cycle */ + mii_getbit (dev); + return retval; +} + +static void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data) +{ + u32 cmd; + + /* Ensure sync */ + mii_send_bits (dev, 0xffffffff, 32); + /* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */ + /* ST,OP,AAAAA,RRRRR,TA = 0101xxxxxxxxxx10'b = 0x5002 for write */ + cmd = (0x5002 << 16) | (phy_id << 23) | (reg << 18) | data; + mii_send_bits (dev, cmd, 32); + /* End cycle */ + mii_getbit (dev); +} + +static int mdio_read(struct net_device *dev, int reg) +{ + struct netdev_private *np = dev->priv; + + /* The 83815 series has two ports: + * - an internal transceiver + * - an external mii bus + */ + if (dev->if_port == PORT_TP) + return readw(dev->base_addr+BasicControl+(reg<<2)); else - return 0xffff; + return miiport_read(dev, np->phy_addr_external, reg); } -static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data) +static void mdio_write(struct net_device *dev, int reg, u16 data) { struct netdev_private *np = dev->priv; - if (phy_id == 1 && reg < 32) { + + /* The 83815 series has an internal transceiver; handle separately */ + if (dev->if_port == PORT_TP) writew(data, dev->base_addr+BasicControl+(reg<<2)); - switch (reg) { - case MII_ADVERTISE: np->advertising = data; break; + else + miiport_write(dev, np->phy_addr_external, reg, data); +} + +static void init_phy_fixup(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + int i; + u32 cfg; + u16 tmp; + + /* restore stuff lost when power was out */ + tmp = mdio_read(dev, MII_BMCR); + if (np->autoneg == AUTONEG_ENABLE) { + /* renegotiate if something changed */ + if ((tmp & BMCR_ANENABLE) == 0 + || np->advertising != mdio_read(dev, MII_ADVERTISE)) + { + /* turn on autonegotiation and force negotiation */ + tmp |= (BMCR_ANENABLE | BMCR_ANRESTART); + mdio_write(dev, MII_ADVERTISE, np->advertising); } + } else { + /* turn off auto negotiation, set speed and duplexity */ + tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX); + if (np->speed == SPEED_100) + tmp |= BMCR_SPEED100; + if (np->duplex == DUPLEX_FULL) + tmp |= BMCR_FULLDPLX; + /* + * Note: there is no good way to inform the link partner + * that our capabilities changed. The user has to unplug + * and replug the network cable after some changes, e.g. + * after switching from 10HD, autoneg off to 100 HD, + * autoneg off. + */ + } + mdio_write(dev, MII_BMCR, tmp); + readl(dev->base_addr + ChipConfig); + udelay(1); + + /* find out what phy this is */ + np->mii = (mdio_read(dev, MII_PHYSID1) << 16) + + mdio_read(dev, MII_PHYSID2); + + /* handle external phys here */ + switch (np->mii) { + case PHYID_AM79C874: + /* phy specific configuration for fibre/tp operation */ + tmp = mdio_read(dev, MII_MCTRL); + tmp &= ~(MII_FX_SEL | MII_EN_SCRM); + if (dev->if_port == PORT_FIBRE) + tmp |= MII_FX_SEL; + else + tmp |= MII_EN_SCRM; + mdio_write(dev, MII_MCTRL, tmp); + break; + default: + break; } + cfg = readl(dev->base_addr + ChipConfig); + if (cfg & CfgExtPhy) + return; + + /* On page 78 of the spec, they recommend some settings for "optimum + performance" to be done in sequence. These settings optimize some + of the 100Mbit autodetection circuitry. They say we only want to + do this for rev C of the chip, but engineers at NSC (Bradley + Kennedy) recommends always setting them. If you don't, you get + errors on some autonegotiations that make the device unusable. + + It seems that the DSP needs a few usec to reinitialize after + the start of the phy. Just retry writing these values until they + stick. + */ + for (i=0;idspcfg = DSPCFG_VAL; + writew(np->dspcfg, ioaddr + DSPCFG); + writew(SDCFG_VAL, ioaddr + SDCFG); + writew(0, ioaddr + PGSEL); + readl(ioaddr + ChipConfig); + udelay(10); + + writew(1, ioaddr + PGSEL); + dspcfg = readw(ioaddr + DSPCFG); + writew(0, ioaddr + PGSEL); + if (np->dspcfg == dspcfg) + break; + } + + if (netif_msg_link(np)) { + if (i==NATSEMI_HW_TIMEOUT) { + printk(KERN_INFO + "%s: DSPCFG mismatch after retrying for %d usec.\n", + dev->name, i*10); + } else { + printk(KERN_INFO + "%s: DSPCFG accepted after %d usec.\n", + dev->name, i*10); + } + } + /* + * Enable PHY Specific event based interrupts. Link state change + * and Auto-Negotiation Completion are among the affected. + * Read the intr status to clear it (needed for wake events). + */ + readw(ioaddr + MIntrStatus); + writew(MICRIntEn, ioaddr + MIntrCtrl); +} + +static int switch_port_external(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + u32 cfg; + + cfg = readl(dev->base_addr + ChipConfig); + if (cfg & CfgExtPhy) + return 0; + + if (netif_msg_link(np)) { + printk(KERN_INFO "%s: switching to external transceiver.\n", + dev->name); + } + + /* 1) switch back to external phy */ + writel(cfg | (CfgExtPhy | CfgPhyDis), dev->base_addr + ChipConfig); + readl(dev->base_addr + ChipConfig); + udelay(1); + + /* 2) reset the external phy: */ + /* resetting the external PHY has been known to cause a hub supplying + * power over Ethernet to kill the power. We don't want to kill + * power to this computer, so we avoid resetting the phy. + */ + + /* 3) reinit the phy fixup, it got lost during power down. */ + move_int_phy(dev, np->phy_addr_external); + init_phy_fixup(dev); + + return 1; +} + +static int switch_port_internal(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int i; + u32 cfg; + u16 bmcr; + + cfg = readl(dev->base_addr + ChipConfig); + if (!(cfg &CfgExtPhy)) + return 0; + + if (netif_msg_link(np)) { + printk(KERN_INFO "%s: switching to internal transceiver.\n", + dev->name); + } + /* 1) switch back to internal phy: */ + cfg = cfg & ~(CfgExtPhy | CfgPhyDis); + writel(cfg, dev->base_addr + ChipConfig); + readl(dev->base_addr + ChipConfig); + udelay(1); + + /* 2) reset the internal phy: */ + bmcr = readw(dev->base_addr+BasicControl+(MII_BMCR<<2)); + writel(bmcr | BMCR_RESET, dev->base_addr+BasicControl+(MII_BMCR<<2)); + readl(dev->base_addr + ChipConfig); + udelay(10); + for (i=0;ibase_addr+BasicControl+(MII_BMCR<<2)); + if (!(bmcr & BMCR_RESET)) + break; + udelay(10); + } + if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) { + printk(KERN_INFO + "%s: phy reset did not complete in %d usec.\n", + dev->name, i*10); + } + /* 3) reinit the phy fixup, it got lost during power down. */ + init_phy_fixup(dev); + + return 1; +} + +/* Scan for a PHY on the external mii bus. + * There are two tricky points: + * - Do not scan while the internal phy is enabled. The internal phy will + * crash: e.g. reads from the DSPCFG register will return odd values and + * the nasty random phy reset code will reset the nic every few seconds. + * - The internal phy must be moved around, an external phy could + * have the same address as the internal phy. + */ +static int find_mii(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + int tmp; + int i; + int did_switch; + + /* Switch to external phy */ + did_switch = switch_port_external(dev); + + /* Scan the possible phy addresses: + * + * PHY address 0 means that the phy is in isolate mode. Not yet + * supported due to lack of test hardware. User space should + * handle it through ethtool. + */ + for (i = 1; i <= 31; i++) { + move_int_phy(dev, i); + tmp = miiport_read(dev, i, MII_BMSR); + if (tmp != 0xffff && tmp != 0x0000) { + /* found something! */ + np->mii = (mdio_read(dev, MII_PHYSID1) << 16) + + mdio_read(dev, MII_PHYSID2); + if (netif_msg_probe(np)) { + printk(KERN_INFO "natsemi %s: found external phy %08x at address %d.\n", + pci_name(np->pci_dev), np->mii, i); + } + break; + } + } + /* And switch back to internal phy: */ + if (did_switch) + switch_port_internal(dev); + return i; } /* CFG bits [13:16] [18:23] */ @@ -1019,6 +1415,11 @@ /* restore CFG */ cfg |= readl(dev->base_addr + ChipConfig) & ~CFG_RESET_SAVE; + /* turn on external phy if it was selected */ + if (dev->if_port == PORT_TP) + cfg &= ~(CfgExtPhy | CfgPhyDis); + else + cfg |= (CfgExtPhy | CfgPhyDis); writel(cfg, dev->base_addr + ChipConfig); /* restore WCSR */ wcsr |= readl(dev->base_addr + WOLCmd) & ~WCSR_RESET_SAVE; @@ -1050,11 +1451,11 @@ break; } if (i==NATSEMI_HW_TIMEOUT) { - printk(KERN_WARNING "%s: EEPROM did not reload in %d usec.\n", - dev->name, i*50); + printk(KERN_WARNING "natsemi %s: EEPROM did not reload in %d usec.\n", + pci_name(np->pci_dev), i*50); } else if (netif_msg_hw(np)) { - printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n", - dev->name, i*50); + printk(KERN_DEBUG "natsemi %s: EEPROM reloaded in %d usec.\n", + pci_name(np->pci_dev), i*50); } } @@ -1132,6 +1533,9 @@ { struct netdev_private *np = dev->priv; + if (dev->if_port != PORT_TP) + return; + if (np->srr >= SRR_DP83816_A5) return; @@ -1173,6 +1577,9 @@ u16 data; struct netdev_private *np = dev->priv; + if (dev->if_port != PORT_TP) + return; + if (np->srr >= SRR_DP83816_A5) return; @@ -1189,9 +1596,16 @@ struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; int duplex; - int chipcfg = readl(ioaddr + ChipConfig); + u16 bmsr; + + /* The link status field is latched: it remains low after a temporary + * link failure until it's read. We need the current link status, + * thus read twice. + */ + mdio_read(dev, MII_BMSR); + bmsr = mdio_read(dev, MII_BMSR); - if (!(chipcfg & CfgLink)) { + if (!(bmsr & BMSR_LSTATUS)) { if (netif_carrier_ok(dev)) { if (netif_msg_link(np)) printk(KERN_NOTICE "%s: link down.\n", @@ -1208,7 +1622,16 @@ do_cable_magic(dev); } - duplex = np->full_duplex || (chipcfg & CfgFullDuplex ? 1 : 0); + duplex = np->full_duplex; + if (!duplex) { + if (bmsr & BMSR_ANEGCOMPLETE) { + int tmp = mii_nway_result( + np->advertising & mdio_read(dev, MII_LPA)); + if (tmp == LPA_100FULL || tmp == LPA_10FULL) + duplex = 1; + } else if (mdio_read(dev, MII_BMCR) & BMCR_FULLDPLX) + duplex = 1; + } /* if duplex is set then bit 28 must be set, too */ if (duplex ^ !!(np->rx_config & RxAcceptTx)) { @@ -1233,40 +1656,8 @@ { struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; - int i; - - for (i=0;ibase_addr + ChipConfig) & CfgAnegDone) - break; - udelay(10); - } - if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) { - printk(KERN_INFO - "%s: autonegotiation did not complete in %d usec.\n", - dev->name, i*10); - } - /* On page 78 of the spec, they recommend some settings for "optimum - performance" to be done in sequence. These settings optimize some - of the 100Mbit autodetection circuitry. They say we only want to - do this for rev C of the chip, but engineers at NSC (Bradley - Kennedy) recommends always setting them. If you don't, you get - errors on some autonegotiations that make the device unusable. - */ - writew(1, ioaddr + PGSEL); - writew(PMDCSR_VAL, ioaddr + PMDCSR); - writew(TSTDAT_VAL, ioaddr + TSTDAT); - writew(DSPCFG_VAL, ioaddr + DSPCFG); - writew(SDCFG_VAL, ioaddr + SDCFG); - writew(0, ioaddr + PGSEL); - np->dspcfg = DSPCFG_VAL; - - /* Enable PHY Specific event based interrupts. Link state change - and Auto-Negotiation Completion are among the affected. - Read the intr status to clear it (needed for wake events). - */ - readw(ioaddr + MIntrStatus); - writew(MICRIntEn, ioaddr + MIntrCtrl); + init_phy_fixup(dev); /* clear any interrupts that are pending, such as wake events */ readl(ioaddr + IntrStatus); @@ -1296,6 +1687,10 @@ * MXDMA 0: up to 256 byte bursts */ np->rx_config = RxMxdma_256 | 0x20; + /* if receive ring now has bigger buffers than normal, enable jumbo */ + if (np->rx_buf_sz > PKT_BUF_SZ) + np->rx_config |= RxAcceptLong; + writel(np->rx_config, ioaddr + RxConfig); /* Disable PME: @@ -1339,8 +1734,6 @@ struct net_device *dev = (struct net_device *)data; struct netdev_private *np = dev->priv; int next_tick = 5*HZ; - long ioaddr = dev->base_addr; - u16 dspcfg; if (netif_msg_timer(np)) { /* DO NOT read the IntrStatus register, @@ -1350,33 +1743,41 @@ dev->name); } - spin_lock_irq(&np->lock); + if (dev->if_port == PORT_TP) { + long ioaddr = dev->base_addr; + u16 dspcfg; - /* check for a nasty random phy-reset - use dspcfg as a flag */ - writew(1, ioaddr+PGSEL); - dspcfg = readw(ioaddr+DSPCFG); - writew(0, ioaddr+PGSEL); - if (dspcfg != np->dspcfg) { - if (!netif_queue_stopped(dev)) { - spin_unlock_irq(&np->lock); - if (netif_msg_hw(np)) - printk(KERN_NOTICE "%s: possible phy reset: " - "re-initializing\n", dev->name); - disable_irq(dev->irq); - spin_lock_irq(&np->lock); - natsemi_stop_rxtx(dev); - dump_ring(dev); - reinit_ring(dev); - init_registers(dev); - spin_unlock_irq(&np->lock); - enable_irq(dev->irq); + spin_lock_irq(&np->lock); + /* check for a nasty random phy-reset - use dspcfg as a flag */ + writew(1, ioaddr+PGSEL); + dspcfg = readw(ioaddr+DSPCFG); + writew(0, ioaddr+PGSEL); + if (dspcfg != np->dspcfg) { + if (!netif_queue_stopped(dev)) { + spin_unlock_irq(&np->lock); + if (netif_msg_hw(np)) + printk(KERN_NOTICE "%s: possible phy reset: " + "re-initializing\n", dev->name); + disable_irq(dev->irq); + spin_lock_irq(&np->lock); + natsemi_stop_rxtx(dev); + dump_ring(dev); + reinit_ring(dev); + init_registers(dev); + spin_unlock_irq(&np->lock); + enable_irq(dev->irq); + } else { + /* hurry back */ + next_tick = HZ; + spin_unlock_irq(&np->lock); + } } else { - /* hurry back */ - next_tick = HZ; + /* init_registers() calls check_link() for the above case */ + check_link(dev); spin_unlock_irq(&np->lock); } } else { - /* init_registers() calls check_link() for the above case */ + spin_lock_irq(&np->lock); check_link(dev); spin_unlock_irq(&np->lock); } @@ -1507,8 +1908,10 @@ /* 2) RX ring */ np->dirty_rx = 0; np->cur_rx = RX_RING_SIZE; - np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); np->oom = 0; + np->rx_buf_sz = PKT_BUF_SZ; + if (dev->mtu > ETH_DATA_LEN) + np->rx_buf_sz += dev->mtu - ETH_DATA_LEN; np->rx_head_desc = &np->rx_ring[0]; /* Please be carefull before changing this loop - at least gcc-2.95.1 @@ -1755,12 +2158,14 @@ /* If the driver owns the next entry it's a new packet. Send it up. */ while (desc_status < 0) { /* e.g. & DescOwn */ + int pkt_len; if (netif_msg_rx_status(np)) printk(KERN_DEBUG " netdev_rx() entry %d status was %#08x.\n", entry, desc_status); if (--boguscnt < 0) break; + pkt_len = (desc_status & DescSizeMask) - 4; if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){ if (desc_status & DescMore) { if (netif_msg_rx_err(np)) @@ -1783,10 +2188,14 @@ if (desc_status & DescRxCRC) np->stats.rx_crc_errors++; } + } else if (pkt_len > np->rx_buf_sz) { + /* if this is the tail of a double buffer + * packet, we've already counted the error + * on the first part. Ignore the second half. + */ } else { struct sk_buff *skb; /* Omit CRC size. */ - int pkt_len = (desc_status & DescSizeMask) - 4; /* Check if the packet is long enough to accept * without copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak @@ -1837,14 +2246,13 @@ spin_lock(&np->lock); if (intr_status & LinkChange) { - u16 adv = mdio_read(dev, 1, MII_ADVERTISE); - u16 lpa = mdio_read(dev, 1, MII_LPA); - if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE + u16 lpa = mdio_read(dev, MII_LPA); + if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE && netif_msg_link(np)) { printk(KERN_INFO "%s: Autonegotiation advertising" " %#04x partner %#04x.\n", dev->name, - adv, lpa); + np->advertising, lpa); } /* read MII int status to clear the flag */ @@ -2072,10 +2480,10 @@ int tmp; int r = -EINVAL; /* if autoneg is off, it's an error */ - tmp = mdio_read(dev, 1, MII_BMCR); + tmp = mdio_read(dev, MII_BMCR); if (tmp & BMCR_ANENABLE) { tmp |= (BMCR_ANRESTART); - mdio_write(dev, 1, MII_BMCR, tmp); + mdio_write(dev, MII_BMCR, tmp); r = 0; } return r; @@ -2084,8 +2492,8 @@ case ETHTOOL_GLINK: { struct ethtool_value edata = {ETHTOOL_GLINK}; /* LSTATUS is latched low until a read - so read twice */ - mdio_read(dev, 1, MII_BMSR); - edata.data = (mdio_read(dev, 1, MII_BMSR)&BMSR_LSTATUS) ? 1:0; + mdio_read(dev, MII_BMSR); + edata.data = (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; @@ -2252,56 +2660,75 @@ static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) { + struct netdev_private *np = dev->priv; u32 tmp; - ecmd->supported = - (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); - - /* only supports twisted-pair or MII */ - tmp = readl(dev->base_addr + ChipConfig); - if (tmp & CfgExtPhy) - ecmd->port = PORT_MII; - else - ecmd->port = PORT_TP; - - /* only supports internal transceiver */ - ecmd->transceiver = XCVR_INTERNAL; - - /* not sure what this is for */ - ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & PhyAddrMask; - - ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII; - tmp = mdio_read(dev, 1, MII_ADVERTISE); - if (tmp & ADVERTISE_10HALF) + ecmd->port = dev->if_port; + ecmd->speed = np->speed; + ecmd->duplex = np->duplex; + ecmd->autoneg = np->autoneg; + ecmd->advertising = 0; + if (np->advertising & ADVERTISE_10HALF) ecmd->advertising |= ADVERTISED_10baseT_Half; - if (tmp & ADVERTISE_10FULL) + if (np->advertising & ADVERTISE_10FULL) ecmd->advertising |= ADVERTISED_10baseT_Full; - if (tmp & ADVERTISE_100HALF) + if (np->advertising & ADVERTISE_100HALF) ecmd->advertising |= ADVERTISED_100baseT_Half; - if (tmp & ADVERTISE_100FULL) + if (np->advertising & ADVERTISE_100FULL) ecmd->advertising |= ADVERTISED_100baseT_Full; + ecmd->supported = (SUPPORTED_Autoneg | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE); + ecmd->phy_address = np->phy_addr_external; + /* + * We intentionally report the phy address of the external + * phy, even if the internal phy is used. This is necessary + * to work around a deficiency of the ethtool interface: + * It's only possible to query the settings of the active + * port. Therefore + * # ethtool -s ethX port mii + * actually sends an ioctl to switch to port mii with the + * settings that are used for the current active port. + * If we would report a different phy address in this + * command, then + * # ethtool -s ethX port tp;ethtool -s ethX port mii + * would unintentionally change the phy address. + * + * Fortunately the phy address doesn't matter with the + * internal phy... + */ - tmp = mdio_read(dev, 1, MII_BMCR); - if (tmp & BMCR_ANENABLE) { - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->autoneg = AUTONEG_ENABLE; - } else { - ecmd->autoneg = AUTONEG_DISABLE; - } - - tmp = readl(dev->base_addr + ChipConfig); - if (tmp & CfgSpeed100) { - ecmd->speed = SPEED_100; - } else { - ecmd->speed = SPEED_10; + /* set information based on active port type */ + switch (ecmd->port) { + default: + case PORT_TP: + ecmd->advertising |= ADVERTISED_TP; + ecmd->transceiver = XCVR_INTERNAL; + break; + case PORT_MII: + ecmd->advertising |= ADVERTISED_MII; + ecmd->transceiver = XCVR_EXTERNAL; + break; + case PORT_FIBRE: + ecmd->advertising |= ADVERTISED_FIBRE; + ecmd->transceiver = XCVR_EXTERNAL; + break; } - if (tmp & CfgFullDuplex) { - ecmd->duplex = DUPLEX_FULL; - } else { - ecmd->duplex = DUPLEX_HALF; + /* if autonegotiation is on, try to return the active speed/duplex */ + if (ecmd->autoneg == AUTONEG_ENABLE) { + ecmd->advertising |= ADVERTISED_Autoneg; + tmp = mii_nway_result( + np->advertising & mdio_read(dev, MII_LPA)); + if (tmp == LPA_100FULL || tmp == LPA_100HALF) + ecmd->speed = SPEED_100; + else + ecmd->speed = SPEED_10; + if (tmp == LPA_100FULL || tmp == LPA_10FULL) + ecmd->duplex = DUPLEX_FULL; + else + ecmd->duplex = DUPLEX_HALF; } /* ignore maxtxpkt, maxrxpkt for now */ @@ -2312,38 +2739,74 @@ static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) { struct netdev_private *np = dev->priv; - u32 tmp; - if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->port != PORT_TP && ecmd->port != PORT_MII) + if (ecmd->port != PORT_TP && ecmd->port != PORT_MII && ecmd->port != PORT_FIBRE) return -EINVAL; - if (ecmd->transceiver != XCVR_INTERNAL) + if (ecmd->transceiver != XCVR_INTERNAL && ecmd->transceiver != XCVR_EXTERNAL) return -EINVAL; - if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) + if (ecmd->autoneg == AUTONEG_ENABLE) { + if ((ecmd->advertising & (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full)) == 0) { + return -EINVAL; + } + } else if (ecmd->autoneg == AUTONEG_DISABLE) { + if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) + return -EINVAL; + } else { return -EINVAL; - /* ignore phy_address, maxtxpkt, maxrxpkt for now */ + } + + /* + * maxtxpkt, maxrxpkt: ignored for now. + * + * transceiver: + * PORT_TP is always XCVR_INTERNAL, PORT_MII and PORT_FIBRE are always + * XCVR_EXTERNAL. The implementation thus ignores ecmd->transceiver and + * selects based on ecmd->port. + * + * Actually PORT_FIBRE is nearly identical to PORT_MII: it's for fibre + * phys that are connected to the mii bus. It's used to apply fibre + * specific updates. + */ /* WHEW! now lets bang some bits */ - tmp = mdio_read(dev, 1, MII_BMCR); - if (ecmd->autoneg == AUTONEG_ENABLE) { - /* turn on autonegotiation */ - tmp |= BMCR_ANENABLE; - np->advertising = mdio_read(dev, 1, MII_ADVERTISE); + /* save the parms */ + dev->if_port = ecmd->port; + np->autoneg = ecmd->autoneg; + np->phy_addr_external = ecmd->phy_address & PhyAddrMask; + if (np->autoneg == AUTONEG_ENABLE) { + /* advertise only what has been requested */ + np->advertising &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (ecmd->advertising & ADVERTISED_10baseT_Half) + np->advertising |= ADVERTISE_10HALF; + if (ecmd->advertising & ADVERTISED_10baseT_Full) + np->advertising |= ADVERTISE_10FULL; + if (ecmd->advertising & ADVERTISED_100baseT_Half) + np->advertising |= ADVERTISE_100HALF; + if (ecmd->advertising & ADVERTISED_100baseT_Full) + np->advertising |= ADVERTISE_100FULL; } else { - /* turn off auto negotiation, set speed and duplexity */ - tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX); - if (ecmd->speed == SPEED_100) - tmp |= BMCR_SPEED100; - if (ecmd->duplex == DUPLEX_FULL) - tmp |= BMCR_FULLDPLX; - else + np->speed = ecmd->speed; + np->duplex = ecmd->duplex; + /* user overriding the initial full duplex parm? */ + if (np->duplex == DUPLEX_HALF) np->full_duplex = 0; } - mdio_write(dev, 1, MII_BMCR, tmp); + + /* get the right phy enabled */ + if (ecmd->port == PORT_TP) + switch_port_internal(dev); + else + switch_port_external(dev); + + /* set parms and see how this affected our link status */ + init_phy_fixup(dev); + check_link(dev); return 0; } @@ -2354,11 +2817,15 @@ u32 rfcr; u32 *rbuf = (u32 *)buf; - /* read all of page 0 of registers */ - for (i = 0; i < NATSEMI_PG0_NREGS; i++) { + /* read non-mii page 0 of registers */ + for (i = 0; i < NATSEMI_PG0_NREGS/2; i++) { rbuf[i] = readl(dev->base_addr + i*4); } + /* read current mii registers */ + for (i = NATSEMI_PG0_NREGS/2; i < NATSEMI_PG0_NREGS; i++) + rbuf[i] = mdio_read(dev, i & 0x1f); + /* read only the 'magic' registers from page 1 */ writew(1, dev->base_addr + PGSEL); rbuf[i++] = readw(dev->base_addr + PMDCSR); @@ -2409,31 +2876,59 @@ } return 0; } - static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct mii_ioctl_data *data = if_mii(rq); + struct netdev_private *np = dev->priv; switch(cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(dev, rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ - data->phy_id = 1; + data->phy_id = np->phy_addr_external; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ - data->val_out = mdio_read(dev, data->phy_id & 0x1f, - data->reg_num & 0x1f); + /* The phy_id is not enough to uniquely identify + * the intended target. Therefore the command is sent to + * the given mii on the current port. + */ + if (dev->if_port == PORT_TP) { + if ((data->phy_id & 0x1f) == np->phy_addr_external) + data->val_out = mdio_read(dev, + data->reg_num & 0x1f); + else + data->val_out = 0; + } else { + move_int_phy(dev, data->phy_id & 0x1f); + data->val_out = miiport_read(dev, data->phy_id & 0x1f, + data->reg_num & 0x1f); + } return 0; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ if (!capable(CAP_NET_ADMIN)) return -EPERM; - mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, - data->val_in); + if (dev->if_port == PORT_TP) { + if ((data->phy_id & 0x1f) == np->phy_addr_external) { + if ((data->reg_num & 0x1f) == MII_ADVERTISE) + np->advertising = data->val_in; + mdio_write(dev, data->reg_num & 0x1f, + data->val_in); + } + } else { + if ((data->phy_id & 0x1f) == np->phy_addr_external) { + if ((data->reg_num & 0x1f) == MII_ADVERTISE) + np->advertising = data->val_in; + } + move_int_phy(dev, data->phy_id & 0x1f); + miiport_write(dev, data->phy_id & 0x1f, + data->reg_num & 0x1f, + data->val_in); + } return 0; default: return -EOPNOTSUPP; diff -Nru a/drivers/net/r8169.c b/drivers/net/r8169.c --- a/drivers/net/r8169.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/r8169.c 2004-06-20 13:15:18 -07:00 @@ -7,7 +7,7 @@ Feb 4 2002 - created initially by ShuChen . May 20 2002 - Add link status force-mode and TBI mode support. ========================================================================= - 1. The media can be forced in 5 modes. + 1. [DEPRECATED: use ethtool instead] The media can be forced in 5 modes. Command: 'insmod r8169 media = SET_MEDIA' Ex: 'insmod r8169 media = 0x04' will force PHY to operate in 100Mpbs Half-duplex. @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,14 @@ #define dprintk(fmt, args...) do {} while (0) #endif /* RTL8169_DEBUG */ +#ifdef CONFIG_R8169_NAPI +#define rtl8169_rx_skb netif_receive_skb +#define rtl8169_rx_quota(count, quota) min(count, quota) +#else +#define rtl8169_rx_skb netif_rx +#define rtl8169_rx_quota(count, quota) count +#endif + /* media options */ #define MAX_UNITS 8 static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; @@ -90,15 +99,16 @@ #define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */ #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ +#define R8169_NAPI_WEIGHT 64 #define NUM_TX_DESC 64 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 64 /* Number of Rx descriptor registers */ +#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */ #define RX_BUF_SIZE 1536 /* Rx Buffer size */ #define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) #define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) #define RTL_MIN_IO_SIZE 0x80 #define RTL8169_TX_TIMEOUT (6*HZ) -#define RTL8169_PHY_TIMEOUT (HZ) +#define RTL8169_PHY_TIMEOUT (10*HZ) /* write/read MMIO register */ #define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) @@ -194,7 +204,7 @@ SWInt = 0x0100, TxDescUnavail = 0x80, RxFIFOOver = 0x40, - RxUnderrun = 0x20, + LinkChg = 0x20, RxOverflow = 0x10, TxErr = 0x08, TxOK = 0x04, @@ -233,6 +243,14 @@ TxInterFrameGapShift = 24, TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + /* TBICSR p.28 */ + TBIReset = 0x80000000, + TBILoopback = 0x40000000, + TBINwEnable = 0x20000000, + TBINwRestart = 0x10000000, + TBILinkOk = 0x02000000, + TBINwComplete = 0x01000000, + /* CPlusCmd p.31 */ RxVlan = (1 << 6), RxChkSum = (1 << 5), @@ -306,10 +324,10 @@ }; struct rtl8169_private { - void *mmio_addr; /* memory map physical address */ + void *mmio_addr; /* memory map physical address */ struct pci_dev *pci_dev; /* Index of PCI device */ struct net_device_stats stats; /* statistics of net device */ - spinlock_t lock; /* spin lock flag */ + spinlock_t lock; /* spin lock flag */ int chipset; int mac_version; int phy_version; @@ -317,15 +335,23 @@ u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ u32 dirty_rx; u32 dirty_tx; - struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ - struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ + struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */ + struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */ dma_addr_t TxPhyAddr; dma_addr_t RxPhyAddr; struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ - struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Index of Transmit data buffer */ + struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Tx data buffers */ struct timer_list timer; - unsigned long phy_link_down_cnt; u16 cp_cmd; + u16 intr_mask; + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; + + int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); + void (*get_settings)(struct net_device *, struct ethtool_cmd *); + void (*phy_reset_enable)(void *); + unsigned int (*phy_reset_pending)(void *); + unsigned int (*link_ok)(void *); }; MODULE_AUTHOR("Realtek"); @@ -344,9 +370,14 @@ static void rtl8169_set_rx_mode(struct net_device *dev); static void rtl8169_tx_timeout(struct net_device *dev); static struct net_device_stats *rtl8169_get_stats(struct net_device *netdev); +#ifdef CONFIG_R8169_NAPI +static int rtl8169_poll(struct net_device *dev, int *budget); +#endif static const u16 rtl8169_intr_mask = - RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; + LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; +static const u16 rtl8169_napi_event = + RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr; static const unsigned int rtl8169_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); @@ -364,11 +395,9 @@ for (i = 2000; i > 0; i--) { // Check if the RTL8169 has completed writing to the specified MII register - if (!(RTL_R32(PHYAR) & 0x80000000)) { + if (!(RTL_R32(PHYAR) & 0x80000000)) break; - } else { - udelay(100); - } + udelay(100); } } @@ -390,18 +419,264 @@ return value; } +static unsigned int rtl8169_tbi_reset_pending(void *ioaddr) +{ + return RTL_R32(TBICSR) & TBIReset; +} + +static unsigned int rtl8169_xmii_reset_pending(void *ioaddr) +{ + return mdio_read(ioaddr, 0) & 0x8000; +} + +static unsigned int rtl8169_tbi_link_ok(void *ioaddr) +{ + return RTL_R32(TBICSR) & TBILinkOk; +} + +static unsigned int rtl8169_xmii_link_ok(void *ioaddr) +{ + return RTL_R8(PHYstatus) & LinkStatus; +} + +static void rtl8169_tbi_reset_enable(void *ioaddr) +{ + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} + +static void rtl8169_xmii_reset_enable(void *ioaddr) +{ + unsigned int val; + + val = (mdio_read(ioaddr, PHY_CTRL_REG) | 0x8000) & 0xffff; + mdio_write(ioaddr, PHY_CTRL_REG, val); +} + +static void rtl8169_check_link_status(struct net_device *dev, + struct rtl8169_private *tp, void *ioaddr) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + if (tp->link_ok(ioaddr)) { + netif_carrier_on(dev); + printk(KERN_INFO PFX "%s: link up\n", dev->name); + } else + netif_carrier_off(dev); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static void rtl8169_link_option(int idx, u8 *autoneg, u16 *speed, u8 *duplex) +{ + struct { + u16 speed; + u8 duplex; + u8 autoneg; + u8 media; + } link_settings[] = { + { SPEED_10, DUPLEX_HALF, AUTONEG_DISABLE, _10_Half }, + { SPEED_10, DUPLEX_FULL, AUTONEG_DISABLE, _10_Full }, + { SPEED_100, DUPLEX_HALF, AUTONEG_DISABLE, _100_Half }, + { SPEED_100, DUPLEX_FULL, AUTONEG_DISABLE, _100_Full }, + { SPEED_1000, DUPLEX_FULL, AUTONEG_DISABLE, _1000_Full }, + /* Make TBI happy */ + { SPEED_1000, DUPLEX_FULL, AUTONEG_ENABLE, 0xff } + }, *p; + unsigned char option; + + option = ((idx < MAX_UNITS) && (idx >= 0)) ? media[idx] : 0xff; + + if ((option != 0xff) && !idx) + printk(KERN_WARNING PFX "media option is deprecated.\n"); + + for (p = link_settings; p->media != 0xff; p++) { + if (p->media == option) + break; + } + *autoneg = p->autoneg; + *speed = p->speed; + *duplex = p->duplex; +} + static void rtl8169_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); strcpy(info->driver, RTL8169_DRIVER_NAME); strcpy(info->version, RTL8169_VERSION ); strcpy(info->bus_info, pci_name(tp->pci_dev)); } +static int rtl8169_set_speed_tbi(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int ret = 0; + u32 reg; + + reg = RTL_R32(TBICSR); + if ((autoneg == AUTONEG_DISABLE) && (speed == SPEED_1000) && + (duplex == DUPLEX_FULL)) { + RTL_W32(TBICSR, reg & ~(TBINwEnable | TBINwRestart)); + } else if (autoneg == AUTONEG_ENABLE) + RTL_W32(TBICSR, reg | TBINwEnable | TBINwRestart); + else { + printk(KERN_WARNING PFX + "%s: incorrect speed setting refused in TBI mode\n", + dev->name); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int rtl8169_set_speed_xmii(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int auto_nego, giga_ctrl; + + auto_nego = mdio_read(ioaddr, PHY_AUTO_NEGO_REG); + auto_nego &= ~(PHY_Cap_10_Half | PHY_Cap_10_Full | + PHY_Cap_100_Half | PHY_Cap_100_Full); + giga_ctrl = mdio_read(ioaddr, PHY_1000_CTRL_REG); + giga_ctrl &= ~(PHY_Cap_1000_Full | PHY_Cap_Null); + + if (autoneg == AUTONEG_ENABLE) { + auto_nego |= (PHY_Cap_10_Half | PHY_Cap_10_Full | + PHY_Cap_100_Half | PHY_Cap_100_Full); + giga_ctrl |= PHY_Cap_1000_Full; + } else { + if (speed == SPEED_10) + auto_nego |= PHY_Cap_10_Half | PHY_Cap_10_Full; + else if (speed == SPEED_100) + auto_nego |= PHY_Cap_100_Half | PHY_Cap_100_Full; + else if (speed == SPEED_1000) + giga_ctrl |= PHY_Cap_1000_Full; + + if (duplex == DUPLEX_HALF) + auto_nego &= ~(PHY_Cap_10_Full | PHY_Cap_100_Full); + } + + tp->phy_auto_nego_reg = auto_nego; + tp->phy_1000_ctrl_reg = giga_ctrl; + + mdio_write(ioaddr, PHY_AUTO_NEGO_REG, auto_nego); + mdio_write(ioaddr, PHY_1000_CTRL_REG, giga_ctrl); + mdio_write(ioaddr, PHY_CTRL_REG, PHY_Enable_Auto_Nego | + PHY_Restart_Auto_Nego); + return 0; +} + +static int rtl8169_set_speed(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + ret = tp->set_speed(dev, autoneg, speed, duplex); + + if (netif_running(dev) && (tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full)) + mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT); + + return ret; +} + +static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&tp->lock, flags); + ret = rtl8169_set_speed(dev, cmd->autoneg, cmd->speed, cmd->duplex); + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +static void rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + u32 status; + + cmd->supported = + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE; + cmd->port = PORT_FIBRE; + cmd->transceiver = XCVR_INTERNAL; + + status = RTL_R32(TBICSR); + cmd->advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0; + cmd->autoneg = !!(status & TBINwEnable); + + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; /* Always set */ +} + +static void rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + u8 status; + + cmd->supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP; + + cmd->autoneg = 1; + cmd->advertising = ADVERTISED_TP | ADVERTISED_Autoneg; + + if (tp->phy_auto_nego_reg & PHY_Cap_10_Half) + cmd->advertising |= ADVERTISED_10baseT_Half; + if (tp->phy_auto_nego_reg & PHY_Cap_10_Full) + cmd->advertising |= ADVERTISED_10baseT_Full; + if (tp->phy_auto_nego_reg & PHY_Cap_100_Half) + cmd->advertising |= ADVERTISED_100baseT_Half; + if (tp->phy_auto_nego_reg & PHY_Cap_100_Full) + cmd->advertising |= ADVERTISED_100baseT_Full; + if (tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full) + cmd->advertising |= ADVERTISED_1000baseT_Full; + + status = RTL_R8(PHYstatus); + + if (status & _1000bpsF) + cmd->speed = SPEED_1000; + else if (status & _100bps) + cmd->speed = SPEED_100; + else if (status & _10bps) + cmd->speed = SPEED_10; + + cmd->duplex = ((status & _1000bpsF) || (status & FullDup)) ? + DUPLEX_FULL : DUPLEX_HALF; +} + +static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + tp->get_settings(dev, cmd); + + spin_unlock_irqrestore(&tp->lock, flags); + return 0; +} + + static struct ethtool_ops rtl8169_ethtool_ops = { .get_drvinfo = rtl8169_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_settings = rtl8169_get_settings, + .set_settings = rtl8169_set_settings, }; static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum, @@ -500,7 +775,7 @@ static void rtl8169_hw_phy_config(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; struct { u16 regs[5]; /* Beware of bit-sign propagation */ @@ -566,61 +841,47 @@ mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0 } -static void rtl8169_hw_phy_reset(struct net_device *dev) -{ - struct rtl8169_private *tp = dev->priv; - void *ioaddr = tp->mmio_addr; - int i, val; - - printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev->name); - - val = (mdio_read(ioaddr, 0) | 0x8000) & 0xffff; - mdio_write(ioaddr, 0, val); - - for (i = 50; i >= 0; i--) { - if (!(mdio_read(ioaddr, 0) & 0x8000)) - break; - udelay(100); /* Gross */ - } - - if (i < 0) { - printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n", - dev->name); - } -} - static void rtl8169_phy_timer(unsigned long __opaque) { struct net_device *dev = (struct net_device *)__opaque; - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); struct timer_list *timer = &tp->timer; void *ioaddr = tp->mmio_addr; + unsigned long timeout = RTL8169_PHY_TIMEOUT; assert(tp->mac_version > RTL_GIGA_MAC_VER_B); assert(tp->phy_version < RTL_GIGA_PHY_VER_G); - if (RTL_R8(PHYstatus) & LinkStatus) - tp->phy_link_down_cnt = 0; - else { - tp->phy_link_down_cnt++; - if (tp->phy_link_down_cnt >= 12) { - int reg; - - // If link on 1000, perform phy reset. - reg = mdio_read(ioaddr, PHY_1000_CTRL_REG); - if (reg & PHY_Cap_1000_Full) - rtl8169_hw_phy_reset(dev); + if (!(tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full)) + return; - tp->phy_link_down_cnt = 0; - } + spin_lock_irq(&tp->lock); + + if (tp->phy_reset_pending(ioaddr)) { + /* + * A busy loop could burn quite a few cycles on nowadays CPU. + * Let's delay the execution of the timer for a few ticks. + */ + timeout = HZ/10; + goto out_mod_timer; } - mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); + if (tp->link_ok(ioaddr)) + goto out_unlock; + + printk(KERN_WARNING PFX "%s: PHY reset until link up\n", dev->name); + + tp->phy_reset_enable(ioaddr); + +out_mod_timer: + mod_timer(timer, jiffies + timeout); +out_unlock: + spin_unlock_irq(&tp->lock); } static inline void rtl8169_delete_timer(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); struct timer_list *timer = &tp->timer; if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || @@ -628,21 +889,17 @@ return; del_timer_sync(timer); - - tp->phy_link_down_cnt = 0; } static inline void rtl8169_request_timer(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); struct timer_list *timer = &tp->timer; if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || (tp->phy_version >= RTL_GIGA_PHY_VER_G)) return; - tp->phy_link_down_cnt = 0; - init_timer(timer); timer->expires = jiffies + RTL8169_PHY_TIMEOUT; timer->data = (unsigned long)(dev); @@ -681,7 +938,7 @@ // enable device (incl. PCI PM wakeup and hotplug setup) rc = pci_enable_device(pdev); if (rc) { - printk(KERN_ERR PFX "%s: unable to enable device\n", pdev->slot_name); + printk(KERN_ERR PFX "%s: enable failure\n", pdev->slot_name); goto err_out; } @@ -693,7 +950,8 @@ pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command); acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; } else { - printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n"); + printk(KERN_ERR PFX + "Cannot find PowerManagement capability, aborting.\n"); goto err_out_free_res; } @@ -718,7 +976,8 @@ rc = pci_request_regions(pdev, MODULENAME); if (rc) { - printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev->slot_name); + printk(KERN_ERR PFX "%s: could not request regions.\n", + pdev->slot_name); goto err_out_disable; } @@ -800,8 +1059,9 @@ void *ioaddr = NULL; static int board_idx = -1; static int printed_version = 0; + u8 autoneg, duplex; + u16 speed; int i, rc; - int option = -1, Cap10_100 = 0, Cap1000 = 0; assert(pdev != NULL); assert(ent != NULL); @@ -822,6 +1082,22 @@ assert(dev != NULL); assert(tp != NULL); + if (RTL_R8(PHYstatus) & TBI_Enable) { + tp->set_speed = rtl8169_set_speed_tbi; + tp->get_settings = rtl8169_gset_tbi; + tp->phy_reset_enable = rtl8169_tbi_reset_enable; + tp->phy_reset_pending = rtl8169_tbi_reset_pending; + tp->link_ok = rtl8169_tbi_link_ok; + + tp->phy_1000_ctrl_reg = PHY_Cap_1000_Full; /* Implied by TBI */ + } else { + tp->set_speed = rtl8169_set_speed_xmii; + tp->get_settings = rtl8169_gset_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; + } + // Get MAC address. FIXME: read EEPROM for (i = 0; i < MAC_ADDR_LEN; i++) dev->dev_addr[i] = RTL_R8(MAC0 + i); @@ -836,9 +1112,12 @@ dev->watchdog_timeo = RTL8169_TX_TIMEOUT; dev->irq = pdev->irq; dev->base_addr = (unsigned long) ioaddr; -// dev->do_ioctl = mii_ioctl; - - tp = dev->priv; // private data // +#ifdef CONFIG_R8169_NAPI + dev->poll = rtl8169_poll; + dev->weight = R8169_NAPI_WEIGHT; + printk(KERN_INFO PFX "NAPI enabled\n"); +#endif + tp->intr_mask = 0xffff; tp->pci_dev = pdev; tp->mmio_addr = ioaddr; @@ -885,95 +1164,12 @@ mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 } - // if TBI is not endbled - if (!(RTL_R8(PHYstatus) & TBI_Enable)) { - int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG); - - option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; - // Force RTL8169 in 10/100/1000 Full/Half mode. - if (option > 0) { - printk(KERN_INFO "%s: Force-mode Enabled.\n", - dev->name); - Cap10_100 = 0, Cap1000 = 0; - switch (option) { - case _10_Half: - Cap10_100 = PHY_Cap_10_Half_Or_Less; - Cap1000 = PHY_Cap_Null; - break; - case _10_Full: - Cap10_100 = PHY_Cap_10_Full_Or_Less; - Cap1000 = PHY_Cap_Null; - break; - case _100_Half: - Cap10_100 = PHY_Cap_100_Half_Or_Less; - Cap1000 = PHY_Cap_Null; - break; - case _100_Full: - Cap10_100 = PHY_Cap_100_Full_Or_Less; - Cap1000 = PHY_Cap_Null; - break; - case _1000_Full: - Cap10_100 = PHY_Cap_100_Full_Or_Less; - Cap1000 = PHY_Cap_1000_Full; - break; - default: - break; - } - mdio_write(ioaddr, PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0x1F)); //leave PHY_AUTO_NEGO_REG bit4:0 unchanged - mdio_write(ioaddr, PHY_1000_CTRL_REG, Cap1000); - } else { - printk(KERN_INFO "%s: Auto-negotiation Enabled.\n", - dev->name); - - // enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged - mdio_write(ioaddr, PHY_AUTO_NEGO_REG, - PHY_Cap_100_Full_Or_Less | (val & 0x1f)); - - // enable 1000 Full Mode - mdio_write(ioaddr, PHY_1000_CTRL_REG, - PHY_Cap_1000_Full); - - } - - // Enable auto-negotiation and restart auto-nigotiation - mdio_write(ioaddr, PHY_CTRL_REG, - PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego); - udelay(100); - - // wait for auto-negotiation process - for (i = 10000; i > 0; i--) { - //check if auto-negotiation complete - if (mdio_read(ioaddr, PHY_STAT_REG) & - PHY_Auto_Neco_Comp) { - udelay(100); - option = RTL_R8(PHYstatus); - if (option & _1000bpsF) { - printk(KERN_INFO - "%s: 1000Mbps Full-duplex operation.\n", - dev->name); - } else { - printk(KERN_INFO - "%s: %sMbps %s-duplex operation.\n", - dev->name, - (option & _100bps) ? "100" : - "10", - (option & FullDup) ? "Full" : - "Half"); - } - break; - } else { - udelay(100); - } - } // end for-loop to wait for auto-negotiation process - - } else { - udelay(100); - printk(KERN_INFO - "%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", - dev->name, - (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); + rtl8169_link_option(board_idx, &autoneg, &speed, &duplex); - } + rtl8169_set_speed(dev, autoneg, speed, duplex); + + if (RTL_R8(PHYstatus) & TBI_Enable) + printk(KERN_INFO PFX "%s: TBI auto-negotiating\n", dev->name); return 0; } @@ -982,7 +1178,7 @@ rtl8169_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); assert(dev != NULL); assert(tp != NULL); @@ -1001,7 +1197,7 @@ static int rtl8169_suspend(struct pci_dev *pdev, u32 state) { struct net_device *dev = pci_get_drvdata(pdev); - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; unsigned long flags; @@ -1042,7 +1238,7 @@ static int rtl8169_open(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); struct pci_dev *pdev = tp->pci_dev; int retval; @@ -1074,6 +1270,8 @@ rtl8169_hw_start(dev); rtl8169_request_timer(dev); + + rtl8169_check_link_status(dev, tp, tp->mmio_addr); out: return retval; @@ -1091,7 +1289,7 @@ static void rtl8169_hw_start(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; u32 i; @@ -1102,8 +1300,7 @@ for (i = 1000; i > 0; i--) { if ((RTL_R8(ChipCmd) & CmdReset) == 0) break; - else - udelay(10); + udelay(10); } RTL_W8(Cfg9346, Cfg9346_Unlock); @@ -1114,8 +1311,8 @@ RTL_W16(RxMaxSize, RxPacketMaxSize); // Set Rx Config register - i = rtl8169_rx_config | (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset]. - RxConfigMask); + i = rtl8169_rx_config | + (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); RTL_W32(RxConfig, i); /* Set DMA burst size and Interframe Gap Time */ @@ -1126,7 +1323,8 @@ RTL_W16(CPlusCmd, tp->cp_cmd); if (tp->mac_version == RTL_GIGA_MAC_VER_D) { - dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14 MUST be 1\n"); + dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0. " + "Bit-3 and bit-14 MUST be 1\n"); tp->cp_cmd |= (1 << 14) | PCIMulRW; RTL_W16(CPlusCmd, tp->cp_cmd); } @@ -1151,7 +1349,6 @@ RTL_W16(IntrMask, rtl8169_intr_mask); netif_start_queue(dev); - } static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) @@ -1248,7 +1445,7 @@ static int rtl8169_init_ring(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); tp->cur_rx = tp->dirty_rx = 0; tp->cur_tx = tp->dirty_tx = 0; @@ -1302,10 +1499,11 @@ static void rtl8169_tx_timeout(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; u8 tmp8; + printk(KERN_INFO "%s: TX Timeout\n", dev->name); /* disable Tx, if not already */ tmp8 = RTL_R8(ChipCmd); if (tmp8 & CmdTxEnb) @@ -1328,9 +1526,9 @@ static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; - int entry = tp->cur_tx % NUM_TX_DESC; + unsigned int entry = tp->cur_tx % NUM_TX_DESC; u32 len = skb->len; if (unlikely(skb->len < ETH_ZLEN)) { @@ -1340,10 +1538,9 @@ len = ETH_ZLEN; } - spin_lock_irq(&tp->lock); - if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) { dma_addr_t mapping; + u32 status; mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE); @@ -1351,24 +1548,30 @@ tp->Tx_skbuff[entry] = skb; tp->TxDescArray[entry].addr = cpu_to_le64(mapping); - tp->TxDescArray[entry].status = cpu_to_le32(OWNbit | FSbit | - LSbit | len | (EORbit * !((entry + 1) % NUM_TX_DESC))); + /* anti gcc 2.95.3 bugware */ + status = OWNbit | FSbit | LSbit | len | + (EORbit * !((entry + 1) % NUM_TX_DESC)); + tp->TxDescArray[entry].status = cpu_to_le32(status); RTL_W8(TxPoll, 0x40); //set polling bit dev->trans_start = jiffies; tp->cur_tx++; + smp_wmb(); } else goto err_drop; - if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) { + u32 dirty = tp->dirty_tx; + netif_stop_queue(dev); + smp_rmb(); + if (dirty != tp->dirty_tx) + netif_wake_queue(dev); } -out: - spin_unlock_irq(&tp->lock); +out: return 0; err_drop: @@ -1382,17 +1585,18 @@ rtl8169_tx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void *ioaddr) { - unsigned long dirty_tx, tx_left; + unsigned int dirty_tx, tx_left; assert(dev != NULL); assert(tp != NULL); assert(ioaddr != NULL); dirty_tx = tp->dirty_tx; + smp_rmb(); tx_left = tp->cur_tx - dirty_tx; while (tx_left > 0) { - int entry = dirty_tx % NUM_TX_DESC; + unsigned int entry = dirty_tx % NUM_TX_DESC; struct sk_buff *skb = tp->Tx_skbuff[entry]; u32 status; @@ -1415,6 +1619,7 @@ if (tp->dirty_tx != dirty_tx) { tp->dirty_tx = dirty_tx; + smp_wmb(); if (netif_queue_stopped(dev)) netif_wake_queue(dev); } @@ -1442,11 +1647,11 @@ return ret; } -static void +static int rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void *ioaddr) { - unsigned long cur_rx, rx_left; + unsigned int cur_rx, rx_left, count; int delta; assert(dev != NULL); @@ -1455,9 +1660,10 @@ cur_rx = tp->cur_rx; rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; + rx_left = rtl8169_rx_quota(rx_left, (u32) dev->quota); while (rx_left > 0) { - int entry = cur_rx % NUM_RX_DESC; + unsigned int entry = cur_rx % NUM_RX_DESC; u32 status; rmb(); @@ -1494,7 +1700,7 @@ skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); + rtl8169_rx_skb(skb); dev->last_rx = jiffies; tp->stats.rx_bytes += pkt_size; @@ -1505,13 +1711,15 @@ rx_left--; } + count = cur_rx - tp->cur_rx; tp->cur_rx = cur_rx; delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); - if (delta > 0) - tp->dirty_rx += delta; - else if (delta < 0) + if (delta < 0) { printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name); + delta = 0; + } + tp->dirty_rx += delta; /* * FIXME: until there is periodic timer to try and refill the ring, @@ -1522,6 +1730,8 @@ */ if (tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name); + + return count; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ @@ -1529,7 +1739,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_instance; - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); int boguscnt = max_interrupt_work; void *ioaddr = tp->mmio_addr; int status = 0; @@ -1543,26 +1753,37 @@ break; handled = 1; -/* - if (status & RxUnderrun) - link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; -*/ + + status &= tp->intr_mask; RTL_W16(IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status); if (!(status & rtl8169_intr_mask)) break; + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); + +#ifdef CONFIG_R8169_NAPI + RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event); + tp->intr_mask = ~rtl8169_napi_event; + + if (likely(netif_rx_schedule_prep(dev))) + __netif_rx_schedule(dev); + else { + printk(KERN_INFO "%s: interrupt %x taken in poll\n", + dev->name, status); + } + break; +#else // Rx interrupt - if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) { + if (status & (RxOK | RxOverflow | RxFIFOOver)) { rtl8169_rx_interrupt(dev, tp, ioaddr); } // Tx interrupt - if (status & (TxOK | TxErr)) { - spin_lock(&tp->lock); + if (status & (TxOK | TxErr)) rtl8169_tx_interrupt(dev, tp, ioaddr); - spin_unlock(&tp->lock); - } +#endif boguscnt--; } while (boguscnt > 0); @@ -1576,10 +1797,40 @@ return IRQ_RETVAL(handled); } +#ifdef CONFIG_R8169_NAPI +static int rtl8169_poll(struct net_device *dev, int *budget) +{ + unsigned int work_done, work_to_do = min(*budget, dev->quota); + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + + work_done = rtl8169_rx_interrupt(dev, tp, ioaddr); + rtl8169_tx_interrupt(dev, tp, ioaddr); + + *budget -= work_done; + dev->quota -= work_done; + + if ((work_done < work_to_do) || !netif_running(dev)) { + netif_rx_complete(dev); + tp->intr_mask = 0xffff; + /* + * 20040426: the barrier is not strictly required but the + * behavior of the irq handler could be less predictable + * without it. Btw, the lack of flush for the posted pci + * write is safe - FR + */ + smp_wmb(); + RTL_W16(IntrMask, rtl8169_intr_mask); + } + + return (work_done >= work_to_do); +} +#endif + static int rtl8169_close(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); struct pci_dev *pdev = tp->pci_dev; void *ioaddr = tp->mmio_addr; @@ -1621,7 +1872,7 @@ static void rtl8169_set_rx_mode(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; unsigned long flags; u32 mc_filter[2]; /* Multicast hash filter */ @@ -1655,10 +1906,8 @@ spin_lock_irqsave(&tp->lock, flags); - tmp = - rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) & - rtl_chip_info[tp->chipset]. - RxConfigMask); + tmp = rtl8169_rx_config | rx_mode | + (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); RTL_W32(RxConfig, tmp); RTL_W32(MAR0 + 0, mc_filter[0]); @@ -1675,7 +1924,7 @@ */ static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) { - struct rtl8169_private *tp = dev->priv; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; unsigned long flags; diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/sis900.c 2004-06-20 13:15:18 -07:00 @@ -116,6 +116,7 @@ #define HOME 0x0001 #define LAN 0x0002 #define MIX 0x0003 +#define UNKNOWN 0x0 } mii_chip_table[] = { { "SiS 900 Internal MII PHY", 0x001d, 0x8000, LAN }, { "SiS 7014 Physical Layer Solution", 0x0016, 0xf830, LAN }, @@ -577,9 +578,11 @@ break; } - if( !mii_chip_table[i].phy_id1 ) + if( !mii_chip_table[i].phy_id1 ) { printk(KERN_INFO "%s: Unknown PHY transceiver found at address %d.\n", - net_dev->name, phy_addr); + net_dev->name, phy_addr); + mii_phy->phy_types = UNKNOWN; + } } if (sis_priv->mii == NULL) { @@ -644,15 +647,15 @@ static u16 sis900_default_phy(struct net_device * net_dev) { struct sis900_private * sis_priv = net_dev->priv; - struct mii_phy *phy = NULL, *phy_home = NULL, *default_phy = NULL; + struct mii_phy *phy = NULL, *phy_home = NULL, *default_phy = NULL, *phy_lan = NULL; u16 status; for( phy=sis_priv->first_mii; phy; phy=phy->next ){ status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); - /* Link ON & Not select deafalut PHY */ - if ( (status & MII_STAT_LINK) && !(default_phy) ) + /* Link ON & Not select default PHY & not ghost PHY */ + if ( (status & MII_STAT_LINK) && !default_phy && (phy->phy_types != UNKNOWN) ) default_phy = phy; else{ status = mdio_read(net_dev, phy->phy_addr, MII_CONTROL); @@ -660,12 +663,16 @@ status | MII_CNTL_AUTO | MII_CNTL_ISOLATE); if( phy->phy_types == HOME ) phy_home = phy; + else if (phy->phy_types == LAN) + phy_lan = phy; } } - if( (!default_phy) && phy_home ) + if( !default_phy && phy_home ) default_phy = phy_home; - else if(!default_phy) + else if( !default_phy && phy_lan ) + default_phy = phy_lan; + else if ( !default_phy ) default_phy = sis_priv->first_mii; if( sis_priv->mii != default_phy ){ diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c --- a/drivers/net/via-rhine.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/via-rhine.c 2004-06-20 13:15:18 -07:00 @@ -125,11 +125,16 @@ LK1.1.19 (Roger Luethi) - Increase Tx threshold for unspecified errors + LK1.2.0-2.6 (Roger Luethi) + - Massive clean-up + - Rewrite PHY, media handling (remove options, full_duplex, backoff) + - Fix Tx engine race for good + */ #define DRV_NAME "via-rhine" -#define DRV_VERSION "1.1.20-2.6" -#define DRV_RELDATE "May-23-2004" +#define DRV_VERSION "1.2.0-2.6" +#define DRV_RELDATE "June-10-2004" /* A few user-configurable values. @@ -142,22 +147,10 @@ Setting to > 1518 effectively disables this feature. */ static int rx_copybreak; -/* Select a backoff algorithm (Ethernet capture effect) */ -static int backoff; - -/* Used to pass the media type, etc. - Both 'options[]' and 'full_duplex[]' should exist for driver - interoperability. - The media type is usually passed in 'options[]'. - The default is autonegotiation for speed and duplex. - This should rarely be overridden. - Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. - Use option values 0x10 and 0x100 for forcing half duplex fixed speed. - Use option values 0x20 and 0x200 for forcing full duplex operation. -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +/* + * In case you are looking for 'options[]' or 'full_duplex[]', they + * are gone. Use ethtool(8) instead. + */ /* Maximum number of multicast addresses to filter (vs. rx-all-multicast). The Rhine has a 64 element 8390-like hash table. */ @@ -216,9 +209,6 @@ static char version[] __devinitdata = KERN_INFO DRV_NAME ".c:v1.10-LK" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n"; -static char shortname[] = DRV_NAME; - - /* This driver was written to use PCI memory space. Some early versions of the Rhine may only work correctly with I/O space accesses. */ #ifdef CONFIG_VIA_RHINE_MMIO @@ -245,15 +235,9 @@ MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(backoff, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM_DESC(max_interrupt_work, "VIA Rhine maximum events handled per interrupt"); MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)"); MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(backoff, "VIA Rhine: Bits 0-3: backoff algorithm"); -MODULE_PARM_DESC(options, "VIA Rhine: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)"); /* Theory of Operation @@ -356,24 +340,24 @@ enum rhine_revs { VT86C100A = 0x00, + VTunknown0 = 0x20, VT6102 = 0x40, VT8231 = 0x50, /* Integrated MAC */ VT8233 = 0x60, /* Integrated MAC */ VT8235 = 0x74, /* Integrated MAC */ VT8237 = 0x78, /* Integrated MAC */ - VTunknown0 = 0x7C, + VTunknown1 = 0x7C, VT6105 = 0x80, VT6105_B0 = 0x83, VT6105L = 0x8A, VT6107 = 0x8C, - VTunknown1 = 0x8E, + VTunknown2 = 0x8E, VT6105M = 0x90, }; enum rhine_quirks { rqWOL = 0x0001, /* Wake-On-LAN support */ rqForceReset = 0x0002, - rqDavicomPhy = 0x0020, rq6patterns = 0x0040, /* 6 instead of 4 patterns for WOL */ rqStatusWBRace = 0x0080, /* Tx Status Writeback Error possible */ rqRhineI = 0x0100, /* See comment below */ @@ -401,6 +385,7 @@ /* Offsets to the device registers. */ enum register_offsets { StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, + ChipCmd1=0x09, IntrStatus=0x0C, IntrEnable=0x0E, MulticastFilter0=0x10, MulticastFilter1=0x14, RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54, @@ -409,8 +394,8 @@ ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B, RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81, StickyHW=0x83, IntrStatus2=0x84, - WOLcrSet=0xA0, WOLcrClr=0xA4, WOLcrClr1=0xA6, - WOLcgClr=0xA7, + WOLcrSet=0xA0, PwcfgSet=0xA1, WOLcgSet=0xA3, WOLcrClr=0xA4, + WOLcrClr1=0xA6, WOLcgClr=0xA7, PwrcsrSet=0xA8, PwrcsrSet1=0xA9, PwrcsrClr=0xAC, PwrcsrClr1=0xAD, }; @@ -442,6 +427,15 @@ IntrTxErrSummary=0x082218, }; +/* Bits in WOLcrSet/WOLcrClr and PwrcsrSet/PwrcsrClr */ +enum wol_bits { + WOLucast = 0x10, + WOLmagic = 0x20, + WOLbmcast = 0x30, + WOLlnkon = 0x40, + WOLlnkoff = 0x80, +}; + /* The Rx and Tx buffer descriptors. */ struct rx_desc { s32 rx_status; @@ -470,13 +464,12 @@ /* Bits in ChipCmd. */ enum chip_cmd_bits { - CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008, - CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040, - CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400, - CmdNoTxPoll=0x0800, CmdReset=0x8000, + CmdInit=0x01, CmdStart=0x02, CmdStop=0x04, CmdRxOn=0x08, + CmdTxOn=0x10, Cmd1TxDemand=0x20, CmdRxDemand=0x40, + Cmd1EarlyRx=0x01, Cmd1EarlyTx=0x02, Cmd1FDuplex=0x04, + Cmd1NoTxPoll=0x08, Cmd1Reset=0x80, }; -#define MAX_MII_CNT 4 struct rhine_private { /* Descriptor rings */ struct rx_desc *rx_ring; @@ -499,7 +492,6 @@ struct pci_dev *pdev; struct net_device_stats stats; - struct timer_list timer; /* Media monitoring timer. */ spinlock_t lock; /* Frequently used values: keep some adjacent for cache effect. */ @@ -508,23 +500,16 @@ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ unsigned int cur_tx, dirty_tx; unsigned int rx_buf_sz; /* Based on MTU+slack. */ - u16 chip_cmd; /* Current setting for ChipCmd */ + u8 wolopts; - /* These values are keep track of the transceiver/media in use. */ u8 tx_thresh, rx_thresh; - /* MII transceiver section. */ - unsigned char phys[MAX_MII_CNT]; /* MII device addresses. */ - unsigned int mii_cnt; /* number of MIIs found, but only the first one is used */ - u16 mii_status; /* last read MII status */ struct mii_if_info mii_if; }; static int mdio_read(struct net_device *dev, int phy_id, int location); static void mdio_write(struct net_device *dev, int phy_id, int location, int value); static int rhine_open(struct net_device *dev); -static void rhine_check_duplex(struct net_device *dev); -static void rhine_timer(unsigned long data); static void rhine_tx_timeout(struct net_device *dev); static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev); static irqreturn_t rhine_interrupt(int irq, void *dev_instance, struct pt_regs *regs); @@ -536,6 +521,16 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static struct ethtool_ops netdev_ethtool_ops; static int rhine_close(struct net_device *dev); +static void rhine_shutdown (struct device *gdev); + +#define RHINE_WAIT_FOR(condition) do { \ + int i=1024; \ + while (!(condition) && --i) \ + ; \ + if (debug > 1 && i < 512) \ + printk(KERN_INFO "%s: %4d cycles used @ %s:%d\n", \ + DRV_NAME, 1024-i, __func__, __LINE__); \ +} while(0) static inline u32 get_intr_status(struct net_device *dev) { @@ -552,12 +547,13 @@ /* * Get power related registers into sane state. - * Returns content of power-event (WOL) registers. + * Notify user about past WOL event. */ static void rhine_power_init(struct net_device *dev) { long ioaddr = dev->base_addr; struct rhine_private *rp = netdev_priv(dev); + u16 wolstat; if (rp->quirks & rqWOL) { /* Make sure chip is in power state D0 */ @@ -572,63 +568,109 @@ if (rp->quirks & rq6patterns) writeb(0x03, ioaddr + WOLcrClr1); + /* Save power-event status bits */ + wolstat = readb(ioaddr + PwrcsrSet); + if (rp->quirks & rq6patterns) + wolstat |= (readb(ioaddr + PwrcsrSet1) & 0x03) << 8; + /* Clear power-event status bits */ writeb(0xFF, ioaddr + PwrcsrClr); if (rp->quirks & rq6patterns) writeb(0x03, ioaddr + PwrcsrClr1); + + if (wolstat) { + char *reason; + switch (wolstat) { + case WOLmagic: + reason = "Magic packet"; + break; + case WOLlnkon: + reason = "Link went up"; + break; + case WOLlnkoff: + reason = "Link went down"; + break; + case WOLucast: + reason = "Unicast packet"; + break; + case WOLbmcast: + reason = "Multicast/broadcast packet"; + break; + default: + reason = "Unknown"; + } + printk("%s: Woke system up. Reason: %s.\n", + DRV_NAME, reason); + } } } -static void wait_for_reset(struct net_device *dev, u32 quirks, char *name) +static void rhine_chip_reset(struct net_device *dev) { long ioaddr = dev->base_addr; - int boguscnt = 20; + struct rhine_private *rp = netdev_priv(dev); + writeb(Cmd1Reset, ioaddr + ChipCmd1); IOSYNC; - if (readw(ioaddr + ChipCmd) & CmdReset) { + if (readb(ioaddr + ChipCmd1) & Cmd1Reset) { printk(KERN_INFO "%s: Reset not complete yet. " - "Trying harder.\n", name); + "Trying harder.\n", DRV_NAME); - /* Rhine-II needs to be forced sometimes */ - if (quirks & rqForceReset) + /* Force reset */ + if (rp->quirks & rqForceReset) writeb(0x40, ioaddr + MiscCmd); - /* VT86C100A may need long delay after reset (dlink) */ - /* Seen on Rhine-II as well (rl) */ - while ((readw(ioaddr + ChipCmd) & CmdReset) && --boguscnt) - udelay(5); - + /* Reset can take somewhat longer (rare) */ + RHINE_WAIT_FOR(!(readb(ioaddr + ChipCmd1) & Cmd1Reset)); } if (debug > 1) - printk(KERN_INFO "%s: Reset %s.\n", name, - boguscnt ? "succeeded" : "failed"); + printk(KERN_INFO "%s: Reset %s.\n", dev->name, + (readb(ioaddr + ChipCmd1) & Cmd1Reset) ? + "failed" : "succeeded"); } #ifdef USE_MMIO -static void __devinit enable_mmio(long ioaddr, u32 quirks) +static void __devinit enable_mmio(long pioaddr, u32 quirks) { int n; if (quirks & rqRhineI) { /* More recent docs say that this bit is reserved ... */ - n = inb(ioaddr + ConfigA) | 0x20; - outb(n, ioaddr + ConfigA); + n = inb(pioaddr + ConfigA) | 0x20; + outb(n, pioaddr + ConfigA); } else { - n = inb(ioaddr + ConfigD) | 0x80; - outb(n, ioaddr + ConfigD); + n = inb(pioaddr + ConfigD) | 0x80; + outb(n, pioaddr + ConfigD); } } #endif -static void __devinit reload_eeprom(long ioaddr) +/* + * Loads bytes 0x00-0x05, 0x6E-0x6F, 0x78-0x7B from EEPROM + * (plus 0x6C for Rhine-I/II) + */ +static void __devinit rhine_reload_eeprom(long pioaddr, struct net_device *dev) { - int i; - outb(0x20, ioaddr + MACRegEEcsr); - /* Typically 2 cycles to reload. */ - for (i = 0; i < 150; i++) - if (! (inb(ioaddr + MACRegEEcsr) & 0x20)) - break; + long ioaddr = dev->base_addr; + struct rhine_private *rp = netdev_priv(dev); + + outb(0x20, pioaddr + MACRegEEcsr); + RHINE_WAIT_FOR(!(inb(pioaddr + MACRegEEcsr) & 0x20)); + +#ifdef USE_MMIO + /* + * Reloading from EEPROM overwrites ConfigA-D, so we must re-enable + * MMIO. If reloading EEPROM was done first this could be avoided, but + * it is not known if that still works with the "win98-reboot" problem. + */ + enable_mmio(pioaddr, rp->quirks); +#endif + + /* Turn off EEPROM-controlled wake-up (magic packet) */ + if (rp->quirks & rqWOL) + writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA); + } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -640,23 +682,34 @@ } #endif +static void rhine_hw_init(struct net_device *dev, long pioaddr) +{ + struct rhine_private *rp = netdev_priv(dev); + + /* Reset the chip to erase previous misconfiguration. */ + rhine_chip_reset(dev); + + /* Rhine-I needs extra time to recuperate before EEPROM reload */ + if (rp->quirks & rqRhineI) + msleep(5); + + /* Reload EEPROM controlled bytes cleared by soft reset */ + rhine_reload_eeprom(pioaddr, dev); +} + static int __devinit rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev; struct rhine_private *rp; - int i, option, rc; + int i, rc; u8 pci_rev; u32 quirks; - static int card_idx = -1; - long ioaddr; + long pioaddr; long memaddr; - int io_size; - int phy, phy_idx = 0; -#ifdef USE_MMIO - long ioaddr0; -#endif - const char *name; + long ioaddr; + int io_size, phy_id; + const char *name, *mname; /* when built into the kernel, we only print version if device is found */ #ifndef MODULE @@ -665,26 +718,47 @@ printk(version); #endif - card_idx++; - option = card_idx < MAX_UNITS ? options[card_idx] : 0; pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev); io_size = 256; - if (pci_rev < VT6102) { - quirks = rqRhineI | rqDavicomPhy; + phy_id = 0; + quirks = 0; + name = "Rhine"; + mname = "unknown"; + if (pci_rev < VTunknown0) { + quirks = rqRhineI; io_size = 128; - name = "VT86C100A Rhine"; + mname = "VT86C100A"; } - else { + else if (pci_rev >= VT6102) { quirks = rqWOL | rqForceReset; if (pci_rev < VT6105) { name = "Rhine II"; quirks |= rqStatusWBRace; /* Rhine-II exclusive */ + if (pci_rev < VT8231) + mname = "VT6102"; + else if (pci_rev < VT8233) + mname = "VT8231"; + else if (pci_rev < VT8235) + mname = "VT8233"; + else if (pci_rev < VT8237) + mname = "VT8235"; + else if (pci_rev < VTunknown1) + mname = "VT8237"; } else { name = "Rhine III"; + phy_id = 1; /* Integrated PHY, phy_id fixed to 1 */ if (pci_rev >= VT6105_B0) quirks |= rq6patterns; + if (pci_rev < VT6105L) + mname = "VT6105"; + else if (pci_rev < VT6107) + mname = "VT6105L"; + else if (pci_rev < VT6105M) + mname = "VT6107"; + else if (pci_rev >= VT6105M) + mname = "Management Adapter VT6105M"; } } @@ -708,28 +782,26 @@ goto err_out; } - ioaddr = pci_resource_start(pdev, 0); + pioaddr = pci_resource_start(pdev, 0); memaddr = pci_resource_start(pdev, 1); pci_set_master(pdev); - dev = alloc_etherdev(sizeof(*rp)); - if (dev == NULL) { + dev = alloc_etherdev(sizeof(struct rhine_private)); + if (!dev) { rc = -ENOMEM; - printk(KERN_ERR "init_ethernet failed for card #%d\n", - card_idx); + printk(KERN_ERR "alloc_etherdev failed\n"); goto err_out; } SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - rc = pci_request_regions(pdev, shortname); + rc = pci_request_regions(pdev, DRV_NAME); if (rc) goto err_out_free_netdev; #ifdef USE_MMIO - ioaddr0 = ioaddr; - enable_mmio(ioaddr0, quirks); + enable_mmio(pioaddr, quirks); ioaddr = (long) ioremap(memaddr, io_size); if (!ioaddr) { @@ -743,7 +815,7 @@ i = 0; while (mmio_verify_registers[i]) { int reg = mmio_verify_registers[i++]; - unsigned char a = inb(ioaddr0+reg); + unsigned char a = inb(pioaddr+reg); unsigned char b = readb(ioaddr+reg); if (a != b) { rc = -EIO; @@ -752,65 +824,41 @@ goto err_out_unmap; } } +#else + ioaddr = pioaddr; #endif /* USE_MMIO */ + dev->base_addr = ioaddr; + rp = netdev_priv(dev); + rp->quirks = quirks; + /* Get chip registers into a sane state */ rhine_power_init(dev); - - /* Reset the chip to erase previous misconfiguration. */ - writew(CmdReset, ioaddr + ChipCmd); - - wait_for_reset(dev, quirks, shortname); - - /* Reload the station address from the EEPROM. */ -#ifdef USE_MMIO - reload_eeprom(ioaddr0); - /* Reloading from eeprom overwrites cfgA-D, so we must re-enable MMIO. - If reload_eeprom() was done first this could be avoided, but it is - not known if that still works with the "win98-reboot" problem. */ - enable_mmio(ioaddr0, quirks); -#else - reload_eeprom(ioaddr); -#endif + rhine_hw_init(dev, pioaddr); for (i = 0; i < 6; i++) dev->dev_addr[i] = readb(ioaddr + StationAddr + i); if (!is_valid_ether_addr(dev->dev_addr)) { rc = -EIO; - printk(KERN_ERR "Invalid MAC address for card #%d\n", card_idx); + printk(KERN_ERR "Invalid MAC address\n"); goto err_out_unmap; } - if (quirks & rqWOL) { - /* - * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA - * turned on. it makes MAC receive magic packet - * automatically. So, we turn it off. (D-Link) - */ - writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA); - } - - /* Select backoff algorithm */ - if (backoff) - writeb(readb(ioaddr + ConfigD) & (0xF0 | backoff), - ioaddr + ConfigD); + /* For Rhine-I/II, phy_id is loaded from EEPROM */ + if (!phy_id) + phy_id = readb(ioaddr + 0x6C); dev->irq = pdev->irq; - rp = netdev_priv(dev); spin_lock_init(&rp->lock); rp->pdev = pdev; - rp->quirks = quirks; rp->mii_if.dev = dev; rp->mii_if.mdio_read = mdio_read; rp->mii_if.mdio_write = mdio_write; rp->mii_if.phy_id_mask = 0x1f; rp->mii_if.reg_num_mask = 0x1f; - if (dev->mem_start) - option = dev->mem_start; - /* The chip-specific entries in the device structure. */ dev->open = rhine_open; dev->hard_start_xmit = rhine_start_tx; @@ -832,22 +880,8 @@ if (rc) goto err_out_unmap; - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x220) - rp->mii_if.full_duplex = 1; - } - if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) - rp->mii_if.full_duplex = 1; - - if (rp->mii_if.full_duplex) { - printk(KERN_INFO "%s: Set to forced full duplex, " - "autonegotiation disabled.\n", dev->name); - rp->mii_if.force_media = 1; - } - - printk(KERN_INFO "%s: VIA %s at 0x%lx, ", - dev->name, name, + printk(KERN_INFO "%s: VIA %s (%s) at 0x%lx, ", + dev->name, name, mname, #ifdef USE_MMIO memaddr #else @@ -861,17 +895,15 @@ pci_set_drvdata(pdev, dev); - rp->phys[0] = 1; /* Standard for this chip. */ - for (phy = 1; phy < 32 && phy_idx < MAX_MII_CNT; phy++) { - int mii_status = mdio_read(dev, phy, 1); + { + int mii_status = mdio_read(dev, phy_id, 1); if (mii_status != 0xffff && mii_status != 0x0000) { - rp->phys[phy_idx++] = phy; - rp->mii_if.advertising = mdio_read(dev, phy, 4); + rp->mii_if.advertising = mdio_read(dev, phy_id, 4); printk(KERN_INFO "%s: MII PHY found at address " "%d, status 0x%4.4x advertising %4.4x " - "Link %4.4x.\n", dev->name, phy, + "Link %4.4x.\n", dev->name, phy_id, mii_status, rp->mii_if.advertising, - mdio_read(dev, phy, 5)); + mdio_read(dev, phy_id, 5)); /* set IFF_RUNNING */ if (mii_status & BMSR_LSTATUS) @@ -879,27 +911,9 @@ else netif_carrier_off(dev); - break; - } - } - rp->mii_cnt = phy_idx; - rp->mii_if.phy_id = rp->phys[0]; - - /* Allow forcing the media type. */ - if (option > 0) { - if (option & 0x220) - rp->mii_if.full_duplex = 1; - if (option & 0x330) { - printk(KERN_INFO " Forcing %dMbs %s-duplex " - "operation.\n", - (option & 0x300 ? 100 : 10), - (option & 0x220 ? "full" : "half")); - if (rp->mii_cnt) - mdio_write(dev, rp->phys[0], MII_BMCR, - ((option & 0x300) ? 0x2000 : 0) | /* 100mbps? */ - ((option & 0x220) ? 0x0100 : 0)); /* Full duplex? */ } } + rp->mii_if.phy_id = phy_id; return 0; @@ -1071,6 +1085,21 @@ } } +static void rhine_check_media(struct net_device *dev, unsigned int init_media) +{ + struct rhine_private *rp = netdev_priv(dev); + long ioaddr = dev->base_addr; + + mii_check_media(&rp->mii_if, debug, init_media); + + if (rp->mii_if.full_duplex) + writeb(readb(ioaddr + ChipCmd1) | Cmd1FDuplex, + ioaddr + ChipCmd1); + else + writeb(readb(ioaddr + ChipCmd1) & ~Cmd1FDuplex, + ioaddr + ChipCmd1); +} + static void init_registers(struct net_device *dev) { struct rhine_private *rp = netdev_priv(dev); @@ -1086,7 +1115,6 @@ writeb(0x20, ioaddr + TxConfig); rp->tx_thresh = 0x20; rp->rx_thresh = 0x60; /* Written in rhine_set_rx_mode(). */ - rp->mii_if.full_duplex = 0; writel(rp->rx_ring_dma, ioaddr + RxRingPtr); writel(rp->tx_ring_dma, ioaddr + TxRingPtr); @@ -1100,17 +1128,44 @@ IntrPCIErr | IntrStatsMax | IntrLinkChange, ioaddr + IntrEnable); - rp->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; - if (rp->mii_if.force_media) - rp->chip_cmd |= CmdFDuplex; - writew(rp->chip_cmd, ioaddr + ChipCmd); - - rhine_check_duplex(dev); - - /* The LED outputs of various MII xcvrs should be configured. */ - /* For NS or Mison phys, turn on bit 1 in register 0x17 */ - mdio_write(dev, rp->phys[0], 0x17, mdio_read(dev, rp->phys[0], 0x17) | - 0x0001); + writew(CmdStart | CmdTxOn | CmdRxOn | (Cmd1NoTxPoll << 8), + ioaddr + ChipCmd); + rhine_check_media(dev, 1); +} + +/* Enable MII link status auto-polling (required for IntrLinkChange) */ +static void rhine_enable_linkmon(long ioaddr) +{ + writeb(0, ioaddr + MIICmd); + writeb(MII_BMSR, ioaddr + MIIRegAddr); + writeb(0x80, ioaddr + MIICmd); + + RHINE_WAIT_FOR((readb(ioaddr + MIIRegAddr) & 0x20)); + + writeb(MII_BMSR | 0x40, ioaddr + MIIRegAddr); +} + +/* Disable MII link status auto-polling (required for MDIO access) */ +static void rhine_disable_linkmon(long ioaddr, u32 quirks) +{ + writeb(0, ioaddr + MIICmd); + + if (quirks & rqRhineI) { + writeb(0x01, ioaddr + MIIRegAddr); // MII_BMSR + + /* Can be called from ISR. Evil. */ + mdelay(1); + + /* 0x80 must be set immediately before turning it off */ + writeb(0x80, ioaddr + MIICmd); + + RHINE_WAIT_FOR(readb(ioaddr + MIIRegAddr) & 0x20); + + /* Heh. Now clear 0x80 again. */ + writeb(0, ioaddr + MIICmd); + } + else + RHINE_WAIT_FOR(readb(ioaddr + MIIRegAddr) & 0x80); } /* Read and write over the MII Management Data I/O (MDIO) interface. */ @@ -1118,156 +1173,72 @@ static int mdio_read(struct net_device *dev, int phy_id, int regnum) { long ioaddr = dev->base_addr; - int boguscnt = 1024; + struct rhine_private *rp = netdev_priv(dev); + int result; - /* Wait for a previous command to complete. */ - while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) - ; - writeb(0x00, ioaddr + MIICmd); + rhine_disable_linkmon(ioaddr, rp->quirks); + + writeb(0, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writeb(0x40, ioaddr + MIICmd); /* Trigger read */ - boguscnt = 1024; - while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) - ; - return readw(ioaddr + MIIData); + RHINE_WAIT_FOR(!(readb(ioaddr + MIICmd) & 0x40)); + result = readw(ioaddr + MIIData); + + rhine_enable_linkmon(ioaddr); + return result; } static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value) { struct rhine_private *rp = netdev_priv(dev); long ioaddr = dev->base_addr; - int boguscnt = 1024; - if (phy_id == rp->phys[0]) { - switch (regnum) { - case MII_BMCR: /* Is user forcing speed/duplex? */ - if (value & 0x9000) /* Autonegotiation. */ - rp->mii_if.force_media = 0; - else - rp->mii_if.full_duplex = (value & 0x0100) ? 1 : 0; - break; - case MII_ADVERTISE: - rp->mii_if.advertising = value; - break; - } - } + rhine_disable_linkmon(ioaddr, rp->quirks); - /* Wait for a previous command to complete. */ - while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) - ; - writeb(0x00, ioaddr + MIICmd); + writeb(0, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writew(value, ioaddr + MIIData); - writeb(0x20, ioaddr + MIICmd); /* Trigger write. */ -} + writeb(0x20, ioaddr + MIICmd); /* Trigger write */ + RHINE_WAIT_FOR(!(readb(ioaddr + MIICmd) & 0x20)); + rhine_enable_linkmon(ioaddr); +} static int rhine_open(struct net_device *dev) { struct rhine_private *rp = netdev_priv(dev); long ioaddr = dev->base_addr; - int i; - - /* Reset the chip. */ - writew(CmdReset, ioaddr + ChipCmd); + int rc; - i = request_irq(rp->pdev->irq, &rhine_interrupt, SA_SHIRQ, dev->name, + rc = request_irq(rp->pdev->irq, &rhine_interrupt, SA_SHIRQ, dev->name, dev); - if (i) - return i; + if (rc) + return rc; if (debug > 1) printk(KERN_DEBUG "%s: rhine_open() irq %d.\n", dev->name, rp->pdev->irq); - i = alloc_ring(dev); - if (i) - return i; + rc = alloc_ring(dev); + if (rc) + return rc; alloc_rbufs(dev); alloc_tbufs(dev); - wait_for_reset(dev, rp->quirks, dev->name); + rhine_chip_reset(dev); init_registers(dev); if (debug > 2) printk(KERN_DEBUG "%s: Done rhine_open(), status %4.4x " "MII status: %4.4x.\n", dev->name, readw(ioaddr + ChipCmd), - mdio_read(dev, rp->phys[0], MII_BMSR)); + mdio_read(dev, rp->mii_if.phy_id, MII_BMSR)); netif_start_queue(dev); - /* Set the timer to check for link beat. */ - init_timer(&rp->timer); - rp->timer.expires = jiffies + 2 * HZ/100; - rp->timer.data = (unsigned long)dev; - rp->timer.function = &rhine_timer; /* timer handler */ - add_timer(&rp->timer); - return 0; } -static void rhine_check_duplex(struct net_device *dev) -{ - struct rhine_private *rp = netdev_priv(dev); - long ioaddr = dev->base_addr; - int mii_lpa = mdio_read(dev, rp->phys[0], MII_LPA); - int negotiated = mii_lpa & rp->mii_if.advertising; - int duplex; - - if (rp->mii_if.force_media || mii_lpa == 0xffff) - return; - duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; - if (rp->mii_if.full_duplex != duplex) { - rp->mii_if.full_duplex = duplex; - if (debug) - printk(KERN_INFO "%s: Setting %s-duplex based on " - "MII #%d link partner capability of %4.4x.\n", - dev->name, duplex ? "full" : "half", - rp->phys[0], mii_lpa); - if (duplex) - rp->chip_cmd |= CmdFDuplex; - else - rp->chip_cmd &= ~CmdFDuplex; - writew(rp->chip_cmd, ioaddr + ChipCmd); - } -} - - -static void rhine_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct rhine_private *rp = netdev_priv(dev); - long ioaddr = dev->base_addr; - int next_tick = 10*HZ; - int mii_status; - - if (debug > 3) { - printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", - dev->name, readw(ioaddr + IntrStatus)); - } - - spin_lock_irq (&rp->lock); - - rhine_check_duplex(dev); - - /* make IFF_RUNNING follow the MII status bit "Link established" */ - mii_status = mdio_read(dev, rp->phys[0], MII_BMSR); - if ((mii_status & BMSR_LSTATUS) != (rp->mii_status & BMSR_LSTATUS)) { - if (mii_status & BMSR_LSTATUS) - netif_carrier_on(dev); - else - netif_carrier_off(dev); - } - rp->mii_status = mii_status; - - spin_unlock_irq(&rp->lock); - - rp->timer.expires = jiffies + next_tick; - add_timer(&rp->timer); -} - - static void rhine_tx_timeout(struct net_device *dev) { struct rhine_private *rp = netdev_priv(dev); @@ -1276,16 +1247,13 @@ printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status " "%4.4x, resetting...\n", dev->name, readw(ioaddr + IntrStatus), - mdio_read(dev, rp->phys[0], MII_BMSR)); + mdio_read(dev, rp->mii_if.phy_id, MII_BMSR)); /* protect against concurrent rx interrupts */ disable_irq(rp->pdev->irq); spin_lock(&rp->lock); - /* Reset the chip. */ - writew(CmdReset, ioaddr + ChipCmd); - /* clear all descriptors */ free_tbufs(dev); free_rbufs(dev); @@ -1293,7 +1261,7 @@ alloc_rbufs(dev); /* Reinitialize the hardware. */ - wait_for_reset(dev, rp->quirks, dev->name); + rhine_chip_reset(dev); init_registers(dev); spin_unlock(&rp->lock); @@ -1307,8 +1275,8 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev) { struct rhine_private *rp = netdev_priv(dev); + long ioaddr = dev->base_addr; unsigned entry; - u32 intr_status; /* Caution: the write order is important here, set the field with the "ownership" bits last. */ @@ -1359,14 +1327,9 @@ /* Non-x86 Todo: explicitly flush cache lines here. */ - /* - * Wake the potentially-idle transmit channel unless errors are - * pending (the ISR must sort them out first). - */ - intr_status = get_intr_status(dev); - if ((intr_status & IntrTxErrSummary) == 0) { - writew(CmdTxDemand | rp->chip_cmd, dev->base_addr + ChipCmd); - } + /* Wake the potentially-idle transmit channel */ + writeb(readb(ioaddr + ChipCmd1) | Cmd1TxDemand, + ioaddr + ChipCmd1); IOSYNC; if (rp->cur_tx == rp->dirty_tx + TX_QUEUE_LEN) @@ -1414,11 +1377,10 @@ if (intr_status & (IntrTxErrSummary | IntrTxDone)) { if (intr_status & IntrTxErrSummary) { - int cnt = 20; /* Avoid scavenging before Tx engine turned off */ - while ((readw(ioaddr+ChipCmd) & CmdTxOn) && --cnt) - udelay(5); - if (debug > 2 && !cnt) + RHINE_WAIT_FOR(!(readb(ioaddr+ChipCmd) & CmdTxOn)); + if (debug > 2 && + readb(ioaddr+ChipCmd) & CmdTxOn) printk(KERN_WARNING "%s: " "rhine_interrupt() Tx engine" "still on.\n", dev->name); @@ -1578,10 +1540,6 @@ rp->rx_buf_sz, PCI_DMA_FROMDEVICE); - /* *_IP_COPYSUM isn't defined anywhere and - eth_copy_and_sum is memcpy for all archs so - this is kind of pointless right now - ... or? */ eth_copy_and_sum(skb, rp->rx_skbuff[entry]->tail, pkt_len, 0); @@ -1633,10 +1591,6 @@ } rp->rx_ring[entry].rx_status = cpu_to_le32(DescOwn); } - - /* Pre-emptively restart Rx engine. */ - writew(readw(dev->base_addr + ChipCmd) | CmdRxOn | CmdRxDemand, - dev->base_addr + ChipCmd); } /* @@ -1670,7 +1624,10 @@ writel(rp->tx_ring_dma + entry * sizeof(struct tx_desc), ioaddr + TxRingPtr); - writew(CmdTxDemand | rp->chip_cmd, ioaddr + ChipCmd); + writeb(readb(ioaddr + ChipCmd) | CmdTxOn, + ioaddr + ChipCmd); + writeb(readb(ioaddr + ChipCmd1) | Cmd1TxDemand, + ioaddr + ChipCmd1); IOSYNC; } else { @@ -1690,20 +1647,8 @@ spin_lock(&rp->lock); - if (intr_status & (IntrLinkChange)) { - if (readb(ioaddr + MIIStatus) & 0x02) { - /* Link failed, restart autonegotiation. */ - if (rp->quirks & rqRhineI) - mdio_write(dev, rp->phys[0], MII_BMCR, 0x3300); - } else - rhine_check_duplex(dev); - if (debug) - printk(KERN_ERR "%s: MII status changed: " - "Autonegotiation advertising %4.4x partner " - "%4.4x.\n", dev->name, - mdio_read(dev, rp->phys[0], MII_ADVERTISE), - mdio_read(dev, rp->phys[0], MII_LPA)); - } + if (intr_status & IntrLinkChange) + rhine_check_media(dev, 0); if (intr_status & IntrStatsMax) { rp->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); rp->stats.rx_missed_errors += readw(ioaddr + RxMissed); @@ -1796,7 +1741,7 @@ i++, mclist = mclist->next) { int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31)); + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); } writel(mc_filter[0], ioaddr + MulticastFilter0); writel(mc_filter[1], ioaddr + MulticastFilter1); @@ -1862,6 +1807,39 @@ debug = value; } +static void rhine_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rhine_private *rp = netdev_priv(dev); + + if (!(rp->quirks & rqWOL)) + return; + + spin_lock_irq(&rp->lock); + wol->supported = WAKE_PHY | WAKE_MAGIC | + WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; /* Untested */ + wol->wolopts = rp->wolopts; + spin_unlock_irq(&rp->lock); +} + +static int rhine_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rhine_private *rp = netdev_priv(dev); + u32 support = WAKE_PHY | WAKE_MAGIC | + WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; /* Untested */ + + if (!(rp->quirks & rqWOL)) + return -EINVAL; + + if (wol->wolopts & ~support) + return -EINVAL; + + spin_lock_irq(&rp->lock); + rp->wolopts = wol->wolopts; + spin_unlock_irq(&rp->lock); + + return 0; +} + static struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, .get_settings = netdev_get_settings, @@ -1870,6 +1848,8 @@ .get_link = netdev_get_link, .get_msglevel = netdev_get_msglevel, .set_msglevel = netdev_set_msglevel, + .get_wol = rhine_get_wol, + .set_wol = rhine_set_wol, .get_sg = ethtool_op_get_sg, .get_tx_csum = ethtool_op_get_tx_csum, }; @@ -1894,8 +1874,6 @@ long ioaddr = dev->base_addr; struct rhine_private *rp = netdev_priv(dev); - del_timer_sync(&rp->timer); - spin_lock_irq(&rp->lock); netif_stop_queue(dev); @@ -1942,12 +1920,51 @@ pci_set_drvdata(pdev, NULL); } +static void rhine_shutdown (struct device *gendev) +{ + struct pci_dev *pdev = to_pci_dev(gendev); + struct net_device *dev = pci_get_drvdata(pdev); + struct rhine_private *rp = netdev_priv(dev); + + long ioaddr = dev->base_addr; + + rhine_power_init(dev); + + /* Make sure we use pattern 0, 1 and not 4, 5 */ + if (rp->quirks & rq6patterns) + writeb(0x04, ioaddr + 0xA7); + + if (rp->wolopts & WAKE_MAGIC) + writeb(WOLmagic, ioaddr + WOLcrSet); + + if (rp->wolopts & (WAKE_BCAST|WAKE_MCAST)) + writeb(WOLbmcast, ioaddr + WOLcgSet); + + if (rp->wolopts & WAKE_PHY) + writeb(WOLlnkon | WOLlnkoff, ioaddr + WOLcrSet); + + if (rp->wolopts & WAKE_UCAST) + writeb(WOLucast, ioaddr + WOLcrSet); + + /* Enable legacy WOL (for old motherboards) */ + writeb(0x01, ioaddr + PwcfgSet); + writeb(readb(ioaddr + StickyHW) | 0x04, ioaddr + StickyHW); + + /* Hit power state D3 (sleep) */ + writeb(readb(ioaddr + StickyHW) | 0x03, ioaddr + StickyHW); + + /* TODO: Check use of pci_enable_wake() */ + +} static struct pci_driver rhine_driver = { - .name = "via-rhine", + .name = DRV_NAME, .id_table = rhine_pci_tbl, .probe = rhine_init_one, .remove = __devexit_p(rhine_remove_one), + .driver = { + .shutdown = rhine_shutdown, + } }; diff -Nru a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c --- a/drivers/net/via-velocity.c 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/via-velocity.c 2004-06-20 13:15:18 -07:00 @@ -269,8 +269,9 @@ static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr); static struct notifier_block velocity_inetaddr_notifier = { - notifier_call:velocity_netdev_event, + .notifier_call = velocity_netdev_event, }; +static int velocity_notifier_registered; #endif /* CONFIG_PM */ @@ -289,8 +290,9 @@ */ static struct pci_device_id velocity_id_table[] __devinitdata = { - {0x1106, 0x3119, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &chip_info_table[0]}, - {0,} + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) chip_info_table}, + {0, } }; MODULE_DEVICE_TABLE(pci, velocity_id_table); @@ -463,6 +465,12 @@ } } +static inline void velocity_give_rx_desc(struct rx_desc *rd) +{ + *(u32 *)&rd->rdesc0 = 0; + rd->rdesc0.owner = cpu_to_le32(OWNED_BY_NIC); +} + /** * velocity_rx_reset - handle a receive reset * @vptr: velocity we are resetting @@ -483,7 +491,7 @@ * Init state, all RD entries belong to the NIC */ for (i = 0; i < vptr->options.numrx; ++i) - vptr->rd_ring[i].rdesc0.owner = cpu_to_le32(OWNED_BY_NIC); + velocity_give_rx_desc(vptr->rd_ring + i); writew(vptr->options.numrx, ®s->RBRDU); writel(vptr->rd_pool_dma, ®s->RDBaseLo); @@ -776,6 +784,12 @@ pci_set_power_state(pdev, 3); out: +#ifdef CONFIG_PM + if (ret == 0 && !velocity_notifier_registered) { + velocity_notifier_registered = 1; + register_inetaddr_notifier(&velocity_inetaddr_notifier); + } +#endif return ret; err_iounmap: @@ -1000,7 +1014,7 @@ velocity_free_rd_ring(vptr); goto out; } - rd->rdesc0.owner = OWNED_BY_NIC; + velocity_give_rx_desc(rd); } vptr->rd_used = vptr->rd_curr = 0; out: @@ -1197,8 +1211,7 @@ if (--rd_prev < 0) rd_prev = vptr->options.numrx - 1; - rd = &(vptr->rd_ring[rd_prev]); - rd->rdesc0.owner = OWNED_BY_NIC; + velocity_give_rx_desc(vptr->rd_ring + rd_prev); } writew(4, &(regs->RBRDU)); vptr->rd_used -= 4; @@ -1242,6 +1255,28 @@ } /** + * velocity_iph_realign - IP header alignment + * @vptr: velocity we are handling + * @skb: network layer packet buffer + * @pkt_size: received data size + * + * Align IP header on a 2 bytes boundary. This behavior can be + * configured by the user. + */ +static inline void velocity_iph_realign(struct velocity_info *vptr, + struct sk_buff *skb, int pkt_size) +{ + /* FIXME - memmove ? */ + if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { + int i; + + for (i = pkt_size; i >= 0; i--) + *(skb->data + i + 2) = *(skb->data + i); + skb_reserve(skb, 2); + } +} + +/** * velocity_receive_frame - received packet processor * @vptr: velocity we are handling * @idx: ring index @@ -1255,6 +1290,7 @@ struct net_device_stats *stats = &vptr->stats; struct velocity_rd_info *rd_info = &(vptr->rd_info[idx]); struct rx_desc *rd = &(vptr->rd_ring[idx]); + int pkt_len = rd->rdesc0.len; struct sk_buff *skb; if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) { @@ -1274,16 +1310,9 @@ rd_info->skb_dma = (dma_addr_t) NULL; rd_info->skb = NULL; - /* FIXME - memmove ? */ - if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { - int i; - for (i = rd->rdesc0.len + 4; i >= 0; i--) - *(skb->data + i + 2) = *(skb->data + i); - skb->data += 2; - skb->tail += 2; - } + velocity_iph_realign(vptr, skb, pkt_len); - skb_put(skb, (rd->rdesc0.len - 4)); + skb_put(skb, pkt_len - 4); skb->protocol = eth_type_trans(skb, skb->dev); /* @@ -1303,7 +1332,7 @@ * FIXME: need rx_copybreak handling */ - stats->rx_bytes += skb->len; + stats->rx_bytes += pkt_len; netif_rx(skb); return 0; @@ -2123,13 +2152,13 @@ */ static struct pci_driver velocity_driver = { - name:VELOCITY_NAME, - id_table:velocity_id_table, - probe:velocity_found1, - remove:velocity_remove1, + .name = VELOCITY_NAME, + .id_table = velocity_id_table, + .probe = velocity_found1, + .remove = __devexit_p(velocity_remove1), #ifdef CONFIG_PM - suspend:velocity_suspend, - resume:velocity_resume, + .suspend = velocity_suspend, + .resume = velocity_resume, #endif }; @@ -2147,9 +2176,6 @@ int ret; ret = pci_module_init(&velocity_driver); -#ifdef CONFIG_PM - register_inetaddr_notifier(&velocity_inetaddr_notifier); -#endif return ret; } @@ -2165,7 +2191,10 @@ static void __exit velocity_cleanup_module(void) { #ifdef CONFIG_PM - unregister_inetaddr_notifier(&velocity_inetaddr_notifier); + if (velocity_notifier_registered) { + unregister_inetaddr_notifier(&velocity_inetaddr_notifier); + velocity_notifier_registered = 0; + } #endif pci_unregister_driver(&velocity_driver); } diff -Nru a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h --- a/drivers/net/via-velocity.h 2004-06-20 13:15:18 -07:00 +++ b/drivers/net/via-velocity.h 2004-06-20 13:15:18 -07:00 @@ -37,7 +37,6 @@ #define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1} #define REV_ID_VT6110 (0) -#define DEVICE_ID (0x3119) #define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0) #define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0) diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h 2004-06-20 13:15:18 -07:00 +++ b/include/linux/pci_ids.h 2004-06-20 13:15:18 -07:00 @@ -1215,6 +1215,7 @@ #define PCI_DEVICE_ID_VIA_8233C_0 0x3109 #define PCI_DEVICE_ID_VIA_8361 0x3112 #define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_612X 0x3119 #define PCI_DEVICE_ID_VIA_862X_0 0x3123 #define PCI_DEVICE_ID_VIA_8753_0 0x3128 #define PCI_DEVICE_ID_VIA_8233A 0x3147