diff options
Diffstat (limited to 'drivers/net/via-velocity.c')
-rw-r--r-- | drivers/net/via-velocity.c | 3430 |
1 files changed, 1628 insertions, 1802 deletions
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index cee08a1e497..e04e5bee005 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -61,9 +61,9 @@ #include <linux/interrupt.h> #include <linux/string.h> #include <linux/wait.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/if.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/proc_fs.h> #include <linux/inetdevice.h> #include <linux/reboot.h> @@ -81,7 +81,7 @@ #include "via-velocity.h" -static int velocity_nics = 0; +static int velocity_nics; static int msglevel = MSG_LEVEL_INFO; /** @@ -92,8 +92,7 @@ static int msglevel = MSG_LEVEL_INFO; * Fetch the mask bits of the selected CAM and store them into the * provided mask buffer. */ - -static void mac_get_cam_mask(struct mac_regs __iomem * regs, u8 * mask) +static void mac_get_cam_mask(struct mac_regs __iomem *regs, u8 *mask) { int i; @@ -111,7 +110,6 @@ static void mac_get_cam_mask(struct mac_regs __iomem * regs, u8 * mask) /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); - } @@ -122,8 +120,7 @@ static void mac_get_cam_mask(struct mac_regs __iomem * regs, u8 * mask) * * Store a new mask into a CAM */ - -static void mac_set_cam_mask(struct mac_regs __iomem * regs, u8 * mask) +static void mac_set_cam_mask(struct mac_regs __iomem *regs, u8 *mask) { int i; /* Select CAM mask */ @@ -131,9 +128,9 @@ static void mac_set_cam_mask(struct mac_regs __iomem * regs, u8 * mask) writeb(CAMADDR_CAMEN, ®s->CAMADDR); - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) writeb(*mask++, &(regs->MARCAM[i])); - } + /* disable CAMEN */ writeb(0, ®s->CAMADDR); @@ -141,7 +138,7 @@ static void mac_set_cam_mask(struct mac_regs __iomem * regs, u8 * mask) BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); } -static void mac_set_vlan_cam_mask(struct mac_regs __iomem * regs, u8 * mask) +static void mac_set_vlan_cam_mask(struct mac_regs __iomem *regs, u8 *mask) { int i; /* Select CAM mask */ @@ -149,9 +146,9 @@ static void mac_set_vlan_cam_mask(struct mac_regs __iomem * regs, u8 * mask) writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) writeb(*mask++, &(regs->MARCAM[i])); - } + /* disable CAMEN */ writeb(0, ®s->CAMADDR); @@ -167,8 +164,7 @@ static void mac_set_vlan_cam_mask(struct mac_regs __iomem * regs, u8 * mask) * * Load an address or vlan tag into a CAM */ - -static void mac_set_cam(struct mac_regs __iomem * regs, int idx, const u8 *addr) +static void mac_set_cam(struct mac_regs __iomem *regs, int idx, const u8 *addr) { int i; @@ -179,9 +175,9 @@ static void mac_set_cam(struct mac_regs __iomem * regs, int idx, const u8 *addr) writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); - for (i = 0; i < 6; i++) { + for (i = 0; i < 6; i++) writeb(*addr++, &(regs->MARCAM[i])); - } + BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); udelay(10); @@ -192,7 +188,7 @@ static void mac_set_cam(struct mac_regs __iomem * regs, int idx, const u8 *addr) BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); } -static void mac_set_vlan_cam(struct mac_regs __iomem * regs, int idx, +static void mac_set_vlan_cam(struct mac_regs __iomem *regs, int idx, const u8 *addr) { @@ -223,8 +219,7 @@ static void mac_set_vlan_cam(struct mac_regs __iomem * regs, int idx, * reset the Wake on lan features. This function doesn't restore * the rest of the logic from the result of sleep/wakeup */ - -static void mac_wol_reset(struct mac_regs __iomem * regs) +static void mac_wol_reset(struct mac_regs __iomem *regs) { /* Turn off SWPTAG right after leaving power mode */ @@ -242,7 +237,6 @@ static void mac_wol_reset(struct mac_regs __iomem * regs) writew(0xFFFF, ®s->WOLSRClr); } -static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); static const struct ethtool_ops velocity_ethtool_ops; /* @@ -253,10 +247,10 @@ MODULE_AUTHOR("VIA Networking Technologies, Inc."); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"); -#define VELOCITY_PARAM(N,D) \ - static int N[MAX_UNITS]=OPTION_DEFAULT;\ +#define VELOCITY_PARAM(N, D) \ + static int N[MAX_UNITS] = OPTION_DEFAULT;\ module_param_array(N, int, NULL, 0); \ - MODULE_PARM_DESC(N, D); + MODULE_PARM_DESC(N, D); #define RX_DESC_MIN 64 #define RX_DESC_MAX 255 @@ -336,8 +330,8 @@ VELOCITY_PARAM(flow_control, "Enable flow control ability"); 4: indicate 10Mbps full duplex mode Note: - if EEPROM have been set to the force mode, this option is ignored - by driver. + if EEPROM have been set to the force mode, this option is ignored + by driver. */ VELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); @@ -370,76 +364,14 @@ static int rx_copybreak = 200; module_param(rx_copybreak, int, 0644); MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); -static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, - const struct velocity_info_tbl *info); -static int velocity_get_pci_info(struct velocity_info *, struct pci_dev *pdev); -static void velocity_print_info(struct velocity_info *vptr); -static int velocity_open(struct net_device *dev); -static int velocity_change_mtu(struct net_device *dev, int mtu); -static int velocity_xmit(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t velocity_intr(int irq, void *dev_instance); -static void velocity_set_multi(struct net_device *dev); -static struct net_device_stats *velocity_get_stats(struct net_device *dev); -static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int velocity_close(struct net_device *dev); -static int velocity_receive_frame(struct velocity_info *, int idx); -static int velocity_alloc_rx_buf(struct velocity_info *, int idx); -static void velocity_free_rd_ring(struct velocity_info *vptr); -static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *); -static int velocity_soft_reset(struct velocity_info *vptr); -static void mii_init(struct velocity_info *vptr, u32 mii_status); -static u32 velocity_get_link(struct net_device *dev); -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr); -static void velocity_print_link_status(struct velocity_info *vptr); -static void safe_disable_mii_autopoll(struct mac_regs __iomem * regs); -static void velocity_shutdown(struct velocity_info *vptr); -static void enable_flow_control_ability(struct velocity_info *vptr); -static void enable_mii_autopoll(struct mac_regs __iomem * regs); -static int velocity_mii_read(struct mac_regs __iomem *, u8 byIdx, u16 * pdata); -static int velocity_mii_write(struct mac_regs __iomem *, u8 byMiiAddr, u16 data); -static u32 mii_check_media_mode(struct mac_regs __iomem * regs); -static u32 check_connection_type(struct mac_regs __iomem * regs); -static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status); - #ifdef CONFIG_PM - -static int velocity_suspend(struct pci_dev *pdev, pm_message_t state); -static int velocity_resume(struct pci_dev *pdev); - static DEFINE_SPINLOCK(velocity_dev_list_lock); static LIST_HEAD(velocity_dev_list); - -#endif - -#if defined(CONFIG_PM) && defined(CONFIG_INET) - -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, -}; - -static void velocity_register_notifier(void) -{ - register_inetaddr_notifier(&velocity_inetaddr_notifier); -} - -static void velocity_unregister_notifier(void) -{ - unregister_inetaddr_notifier(&velocity_inetaddr_notifier); -} - -#else - -#define velocity_register_notifier() do {} while (0) -#define velocity_unregister_notifier() do {} while (0) - #endif /* * Internal board variants. At the moment we have only one */ - static struct velocity_info_tbl chip_info_table[] = { {CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL}, { } @@ -449,7 +381,6 @@ static struct velocity_info_tbl chip_info_table[] = { * Describe the PCI device identifiers that we support in this * device driver. Used for hotplug autoloading. */ - static const struct pci_device_id velocity_id_table[] __devinitdata = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, { } @@ -464,7 +395,6 @@ MODULE_DEVICE_TABLE(pci, velocity_id_table); * Given a chip identifier return a suitable description. Returns * a pointer a static string valid while the driver is loaded. */ - static const char __devinit *get_chip_name(enum chip_type chip_id) { int i; @@ -482,7 +412,6 @@ static const char __devinit *get_chip_name(enum chip_type chip_id) * unload for each active device that is present. Disconnects * the device from the network layer and frees all the resources */ - static void __devexit velocity_remove1(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -520,7 +449,6 @@ static void __devexit velocity_remove1(struct pci_dev *pdev) * all the verification and checking as well as reporting so that * we don't duplicate code for each option. */ - static void __devinit velocity_set_int_opt(int *opt, int val, int min, int max, int def, char *name, const char *devname) { if (val == -1) @@ -549,8 +477,7 @@ static void __devinit velocity_set_int_opt(int *opt, int val, int min, int max, * all the verification and checking as well as reporting so that * we don't duplicate code for each option. */ - -static void __devinit velocity_set_bool_opt(u32 * opt, int val, int def, u32 flag, char *name, const char *devname) +static void __devinit velocity_set_bool_opt(u32 *opt, int val, int def, u32 flag, char *name, const char *devname) { (*opt) &= (~flag); if (val == -1) @@ -575,7 +502,6 @@ static void __devinit velocity_set_bool_opt(u32 * opt, int val, int def, u32 fla * Turn the module and command options into a single structure * for the current device */ - static void __devinit velocity_get_options(struct velocity_opt *opts, int index, const char *devname) { @@ -601,10 +527,9 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index, * Initialize the content addressable memory used for filters. Load * appropriately according to the presence of VLAN */ - static void velocity_init_cam_filter(struct velocity_info *vptr) { - struct mac_regs __iomem * regs = vptr->mac_regs; + struct mac_regs __iomem *regs = vptr->mac_regs; /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); @@ -647,19 +572,19 @@ static void velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct velocity_info *vptr = netdev_priv(dev); - spin_lock_irq(&vptr->lock); + spin_lock_irq(&vptr->lock); velocity_init_cam_filter(vptr); - spin_unlock_irq(&vptr->lock); + spin_unlock_irq(&vptr->lock); } static void velocity_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct velocity_info *vptr = netdev_priv(dev); - spin_lock_irq(&vptr->lock); + spin_lock_irq(&vptr->lock); vlan_group_set_device(vptr->vlgrp, vid, NULL); velocity_init_cam_filter(vptr); - spin_unlock_irq(&vptr->lock); + spin_unlock_irq(&vptr->lock); } static void velocity_init_rx_ring_indexes(struct velocity_info *vptr) @@ -674,11 +599,10 @@ static void velocity_init_rx_ring_indexes(struct velocity_info *vptr) * Reset the ownership and status for the receive ring side. * Hand all the receive queue to the NIC. */ - static void velocity_rx_reset(struct velocity_info *vptr) { - struct mac_regs __iomem * regs = vptr->mac_regs; + struct mac_regs __iomem *regs = vptr->mac_regs; int i; velocity_init_rx_ring_indexes(vptr); @@ -696,6 +620,647 @@ static void velocity_rx_reset(struct velocity_info *vptr) } /** + * velocity_get_opt_media_mode - get media selection + * @vptr: velocity adapter + * + * Get the media mode stored in EEPROM or module options and load + * mii_status accordingly. The requested link state information + * is also returned. + */ +static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) +{ + u32 status = 0; + + switch (vptr->options.spd_dpx) { + case SPD_DPX_AUTO: + status = VELOCITY_AUTONEG_ENABLE; + break; + case SPD_DPX_100_FULL: + status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; + break; + case SPD_DPX_10_FULL: + status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; + break; + case SPD_DPX_100_HALF: + status = VELOCITY_SPEED_100; + break; + case SPD_DPX_10_HALF: + status = VELOCITY_SPEED_10; + break; + } + vptr->mii_status = status; + return status; +} + +/** + * safe_disable_mii_autopoll - autopoll off + * @regs: velocity registers + * + * Turn off the autopoll and wait for it to disable on the chip + */ +static void safe_disable_mii_autopoll(struct mac_regs __iomem *regs) +{ + u16 ww; + + /* turn off MAUTO */ + writeb(0, ®s->MIICR); + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + udelay(1); + if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } +} + +/** + * enable_mii_autopoll - turn on autopolling + * @regs: velocity registers + * + * Enable the MII link status autopoll feature on the Velocity + * hardware. Wait for it to enable. + */ +static void enable_mii_autopoll(struct mac_regs __iomem *regs) +{ + int ii; + + writeb(0, &(regs->MIICR)); + writeb(MIIADR_SWMPL, ®s->MIIADR); + + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } + + writeb(MIICR_MAUTO, ®s->MIICR); + + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } + +} + +/** + * velocity_mii_read - read MII data + * @regs: velocity registers + * @index: MII register index + * @data: buffer for received data + * + * Perform a single read of an MII 16bit register. Returns zero + * on success or -ETIMEDOUT if the PHY did not respond. + */ +static int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data) +{ + u16 ww; + + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); + + writeb(index, ®s->MIIADR); + + BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); + + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if (!(readb(®s->MIICR) & MIICR_RCMD)) + break; + } + + *data = readw(®s->MIIDATA); + + enable_mii_autopoll(regs); + if (ww == W_MAX_TIMEOUT) + return -ETIMEDOUT; + return 0; +} + + +/** + * mii_check_media_mode - check media state + * @regs: velocity registers + * + * Check the current MII status and determine the link status + * accordingly + */ +static u32 mii_check_media_mode(struct mac_regs __iomem *regs) +{ + u32 status = 0; + u16 ANAR; + + if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) + status |= VELOCITY_LINK_FAIL; + + if (MII_REG_BITS_IS_ON(G1000CR_1000FD, MII_REG_G1000CR, regs)) + status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; + else if (MII_REG_BITS_IS_ON(G1000CR_1000, MII_REG_G1000CR, regs)) + status |= (VELOCITY_SPEED_1000); + else { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if (ANAR & ANAR_TXFD) + status |= (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); + else if (ANAR & ANAR_TX) + status |= VELOCITY_SPEED_100; + else if (ANAR & ANAR_10FD) + status |= (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); + else + status |= (VELOCITY_SPEED_10); + } + + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) + == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { + if (MII_REG_BITS_IS_ON(G1000CR_1000 | G1000CR_1000FD, MII_REG_G1000CR, regs)) + status |= VELOCITY_AUTONEG_ENABLE; + } + } + + return status; +} + +/** + * velocity_mii_write - write MII data + * @regs: velocity registers + * @index: MII register index + * @data: 16bit data for the MII register + * + * Perform a single write to an MII 16bit register. Returns zero + * on success or -ETIMEDOUT if the PHY did not respond. + */ +static int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data) +{ + u16 ww; + + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); + + /* MII reg offset */ + writeb(mii_addr, ®s->MIIADR); + /* set MII data */ + writew(data, ®s->MIIDATA); + + /* turn on MIICR_WCMD */ + BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); + + /* W_MAX_TIMEOUT is the timeout period */ + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + udelay(5); + if (!(readb(®s->MIICR) & MIICR_WCMD)) + break; + } + enable_mii_autopoll(regs); + + if (ww == W_MAX_TIMEOUT) + return -ETIMEDOUT; + return 0; +} + +/** + * set_mii_flow_control - flow control setup + * @vptr: velocity interface + * + * Set up the flow control on this interface according to + * the supplied user/eeprom options. + */ +static void set_mii_flow_control(struct velocity_info *vptr) +{ + /*Enable or Disable PAUSE in ANAR */ + switch (vptr->options.flow_cntl) { + case FLOW_CNTL_TX: + MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_RX: + MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_TX_RX: + MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_DISABLE: + MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_OFF(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + default: + break; + } +} + +/** + * mii_set_auto_on - autonegotiate on + * @vptr: velocity + * + * Enable autonegotation on this interface + */ +static void mii_set_auto_on(struct velocity_info *vptr) +{ + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs)) + MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); + else + MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); +} + +static u32 check_connection_type(struct mac_regs __iomem *regs) +{ + u32 status = 0; + u8 PHYSR0; + u16 ANAR; + PHYSR0 = readb(®s->PHYSR0); + + /* + if (!(PHYSR0 & PHYSR0_LINKGD)) + status|=VELOCITY_LINK_FAIL; + */ + + if (PHYSR0 & PHYSR0_FDPX) + status |= VELOCITY_DUPLEX_FULL; + + if (PHYSR0 & PHYSR0_SPDG) + status |= VELOCITY_SPEED_1000; + else if (PHYSR0 & PHYSR0_SPD10) + status |= VELOCITY_SPEED_10; + else + status |= VELOCITY_SPEED_100; + + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) + == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { + if (MII_REG_BITS_IS_ON(G1000CR_1000 | G1000CR_1000FD, MII_REG_G1000CR, regs)) + status |= VELOCITY_AUTONEG_ENABLE; + } + } + + return status; +} + + + +/** + * velocity_set_media_mode - set media mode + * @mii_status: old MII link state + * + * Check the media link state and configure the flow control + * PHY and also velocity hardware setup accordingly. In particular + * we need to set up CD polling and frame bursting. + */ +static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status) +{ + u32 curr_status; + struct mac_regs __iomem *regs = vptr->mac_regs; + + vptr->mii_status = mii_check_media_mode(vptr->mac_regs); + curr_status = vptr->mii_status & (~VELOCITY_LINK_FAIL); + + /* Set mii link status */ + set_mii_flow_control(vptr); + + /* + Check if new status is consisent with current status + if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) + || (mii_status==curr_status)) { + vptr->mii_status=mii_check_media_mode(vptr->mac_regs); + vptr->mii_status=check_connection_type(vptr->mac_regs); + VELOCITY_PRT(MSG_LEVEL_INFO, "Velocity link no change\n"); + return 0; + } + */ + + if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) + MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, vptr->mac_regs); + + /* + * If connection type is AUTO + */ + if (mii_status & VELOCITY_AUTONEG_ENABLE) { + VELOCITY_PRT(MSG_LEVEL_INFO, "Velocity is AUTO mode\n"); + /* clear force MAC mode bit */ + BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); + /* set duplex mode of MAC according to duplex mode of MII */ + MII_REG_BITS_ON(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(G1000CR_1000FD | G1000CR_1000, MII_REG_G1000CR, vptr->mac_regs); + MII_REG_BITS_ON(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); + + /* enable AUTO-NEGO mode */ + mii_set_auto_on(vptr); + } else { + u16 ANAR; + u8 CHIPGCR; + + /* + * 1. if it's 3119, disable frame bursting in halfduplex mode + * and enable it in fullduplex mode + * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR + * 3. only enable CD heart beat counter in 10HD mode + */ + + /* set force MAC mode bit */ + BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); + + CHIPGCR = readb(®s->CHIPGCR); + CHIPGCR &= ~CHIPGCR_FCGMII; + + if (mii_status & VELOCITY_DUPLEX_FULL) { + CHIPGCR |= CHIPGCR_FCFDX; + writeb(CHIPGCR, ®s->CHIPGCR); + VELOCITY_PRT(MSG_LEVEL_INFO, "set Velocity to forced full mode\n"); + if (vptr->rev_id < REV_ID_VT3216_A0) + BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); + } else { + CHIPGCR &= ~CHIPGCR_FCFDX; + VELOCITY_PRT(MSG_LEVEL_INFO, "set Velocity to forced half mode\n"); + writeb(CHIPGCR, ®s->CHIPGCR); + if (vptr->rev_id < REV_ID_VT3216_A0) + BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); + } + + MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000, MII_REG_G1000CR, vptr->mac_regs); + + if (!(mii_status & VELOCITY_DUPLEX_FULL) && (mii_status & VELOCITY_SPEED_10)) + BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); + else + BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); + + /* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */ + velocity_mii_read(vptr->mac_regs, MII_REG_ANAR, &ANAR); + ANAR &= (~(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)); + if (mii_status & VELOCITY_SPEED_100) { + if (mii_status & VELOCITY_DUPLEX_FULL) + ANAR |= ANAR_TXFD; + else + ANAR |= ANAR_TX; + } else { + if (mii_status & VELOCITY_DUPLEX_FULL) + ANAR |= ANAR_10FD; + else + ANAR |= ANAR_10; + } + velocity_mii_write(vptr->mac_regs, MII_REG_ANAR, ANAR); + /* enable AUTO-NEGO mode */ + mii_set_auto_on(vptr); + /* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */ + } + /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ + /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ + return VELOCITY_LINK_CHANGE; +} + +/** + * velocity_print_link_status - link status reporting + * @vptr: velocity to report on + * + * Turn the link status of the velocity card into a kernel log + * description of the new link state, detailing speed and duplex + * status + */ +static void velocity_print_link_status(struct velocity_info *vptr) +{ + + if (vptr->mii_status & VELOCITY_LINK_FAIL) { + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->dev->name); + } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->dev->name); + + if (vptr->mii_status & VELOCITY_SPEED_1000) + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps"); + else if (vptr->mii_status & VELOCITY_SPEED_100) + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 100M bps"); + else + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 10M bps"); + + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + VELOCITY_PRT(MSG_LEVEL_INFO, " full duplex\n"); + else + VELOCITY_PRT(MSG_LEVEL_INFO, " half duplex\n"); + } else { + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->dev->name); + switch (vptr->options.spd_dpx) { + case SPD_DPX_100_HALF: + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 100M bps half duplex\n"); + break; + case SPD_DPX_100_FULL: + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 100M bps full duplex\n"); + break; + case SPD_DPX_10_HALF: + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 10M bps half duplex\n"); + break; + case SPD_DPX_10_FULL: + VELOCITY_PRT(MSG_LEVEL_INFO, " speed 10M bps full duplex\n"); + break; + default: + break; + } + } +} + +/** + * enable_flow_control_ability - flow control + * @vptr: veloity to configure + * + * Set up flow control according to the flow control options + * determined by the eeprom/configuration. + */ +static void enable_flow_control_ability(struct velocity_info *vptr) +{ + + struct mac_regs __iomem *regs = vptr->mac_regs; + + switch (vptr->options.flow_cntl) { + + case FLOW_CNTL_DEFAULT: + if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) + writel(CR0_FDXRFCEN, ®s->CR0Set); + else + writel(CR0_FDXRFCEN, ®s->CR0Clr); + + if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) + writel(CR0_FDXTFCEN, ®s->CR0Set); + else + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_TX: + writel(CR0_FDXTFCEN, ®s->CR0Set); + writel(CR0_FDXRFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_RX: + writel(CR0_FDXRFCEN, ®s->CR0Set); + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_TX_RX: + writel(CR0_FDXTFCEN, ®s->CR0Set); + writel(CR0_FDXRFCEN, ®s->CR0Set); + break; + + case FLOW_CNTL_DISABLE: + writel(CR0_FDXRFCEN, ®s->CR0Clr); + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + default: + break; + } + +} + +/** + * velocity_soft_reset - soft reset + * @vptr: velocity to reset + * + * Kick off a soft reset of the velocity adapter and then poll + * until the reset sequence has completed before returning. + */ +static int velocity_soft_reset(struct velocity_info *vptr) +{ + struct mac_regs __iomem *regs = vptr->mac_regs; + int i = 0; + + writel(CR0_SFRST, ®s->CR0Set); + + for (i = 0; i < W_MAX_TIMEOUT; i++) { + udelay(5); + if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) + break; + } + + if (i == W_MAX_TIMEOUT) { + writel(CR0_FORSRST, ®s->CR0Set); + /* FIXME: PCI POSTING */ + /* delay 2ms */ + mdelay(2); + } + return 0; +} + +/** + * velocity_set_multi - filter list change callback + * @dev: network device + * + * Called by the network layer when the filter lists need to change + * for a velocity adapter. Reload the CAMs with the new address + * filter ruleset. + */ +static void velocity_set_multi(struct net_device *dev) +{ + struct velocity_info *vptr = netdev_priv(dev); + struct mac_regs __iomem *regs = vptr->mac_regs; + u8 rx_mode; + int i; + struct dev_mc_list *mclist; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + writel(0xffffffff, ®s->MARCAM[0]); + writel(0xffffffff, ®s->MARCAM[4]); + rx_mode = (RCR_AM | RCR_AB | RCR_PROM); + } else if ((dev->mc_count > vptr->multicast_limit) + || (dev->flags & IFF_ALLMULTI)) { + writel(0xffffffff, ®s->MARCAM[0]); + writel(0xffffffff, ®s->MARCAM[4]); + rx_mode = (RCR_AM | RCR_AB); + } else { + int offset = MCAM_SIZE - vptr->multicast_limit; + mac_get_cam_mask(regs, vptr->mCAMmask); + + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { + mac_set_cam(regs, i + offset, mclist->dmi_addr); + vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); + } + + mac_set_cam_mask(regs, vptr->mCAMmask); + rx_mode = RCR_AM | RCR_AB | RCR_AP; + } + if (dev->mtu > 1500) + rx_mode |= RCR_AL; + + BYTE_REG_BITS_ON(rx_mode, ®s->RCR); + +} + +/* + * MII access , media link mode setting functions + */ + +/** + * mii_init - set up MII + * @vptr: velocity adapter + * @mii_status: links tatus + * + * Set up the PHY for the current link state. + */ +static void mii_init(struct velocity_info *vptr, u32 mii_status) +{ + u16 BMCR; + + switch (PHYID_GET_PHY_ID(vptr->phy_id)) { + case PHYID_CICADA_CS8201: + /* + * Reset to hardware default + */ + MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); + /* + * Turn on ECHODIS bit in NWay-forced full mode and turn it + * off it in NWay-forced half mode for NWay-forced v.s. + * legacy-forced issue. + */ + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); + /* + * Turn on Link/Activity LED enable bit for CIS8201 + */ + MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs); + break; + case PHYID_VT3216_32BIT: + case PHYID_VT3216_64BIT: + /* + * Reset to hardware default + */ + MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); + /* + * Turn on ECHODIS bit in NWay-forced full mode and turn it + * off it in NWay-forced half mode for NWay-forced v.s. + * legacy-forced issue + */ + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); + break; + + case PHYID_MARVELL_1000: + case PHYID_MARVELL_1000S: + /* + * Assert CRS on Transmit + */ + MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); + /* + * Reset to hardware default + */ + MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); + break; + default: + ; + } + velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR); + if (BMCR & BMCR_ISO) { + BMCR &= ~BMCR_ISO; + velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR); + } +} + + +/** * velocity_init_registers - initialise MAC registers * @vptr: velocity to init * @type: type of initialisation (hot or cold) @@ -703,11 +1268,10 @@ static void velocity_rx_reset(struct velocity_info *vptr) * Initialise the MAC on a reset or on first set up on the * hardware. */ - static void velocity_init_registers(struct velocity_info *vptr, enum velocity_init_type type) { - struct mac_regs __iomem * regs = vptr->mac_regs; + struct mac_regs __iomem *regs = vptr->mac_regs; int i, mii_status; mac_wol_reset(regs); @@ -750,9 +1314,9 @@ static void velocity_init_registers(struct velocity_info *vptr, mdelay(5); mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) { + for (i = 0; i < 6; i++) writeb(vptr->dev->dev_addr[i], &(regs->PAR[i])); - } + /* * clear Pre_ACPI bit. */ @@ -819,291 +1383,29 @@ static void velocity_init_registers(struct velocity_info *vptr, } } -/** - * velocity_soft_reset - soft reset - * @vptr: velocity to reset - * - * Kick off a soft reset of the velocity adapter and then poll - * until the reset sequence has completed before returning. - */ - -static int velocity_soft_reset(struct velocity_info *vptr) -{ - struct mac_regs __iomem * regs = vptr->mac_regs; - int i = 0; - - writel(CR0_SFRST, ®s->CR0Set); - - for (i = 0; i < W_MAX_TIMEOUT; i++) { - udelay(5); - if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) - break; - } - - if (i == W_MAX_TIMEOUT) { - writel(CR0_FORSRST, ®s->CR0Set); - /* FIXME: PCI POSTING */ - /* delay 2ms */ - mdelay(2); - } - return 0; -} - -static const struct net_device_ops velocity_netdev_ops = { - .ndo_open = velocity_open, - .ndo_stop = velocity_close, - .ndo_start_xmit = velocity_xmit, - .ndo_get_stats = velocity_get_stats, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_set_multicast_list = velocity_set_multi, - .ndo_change_mtu = velocity_change_mtu, - .ndo_do_ioctl = velocity_ioctl, - .ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid, - .ndo_vlan_rx_register = velocity_vlan_rx_register, -}; - -/** - * velocity_found1 - set up discovered velocity card - * @pdev: PCI device - * @ent: PCI device table entry that matched - * - * Configure a discovered adapter from scratch. Return a negative - * errno error code on failure paths. - */ - -static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_device_id *ent) +static void velocity_give_many_rx_descs(struct velocity_info *vptr) { - static int first = 1; - struct net_device *dev; - int i; - const char *drv_string; - const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; - struct velocity_info *vptr; - struct mac_regs __iomem * regs; - int ret = -ENOMEM; - - /* FIXME: this driver, like almost all other ethernet drivers, - * can support more than MAX_UNITS. - */ - if (velocity_nics >= MAX_UNITS) { - dev_notice(&pdev->dev, "already found %d NICs.\n", - velocity_nics); - return -ENODEV; - } - - dev = alloc_etherdev(sizeof(struct velocity_info)); - if (!dev) { - dev_err(&pdev->dev, "allocate net device failed.\n"); - goto out; - } - - /* Chain it all together */ - - SET_NETDEV_DEV(dev, &pdev->dev); - vptr = netdev_priv(dev); - - - if (first) { - printk(KERN_INFO "%s Ver. %s\n", - VELOCITY_FULL_DRV_NAM, VELOCITY_VERSION); - printk(KERN_INFO "Copyright (c) 2002, 2003 VIA Networking Technologies, Inc.\n"); - printk(KERN_INFO "Copyright (c) 2004 Red Hat Inc.\n"); - first = 0; - } - - velocity_init_info(pdev, vptr, info); - - vptr->dev = dev; - - dev->irq = pdev->irq; - - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_free_dev; - - ret = velocity_get_pci_info(vptr, pdev); - if (ret < 0) { - /* error message already printed */ - goto err_disable; - } - - ret = pci_request_regions(pdev, VELOCITY_NAME); - if (ret < 0) { - dev_err(&pdev->dev, "No PCI resources.\n"); - goto err_disable; - } - - regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); - if (regs == NULL) { - ret = -EIO; - goto err_release_res; |