X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fvia-velocity.c;h=bc278d4ee89debb359c4d70ef21e06bfd7479ae3;hb=a26272e5200765691e67d6780e52b32498fdb659;hp=b02f7adff5dcef08f6889ba7ef7da8d5a4e34400;hpb=d3b238a03efd6d644ff93c8b10a1d38a596f2e34;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index b02f7ad..bc278d4 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -9,7 +9,6 @@ * * TODO * rx_copybreak/alignment - * Scatter gather * More testing * * The changes are (c) Copyright 2004, Red Hat Inc. @@ -61,9 +60,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -81,7 +80,7 @@ #include "via-velocity.h" -static int velocity_nics = 0; +static int velocity_nics; static int msglevel = MSG_LEVEL_INFO; /** @@ -92,8 +91,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 +109,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 +119,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 +127,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 +137,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 +145,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 +163,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 +174,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 +187,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 +218,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 +236,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 +246,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 @@ -281,7 +274,7 @@ VELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); #define DMA_LENGTH_MIN 0 #define DMA_LENGTH_MAX 7 -#define DMA_LENGTH_DEF 0 +#define DMA_LENGTH_DEF 6 /* DMA_length[] is used for controlling the DMA length 0: 8 DWORDs @@ -304,14 +297,6 @@ VELOCITY_PARAM(DMA_length, "DMA length"); */ VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); -#define TX_CSUM_DEF 1 -/* txcsum_offload[] is used for setting the checksum offload ability of NIC. - (We only support RX checksum offload now) - 0: disable csum_offload[checksum offload - 1: enable checksum offload. (Default) -*/ -VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload"); - #define FLOW_CNTL_DEF 1 #define FLOW_CNTL_MIN 1 #define FLOW_CNTL_MAX 5 @@ -336,8 +321,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"); @@ -360,86 +345,13 @@ VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame"); */ VELOCITY_PARAM(wol_opts, "Wake On Lan options"); -#define INT_WORKS_DEF 20 -#define INT_WORKS_MIN 10 -#define INT_WORKS_MAX 64 - -VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); - 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,8 +361,7 @@ 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 = { +static DEFINE_PCI_DEVICE_TABLE(velocity_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, { } }; @@ -464,7 +375,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,20 +392,11 @@ 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); struct velocity_info *vptr = netdev_priv(dev); -#ifdef CONFIG_PM - unsigned long flags; - - spin_lock_irqsave(&velocity_dev_list_lock, flags); - if (!list_empty(&velocity_dev_list)) - list_del(&vptr->list); - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); -#endif unregister_netdev(dev); iounmap(vptr->mac_regs); pci_release_regions(pdev); @@ -520,7 +421,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 +449,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 +474,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) { @@ -584,13 +482,11 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index, velocity_set_int_opt(&opts->numrx, RxDescriptors[index], RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, "RxDescriptors", devname); velocity_set_int_opt(&opts->numtx, TxDescriptors[index], TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, "TxDescriptors", devname); - velocity_set_bool_opt(&opts->flags, txcsum_offload[index], TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, "txcsum_offload", devname); velocity_set_int_opt(&opts->flow_cntl, flow_control[index], FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, "flow_control", devname); velocity_set_bool_opt(&opts->flags, IP_byte_align[index], IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, "IP_byte_align", devname); velocity_set_bool_opt(&opts->flags, ValPktLen[index], VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, "ValPktLen", devname); velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, "Media link mode", devname); velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, "Wake On Lan options", devname); - velocity_set_int_opt((int *) &opts->int_works, int_works[index], INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, "Interrupt service works", devname); opts->numrx = (opts->numrx & ~3); } @@ -601,10 +497,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 +542,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 +569,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,414 +590,854 @@ static void velocity_rx_reset(struct velocity_info *vptr) } /** - * velocity_init_registers - initialise MAC registers - * @vptr: velocity to init - * @type: type of initialisation (hot or cold) + * velocity_get_opt_media_mode - get media selection + * @vptr: velocity adapter * - * Initialise the MAC on a reset or on first set up on the - * hardware. + * Get the media mode stored in EEPROM or module options and load + * mii_status accordingly. The requested link state information + * is also returned. */ - -static void velocity_init_registers(struct velocity_info *vptr, - enum velocity_init_type type) +static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) { - struct mac_regs __iomem * regs = vptr->mac_regs; - int i, mii_status; - - mac_wol_reset(regs); - - switch (type) { - case VELOCITY_INIT_RESET: - case VELOCITY_INIT_WOL: + u32 status = 0; - netif_stop_queue(vptr->dev); + 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; +} - /* - * Reset RX to prevent RX pointer not on the 4X location - */ - velocity_rx_reset(vptr); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); +/** + * 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; - mii_status = velocity_get_opt_media_mode(vptr); - if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); - } + /* 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_flow_control_ability(vptr); +/** + * 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; - mac_clear_isr(regs); - writel(CR0_STOP, ®s->CR0Clr); - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - ®s->CR0Set); + writeb(0, &(regs->MIICR)); + writeb(MIIADR_SWMPL, ®s->MIIADR); - break; + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } - case VELOCITY_INIT_COLD: - default: - /* - * Do reset - */ - velocity_soft_reset(vptr); - mdelay(5); + writeb(MIICR_MAUTO, ®s->MIICR); - mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) { - writeb(vptr->dev->dev_addr[i], &(regs->PAR[i])); - } - /* - * clear Pre_ACPI bit. - */ - BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); - mac_set_rx_thresh(regs, vptr->options.rx_thresh); - mac_set_dma_length(regs, vptr->options.DMA_length); + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } - writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); - /* - * Back off algorithm use original IEEE standard - */ - BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), ®s->CFGB); +} - /* - * Init CAM filter - */ - velocity_init_cam_filter(vptr); +/** + * 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; - /* - * Set packet filter: Receive directed and broadcast address - */ - velocity_set_multi(vptr->dev); + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); - /* - * Enable MII auto-polling - */ - enable_mii_autopoll(regs); + writeb(index, ®s->MIIADR); - vptr->int_mask = INT_MASK_DEF; + BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); - writel(vptr->rx.pool_dma, ®s->RDBaseLo); - writew(vptr->options.numrx - 1, ®s->RDCSize); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if (!(readb(®s->MIICR) & MIICR_RCMD)) + break; + } - writew(vptr->options.numtx - 1, ®s->TDCSize); + *data = readw(®s->MIIDATA); - for (i = 0; i < vptr->tx.numq; i++) { - writel(vptr->tx.pool_dma[i], ®s->TDBaseLo[i]); - mac_tx_queue_run(regs, i); - } + enable_mii_autopoll(regs); + if (ww == W_MAX_TIMEOUT) + return -ETIMEDOUT; + return 0; +} - init_flow_control_register(vptr); - writel(CR0_STOP, ®s->CR0Clr); - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); +/** + * 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; - mii_status = velocity_get_opt_media_mode(vptr); - netif_stop_queue(vptr->dev); + if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) + status |= VELOCITY_LINK_FAIL; - mii_init(vptr, mii_status); + 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 (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); + 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; } - - enable_flow_control_ability(vptr); - mac_hw_mibs_init(regs); - mac_write_int_mask(vptr->int_mask, regs); - mac_clear_isr(regs); - } + + return status; } /** - * velocity_soft_reset - soft reset - * @vptr: velocity to reset + * velocity_mii_write - write MII data + * @regs: velocity registers + * @index: MII register index + * @data: 16bit data for the MII register * - * Kick off a soft reset of the velocity adapter and then poll - * until the reset sequence has completed before returning. + * Perform a single write to an MII 16bit register. Returns zero + * on success or -ETIMEDOUT if the PHY did not respond. */ - -static int velocity_soft_reset(struct velocity_info *vptr) +static int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data) { - struct mac_regs __iomem * regs = vptr->mac_regs; - int i = 0; + u16 ww; - writel(CR0_SFRST, ®s->CR0Set); + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); - for (i = 0; i < W_MAX_TIMEOUT; i++) { + /* 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 (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) + if (!(readb(®s->MIICR) & MIICR_WCMD)) break; } + enable_mii_autopoll(regs); - if (i == W_MAX_TIMEOUT) { - writel(CR0_FORSRST, ®s->CR0Set); - /* FIXME: PCI POSTING */ - /* delay 2ms */ - mdelay(2); - } + if (ww == W_MAX_TIMEOUT) + return -ETIMEDOUT; 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 + * set_mii_flow_control - flow control setup + * @vptr: velocity interface * - * Configure a discovered adapter from scratch. Return a negative - * errno error code on failure paths. + * Set up the flow control on this interface according to + * the supplied user/eeprom options. */ - -static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_device_id *ent) +static void set_mii_flow_control(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 */ + /*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; - SET_NETDEV_DEV(dev, &pdev->dev); - vptr = netdev_priv(dev); + 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_OFF(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; - 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; + 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; } +} - velocity_init_info(pdev, vptr, info); - - vptr->dev = dev; +/** + * 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); +} - dev->irq = pdev->irq; +static u32 check_connection_type(struct mac_regs __iomem *regs) +{ + u32 status = 0; + u8 PHYSR0; + u16 ANAR; + PHYSR0 = readb(®s->PHYSR0); - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_free_dev; + /* + if (!(PHYSR0 & PHYSR0_LINKGD)) + status|=VELOCITY_LINK_FAIL; + */ - ret = velocity_get_pci_info(vptr, pdev); - if (ret < 0) { - /* error message already printed */ - goto err_disable; - } + if (PHYSR0 & PHYSR0_FDPX) + status |= VELOCITY_DUPLEX_FULL; - ret = pci_request_regions(pdev, VELOCITY_NAME); - if (ret < 0) { - dev_err(&pdev->dev, "No PCI resources.\n"); - goto err_disable; - } + if (PHYSR0 & PHYSR0_SPDG) + status |= VELOCITY_SPEED_1000; + else if (PHYSR0 & PHYSR0_SPD10) + status |= VELOCITY_SPEED_10; + else + status |= VELOCITY_SPEED_100; - regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); - if (regs == NULL) { - ret = -EIO; - goto err_release_res; + 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; + } } - vptr->mac_regs = regs; - - mac_wol_reset(regs); + return status; +} - dev->base_addr = vptr->ioaddr; - for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(®s->PAR[i]); +/** + * 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; - drv_string = dev_driver_string(&pdev->dev); + vptr->mii_status = mii_check_media_mode(vptr->mac_regs); + curr_status = vptr->mii_status & (~VELOCITY_LINK_FAIL); - velocity_get_options(&vptr->options, velocity_nics, drv_string); + /* Set mii link status */ + set_mii_flow_control(vptr); /* - * Mask out the options cannot be set to the chip + 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; + } */ - vptr->options.flags &= info->flags; + if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) + MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, vptr->mac_regs); /* - * Enable the chip specified capbilities + * 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); - vptr->flags = vptr->options.flags | (info->flags & 0xFF000000UL); - - vptr->wol_opts = vptr->options.wol_opts; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - - vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - - dev->irq = pdev->irq; - dev->netdev_ops = &velocity_netdev_ops; - dev->ethtool_ops = &velocity_ethtool_ops; + /* enable AUTO-NEGO mode */ + mii_set_auto_on(vptr); + } else { + u16 ANAR; + u8 CHIPGCR; -#ifdef VELOCITY_ZERO_COPY_SUPPORT - dev->features |= NETIF_F_SG; -#endif - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | - NETIF_F_HW_VLAN_RX; + /* + * 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 + */ - if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) - dev->features |= NETIF_F_IP_CSUM; + /* set force MAC mode bit */ + BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); - ret = register_netdev(dev); - if (ret < 0) - goto err_iounmap; + CHIPGCR = readb(®s->CHIPGCR); + CHIPGCR &= ~CHIPGCR_FCGMII; - if (!velocity_get_link(dev)) { - netif_carrier_off(dev); - vptr->mii_status |= VELOCITY_LINK_FAIL; + 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_info(vptr); - pci_set_drvdata(pdev, dev); +/** + * 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) +{ - /* and leave the chip powered down */ + 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); - pci_set_power_state(pdev, PCI_D3hot); -#ifdef CONFIG_PM - { - unsigned long flags; + 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; + } + } +} - spin_lock_irqsave(&velocity_dev_list_lock, flags); - list_add(&vptr->list, &velocity_dev_list); - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); +/** + * 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 ((netdev_mc_count(dev) > 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); + + i = 0; + netdev_for_each_mc_addr(mclist, dev) { + mac_set_cam(regs, i + offset, mclist->dmi_addr); + vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); + i++; + } + + 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); } -#endif - velocity_nics++; -out: - return ret; - -err_iounmap: - iounmap(regs); -err_release_res: - pci_release_regions(pdev); -err_disable: - pci_disable_device(pdev); -err_free_dev: - free_netdev(dev); - goto out; } /** - * velocity_print_info - per driver data - * @vptr: velocity + * setup_queue_timers - Setup interrupt timers * - * Print per driver data as the kernel driver finds Velocity - * hardware + * Setup interrupt frequency during suppression (timeout if the frame + * count isn't filled). */ - -static void __devinit velocity_print_info(struct velocity_info *vptr) +static void setup_queue_timers(struct velocity_info *vptr) { - struct net_device *dev = vptr->dev; + /* Only for newer revisions */ + if (vptr->rev_id >= REV_ID_VT3216_A0) { + u8 txqueue_timer = 0; + u8 rxqueue_timer = 0; - printk(KERN_INFO "%s: %s\n", dev->name, get_chip_name(vptr->chip_id)); - printk(KERN_INFO "%s: Ethernet Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", - dev->name, - 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 (vptr->mii_status & (VELOCITY_SPEED_1000 | + VELOCITY_SPEED_100)) { + txqueue_timer = vptr->options.txqueue_timer; + rxqueue_timer = vptr->options.rxqueue_timer; + } + writeb(txqueue_timer, &vptr->mac_regs->TQETMR); + writeb(rxqueue_timer, &vptr->mac_regs->RQETMR); + } +} /** - * velocity_init_info - init private data - * @pdev: PCI device - * @vptr: Velocity info - * @info: Board type + * setup_adaptive_interrupts - Setup interrupt suppression * - * Set up the initial velocity_info struct for the device that has been - * discovered. + * @vptr velocity adapter + * + * The velocity is able to suppress interrupt during high interrupt load. + * This function turns on that feature. */ - -static void __devinit velocity_init_info(struct pci_dev *pdev, - struct velocity_info *vptr, - const struct velocity_info_tbl *info) +static void setup_adaptive_interrupts(struct velocity_info *vptr) { - memset(vptr, 0, sizeof(struct velocity_info)); + struct mac_regs __iomem *regs = vptr->mac_regs; + u16 tx_intsup = vptr->options.tx_intsup; + u16 rx_intsup = vptr->options.rx_intsup; + + /* Setup default interrupt mask (will be changed below) */ + vptr->int_mask = INT_MASK_DEF; + + /* Set Tx Interrupt Suppression Threshold */ + writeb(CAMCR_PS0, ®s->CAMCR); + if (tx_intsup != 0) { + vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I | + ISR_PTX2I | ISR_PTX3I); + writew(tx_intsup, ®s->ISRCTL); + } else + writew(ISRCTL_TSUPDIS, ®s->ISRCTL); - vptr->pdev = pdev; - vptr->chip_id = info->chip_id; - vptr->tx.numq = info->txqueue; - vptr->multicast_limit = MCAM_SIZE; - spin_lock_init(&vptr->lock); - INIT_LIST_HEAD(&vptr->list); + /* Set Rx Interrupt Suppression Threshold */ + writeb(CAMCR_PS1, ®s->CAMCR); + if (rx_intsup != 0) { + vptr->int_mask &= ~ISR_PRXI; + writew(rx_intsup, ®s->ISRCTL); + } else + writew(ISRCTL_RSUPDIS, ®s->ISRCTL); + + /* Select page to interrupt hold timer */ + writeb(0, ®s->CAMCR); } /** - * velocity_get_pci_info - retrieve PCI info for device - * @vptr: velocity device - * @pdev: PCI device it matches + * velocity_init_registers - initialise MAC registers + * @vptr: velocity to init + * @type: type of initialisation (hot or cold) * - * Retrieve the PCI configuration space data that interests us from - * the kernel PCI layer + * Initialise the MAC on a reset or on first set up on the + * hardware. */ - -static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pci_dev *pdev) +static void velocity_init_registers(struct velocity_info *vptr, + enum velocity_init_type type) { - vptr->rev_id = pdev->revision; + struct mac_regs __iomem *regs = vptr->mac_regs; + int i, mii_status; - pci_set_master(pdev); + mac_wol_reset(regs); - vptr->ioaddr = pci_resource_start(pdev, 0); - vptr->memaddr = pci_resource_start(pdev, 1); + switch (type) { + case VELOCITY_INIT_RESET: + case VELOCITY_INIT_WOL: - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { - dev_err(&pdev->dev, - "region #0 is not an I/O resource, aborting.\n"); - return -EINVAL; - } + netif_stop_queue(vptr->dev); + + /* + * Reset RX to prevent RX pointer not on the 4X location + */ + velocity_rx_reset(vptr); + mac_rx_queue_run(regs); + mac_rx_queue_wake(regs); + + mii_status = velocity_get_opt_media_mode(vptr); + if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { + velocity_print_link_status(vptr); + if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) + netif_wake_queue(vptr->dev); + } + + enable_flow_control_ability(vptr); + + mac_clear_isr(regs); + writel(CR0_STOP, ®s->CR0Clr); + writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), + ®s->CR0Set); + + break; + + case VELOCITY_INIT_COLD: + default: + /* + * Do reset + */ + velocity_soft_reset(vptr); + mdelay(5); + + mac_eeprom_reload(regs); + for (i = 0; i < 6; i++) + writeb(vptr->dev->dev_addr[i], &(regs->PAR[i])); + + /* + * clear Pre_ACPI bit. + */ + BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); + mac_set_rx_thresh(regs, vptr->options.rx_thresh); + mac_set_dma_length(regs, vptr->options.DMA_length); + + writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); + /* + * Back off algorithm use original IEEE standard + */ + BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), ®s->CFGB); + + /* + * Init CAM filter + */ + velocity_init_cam_filter(vptr); + + /* + * Set packet filter: Receive directed and broadcast address + */ + velocity_set_multi(vptr->dev); + + /* + * Enable MII auto-polling + */ + enable_mii_autopoll(regs); + + setup_adaptive_interrupts(vptr); + + writel(vptr->rx.pool_dma, ®s->RDBaseLo); + writew(vptr->options.numrx - 1, ®s->RDCSize); + mac_rx_queue_run(regs); + mac_rx_queue_wake(regs); + + writew(vptr->options.numtx - 1, ®s->TDCSize); + + for (i = 0; i < vptr->tx.numq; i++) { + writel(vptr->tx.pool_dma[i], ®s->TDBaseLo[i]); + mac_tx_queue_run(regs, i); + } + + init_flow_control_register(vptr); + + writel(CR0_STOP, ®s->CR0Clr); + writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); + + mii_status = velocity_get_opt_media_mode(vptr); + netif_stop_queue(vptr->dev); + + mii_init(vptr, mii_status); + + if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { + velocity_print_link_status(vptr); + if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) + netif_wake_queue(vptr->dev); + } + + enable_flow_control_ability(vptr); + mac_hw_mibs_init(regs); + mac_write_int_mask(vptr->int_mask, regs); + mac_clear_isr(regs); - if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { - dev_err(&pdev->dev, - "region #1 is an I/O resource, aborting.\n"); - return -EINVAL; } +} - if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { - dev_err(&pdev->dev, "region #1 is too small.\n"); - return -EINVAL; +static void velocity_give_many_rx_descs(struct velocity_info *vptr) +{ + struct mac_regs __iomem *regs = vptr->mac_regs; + int avail, dirty, unusable; + + /* + * RD number must be equal to 4X per hardware spec + * (programming guide rev 1.20, p.13) + */ + if (vptr->rx.filled < 4) + return; + + wmb(); + + unusable = vptr->rx.filled & 0x0003; + dirty = vptr->rx.dirty - unusable; + for (avail = vptr->rx.filled & 0xfffc; avail; avail--) { + dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; + vptr->rx.ring[dirty].rdesc0.len |= OWNED_BY_NIC; } - vptr->pdev = pdev; - return 0; + writew(vptr->rx.filled & 0xfffc, ®s->RBRDU); + vptr->rx.filled = unusable; } /** @@ -1113,7 +1447,6 @@ static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pc * Allocate PCI mapped DMA rings for the receive and transmit layer * to use. */ - static int velocity_init_dma_rings(struct velocity_info *vptr) { struct velocity_opt *opt = &vptr->options; @@ -1154,46 +1487,51 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) return 0; } +static void velocity_set_rxbufsize(struct velocity_info *vptr, int mtu) +{ + vptr->rx.buf_sz = (mtu <= ETH_DATA_LEN) ? PKT_BUF_SZ : mtu + 32; +} + /** - * velocity_free_dma_rings - free PCI ring pointers - * @vptr: Velocity to free from + * velocity_alloc_rx_buf - allocate aligned receive buffer + * @vptr: velocity + * @idx: ring index * - * Clean up the PCI ring buffers allocated to this velocity. + * Allocate a new full sized buffer for the reception of a frame and + * map it into PCI space for the hardware to use. The hardware + * requires *64* byte alignment of the buffer which makes life + * less fun than would be ideal. */ - -static void velocity_free_dma_rings(struct velocity_info *vptr) +static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) { - const int size = vptr->options.numrx * sizeof(struct rx_desc) + - vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; - - pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma); -} + struct rx_desc *rd = &(vptr->rx.ring[idx]); + struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); -static void velocity_give_many_rx_descs(struct velocity_info *vptr) -{ - struct mac_regs __iomem *regs = vptr->mac_regs; - int avail, dirty, unusable; + rd_info->skb = dev_alloc_skb(vptr->rx.buf_sz + 64); + if (rd_info->skb == NULL) + return -ENOMEM; /* - * RD number must be equal to 4X per hardware spec - * (programming guide rev 1.20, p.13) - */ - if (vptr->rx.filled < 4) - return; - - wmb(); + * Do the gymnastics to get the buffer head for data at + * 64byte alignment. + */ + skb_reserve(rd_info->skb, + 64 - ((unsigned long) rd_info->skb->data & 63)); + rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data, + vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); - unusable = vptr->rx.filled & 0x0003; - dirty = vptr->rx.dirty - unusable; - for (avail = vptr->rx.filled & 0xfffc; avail; avail--) { - dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; - vptr->rx.ring[dirty].rdesc0.len |= OWNED_BY_NIC; - } + /* + * Fill in the descriptor to match + */ - writew(vptr->rx.filled & 0xfffc, ®s->RBRDU); - vptr->rx.filled = unusable; + *((u32 *) & (rd->rdesc0)) = 0; + rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN; + rd->pa_low = cpu_to_le32(rd_info->skb_dma); + rd->pa_high = 0; + return 0; } + static int velocity_rx_refill(struct velocity_info *vptr) { int dirty = vptr->rx.dirty, done = 0; @@ -1221,42 +1559,6 @@ static int velocity_rx_refill(struct velocity_info *vptr) return done; } -static void velocity_set_rxbufsize(struct velocity_info *vptr, int mtu) -{ - vptr->rx.buf_sz = (mtu <= ETH_DATA_LEN) ? PKT_BUF_SZ : mtu + 32; -} - -/** - * velocity_init_rd_ring - set up receive ring - * @vptr: velocity to configure - * - * Allocate and set up the receive buffers for each ring slot and - * assign them to the network adapter. - */ - -static int velocity_init_rd_ring(struct velocity_info *vptr) -{ - int ret = -ENOMEM; - - vptr->rx.info = kcalloc(vptr->options.numrx, - sizeof(struct velocity_rd_info), GFP_KERNEL); - if (!vptr->rx.info) - goto out; - - velocity_init_rx_ring_indexes(vptr); - - if (velocity_rx_refill(vptr) != vptr->options.numrx) { - VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR - "%s: failed to allocate RX buffer.\n", vptr->dev->name); - velocity_free_rd_ring(vptr); - goto out; - } - - ret = 0; -out: - return ret; -} - /** * velocity_free_rd_ring - free receive ring * @vptr: velocity to clean up @@ -1264,7 +1566,6 @@ out: * Free the receive buffers for each ring slot and any * attached socket buffers that need to go away. */ - static void velocity_free_rd_ring(struct velocity_info *vptr) { int i; @@ -1292,6 +1593,38 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) vptr->rx.info = NULL; } + + +/** + * velocity_init_rd_ring - set up receive ring + * @vptr: velocity to configure + * + * Allocate and set up the receive buffers for each ring slot and + * assign them to the network adapter. + */ +static int velocity_init_rd_ring(struct velocity_info *vptr) +{ + int ret = -ENOMEM; + + vptr->rx.info = kcalloc(vptr->options.numrx, + sizeof(struct velocity_rd_info), GFP_KERNEL); + if (!vptr->rx.info) + goto out; + + velocity_init_rx_ring_indexes(vptr); + + if (velocity_rx_refill(vptr) != vptr->options.numrx) { + VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR + "%s: failed to allocate RX buffer.\n", vptr->dev->name); + velocity_free_rd_ring(vptr); + goto out; + } + + ret = 0; +out: + return ret; +} + /** * velocity_init_td_ring - set up transmit ring * @vptr: velocity @@ -1300,21 +1633,18 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) * Returns zero on success or a negative posix errno code for * failure. */ - static int velocity_init_td_ring(struct velocity_info *vptr) { - dma_addr_t curr; int j; /* Init the TD ring entries */ for (j = 0; j < vptr->tx.numq; j++) { - curr = vptr->tx.pool_dma[j]; vptr->tx.infos[j] = kcalloc(vptr->options.numtx, sizeof(struct velocity_td_info), GFP_KERNEL); if (!vptr->tx.infos[j]) { - while(--j >= 0) + while (--j >= 0) kfree(vptr->tx.infos[j]); return -ENOMEM; } @@ -1324,22 +1654,98 @@ static int velocity_init_td_ring(struct velocity_info *vptr) return 0; } +/** + * velocity_free_dma_rings - free PCI ring pointers + * @vptr: Velocity to free from + * + * Clean up the PCI ring buffers allocated to this velocity. + */ +static void velocity_free_dma_rings(struct velocity_info *vptr) +{ + const int size = vptr->options.numrx * sizeof(struct rx_desc) + + vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; + + pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma); +} + + +static int velocity_init_rings(struct velocity_info *vptr, int mtu) +{ + int ret; + + velocity_set_rxbufsize(vptr, mtu); + + ret = velocity_init_dma_rings(vptr); + if (ret < 0) + goto out; + + ret = velocity_init_rd_ring(vptr); + if (ret < 0) + goto err_free_dma_rings_0; + + ret = velocity_init_td_ring(vptr); + if (ret < 0) + goto err_free_rd_ring_1; +out: + return ret; + +err_free_rd_ring_1: + velocity_free_rd_ring(vptr); +err_free_dma_rings_0: + velocity_free_dma_rings(vptr); + goto out; +} + +/** + * velocity_free_tx_buf - free transmit buffer + * @vptr: velocity + * @tdinfo: buffer + * + * Release an transmit buffer. If the buffer was preallocated then + * recycle it, if not then unmap the buffer. + */ +static void velocity_free_tx_buf(struct velocity_info *vptr, + struct velocity_td_info *tdinfo, struct tx_desc *td) +{ + struct sk_buff *skb = tdinfo->skb; + + /* + * Don't unmap the pre-allocated tx_bufs + */ + if (tdinfo->skb_dma) { + int i; + + for (i = 0; i < tdinfo->nskb_dma; i++) { + size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN); + + /* For scatter-gather */ + if (skb_shinfo(skb)->nr_frags > 0) + pktlen = max_t(size_t, pktlen, + td->td_buf[i].size & ~TD_QUEUE); + + pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], + le16_to_cpu(pktlen), PCI_DMA_TODEVICE); + } + } + dev_kfree_skb_irq(skb); + tdinfo->skb = NULL; +} + + /* * FIXME: could we merge this with velocity_free_tx_buf ? */ - static void velocity_free_td_ring_entry(struct velocity_info *vptr, int q, int n) { - struct velocity_td_info * td_info = &(vptr->tx.infos[q][n]); + struct velocity_td_info *td_info = &(vptr->tx.infos[q][n]); int i; if (td_info == NULL) return; if (td_info->skb) { - for (i = 0; i < td_info->nskb_dma; i++) - { + for (i = 0; i < td_info->nskb_dma; i++) { if (td_info->skb_dma[i]) { pci_unmap_single(vptr->pdev, td_info->skb_dma[i], td_info->skb->len, PCI_DMA_TODEVICE); @@ -1358,7 +1764,6 @@ static void velocity_free_td_ring_entry(struct velocity_info *vptr, * Free up the transmit ring for this particular velocity adapter. * We free the ring contents but not the ring itself. */ - static void velocity_free_td_ring(struct velocity_info *vptr) { int i, j; @@ -1366,70 +1771,176 @@ static void velocity_free_td_ring(struct velocity_info *vptr) for (j = 0; j < vptr->tx.numq; j++) { if (vptr->tx.infos[j] == NULL) continue; - for (i = 0; i < vptr->options.numtx; i++) { + for (i = 0; i < vptr->options.numtx; i++) velocity_free_td_ring_entry(vptr, j, i); - } kfree(vptr->tx.infos[j]); vptr->tx.infos[j] = NULL; } } + +static void velocity_free_rings(struct velocity_info *vptr) +{ + velocity_free_td_ring(vptr); + velocity_free_rd_ring(vptr); + velocity_free_dma_rings(vptr); +} + /** - * velocity_rx_srv - service RX interrupt + * velocity_error - handle error from controller * @vptr: velocity - * @status: adapter status (unused) + * @status: card status * - * Walk the receive ring of the velocity adapter and remove - * any received packets from the receive queue. Hand the ring - * slots back to the adapter for reuse. + * Process an error report from the hardware and attempt to recover + * the card itself. At the moment we cannot recover from some + * theoretically impossible errors but this could be fixed using + * the pci_device_failed logic to bounce the hardware + * + */ +static void velocity_error(struct velocity_info *vptr, int status) +{ + + if (status & ISR_TXSTLI) { + struct mac_regs __iomem *regs = vptr->mac_regs; + + printk(KERN_ERR "TD structure error TDindex=%hx\n", readw(®s->TDIdx[0])); + BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); + writew(TRDCSR_RUN, ®s->TDCSRClr); + netif_stop_queue(vptr->dev); + + /* FIXME: port over the pci_device_failed code and use it + here */ + } + + if (status & ISR_SRCI) { + struct mac_regs __iomem *regs = vptr->mac_regs; + int linked; + + if (vptr->options.spd_dpx == SPD_DPX_AUTO) { + vptr->mii_status = check_connection_type(regs); + + /* + * If it is a 3119, disable frame bursting in + * halfduplex mode and enable it in fullduplex + * mode + */ + if (vptr->rev_id < REV_ID_VT3216_A0) { + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); + else + BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); + } + /* + * Only enable CD heart beat counter in 10HD mode + */ + if (!(vptr->mii_status & VELOCITY_DUPLEX_FULL) && (vptr->mii_status & VELOCITY_SPEED_10)) + BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); + else + BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); + + setup_queue_timers(vptr); + } + /* + * Get link status from PHYSR0 + */ + linked = readb(®s->PHYSR0) & PHYSR0_LINKGD; + + if (linked) { + vptr->mii_status &= ~VELOCITY_LINK_FAIL; + netif_carrier_on(vptr->dev); + } else { + vptr->mii_status |= VELOCITY_LINK_FAIL; + netif_carrier_off(vptr->dev); + } + + velocity_print_link_status(vptr); + enable_flow_control_ability(vptr); + + /* + * Re-enable auto-polling because SRCI will disable + * auto-polling + */ + + enable_mii_autopoll(regs); + + if (vptr->mii_status & VELOCITY_LINK_FAIL) + netif_stop_queue(vptr->dev); + else + netif_wake_queue(vptr->dev); + + }; + if (status & ISR_MIBFI) + velocity_update_hw_mibs(vptr); + if (status & ISR_LSTEI) + mac_rx_queue_wake(vptr->mac_regs); +} + +/** + * tx_srv - transmit interrupt service + * @vptr; Velocity + * + * Scan the queues looking for transmitted packets that + * we can complete and clean up. Update any statistics as + * necessary/ */ - -static int velocity_rx_srv(struct velocity_info *vptr, int status) +static int velocity_tx_srv(struct velocity_info *vptr) { - struct net_device_stats *stats = &vptr->dev->stats; - int rd_curr = vptr->rx.curr; + struct tx_desc *td; + int qnum; + int full = 0; + int idx; int works = 0; + struct velocity_td_info *tdinfo; + struct net_device_stats *stats = &vptr->dev->stats; - do { - struct rx_desc *rd = vptr->rx.ring + rd_curr; - - if (!vptr->rx.info[rd_curr].skb) - break; + for (qnum = 0; qnum < vptr->tx.numq; qnum++) { + for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; + idx = (idx + 1) % vptr->options.numtx) { - if (rd->rdesc0.len & OWNED_BY_NIC) - break; + /* + * Get Tx Descriptor + */ + td = &(vptr->tx.rings[qnum][idx]); + tdinfo = &(vptr->tx.infos[qnum][idx]); - rmb(); + if (td->tdesc0.len & OWNED_BY_NIC) + break; - /* - * Don't drop CE or RL error frame although RXOK is off - */ - if (rd->rdesc0.RSR & (RSR_RXOK | RSR_CE | RSR_RL)) { - if (velocity_receive_frame(vptr, rd_curr) < 0) - stats->rx_dropped++; - } else { - if (rd->rdesc0.RSR & RSR_CRC) - stats->rx_crc_errors++; - if (rd->rdesc0.RSR & RSR_FAE) - stats->rx_frame_errors++; + if ((works++ > 15)) + break; - stats->rx_dropped++; + if (td->tdesc0.TSR & TSR0_TERR) { + stats->tx_errors++; + stats->tx_dropped++; + if (td->tdesc0.TSR & TSR0_CDH) + stats->tx_heartbeat_errors++; + if (td->tdesc0.TSR & TSR0_CRS) + stats->tx_carrier_errors++; + if (td->tdesc0.TSR & TSR0_ABT) + stats->tx_aborted_errors++; + if (td->tdesc0.TSR & TSR0_OWC) + stats->tx_window_errors++; + } else { + stats->tx_packets++; + stats->tx_bytes += tdinfo->skb->len; + } + velocity_free_tx_buf(vptr, tdinfo, td); + vptr->tx.used[qnum]--; } + vptr->tx.tail[qnum] = idx; - rd->size |= RX_INTEN; - - rd_curr++; - if (rd_curr >= vptr->options.numrx) - rd_curr = 0; - } while (++works <= 15); - - vptr->rx.curr = rd_curr; - - if ((works > 0) && (velocity_rx_refill(vptr) > 0)) - velocity_give_many_rx_descs(vptr); - - VAR_USED(stats); + if (AVAIL_TD(vptr, qnum) < 1) + full = 1; + } + /* + * Look to see if we should kick the transmit network + * layer for more work. + */ + if (netif_queue_stopped(vptr->dev) && (full == 0) && + (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { + netif_wake_queue(vptr->dev); + } return works; } @@ -1441,7 +1952,6 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) * Process the status bits for the received packet and determine * if the checksum was computed and verified by the hardware */ - static inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) { skb->ip_summed = CHECKSUM_NONE; @@ -1450,9 +1960,8 @@ static inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) if (rd->rdesc1.CSM & CSM_IPOK) { if ((rd->rdesc1.CSM & CSM_TCPKT) || (rd->rdesc1.CSM & CSM_UDPKT)) { - if (!(rd->rdesc1.CSM & CSM_TUPOK)) { + if (!(rd->rdesc1.CSM & CSM_TUPOK)) return; - } } skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -1478,10 +1987,9 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, if (pkt_size < rx_copybreak) { struct sk_buff *new_skb; - new_skb = netdev_alloc_skb(vptr->dev, pkt_size + 2); + new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size); if (new_skb) { new_skb->ip_summed = rx_skb[0]->ip_summed; - skb_reserve(new_skb, 2); skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); *rx_skb = new_skb; ret = 0; @@ -1509,6 +2017,7 @@ static inline void velocity_iph_realign(struct velocity_info *vptr, } } + /** * velocity_receive_frame - received packet processor * @vptr: velocity we are handling @@ -1517,7 +2026,6 @@ static inline void velocity_iph_realign(struct velocity_info *vptr, * A packet has arrived. We process the packet and if appropriate * pass the frame up the network stack */ - static int velocity_receive_frame(struct velocity_info *vptr, int idx) { void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int); @@ -1579,320 +2087,130 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) return 0; } -/** - * velocity_alloc_rx_buf - allocate aligned receive buffer - * @vptr: velocity - * @idx: ring index - * - * Allocate a new full sized buffer for the reception of a frame and - * map it into PCI space for the hardware to use. The hardware - * requires *64* byte alignment of the buffer which makes life - * less fun than would be ideal. - */ - -static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) -{ - struct rx_desc *rd = &(vptr->rx.ring[idx]); - struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); - - rd_info->skb = dev_alloc_skb(vptr->rx.buf_sz + 64); - if (rd_info->skb == NULL) - return -ENOMEM; - - /* - * Do the gymnastics to get the buffer head for data at - * 64byte alignment. - */ - skb_reserve(rd_info->skb, (unsigned long) rd_info->skb->data & 63); - rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data, - vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); - - /* - * Fill in the descriptor to match - */ - - *((u32 *) & (rd->rdesc0)) = 0; - rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN; - rd->pa_low = cpu_to_le32(rd_info->skb_dma); - rd->pa_high = 0; - return 0; -} - -/** - * tx_srv - transmit interrupt service - * @vptr; Velocity - * @status: - * - * Scan the queues looking for transmitted packets that - * we can complete and clean up. Update any statistics as - * necessary/ - */ - -static int velocity_tx_srv(struct velocity_info *vptr, u32 status) -{ - struct tx_desc *td; - int qnum; - int full = 0; - int idx; - int works = 0; - struct velocity_td_info *tdinfo; - struct net_device_stats *stats = &vptr->dev->stats; - - for (qnum = 0; qnum < vptr->tx.numq; qnum++) { - for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; - idx = (idx + 1) % vptr->options.numtx) { - - /* - * Get Tx Descriptor - */ - td = &(vptr->tx.rings[qnum][idx]); - tdinfo = &(vptr->tx.infos[qnum][idx]); - - if (td->tdesc0.len & OWNED_BY_NIC) - break; - - if ((works++ > 15)) - break; - - if (td->tdesc0.TSR & TSR0_TERR) { - stats->tx_errors++; - stats->tx_dropped++; - if (td->tdesc0.TSR & TSR0_CDH) - stats->tx_heartbeat_errors++; - if (td->tdesc0.TSR & TSR0_CRS) - stats->tx_carrier_errors++; - if (td->tdesc0.TSR & TSR0_ABT) - stats->tx_aborted_errors++; - if (td->tdesc0.TSR & TSR0_OWC) - stats->tx_window_errors++; - } else { - stats->tx_packets++; - stats->tx_bytes += tdinfo->skb->len; - } - velocity_free_tx_buf(vptr, tdinfo); - vptr->tx.used[qnum]--; - } - vptr->tx.tail[qnum] = idx; - - if (AVAIL_TD(vptr, qnum) < 1) { - full = 1; - } - } - /* - * Look to see if we should kick the transmit network - * layer for more work. - */ - if (netif_queue_stopped(vptr->dev) && (full == 0) - && (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { - netif_wake_queue(vptr->dev); - } - return works; -} - -/** - * 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; - } - } -} /** - * velocity_error - handle error from controller + * velocity_rx_srv - service RX interrupt * @vptr: velocity - * @status: card status - * - * Process an error report from the hardware and attempt to recover - * the card itself. At the moment we cannot recover from some - * theoretically impossible errors but this could be fixed using - * the pci_device_failed logic to bounce the hardware * + * Walk the receive ring of the velocity adapter and remove + * any received packets from the receive queue. Hand the ring + * slots back to the adapter for reuse. */ - -static void velocity_error(struct velocity_info *vptr, int status) +static int velocity_rx_srv(struct velocity_info *vptr, int budget_left) { + struct net_device_stats *stats = &vptr->dev->stats; + int rd_curr = vptr->rx.curr; + int works = 0; - if (status & ISR_TXSTLI) { - struct mac_regs __iomem * regs = vptr->mac_regs; - - printk(KERN_ERR "TD structure error TDindex=%hx\n", readw(®s->TDIdx[0])); - BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); - writew(TRDCSR_RUN, ®s->TDCSRClr); - netif_stop_queue(vptr->dev); + while (works < budget_left) { + struct rx_desc *rd = vptr->rx.ring + rd_curr; - /* FIXME: port over the pci_device_failed code and use it - here */ - } + if (!vptr->rx.info[rd_curr].skb) + break; - if (status & ISR_SRCI) { - struct mac_regs __iomem * regs = vptr->mac_regs; - int linked; + if (rd->rdesc0.len & OWNED_BY_NIC) + break; - if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - vptr->mii_status = check_connection_type(regs); + rmb(); - /* - * If it is a 3119, disable frame bursting in - * halfduplex mode and enable it in fullduplex - * mode - */ - if (vptr->rev_id < REV_ID_VT3216_A0) { - if (vptr->mii_status | VELOCITY_DUPLEX_FULL) - BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); - else - BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); - } - /* - * Only enable CD heart beat counter in 10HD mode - */ - if (!(vptr->mii_status & VELOCITY_DUPLEX_FULL) && (vptr->mii_status & VELOCITY_SPEED_10)) { - BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); - } else { - BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); - } - } /* - * Get link status from PHYSR0 + * Don't drop CE or RL error frame although RXOK is off */ - linked = readb(®s->PHYSR0) & PHYSR0_LINKGD; - - if (linked) { - vptr->mii_status &= ~VELOCITY_LINK_FAIL; - netif_carrier_on(vptr->dev); + if (rd->rdesc0.RSR & (RSR_RXOK | RSR_CE | RSR_RL)) { + if (velocity_receive_frame(vptr, rd_curr) < 0) + stats->rx_dropped++; } else { - vptr->mii_status |= VELOCITY_LINK_FAIL; - netif_carrier_off(vptr->dev); + if (rd->rdesc0.RSR & RSR_CRC) + stats->rx_crc_errors++; + if (rd->rdesc0.RSR & RSR_FAE) + stats->rx_frame_errors++; + + stats->rx_dropped++; } - velocity_print_link_status(vptr); - enable_flow_control_ability(vptr); + rd->size |= RX_INTEN; - /* - * Re-enable auto-polling because SRCI will disable - * auto-polling - */ + rd_curr++; + if (rd_curr >= vptr->options.numrx) + rd_curr = 0; + works++; + } - enable_mii_autopoll(regs); + vptr->rx.curr = rd_curr; - if (vptr->mii_status & VELOCITY_LINK_FAIL) - netif_stop_queue(vptr->dev); - else - netif_wake_queue(vptr->dev); + if ((works > 0) && (velocity_rx_refill(vptr) > 0)) + velocity_give_many_rx_descs(vptr); - }; - if (status & ISR_MIBFI) - velocity_update_hw_mibs(vptr); - if (status & ISR_LSTEI) - mac_rx_queue_wake(vptr->mac_regs); + VAR_USED(stats); + return works; } -/** - * velocity_free_tx_buf - free transmit buffer - * @vptr: velocity - * @tdinfo: buffer - * - * Release an transmit buffer. If the buffer was preallocated then - * recycle it, if not then unmap the buffer. - */ - -static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *tdinfo) +static int velocity_poll(struct napi_struct *napi, int budget) { - struct sk_buff *skb = tdinfo->skb; - int i; - int pktlen; + struct velocity_info *vptr = container_of(napi, + struct velocity_info, napi); + unsigned int rx_done; + unsigned long flags; + spin_lock_irqsave(&vptr->lock, flags); /* - * Don't unmap the pre-allocated tx_bufs + * Do rx and tx twice for performance (taken from the VIA + * out-of-tree driver). */ - if (tdinfo->skb_dma) { - - pktlen = (skb->len > ETH_ZLEN ? : ETH_ZLEN); - for (i = 0; i < tdinfo->nskb_dma; i++) { -#ifdef VELOCITY_ZERO_COPY_SUPPORT - pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], le16_to_cpu(td->tdesc1.len), PCI_DMA_TODEVICE); -#else - pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], pktlen, PCI_DMA_TODEVICE); -#endif - tdinfo->skb_dma[i] = 0; - } + rx_done = velocity_rx_srv(vptr, budget / 2); + velocity_tx_srv(vptr); + rx_done += velocity_rx_srv(vptr, budget - rx_done); + velocity_tx_srv(vptr); + + /* If budget not fully consumed, exit the polling mode */ + if (rx_done < budget) { + napi_complete(napi); + mac_enable_int(vptr->mac_regs); } - dev_kfree_skb_irq(skb); - tdinfo->skb = NULL; + spin_unlock_irqrestore(&vptr->lock, flags); + + return rx_done; } -static int velocity_init_rings(struct velocity_info *vptr, int mtu) +/** + * velocity_intr - interrupt callback + * @irq: interrupt number + * @dev_instance: interrupting device + * + * Called whenever an interrupt is generated by the velocity + * adapter IRQ line. We may not be the source of the interrupt + * and need to identify initially if we are, and if not exit as + * efficiently as possible. + */ +static irqreturn_t velocity_intr(int irq, void *dev_instance) { - int ret; + struct net_device *dev = dev_instance; + struct velocity_info *vptr = netdev_priv(dev); + u32 isr_status; - velocity_set_rxbufsize(vptr, mtu); + spin_lock(&vptr->lock); + isr_status = mac_read_isr(vptr->mac_regs); - ret = velocity_init_dma_rings(vptr); - if (ret < 0) - goto out; + /* Not us ? */ + if (isr_status == 0) { + spin_unlock(&vptr->lock); + return IRQ_NONE; + } - ret = velocity_init_rd_ring(vptr); - if (ret < 0) - goto err_free_dma_rings_0; + /* Ack the interrupt */ + mac_write_isr(vptr->mac_regs, isr_status); - ret = velocity_init_td_ring(vptr); - if (ret < 0) - goto err_free_rd_ring_1; -out: - return ret; + if (likely(napi_schedule_prep(&vptr->napi))) { + mac_disable_int(vptr->mac_regs); + __napi_schedule(&vptr->napi); + } -err_free_rd_ring_1: - velocity_free_rd_ring(vptr); -err_free_dma_rings_0: - velocity_free_dma_rings(vptr); - goto out; -} + if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) + velocity_error(vptr, isr_status); -static void velocity_free_rings(struct velocity_info *vptr) -{ - velocity_free_td_ring(vptr); - velocity_free_rd_ring(vptr); - velocity_free_dma_rings(vptr); + spin_unlock(&vptr->lock); + + return IRQ_HANDLED; } /** @@ -1905,7 +2223,6 @@ static void velocity_free_rings(struct velocity_info *vptr) * All the ring allocation and set up is done on open for this * adapter to minimise memory usage when inactive */ - static int velocity_open(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); @@ -1918,11 +2235,9 @@ static int velocity_open(struct net_device *dev) /* Ensure chip is running */ pci_set_power_state(vptr->pdev, PCI_D0); - velocity_give_many_rx_descs(vptr); - velocity_init_registers(vptr, VELOCITY_INIT_COLD); - ret = request_irq(vptr->pdev->irq, &velocity_intr, IRQF_SHARED, + ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED, dev->name, dev); if (ret < 0) { /* Power down the chip */ @@ -1931,14 +2246,35 @@ static int velocity_open(struct net_device *dev) goto out; } + velocity_give_many_rx_descs(vptr); + mac_enable_int(vptr->mac_regs); netif_start_queue(dev); + napi_enable(&vptr->napi); vptr->flags |= VELOCITY_FLAGS_OPENED; out: return ret; } /** + * velocity_shutdown - shut down the chip + * @vptr: velocity to deactivate + * + * Shuts down the internal operations of the velocity and + * disables interrupts, autopolling, transmit and receive + */ +static void velocity_shutdown(struct velocity_info *vptr) +{ + struct mac_regs __iomem *regs = vptr->mac_regs; + mac_disable_int(regs); + writel(CR0_STOP, ®s->CR0Set); + writew(0xFFFF, ®s->TDCSRClr); + writeb(0xFF, ®s->RDCSRClr); + safe_disable_mii_autopoll(regs); + mac_clear_isr(regs); +} + +/** * velocity_change_mtu - MTU change callback * @dev: network device * @new_mtu: desired MTU @@ -1947,7 +2283,6 @@ out: * this interface. It gets called on a change by the network layer. * Return zero for success or negative posix error code. */ - static int velocity_change_mtu(struct net_device *dev, int new_mtu) { struct velocity_info *vptr = netdev_priv(dev); @@ -2002,10 +2337,10 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; - velocity_give_many_rx_descs(vptr); - velocity_init_registers(vptr, VELOCITY_INIT_COLD); + velocity_give_many_rx_descs(vptr); + mac_enable_int(vptr->mac_regs); netif_start_queue(dev); @@ -2021,22 +2356,123 @@ out_0: } /** - * velocity_shutdown - shut down the chip - * @vptr: velocity to deactivate + * velocity_mii_ioctl - MII ioctl handler + * @dev: network device + * @ifr: the ifreq block for the ioctl + * @cmd: the command + * + * Process MII requests made via ioctl from the network layer. These + * are used by tools like kudzu to interrogate the link state of the + * hardware + */ +static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct velocity_info *vptr = netdev_priv(dev); + struct mac_regs __iomem *regs = vptr->mac_regs; + unsigned long flags; + struct mii_ioctl_data *miidata = if_mii(ifr); + int err; + + switch (cmd) { + case SIOCGMIIPHY: + miidata->phy_id = readb(®s->MIIADR) & 0x1f; + break; + case SIOCGMIIREG: + if (velocity_mii_read(vptr->mac_regs, miidata->reg_num & 0x1f, &(miidata->val_out)) < 0) + return -ETIMEDOUT; + break; + case SIOCSMIIREG: + spin_lock_irqsave(&vptr->lock, flags); + err = velocity_mii_write(vptr->mac_regs, miidata->reg_num & 0x1f, miidata->val_in); + spin_unlock_irqrestore(&vptr->lock, flags); + check_connection_type(vptr->mac_regs); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + +/** + * velocity_ioctl - ioctl entry point + * @dev: network device + * @rq: interface request ioctl + * @cmd: command code + * + * Called when the user issues an ioctl request to the network + * device in question. The velocity interface supports MII. + */ +static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct velocity_info *vptr = netdev_priv(dev); + int ret; + + /* If we are asked for information and the device is power + saving then we need to bring the device back up to talk to it */ + + if (!netif_running(dev)) + pci_set_power_state(vptr->pdev, PCI_D0); + + switch (cmd) { + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + case SIOCGMIIREG: /* Read MII PHY register. */ + case SIOCSMIIREG: /* Write to MII PHY register. */ + ret = velocity_mii_ioctl(dev, rq, cmd); + break; + + default: + ret = -EOPNOTSUPP; + } + if (!netif_running(dev)) + pci_set_power_state(vptr->pdev, PCI_D3hot); + + + return ret; +} + +/** + * velocity_get_status - statistics callback + * @dev: network device * - * Shuts down the internal operations of the velocity and - * disables interrupts, autopolling, transmit and receive + * Callback from the network layer to allow driver statistics + * to be resynchronized with hardware collected state. In the + * case of the velocity we need to pull the MIB counters from + * the hardware into the counters before letting the network + * layer display them. */ - -static void velocity_shutdown(struct velocity_info *vptr) +static struct net_device_stats *velocity_get_stats(struct net_device *dev) { - struct mac_regs __iomem * regs = vptr->mac_regs; - mac_disable_int(regs); - writel(CR0_STOP, ®s->CR0Set); - writew(0xFFFF, ®s->TDCSRClr); - writeb(0xFF, ®s->RDCSRClr); - safe_disable_mii_autopoll(regs); - mac_clear_isr(regs); + struct velocity_info *vptr = netdev_priv(dev); + + /* If the hardware is down, don't touch MII */ + if (!netif_running(dev)) + return &dev->stats; + + spin_lock_irq(&vptr->lock); + velocity_update_hw_mibs(vptr); + spin_unlock_irq(&vptr->lock); + + dev->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts]; + dev->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts]; + dev->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors]; + +// unsigned long rx_dropped; /* no space in linux buffers */ + dev->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions]; + /* detailed rx_errors: */ +// unsigned long rx_length_errors; +// unsigned long rx_over_errors; /* receiver ring buff overflow */ + dev->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE]; +// unsigned long rx_frame_errors; /* recv'd frame alignment error */ +// unsigned long rx_fifo_errors; /* recv'r fifo overrun */ +// unsigned long rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ +// unsigned long tx_fifo_errors; + + return &dev->stats; } /** @@ -2046,11 +2482,11 @@ static void velocity_shutdown(struct velocity_info *vptr) * Callback from the network layer when the velocity is being * deactivated by the network layer */ - static int velocity_close(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); + napi_disable(&vptr->napi); netif_stop_queue(dev); velocity_shutdown(vptr); @@ -2076,8 +2512,8 @@ static int velocity_close(struct net_device *dev) * Called by the networ layer to request a packet is queued to * the velocity. Returns zero on success. */ - -static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t velocity_xmit(struct sk_buff *skb, + struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); int qnum = 0; @@ -2085,22 +2521,22 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) struct velocity_td_info *tdinfo; unsigned long flags; int pktlen; - __le16 len; - int index; - + int index, prev; + int i = 0; if (skb_padto(skb, ETH_ZLEN)) goto out; - pktlen = max_t(unsigned int, skb->len, ETH_ZLEN); - - len = cpu_to_le16(pktlen); -#ifdef VELOCITY_ZERO_COPY_SUPPORT + /* The hardware can handle at most 7 memory segments, so merge + * the skb if there are more */ if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } -#endif + + pktlen = skb_shinfo(skb)->nr_frags == 0 ? + max_t(unsigned int, skb->len, ETH_ZLEN) : + skb_headlen(skb); spin_lock_irqsave(&vptr->lock, flags); @@ -2111,59 +2547,31 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) td_ptr->tdesc1.TCR = TCR0_TIC; td_ptr->td_buf[0].size &= ~TD_QUEUE; -#ifdef VELOCITY_ZERO_COPY_SUPPORT - if (skb_shinfo(skb)->nr_frags > 0) { - int nfrags = skb_shinfo(skb)->nr_frags; - tdinfo->skb = skb; - if (nfrags > 6) { - skb_copy_from_linear_data(skb, tdinfo->buf, skb->len); - tdinfo->skb_dma[0] = tdinfo->buf_dma; - td_ptr->tdesc0.len = len; - td_ptr->tx.buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); - td_ptr->tx.buf[0].pa_high = 0; - td_ptr->tx.buf[0].size = len; /* queue is 0 anyway */ - tdinfo->nskb_dma = 1; - } else { - int i = 0; - tdinfo->nskb_dma = 0; - tdinfo->skb_dma[i] = pci_map_single(vptr->pdev, skb->data, - skb_headlen(skb), PCI_DMA_TODEVICE); - - td_ptr->tdesc0.len = len; - - /* FIXME: support 48bit DMA later */ - td_ptr->tx.buf[i].pa_low = cpu_to_le32(tdinfo->skb_dma); - td_ptr->tx.buf[i].pa_high = 0; - td_ptr->tx.buf[i].size = cpu_to_le16(skb_headlen(skb)); - - for (i = 0; i < nfrags; i++) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - void *addr = (void *)page_address(frag->page) + frag->page_offset; + /* + * Map the linear network buffer into PCI space and + * add it to the transmit ring. + */ + tdinfo->skb = skb; + tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); + td_ptr->tdesc0.len = cpu_to_le16(pktlen); + td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); + td_ptr->td_buf[0].pa_high = 0; + td_ptr->td_buf[0].size = cpu_to_le16(pktlen); - tdinfo->skb_dma[i + 1] = pci_map_single(vptr->pdev, addr, frag->size, PCI_DMA_TODEVICE); + /* Handle fragments */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - td_ptr->tx.buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]); - td_ptr->tx.buf[i + 1].pa_high = 0; - td_ptr->tx.buf[i + 1].size = cpu_to_le16(frag->size); - } - tdinfo->nskb_dma = i - 1; - } + tdinfo->skb_dma[i + 1] = pci_map_page(vptr->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); - } else -#endif - { - /* - * Map the linear network buffer into PCI space and - * add it to the transmit ring. - */ - tdinfo->skb = skb; - tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); - td_ptr->tdesc0.len = len; - td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); - td_ptr->td_buf[0].pa_high = 0; - td_ptr->td_buf[0].size = len; - tdinfo->nskb_dma = 1; + td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]); + td_ptr->td_buf[i + 1].pa_high = 0; + td_ptr->td_buf[i + 1].size = cpu_to_le16(frag->size); } + tdinfo->nskb_dma = i + 1; + td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16; if (vptr->vlgrp && vlan_tx_tag_present(skb)) { @@ -2174,8 +2582,8 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) /* * Handle hardware checksum */ - if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM) - && (skb->ip_summed == CHECKSUM_PARTIAL)) { + if ((dev->features & NETIF_F_IP_CSUM) && + (skb->ip_summed == CHECKSUM_PARTIAL)) { const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) td_ptr->tdesc1.TCR |= TCR0_TCPCK; @@ -2183,805 +2591,540 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) td_ptr->tdesc1.TCR |= (TCR0_UDPCK); td_ptr->tdesc1.TCR |= TCR0_IPCK; } - { - int prev = index - 1; + prev = index - 1; + if (prev < 0) + prev = vptr->options.numtx - 1; + td_ptr->tdesc0.len |= OWNED_BY_NIC; + vptr->tx.used[qnum]++; + vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; - if (prev < 0) - prev = vptr->options.numtx - 1; - td_ptr->tdesc0.len |= OWNED_BY_NIC; - vptr->tx.used[qnum]++; - vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; + if (AVAIL_TD(vptr, qnum) < 1) + netif_stop_queue(dev); - if (AVAIL_TD(vptr, qnum) < 1) - netif_stop_queue(dev); + td_ptr = &(vptr->tx.rings[qnum][prev]); + td_ptr->td_buf[0].size |= TD_QUEUE; + mac_tx_queue_wake(vptr->mac_regs, qnum); - td_ptr = &(vptr->tx.rings[qnum][prev]); - td_ptr->td_buf[0].size |= TD_QUEUE; - mac_tx_queue_wake(vptr->mac_regs, qnum); - } dev->trans_start = jiffies; spin_unlock_irqrestore(&vptr->lock, flags); out: return NETDEV_TX_OK; } -/** - * velocity_intr - interrupt callback - * @irq: interrupt number - * @dev_instance: interrupting device - * - * Called whenever an interrupt is generated by the velocity - * adapter IRQ line. We may not be the source of the interrupt - * and need to identify initially if we are, and if not exit as - * efficiently as possible. - */ - -static irqreturn_t velocity_intr(int irq, void *dev_instance) -{ - struct net_device *dev = dev_instance; - struct velocity_info *vptr = netdev_priv(dev); - u32 isr_status; - int max_count = 0; - - - spin_lock(&vptr->lock); - isr_status = mac_read_isr(vptr->mac_regs); - - /* Not us ? */ - if (isr_status == 0) { - spin_unlock(&vptr->lock); - return IRQ_NONE; - } - - mac_disable_int(vptr->mac_regs); - - /* - * Keep processing the ISR until we have completed - * processing and the isr_status becomes zero - */ - - while (isr_status != 0) { - mac_write_isr(vptr->mac_regs, isr_status); - if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) - velocity_error(vptr, isr_status); - if (isr_status & (ISR_PRXI | ISR_PPRXI)) - max_count += velocity_rx_srv(vptr, isr_status); - if (isr_status & (ISR_PTXI | ISR_PPTXI)) - max_count += velocity_tx_srv(vptr, isr_status); - isr_status = mac_read_isr(vptr->mac_regs); - if (max_count > vptr->options.int_works) - { - printk(KERN_WARNING "%s: excessive work at interrupt.\n", - dev->name); - max_count = 0; - } - } - spin_unlock(&vptr->lock); - mac_enable_int(vptr->mac_regs); - return IRQ_HANDLED; - -} - - -/** - * 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); - -} - -/** - * velocity_get_status - statistics callback - * @dev: network device - * - * Callback from the network layer to allow driver statistics - * to be resynchronized with hardware collected state. In the - * case of the velocity we need to pull the MIB counters from - * the hardware into the counters before letting the network - * layer display them. - */ - -static struct net_device_stats *velocity_get_stats(struct net_device *dev) -{ - struct velocity_info *vptr = netdev_priv(dev); - - /* If the hardware is down, don't touch MII */ - if(!netif_running(dev)) - return &dev->stats; - - spin_lock_irq(&vptr->lock); - velocity_update_hw_mibs(vptr); - spin_unlock_irq(&vptr->lock); - - dev->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts]; - dev->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts]; - dev->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors]; - -// unsigned long rx_dropped; /* no space in linux buffers */ - dev->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions]; - /* detailed rx_errors: */ -// unsigned long rx_length_errors; -// unsigned long rx_over_errors; /* receiver ring buff overflow */ - dev->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE]; -// unsigned long rx_frame_errors; /* recv'd frame alignment error */ -// unsigned long rx_fifo_errors; /* recv'r fifo overrun */ -// unsigned long rx_missed_errors; /* receiver missed packet */ - - /* detailed tx_errors */ -// unsigned long tx_fifo_errors; - - return &dev->stats; -} - - -/** - * velocity_ioctl - ioctl entry point - * @dev: network device - * @rq: interface request ioctl - * @cmd: command code - * - * Called when the user issues an ioctl request to the network - * device in question. The velocity interface supports MII. - */ - -static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct velocity_info *vptr = netdev_priv(dev); - int ret; - - /* If we are asked for information and the device is power - saving then we need to bring the device back up to talk to it */ - - if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D0); - - switch (cmd) { - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCSMIIREG: /* Write to MII PHY register. */ - ret = velocity_mii_ioctl(dev, rq, cmd); - break; - - default: - ret = -EOPNOTSUPP; - } - if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D3hot); - - - return ret; -} - -/* - * Definition for our device driver. The PCI layer interface - * uses this to handle all our card discover and plugging - */ -static struct pci_driver velocity_driver = { - .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, -#endif +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_init_module - load time function + * velocity_init_info - init private data + * @pdev: PCI device + * @vptr: Velocity info + * @info: Board type * - * Called when the velocity module is loaded. The PCI driver - * is registered with the PCI layer, and in turn will call - * the probe functions for each velocity adapter installed - * in the system. + * Set up the initial velocity_info struct for the device that has been + * discovered. */ - -static int __init velocity_init_module(void) +static void __devinit velocity_init_info(struct pci_dev *pdev, + struct velocity_info *vptr, + const struct velocity_info_tbl *info) { - int ret; + memset(vptr, 0, sizeof(struct velocity_info)); - velocity_register_notifier(); - ret = pci_register_driver(&velocity_driver); - if (ret < 0) - velocity_unregister_notifier(); - return ret; + vptr->pdev = pdev; + vptr->chip_id = info->chip_id; + vptr->tx.numq = info->txqueue; + vptr->multicast_limit = MCAM_SIZE; + spin_lock_init(&vptr->lock); } /** - * velocity_cleanup - module unload + * velocity_get_pci_info - retrieve PCI info for device + * @vptr: velocity device + * @pdev: PCI device it matches * - * When the velocity hardware is unloaded this function is called. - * It will clean up the notifiers and the unregister the PCI - * driver interface for this hardware. This in turn cleans up - * all discovered interfaces before returning from the function + * Retrieve the PCI configuration space data that interests us from + * the kernel PCI layer */ - -static void __exit velocity_cleanup_module(void) +static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pci_dev *pdev) { - velocity_unregister_notifier(); - pci_unregister_driver(&velocity_driver); -} + vptr->rev_id = pdev->revision; -module_init(velocity_init_module); -module_exit(velocity_cleanup_module); + pci_set_master(pdev); + vptr->ioaddr = pci_resource_start(pdev, 0); + vptr->memaddr = pci_resource_start(pdev, 1); -/* - * MII access , media link mode setting functions - */ + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { + dev_err(&pdev->dev, + "region #0 is not an I/O resource, aborting.\n"); + return -EINVAL; + } + + if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { + dev_err(&pdev->dev, + "region #1 is an I/O resource, aborting.\n"); + return -EINVAL; + } + + if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { + dev_err(&pdev->dev, "region #1 is too small.\n"); + return -EINVAL; + } + vptr->pdev = pdev; + return 0; +} /** - * mii_init - set up MII - * @vptr: velocity adapter - * @mii_status: links tatus + * velocity_print_info - per driver data + * @vptr: velocity * - * Set up the PHY for the current link state. + * Print per driver data as the kernel driver finds Velocity + * hardware */ - -static void mii_init(struct velocity_info *vptr, u32 mii_status) +static void __devinit velocity_print_info(struct velocity_info *vptr) { - u16 BMCR; + struct net_device *dev = vptr->dev; - 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; + printk(KERN_INFO "%s: %s\n", dev->name, get_chip_name(vptr->chip_id)); + printk(KERN_INFO "%s: Ethernet Address: %pM\n", + dev->name, dev->dev_addr); +} - 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); - } +static u32 velocity_get_link(struct net_device *dev) +{ + struct velocity_info *vptr = netdev_priv(dev); + struct mac_regs __iomem *regs = vptr->mac_regs; + return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, ®s->PHYSR0) ? 1 : 0; } + /** - * safe_disable_mii_autopoll - autopoll off - * @regs: velocity registers + * velocity_found1 - set up discovered velocity card + * @pdev: PCI device + * @ent: PCI device table entry that matched * - * Turn off the autopoll and wait for it to disable on the chip + * Configure a discovered adapter from scratch. Return a negative + * errno error code on failure paths. */ - -static void safe_disable_mii_autopoll(struct mac_regs __iomem * regs) +static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_device_id *ent) { - u16 ww; + 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; - /* 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; + /* 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; } -} -/** - * 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. - */ + dev = alloc_etherdev(sizeof(struct velocity_info)); + if (!dev) { + dev_err(&pdev->dev, "allocate net device failed.\n"); + goto out; + } -static void enable_mii_autopoll(struct mac_regs __iomem * regs) -{ - int ii; + /* Chain it all together */ - writeb(0, &(regs->MIICR)); - writeb(MIIADR_SWMPL, ®s->MIIADR); + SET_NETDEV_DEV(dev, &pdev->dev); + vptr = netdev_priv(dev); - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; + + 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; } - writeb(MIICR_MAUTO, ®s->MIICR); + velocity_init_info(pdev, vptr, info); - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; + 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; + } -/** - * 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. - */ + regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); + if (regs == NULL) { + ret = -EIO; + goto err_release_res; + } -static int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data) -{ - u16 ww; + vptr->mac_regs = regs; - /* - * Disable MIICR_MAUTO, so that mii addr can be set normally - */ - safe_disable_mii_autopoll(regs); + mac_wol_reset(regs); - writeb(index, ®s->MIIADR); + dev->base_addr = vptr->ioaddr; - BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); + for (i = 0; i < 6; i++) + dev->dev_addr[i] = readb(®s->PAR[i]); - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(readb(®s->MIICR) & MIICR_RCMD)) - break; - } - *data = readw(®s->MIIDATA); + drv_string = dev_driver_string(&pdev->dev); - enable_mii_autopoll(regs); - if (ww == W_MAX_TIMEOUT) - return -ETIMEDOUT; - return 0; -} + velocity_get_options(&vptr->options, velocity_nics, drv_string); -/** - * 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. - */ + /* + * Mask out the options cannot be set to the chip + */ -static int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data) -{ - u16 ww; + vptr->options.flags &= info->flags; /* - * Disable MIICR_MAUTO, so that mii addr can be set normally + * Enable the chip specified capbilities */ - safe_disable_mii_autopoll(regs); - /* MII reg offset */ - writeb(mii_addr, ®s->MIIADR); - /* set MII data */ - writew(data, ®s->MIIDATA); + vptr->flags = vptr->options.flags | (info->flags & 0xFF000000UL); - /* turn on MIICR_WCMD */ - BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); + vptr->wol_opts = vptr->options.wol_opts; + vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - /* 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); + vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - if (ww == W_MAX_TIMEOUT) - return -ETIMEDOUT; - return 0; -} + dev->irq = pdev->irq; + dev->netdev_ops = &velocity_netdev_ops; + dev->ethtool_ops = &velocity_ethtool_ops; + netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT); -/** - * 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. - */ + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | + NETIF_F_HW_VLAN_RX | NETIF_F_IP_CSUM | NETIF_F_SG; -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) -{ - u32 status = 0; + ret = register_netdev(dev); + if (ret < 0) + goto err_iounmap; - 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; + if (!velocity_get_link(dev)) { + netif_carrier_off(dev); + vptr->mii_status |= VELOCITY_LINK_FAIL; } - vptr->mii_status = status; - return status; -} -/** - * mii_set_auto_on - autonegotiate on - * @vptr: velocity - * - * Enable autonegotation on this interface - */ + velocity_print_info(vptr); + pci_set_drvdata(pdev, dev); -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); -} + /* and leave the chip powered down */ + pci_set_power_state(pdev, PCI_D3hot); + velocity_nics++; +out: + return ret; -/* -static void mii_set_auto_off(struct velocity_info * vptr) -{ - MII_REG_BITS_OFF(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); +err_iounmap: + iounmap(regs); +err_release_res: + pci_release_regions(pdev); +err_disable: + pci_disable_device(pdev); +err_free_dev: + free_netdev(dev); + goto out; } -*/ + +#ifdef CONFIG_PM /** - * set_mii_flow_control - flow control setup - * @vptr: velocity interface + * wol_calc_crc - WOL CRC + * @pattern: data pattern + * @mask_pattern: mask * - * Set up the flow control on this interface according to - * the supplied user/eeprom options. + * Compute the wake on lan crc hashes for the packet header + * we are interested in. */ - -static void set_mii_flow_control(struct velocity_info *vptr) +static u16 wol_calc_crc(int size, u8 *pattern, u8 *mask_pattern) { - /*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; + u16 crc = 0xFFFF; + u8 mask; + int i, j; - 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; + for (i = 0; i < size; i++) { + mask = mask_pattern[i]; - 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; + /* Skip this loop if the mask equals to zero */ + if (mask == 0x00) + continue; - 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; + for (j = 0; j < 8; j++) { + if ((mask & 0x01) == 0) { + mask >>= 1; + continue; + } + mask >>= 1; + crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1); + } } + /* Finally, invert the result once to get the correct data */ + crc = ~crc; + return bitrev32(crc) >> 16; } /** - * velocity_set_media_mode - set media mode - * @mii_status: old MII link state + * velocity_set_wol - set up for wake on lan + * @vptr: velocity to set WOL status on * - * 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. + * Set a card up for wake on lan either by unicast or by + * ARP packet. + * + * FIXME: check static buffer is safe here */ - -static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status) +static int velocity_set_wol(struct velocity_info *vptr) { - u32 curr_status; - struct mac_regs __iomem * regs = vptr->mac_regs; + struct mac_regs __iomem *regs = vptr->mac_regs; + static u8 buf[256]; + int i; - vptr->mii_status = mii_check_media_mode(vptr->mac_regs); - curr_status = vptr->mii_status & (~VELOCITY_LINK_FAIL); + static u32 mask_pattern[2][4] = { + {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ + {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ + }; - /* Set mii link status */ - set_mii_flow_control(vptr); + writew(0xFFFF, ®s->WOLCRClr); + writeb(WOLCFG_SAB | WOLCFG_SAM, ®s->WOLCFGSet); + writew(WOLCR_MAGIC_EN, ®s->WOLCRSet); /* - 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 (vptr->wol_opts & VELOCITY_WOL_PHY) + writew((WOLCR_LINKON_EN|WOLCR_LINKOFF_EN), ®s->WOLCRSet); */ - if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) { - MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, vptr->mac_regs); - } + if (vptr->wol_opts & VELOCITY_WOL_UCAST) + writew(WOLCR_UNICAST_EN, ®s->WOLCRSet); - /* - * 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); + if (vptr->wol_opts & VELOCITY_WOL_ARP) { + struct arp_packet *arp = (struct arp_packet *) buf; + u16 crc; + memset(buf, 0, sizeof(struct arp_packet) + 7); + + for (i = 0; i < 4; i++) + writel(mask_pattern[0][i], ®s->ByteMask[0][i]); + + arp->type = htons(ETH_P_ARP); + arp->ar_op = htons(1); + + memcpy(arp->ar_tip, vptr->ip_addr, 4); - /* enable AUTO-NEGO mode */ - mii_set_auto_on(vptr); - } else { - u16 ANAR; - u8 CHIPGCR; + crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, + (u8 *) & mask_pattern[0][0]); - /* - * 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 - */ + writew(crc, ®s->PatternCRC[0]); + writew(WOLCR_ARP_EN, ®s->WOLCRSet); + } - /* set force MAC mode bit */ - BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); + BYTE_REG_BITS_ON(PWCFG_WOLTYPE, ®s->PWCFGSet); + BYTE_REG_BITS_ON(PWCFG_LEGACY_WOLEN, ®s->PWCFGSet); - CHIPGCR = readb(®s->CHIPGCR); - CHIPGCR &= ~CHIPGCR_FCGMII; + writew(0x0FFF, ®s->WOLSRClr); - 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); - } + if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) { + if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) + MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, vptr->mac_regs); 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); */ + if (vptr->mii_status & VELOCITY_SPEED_1000) + MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); + + BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); + + { + u8 GCR; + GCR = readb(®s->CHIPGCR); + GCR = (GCR & ~CHIPGCR_FCGMII) | CHIPGCR_FCFDX; + writeb(GCR, ®s->CHIPGCR); } - /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ - /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ - return VELOCITY_LINK_CHANGE; + + BYTE_REG_BITS_OFF(ISR_PWEI, ®s->ISR); + /* Turn on SWPTAG just before entering power mode */ + BYTE_REG_BITS_ON(STICKHW_SWPTAG, ®s->STICKHW); + /* Go to bed ..... */ + BYTE_REG_BITS_ON((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); + + return 0; } /** - * mii_check_media_mode - check media state - * @regs: velocity registers + * velocity_save_context - save registers + * @vptr: velocity + * @context: buffer for stored context * - * Check the current MII status and determine the link status - * accordingly + * Retrieve the current configuration from the velocity hardware + * and stash it in the context structure, for use by the context + * restore functions. This allows us to save things we need across + * power down states */ - -static u32 mii_check_media_mode(struct mac_regs __iomem * regs) +static void velocity_save_context(struct velocity_info *vptr, struct velocity_context *context) { - u32 status = 0; - u16 ANAR; + struct mac_regs __iomem *regs = vptr->mac_regs; + u16 i; + u8 __iomem *ptr = (u8 __iomem *)regs; - if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) - status |= VELOCITY_LINK_FAIL; + for (i = MAC_REG_PAR; i < MAC_REG_CR0_CLR; i += 4) + *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - 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); - } + for (i = MAC_REG_MAR; i < MAC_REG_TDCSR_CLR; i += 4) + *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - 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; - } - } + for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) + *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - return status; } -static u32 check_connection_type(struct mac_regs __iomem * regs) +static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) { - u32 status = 0; - u8 PHYSR0; - u16 ANAR; - PHYSR0 = readb(®s->PHYSR0); - - /* - if (!(PHYSR0 & PHYSR0_LINKGD)) - status|=VELOCITY_LINK_FAIL; - */ + struct net_device *dev = pci_get_drvdata(pdev); + struct velocity_info *vptr = netdev_priv(dev); + unsigned long flags; - if (PHYSR0 & PHYSR0_FDPX) - status |= VELOCITY_DUPLEX_FULL; + if (!netif_running(vptr->dev)) + return 0; - if (PHYSR0 & PHYSR0_SPDG) - status |= VELOCITY_SPEED_1000; - else if (PHYSR0 & PHYSR0_SPD10) - status |= VELOCITY_SPEED_10; - else - status |= VELOCITY_SPEED_100; + netif_device_detach(vptr->dev); - 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; - } + spin_lock_irqsave(&vptr->lock, flags); + pci_save_state(pdev); +#ifdef ETHTOOL_GWOL + if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { + velocity_get_ip(vptr); + velocity_save_context(vptr, &vptr->context); + velocity_shutdown(vptr); + velocity_set_wol(vptr); + pci_enable_wake(pdev, PCI_D3hot, 1); + pci_set_power_state(pdev, PCI_D3hot); + } else { + velocity_save_context(vptr, &vptr->context); + velocity_shutdown(vptr); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); } - - return status; +#else + pci_set_power_state(pdev, pci_choose_state(pdev, state)); +#endif + spin_unlock_irqrestore(&vptr->lock, flags); + return 0; } /** - * enable_flow_control_ability - flow control - * @vptr: veloity to configure + * velocity_restore_context - restore registers + * @vptr: velocity + * @context: buffer for stored context * - * Set up flow control according to the flow control options - * determined by the eeprom/configuration. + * Reload the register configuration from the velocity context + * created by velocity_save_context. */ - -static void enable_flow_control_ability(struct velocity_info *vptr) +static void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context) { + struct mac_regs __iomem *regs = vptr->mac_regs; + int i; + u8 __iomem *ptr = (u8 __iomem *)regs; - struct mac_regs __iomem * regs = vptr->mac_regs; + for (i = MAC_REG_PAR; i < MAC_REG_CR0_SET; i += 4) + writel(*((u32 *) (context->mac_reg + i)), ptr + i); - switch (vptr->options.flow_cntl) { + /* Just skip cr0 */ + for (i = MAC_REG_CR1_SET; i < MAC_REG_CR0_CLR; i++) { + /* Clear */ + writeb(~(*((u8 *) (context->mac_reg + i))), ptr + i + 4); + /* Set */ + writeb(*((u8 *) (context->mac_reg + i)), ptr + i); + } - 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); + for (i = MAC_REG_MAR; i < MAC_REG_IMR; i += 4) + writel(*((u32 *) (context->mac_reg + i)), ptr + i); - if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) - writel(CR0_FDXTFCEN, ®s->CR0Set); - else - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; + for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) + writel(*((u32 *) (context->mac_reg + i)), ptr + i); - case FLOW_CNTL_TX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Clr); - break; + for (i = MAC_REG_TDCSR_SET; i <= MAC_REG_RDCSR_SET; i++) + writeb(*((u8 *) (context->mac_reg + i)), ptr + i); +} - case FLOW_CNTL_RX: - writel(CR0_FDXRFCEN, ®s->CR0Set); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; +static int velocity_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct velocity_info *vptr = netdev_priv(dev); + unsigned long flags; + int i; - case FLOW_CNTL_TX_RX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Set); - break; + if (!netif_running(vptr->dev)) + return 0; - case FLOW_CNTL_DISABLE: - writel(CR0_FDXRFCEN, ®s->CR0Clr); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, 0, 0); + pci_restore_state(pdev); + + mac_wol_reset(vptr->mac_regs); + + spin_lock_irqsave(&vptr->lock, flags); + velocity_restore_context(vptr, &vptr->context); + velocity_init_registers(vptr, VELOCITY_INIT_WOL); + mac_disable_int(vptr->mac_regs); - default: - break; + velocity_tx_srv(vptr); + + for (i = 0; i < vptr->tx.numq; i++) { + if (vptr->tx.used[i]) + mac_tx_queue_wake(vptr->mac_regs, i); } + mac_enable_int(vptr->mac_regs); + spin_unlock_irqrestore(&vptr->lock, flags); + netif_device_attach(vptr->dev); + + return 0; } +#endif + +/* + * Definition for our device driver. The PCI layer interface + * uses this to handle all our card discover and plugging + */ +static struct pci_driver velocity_driver = { + .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, +#endif +}; /** @@ -2991,7 +3134,6 @@ static void enable_flow_control_ability(struct velocity_info *vptr) * Called before an ethtool operation. We need to make sure the * chip is out of D3 state before we poke at it. */ - static int velocity_ethtool_up(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); @@ -3007,7 +3149,6 @@ static int velocity_ethtool_up(struct net_device *dev) * Called after an ethtool operation. Restore the chip back to D3 * state if it isn't running. */ - static void velocity_ethtool_down(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); @@ -3018,7 +3159,7 @@ static void velocity_ethtool_down(struct net_device *dev) static int velocity_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct velocity_info *vptr = netdev_priv(dev); - struct mac_regs __iomem * regs = vptr->mac_regs; + struct mac_regs __iomem *regs = vptr->mac_regs; u32 status; status = check_connection_type(vptr->mac_regs); @@ -3064,431 +3205,264 @@ static int velocity_set_settings(struct net_device *dev, struct ethtool_cmd *cmd new_status |= ((cmd->speed == SPEED_10) ? VELOCITY_SPEED_10 : 0); new_status |= ((cmd->duplex == DUPLEX_FULL) ? VELOCITY_DUPLEX_FULL : 0); - if ((new_status & VELOCITY_AUTONEG_ENABLE) && (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) - ret = -EINVAL; - else - velocity_set_media_mode(vptr, new_status); - - return ret; -} - -static u32 velocity_get_link(struct net_device *dev) -{ - struct velocity_info *vptr = netdev_priv(dev); - struct mac_regs __iomem * regs = vptr->mac_regs; - return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, ®s->PHYSR0) ? 1 : 0; -} - -static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct velocity_info *vptr = netdev_priv(dev); - strcpy(info->driver, VELOCITY_NAME); - strcpy(info->version, VELOCITY_VERSION); - strcpy(info->bus_info, pci_name(vptr->pdev)); -} - -static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct velocity_info *vptr = netdev_priv(dev); - wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP; - wol->wolopts |= WAKE_MAGIC; - /* - if (vptr->wol_opts & VELOCITY_WOL_PHY) - wol.wolopts|=WAKE_PHY; - */ - if (vptr->wol_opts & VELOCITY_WOL_UCAST) - wol->wolopts |= WAKE_UCAST; - if (vptr->wol_opts & VELOCITY_WOL_ARP) - wol->wolopts |= WAKE_ARP; - memcpy(&wol->sopass, vptr->wol_passwd, 6); -} - -static int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - struct velocity_info *vptr = netdev_priv(dev); - - if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP))) - return -EFAULT; - vptr->wol_opts = VELOCITY_WOL_MAGIC; - - /* - if (wol.wolopts & WAKE_PHY) { - vptr->wol_opts|=VELOCITY_WOL_PHY; - vptr->flags |=VELOCITY_FLAGS_WOL_ENABLED; - } - */ - - if (wol->wolopts & WAKE_MAGIC) { - vptr->wol_opts |= VELOCITY_WOL_MAGIC; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - } - if (wol->wolopts & WAKE_UCAST) { - vptr->wol_opts |= VELOCITY_WOL_UCAST; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - } - if (wol->wolopts & WAKE_ARP) { - vptr->wol_opts |= VELOCITY_WOL_ARP; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - } - memcpy(vptr->wol_passwd, wol->sopass, 6); - return 0; -} - -static u32 velocity_get_msglevel(struct net_device *dev) -{ - return msglevel; -} - -static void velocity_set_msglevel(struct net_device *dev, u32 value) -{ - msglevel = value; -} - -static const struct ethtool_ops velocity_ethtool_ops = { - .get_settings = velocity_get_settings, - .set_settings = velocity_set_settings, - .get_drvinfo = velocity_get_drvinfo, - .get_wol = velocity_ethtool_get_wol, - .set_wol = velocity_ethtool_set_wol, - .get_msglevel = velocity_get_msglevel, - .set_msglevel = velocity_set_msglevel, - .get_link = velocity_get_link, - .begin = velocity_ethtool_up, - .complete = velocity_ethtool_down -}; - -/** - * velocity_mii_ioctl - MII ioctl handler - * @dev: network device - * @ifr: the ifreq block for the ioctl - * @cmd: the command - * - * Process MII requests made via ioctl from the network layer. These - * are used by tools like kudzu to interrogate the link state of the - * hardware - */ - -static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct velocity_info *vptr = netdev_priv(dev); - struct mac_regs __iomem * regs = vptr->mac_regs; - unsigned long flags; - struct mii_ioctl_data *miidata = if_mii(ifr); - int err; - - switch (cmd) { - case SIOCGMIIPHY: - miidata->phy_id = readb(®s->MIIADR) & 0x1f; - break; - case SIOCGMIIREG: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if(velocity_mii_read(vptr->mac_regs, miidata->reg_num & 0x1f, &(miidata->val_out)) < 0) - return -ETIMEDOUT; - break; - case SIOCSMIIREG: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - spin_lock_irqsave(&vptr->lock, flags); - err = velocity_mii_write(vptr->mac_regs, miidata->reg_num & 0x1f, miidata->val_in); - spin_unlock_irqrestore(&vptr->lock, flags); - check_connection_type(vptr->mac_regs); - if(err) - return err; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -#ifdef CONFIG_PM - -/** - * velocity_save_context - save registers - * @vptr: velocity - * @context: buffer for stored context - * - * Retrieve the current configuration from the velocity hardware - * and stash it in the context structure, for use by the context - * restore functions. This allows us to save things we need across - * power down states - */ - -static void velocity_save_context(struct velocity_info *vptr, struct velocity_context * context) -{ - struct mac_regs __iomem * regs = vptr->mac_regs; - u16 i; - u8 __iomem *ptr = (u8 __iomem *)regs; - - for (i = MAC_REG_PAR; i < MAC_REG_CR0_CLR; i += 4) - *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - - for (i = MAC_REG_MAR; i < MAC_REG_TDCSR_CLR; i += 4) - *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - - for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) - *((u32 *) (context->mac_reg + i)) = readl(ptr + i); - -} - -/** - * velocity_restore_context - restore registers - * @vptr: velocity - * @context: buffer for stored context - * - * Reload the register configuration from the velocity context - * created by velocity_save_context. - */ - -static void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context) -{ - struct mac_regs __iomem * regs = vptr->mac_regs; - int i; - u8 __iomem *ptr = (u8 __iomem *)regs; - - for (i = MAC_REG_PAR; i < MAC_REG_CR0_SET; i += 4) { - writel(*((u32 *) (context->mac_reg + i)), ptr + i); - } - - /* Just skip cr0 */ - for (i = MAC_REG_CR1_SET; i < MAC_REG_CR0_CLR; i++) { - /* Clear */ - writeb(~(*((u8 *) (context->mac_reg + i))), ptr + i + 4); - /* Set */ - writeb(*((u8 *) (context->mac_reg + i)), ptr + i); - } - - for (i = MAC_REG_MAR; i < MAC_REG_IMR; i += 4) { - writel(*((u32 *) (context->mac_reg + i)), ptr + i); - } - - for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) { - writel(*((u32 *) (context->mac_reg + i)), ptr + i); - } - - for (i = MAC_REG_TDCSR_SET; i <= MAC_REG_RDCSR_SET; i++) { - writeb(*((u8 *) (context->mac_reg + i)), ptr + i); - } - -} - -/** - * wol_calc_crc - WOL CRC - * @pattern: data pattern - * @mask_pattern: mask - * - * Compute the wake on lan crc hashes for the packet header - * we are interested in. - */ - -static u16 wol_calc_crc(int size, u8 * pattern, u8 *mask_pattern) -{ - u16 crc = 0xFFFF; - u8 mask; - int i, j; - - for (i = 0; i < size; i++) { - mask = mask_pattern[i]; - - /* Skip this loop if the mask equals to zero */ - if (mask == 0x00) - continue; - - for (j = 0; j < 8; j++) { - if ((mask & 0x01) == 0) { - mask >>= 1; - continue; - } - mask >>= 1; - crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1); - } - } - /* Finally, invert the result once to get the correct data */ - crc = ~crc; - return bitrev32(crc) >> 16; + if ((new_status & VELOCITY_AUTONEG_ENABLE) && (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) + ret = -EINVAL; + else + velocity_set_media_mode(vptr, new_status); + + return ret; } -/** - * velocity_set_wol - set up for wake on lan - * @vptr: velocity to set WOL status on - * - * Set a card up for wake on lan either by unicast or by - * ARP packet. - * - * FIXME: check static buffer is safe here - */ +static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct velocity_info *vptr = netdev_priv(dev); + strcpy(info->driver, VELOCITY_NAME); + strcpy(info->version, VELOCITY_VERSION); + strcpy(info->bus_info, pci_name(vptr->pdev)); +} -static int velocity_set_wol(struct velocity_info *vptr) +static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct mac_regs __iomem * regs = vptr->mac_regs; - static u8 buf[256]; - int i; + struct velocity_info *vptr = netdev_priv(dev); + wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP; + wol->wolopts |= WAKE_MAGIC; + /* + if (vptr->wol_opts & VELOCITY_WOL_PHY) + wol.wolopts|=WAKE_PHY; + */ + if (vptr->wol_opts & VELOCITY_WOL_UCAST) + wol->wolopts |= WAKE_UCAST; + if (vptr->wol_opts & VELOCITY_WOL_ARP) + wol->wolopts |= WAKE_ARP; + memcpy(&wol->sopass, vptr->wol_passwd, 6); +} - static u32 mask_pattern[2][4] = { - {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ - {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ - }; +static int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct velocity_info *vptr = netdev_priv(dev); - writew(0xFFFF, ®s->WOLCRClr); - writeb(WOLCFG_SAB | WOLCFG_SAM, ®s->WOLCFGSet); - writew(WOLCR_MAGIC_EN, ®s->WOLCRSet); + if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP))) + return -EFAULT; + vptr->wol_opts = VELOCITY_WOL_MAGIC; /* - if (vptr->wol_opts & VELOCITY_WOL_PHY) - writew((WOLCR_LINKON_EN|WOLCR_LINKOFF_EN), ®s->WOLCRSet); + if (wol.wolopts & WAKE_PHY) { + vptr->wol_opts|=VELOCITY_WOL_PHY; + vptr->flags |=VELOCITY_FLAGS_WOL_ENABLED; + } */ - if (vptr->wol_opts & VELOCITY_WOL_UCAST) { - writew(WOLCR_UNICAST_EN, ®s->WOLCRSet); + if (wol->wolopts & WAKE_MAGIC) { + vptr->wol_opts |= VELOCITY_WOL_MAGIC; + vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; } - - if (vptr->wol_opts & VELOCITY_WOL_ARP) { - struct arp_packet *arp = (struct arp_packet *) buf; - u16 crc; - memset(buf, 0, sizeof(struct arp_packet) + 7); - - for (i = 0; i < 4; i++) - writel(mask_pattern[0][i], ®s->ByteMask[0][i]); - - arp->type = htons(ETH_P_ARP); - arp->ar_op = htons(1); - - memcpy(arp->ar_tip, vptr->ip_addr, 4); - - crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, - (u8 *) & mask_pattern[0][0]); - - writew(crc, ®s->PatternCRC[0]); - writew(WOLCR_ARP_EN, ®s->WOLCRSet); + if (wol->wolopts & WAKE_UCAST) { + vptr->wol_opts |= VELOCITY_WOL_UCAST; + vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; + } + if (wol->wolopts & WAKE_ARP) { + vptr->wol_opts |= VELOCITY_WOL_ARP; + vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; } + memcpy(vptr->wol_passwd, wol->sopass, 6); + return 0; +} - BYTE_REG_BITS_ON(PWCFG_WOLTYPE, ®s->PWCFGSet); - BYTE_REG_BITS_ON(PWCFG_LEGACY_WOLEN, ®s->PWCFGSet); +static u32 velocity_get_msglevel(struct net_device *dev) +{ + return msglevel; +} - writew(0x0FFF, ®s->WOLSRClr); +static void velocity_set_msglevel(struct net_device *dev, u32 value) +{ + msglevel = value; +} - if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) { - if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) - MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, vptr->mac_regs); +static int get_pending_timer_val(int val) +{ + int mult_bits = val >> 6; + int mult = 1; - MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000, MII_REG_G1000CR, vptr->mac_regs); + switch (mult_bits) + { + case 1: + mult = 4; break; + case 2: + mult = 16; break; + case 3: + mult = 64; break; + case 0: + default: + break; } - if (vptr->mii_status & VELOCITY_SPEED_1000) - MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); + return (val & 0x3f) * mult; +} - BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); +static void set_pending_timer_val(int *val, u32 us) +{ + u8 mult = 0; + u8 shift = 0; - { - u8 GCR; - GCR = readb(®s->CHIPGCR); - GCR = (GCR & ~CHIPGCR_FCGMII) | CHIPGCR_FCFDX; - writeb(GCR, ®s->CHIPGCR); + if (us >= 0x3f) { + mult = 1; /* mult with 4 */ + shift = 2; + } + if (us >= 0x3f * 4) { + mult = 2; /* mult with 16 */ + shift = 4; + } + if (us >= 0x3f * 16) { + mult = 3; /* mult with 64 */ + shift = 6; } - BYTE_REG_BITS_OFF(ISR_PWEI, ®s->ISR); - /* Turn on SWPTAG just before entering power mode */ - BYTE_REG_BITS_ON(STICKHW_SWPTAG, ®s->STICKHW); - /* Go to bed ..... */ - BYTE_REG_BITS_ON((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); - - return 0; + *val = (mult << 6) | ((us >> shift) & 0x3f); } -static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) + +static int velocity_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) { - struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = netdev_priv(dev); - unsigned long flags; - if(!netif_running(vptr->dev)) - return 0; + ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup; + ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup; - netif_device_detach(vptr->dev); + ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer); + ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer); - spin_lock_irqsave(&vptr->lock, flags); - pci_save_state(pdev); -#ifdef ETHTOOL_GWOL - if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { - velocity_get_ip(vptr); - velocity_save_context(vptr, &vptr->context); - velocity_shutdown(vptr); - velocity_set_wol(vptr); - pci_enable_wake(pdev, PCI_D3hot, 1); - pci_set_power_state(pdev, PCI_D3hot); - } else { - velocity_save_context(vptr, &vptr->context); - velocity_shutdown(vptr); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - } -#else - pci_set_power_state(pdev, pci_choose_state(pdev, state)); -#endif - spin_unlock_irqrestore(&vptr->lock, flags); return 0; } -static int velocity_resume(struct pci_dev *pdev) +static int velocity_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) { - struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = netdev_priv(dev); + int max_us = 0x3f * 64; unsigned long flags; - int i; - if(!netif_running(vptr->dev)) - return 0; + /* 6 bits of */ + if (ecmd->tx_coalesce_usecs > max_us) + return -EINVAL; + if (ecmd->rx_coalesce_usecs > max_us) + return -EINVAL; - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, 0, 0); - pci_restore_state(pdev); + if (ecmd->tx_max_coalesced_frames > 0xff) + return -EINVAL; + if (ecmd->rx_max_coalesced_frames > 0xff) + return -EINVAL; - mac_wol_reset(vptr->mac_regs); + vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames; + vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames; + + set_pending_timer_val(&vptr->options.rxqueue_timer, + ecmd->rx_coalesce_usecs); + set_pending_timer_val(&vptr->options.txqueue_timer, + ecmd->tx_coalesce_usecs); + /* Setup the interrupt suppression and queue timers */ spin_lock_irqsave(&vptr->lock, flags); - velocity_restore_context(vptr, &vptr->context); - velocity_init_registers(vptr, VELOCITY_INIT_WOL); mac_disable_int(vptr->mac_regs); + setup_adaptive_interrupts(vptr); + setup_queue_timers(vptr); - velocity_tx_srv(vptr, 0); - - for (i = 0; i < vptr->tx.numq; i++) { - if (vptr->tx.used[i]) { - mac_tx_queue_wake(vptr->mac_regs, i); - } - } - + mac_write_int_mask(vptr->int_mask, vptr->mac_regs); + mac_clear_isr(vptr->mac_regs); mac_enable_int(vptr->mac_regs); spin_unlock_irqrestore(&vptr->lock, flags); - netif_device_attach(vptr->dev); return 0; } -#ifdef CONFIG_INET +static const struct ethtool_ops velocity_ethtool_ops = { + .get_settings = velocity_get_settings, + .set_settings = velocity_set_settings, + .get_drvinfo = velocity_get_drvinfo, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .get_wol = velocity_ethtool_get_wol, + .set_wol = velocity_ethtool_set_wol, + .get_msglevel = velocity_get_msglevel, + .set_msglevel = velocity_set_msglevel, + .set_sg = ethtool_op_set_sg, + .get_link = velocity_get_link, + .get_coalesce = velocity_get_coalesce, + .set_coalesce = velocity_set_coalesce, + .begin = velocity_ethtool_up, + .complete = velocity_ethtool_down +}; +#ifdef CONFIG_PM +#ifdef CONFIG_INET static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) { struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; struct net_device *dev = ifa->ifa_dev->dev; - struct velocity_info *vptr; - unsigned long flags; - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - - spin_lock_irqsave(&velocity_dev_list_lock, flags); - list_for_each_entry(vptr, &velocity_dev_list, list) { - if (vptr->dev == dev) { - velocity_get_ip(vptr); - break; - } - } - spin_unlock_irqrestore(&velocity_dev_list_lock, flags); + if (dev_net(dev) == &init_net && + dev->netdev_ops == &velocity_netdev_ops) + velocity_get_ip(netdev_priv(dev)); return NOTIFY_DONE; } +#endif /* CONFIG_INET */ +#endif /* CONFIG_PM */ -#endif -#endif +#if defined(CONFIG_PM) && defined(CONFIG_INET) +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 /* defined(CONFIG_PM) && defined(CONFIG_INET) */ + +/** + * velocity_init_module - load time function + * + * Called when the velocity module is loaded. The PCI driver + * is registered with the PCI layer, and in turn will call + * the probe functions for each velocity adapter installed + * in the system. + */ +static int __init velocity_init_module(void) +{ + int ret; + + velocity_register_notifier(); + ret = pci_register_driver(&velocity_driver); + if (ret < 0) + velocity_unregister_notifier(); + return ret; +} + +/** + * velocity_cleanup - module unload + * + * When the velocity hardware is unloaded this function is called. + * It will clean up the notifiers and the unregister the PCI + * driver interface for this hardware. This in turn cleans up + * all discovered interfaces before returning from the function + */ +static void __exit velocity_cleanup_module(void) +{ + velocity_unregister_notifier(); + pci_unregister_driver(&velocity_driver); +} + +module_init(velocity_init_module); +module_exit(velocity_cleanup_module);