162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This code is derived from the VIA reference driver (copyright message
462306a36Sopenharmony_ci * below) provided to Red Hat by VIA Networking Technologies, Inc. for
562306a36Sopenharmony_ci * addition to the Linux kernel.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * The code has been merged into one source file, cleaned up to follow
862306a36Sopenharmony_ci * Linux coding style,  ported to the Linux 2.6 kernel tree and cleaned
962306a36Sopenharmony_ci * for 64bit hardware platforms.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * TODO
1262306a36Sopenharmony_ci *	rx_copybreak/alignment
1362306a36Sopenharmony_ci *	More testing
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk>
1662306a36Sopenharmony_ci * Additional fixes and clean up: Francois Romieu
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * This source has not been verified for use in safety critical systems.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Please direct queries about the revamped driver to the linux-kernel
2162306a36Sopenharmony_ci * list not VIA.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Original code:
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
2662306a36Sopenharmony_ci * All rights reserved.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * Author: Chuang Liang-Shing, AJ Jiang
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Date: Jan 24, 2003
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * MODULE_LICENSE("GPL");
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/module.h>
3862306a36Sopenharmony_ci#include <linux/types.h>
3962306a36Sopenharmony_ci#include <linux/bitops.h>
4062306a36Sopenharmony_ci#include <linux/init.h>
4162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4262306a36Sopenharmony_ci#include <linux/mm.h>
4362306a36Sopenharmony_ci#include <linux/errno.h>
4462306a36Sopenharmony_ci#include <linux/ioport.h>
4562306a36Sopenharmony_ci#include <linux/pci.h>
4662306a36Sopenharmony_ci#include <linux/kernel.h>
4762306a36Sopenharmony_ci#include <linux/netdevice.h>
4862306a36Sopenharmony_ci#include <linux/etherdevice.h>
4962306a36Sopenharmony_ci#include <linux/skbuff.h>
5062306a36Sopenharmony_ci#include <linux/delay.h>
5162306a36Sopenharmony_ci#include <linux/timer.h>
5262306a36Sopenharmony_ci#include <linux/slab.h>
5362306a36Sopenharmony_ci#include <linux/interrupt.h>
5462306a36Sopenharmony_ci#include <linux/string.h>
5562306a36Sopenharmony_ci#include <linux/wait.h>
5662306a36Sopenharmony_ci#include <linux/io.h>
5762306a36Sopenharmony_ci#include <linux/if.h>
5862306a36Sopenharmony_ci#include <linux/uaccess.h>
5962306a36Sopenharmony_ci#include <linux/proc_fs.h>
6062306a36Sopenharmony_ci#include <linux/of.h>
6162306a36Sopenharmony_ci#include <linux/of_address.h>
6262306a36Sopenharmony_ci#include <linux/of_irq.h>
6362306a36Sopenharmony_ci#include <linux/inetdevice.h>
6462306a36Sopenharmony_ci#include <linux/platform_device.h>
6562306a36Sopenharmony_ci#include <linux/reboot.h>
6662306a36Sopenharmony_ci#include <linux/ethtool.h>
6762306a36Sopenharmony_ci#include <linux/mii.h>
6862306a36Sopenharmony_ci#include <linux/in.h>
6962306a36Sopenharmony_ci#include <linux/if_arp.h>
7062306a36Sopenharmony_ci#include <linux/if_vlan.h>
7162306a36Sopenharmony_ci#include <linux/ip.h>
7262306a36Sopenharmony_ci#include <linux/tcp.h>
7362306a36Sopenharmony_ci#include <linux/udp.h>
7462306a36Sopenharmony_ci#include <linux/crc-ccitt.h>
7562306a36Sopenharmony_ci#include <linux/crc32.h>
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#include "via-velocity.h"
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cienum velocity_bus_type {
8062306a36Sopenharmony_ci	BUS_PCI,
8162306a36Sopenharmony_ci	BUS_PLATFORM,
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int velocity_nics;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void velocity_set_power_state(struct velocity_info *vptr, char state)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	void *addr = vptr->mac_regs;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (vptr->pdev)
9162306a36Sopenharmony_ci		pci_set_power_state(vptr->pdev, state);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		writeb(state, addr + 0x154);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci *	mac_get_cam_mask	-	Read a CAM mask
9862306a36Sopenharmony_ci *	@regs: register block for this velocity
9962306a36Sopenharmony_ci *	@mask: buffer to store mask
10062306a36Sopenharmony_ci *
10162306a36Sopenharmony_ci *	Fetch the mask bits of the selected CAM and store them into the
10262306a36Sopenharmony_ci *	provided mask buffer.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cistatic void mac_get_cam_mask(struct mac_regs __iomem *regs, u8 *mask)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Select CAM mask */
10962306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* read mask */
11462306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
11562306a36Sopenharmony_ci		*mask++ = readb(&(regs->MARCAM[i]));
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* disable CAMEN */
11862306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Select mar */
12162306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci *	mac_set_cam_mask	-	Set a CAM mask
12662306a36Sopenharmony_ci *	@regs: register block for this velocity
12762306a36Sopenharmony_ci *	@mask: CAM mask to load
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci *	Store a new mask into a CAM
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic void mac_set_cam_mask(struct mac_regs __iomem *regs, u8 *mask)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int i;
13462306a36Sopenharmony_ci	/* Select CAM mask */
13562306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	writeb(CAMADDR_CAMEN, &regs->CAMADDR);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
14062306a36Sopenharmony_ci		writeb(*mask++, &(regs->MARCAM[i]));
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* disable CAMEN */
14362306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Select mar */
14662306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void mac_set_vlan_cam_mask(struct mac_regs __iomem *regs, u8 *mask)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int i;
15262306a36Sopenharmony_ci	/* Select CAM mask */
15362306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, &regs->CAMADDR);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
15862306a36Sopenharmony_ci		writeb(*mask++, &(regs->MARCAM[i]));
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* disable CAMEN */
16162306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Select mar */
16462306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/**
16862306a36Sopenharmony_ci *	mac_set_cam	-	set CAM data
16962306a36Sopenharmony_ci *	@regs: register block of this velocity
17062306a36Sopenharmony_ci *	@idx: Cam index
17162306a36Sopenharmony_ci *	@addr: 2 or 6 bytes of CAM data
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci *	Load an address or vlan tag into a CAM
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_cistatic void mac_set_cam(struct mac_regs __iomem *regs, int idx, const u8 *addr)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	int i;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Select CAM mask */
18062306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	idx &= (64 - 1);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	writeb(CAMADDR_CAMEN | idx, &regs->CAMADDR);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
18762306a36Sopenharmony_ci		writeb(*addr++, &(regs->MARCAM[i]));
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	BYTE_REG_BITS_ON(CAMCR_CAMWR, &regs->CAMCR);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	udelay(10);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Select mar */
19662306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void mac_set_vlan_cam(struct mac_regs __iomem *regs, int idx,
20062306a36Sopenharmony_ci			     const u8 *addr)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Select CAM mask */
20462306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	idx &= (64 - 1);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, &regs->CAMADDR);
20962306a36Sopenharmony_ci	writew(*((u16 *) addr), &regs->MARCAM[0]);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	BYTE_REG_BITS_ON(CAMCR_CAMWR, &regs->CAMCR);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	udelay(10);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	writeb(0, &regs->CAMADDR);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Select mar */
21862306a36Sopenharmony_ci	BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, &regs->CAMCR);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/**
22362306a36Sopenharmony_ci *	mac_wol_reset	-	reset WOL after exiting low power
22462306a36Sopenharmony_ci *	@regs: register block of this velocity
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci *	Called after we drop out of wake on lan mode in order to
22762306a36Sopenharmony_ci *	reset the Wake on lan features. This function doesn't restore
22862306a36Sopenharmony_ci *	the rest of the logic from the result of sleep/wakeup
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cistatic void mac_wol_reset(struct mac_regs __iomem *regs)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Turn off SWPTAG right after leaving power mode */
23462306a36Sopenharmony_ci	BYTE_REG_BITS_OFF(STICKHW_SWPTAG, &regs->STICKHW);
23562306a36Sopenharmony_ci	/* clear sticky bits */
23662306a36Sopenharmony_ci	BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), &regs->STICKHW);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, &regs->CHIPGCR);
23962306a36Sopenharmony_ci	BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, &regs->CHIPGCR);
24062306a36Sopenharmony_ci	/* disable force PME-enable */
24162306a36Sopenharmony_ci	writeb(WOLCFG_PMEOVR, &regs->WOLCFGClr);
24262306a36Sopenharmony_ci	/* disable power-event config bit */
24362306a36Sopenharmony_ci	writew(0xFFFF, &regs->WOLCRClr);
24462306a36Sopenharmony_ci	/* clear power status */
24562306a36Sopenharmony_ci	writew(0xFFFF, &regs->WOLSRClr);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/*
25162306a36Sopenharmony_ci    Define module options
25262306a36Sopenharmony_ci*/
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciMODULE_AUTHOR("VIA Networking Technologies, Inc.");
25562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
25662306a36Sopenharmony_ciMODULE_DESCRIPTION("VIA Networking Velocity Family Gigabit Ethernet Adapter Driver");
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci#define VELOCITY_PARAM(N, D) \
25962306a36Sopenharmony_ci	static int N[MAX_UNITS] = OPTION_DEFAULT;\
26062306a36Sopenharmony_ci	module_param_array(N, int, NULL, 0); \
26162306a36Sopenharmony_ci	MODULE_PARM_DESC(N, D);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci#define RX_DESC_MIN     64
26462306a36Sopenharmony_ci#define RX_DESC_MAX     255
26562306a36Sopenharmony_ci#define RX_DESC_DEF     64
26662306a36Sopenharmony_ciVELOCITY_PARAM(RxDescriptors, "Number of receive descriptors");
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci#define TX_DESC_MIN     16
26962306a36Sopenharmony_ci#define TX_DESC_MAX     256
27062306a36Sopenharmony_ci#define TX_DESC_DEF     64
27162306a36Sopenharmony_ciVELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors");
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#define RX_THRESH_MIN   0
27462306a36Sopenharmony_ci#define RX_THRESH_MAX   3
27562306a36Sopenharmony_ci#define RX_THRESH_DEF   0
27662306a36Sopenharmony_ci/* rx_thresh[] is used for controlling the receive fifo threshold.
27762306a36Sopenharmony_ci   0: indicate the rxfifo threshold is 128 bytes.
27862306a36Sopenharmony_ci   1: indicate the rxfifo threshold is 512 bytes.
27962306a36Sopenharmony_ci   2: indicate the rxfifo threshold is 1024 bytes.
28062306a36Sopenharmony_ci   3: indicate the rxfifo threshold is store & forward.
28162306a36Sopenharmony_ci*/
28262306a36Sopenharmony_ciVELOCITY_PARAM(rx_thresh, "Receive fifo threshold");
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci#define DMA_LENGTH_MIN  0
28562306a36Sopenharmony_ci#define DMA_LENGTH_MAX  7
28662306a36Sopenharmony_ci#define DMA_LENGTH_DEF  6
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/* DMA_length[] is used for controlling the DMA length
28962306a36Sopenharmony_ci   0: 8 DWORDs
29062306a36Sopenharmony_ci   1: 16 DWORDs
29162306a36Sopenharmony_ci   2: 32 DWORDs
29262306a36Sopenharmony_ci   3: 64 DWORDs
29362306a36Sopenharmony_ci   4: 128 DWORDs
29462306a36Sopenharmony_ci   5: 256 DWORDs
29562306a36Sopenharmony_ci   6: SF(flush till emply)
29662306a36Sopenharmony_ci   7: SF(flush till emply)
29762306a36Sopenharmony_ci*/
29862306a36Sopenharmony_ciVELOCITY_PARAM(DMA_length, "DMA length");
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci#define IP_ALIG_DEF     0
30162306a36Sopenharmony_ci/* IP_byte_align[] is used for IP header DWORD byte aligned
30262306a36Sopenharmony_ci   0: indicate the IP header won't be DWORD byte aligned.(Default) .
30362306a36Sopenharmony_ci   1: indicate the IP header will be DWORD byte aligned.
30462306a36Sopenharmony_ci      In some environment, the IP header should be DWORD byte aligned,
30562306a36Sopenharmony_ci      or the packet will be droped when we receive it. (eg: IPVS)
30662306a36Sopenharmony_ci*/
30762306a36Sopenharmony_ciVELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci#define FLOW_CNTL_DEF   1
31062306a36Sopenharmony_ci#define FLOW_CNTL_MIN   1
31162306a36Sopenharmony_ci#define FLOW_CNTL_MAX   5
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* flow_control[] is used for setting the flow control ability of NIC.
31462306a36Sopenharmony_ci   1: hardware deafult - AUTO (default). Use Hardware default value in ANAR.
31562306a36Sopenharmony_ci   2: enable TX flow control.
31662306a36Sopenharmony_ci   3: enable RX flow control.
31762306a36Sopenharmony_ci   4: enable RX/TX flow control.
31862306a36Sopenharmony_ci   5: disable
31962306a36Sopenharmony_ci*/
32062306a36Sopenharmony_ciVELOCITY_PARAM(flow_control, "Enable flow control ability");
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci#define MED_LNK_DEF 0
32362306a36Sopenharmony_ci#define MED_LNK_MIN 0
32462306a36Sopenharmony_ci#define MED_LNK_MAX 5
32562306a36Sopenharmony_ci/* speed_duplex[] is used for setting the speed and duplex mode of NIC.
32662306a36Sopenharmony_ci   0: indicate autonegotiation for both speed and duplex mode
32762306a36Sopenharmony_ci   1: indicate 100Mbps half duplex mode
32862306a36Sopenharmony_ci   2: indicate 100Mbps full duplex mode
32962306a36Sopenharmony_ci   3: indicate 10Mbps half duplex mode
33062306a36Sopenharmony_ci   4: indicate 10Mbps full duplex mode
33162306a36Sopenharmony_ci   5: indicate 1000Mbps full duplex mode
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci   Note:
33462306a36Sopenharmony_ci   if EEPROM have been set to the force mode, this option is ignored
33562306a36Sopenharmony_ci   by driver.
33662306a36Sopenharmony_ci*/
33762306a36Sopenharmony_ciVELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode");
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#define WOL_OPT_DEF     0
34062306a36Sopenharmony_ci#define WOL_OPT_MIN     0
34162306a36Sopenharmony_ci#define WOL_OPT_MAX     7
34262306a36Sopenharmony_ci/* wol_opts[] is used for controlling wake on lan behavior.
34362306a36Sopenharmony_ci   0: Wake up if recevied a magic packet. (Default)
34462306a36Sopenharmony_ci   1: Wake up if link status is on/off.
34562306a36Sopenharmony_ci   2: Wake up if recevied an arp packet.
34662306a36Sopenharmony_ci   4: Wake up if recevied any unicast packet.
34762306a36Sopenharmony_ci   Those value can be sumed up to support more than one option.
34862306a36Sopenharmony_ci*/
34962306a36Sopenharmony_ciVELOCITY_PARAM(wol_opts, "Wake On Lan options");
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int rx_copybreak = 200;
35262306a36Sopenharmony_cimodule_param(rx_copybreak, int, 0644);
35362306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*
35662306a36Sopenharmony_ci *	Internal board variants. At the moment we have only one
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic struct velocity_info_tbl chip_info_table[] = {
35962306a36Sopenharmony_ci	{CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL},
36062306a36Sopenharmony_ci	{ }
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/*
36462306a36Sopenharmony_ci *	Describe the PCI device identifiers that we support in this
36562306a36Sopenharmony_ci *	device driver. Used for hotplug autoloading.
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic const struct pci_device_id velocity_pci_id_table[] = {
36962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) },
37062306a36Sopenharmony_ci	{ }
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, velocity_pci_id_table);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci *	Describe the OF device identifiers that we support in this
37762306a36Sopenharmony_ci *	device driver. Used for devicetree nodes.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic const struct of_device_id velocity_of_ids[] = {
38062306a36Sopenharmony_ci	{ .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] },
38162306a36Sopenharmony_ci	{ /* Sentinel */ },
38262306a36Sopenharmony_ci};
38362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, velocity_of_ids);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/**
38662306a36Sopenharmony_ci *	get_chip_name	- 	identifier to name
38762306a36Sopenharmony_ci *	@chip_id: chip identifier
38862306a36Sopenharmony_ci *
38962306a36Sopenharmony_ci *	Given a chip identifier return a suitable description. Returns
39062306a36Sopenharmony_ci *	a pointer a static string valid while the driver is loaded.
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_cistatic const char *get_chip_name(enum chip_type chip_id)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	int i;
39562306a36Sopenharmony_ci	for (i = 0; chip_info_table[i].name != NULL; i++)
39662306a36Sopenharmony_ci		if (chip_info_table[i].chip_id == chip_id)
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci	return chip_info_table[i].name;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/**
40262306a36Sopenharmony_ci *	velocity_set_int_opt	-	parser for integer options
40362306a36Sopenharmony_ci *	@opt: pointer to option value
40462306a36Sopenharmony_ci *	@val: value the user requested (or -1 for default)
40562306a36Sopenharmony_ci *	@min: lowest value allowed
40662306a36Sopenharmony_ci *	@max: highest value allowed
40762306a36Sopenharmony_ci *	@def: default value
40862306a36Sopenharmony_ci *	@name: property name
40962306a36Sopenharmony_ci *
41062306a36Sopenharmony_ci *	Set an integer property in the module options. This function does
41162306a36Sopenharmony_ci *	all the verification and checking as well as reporting so that
41262306a36Sopenharmony_ci *	we don't duplicate code for each option.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic void velocity_set_int_opt(int *opt, int val, int min, int max, int def,
41562306a36Sopenharmony_ci				 char *name)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	if (val == -1)
41862306a36Sopenharmony_ci		*opt = def;
41962306a36Sopenharmony_ci	else if (val < min || val > max) {
42062306a36Sopenharmony_ci		pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n",
42162306a36Sopenharmony_ci			  name, min, max);
42262306a36Sopenharmony_ci		*opt = def;
42362306a36Sopenharmony_ci	} else {
42462306a36Sopenharmony_ci		pr_info("set value of parameter %s to %d\n", name, val);
42562306a36Sopenharmony_ci		*opt = val;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/**
43062306a36Sopenharmony_ci *	velocity_set_bool_opt	-	parser for boolean options
43162306a36Sopenharmony_ci *	@opt: pointer to option value
43262306a36Sopenharmony_ci *	@val: value the user requested (or -1 for default)
43362306a36Sopenharmony_ci *	@def: default value (yes/no)
43462306a36Sopenharmony_ci *	@flag: numeric value to set for true.
43562306a36Sopenharmony_ci *	@name: property name
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci *	Set a boolean property in the module options. This function does
43862306a36Sopenharmony_ci *	all the verification and checking as well as reporting so that
43962306a36Sopenharmony_ci *	we don't duplicate code for each option.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic void velocity_set_bool_opt(u32 *opt, int val, int def, u32 flag,
44262306a36Sopenharmony_ci				  char *name)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	(*opt) &= (~flag);
44562306a36Sopenharmony_ci	if (val == -1)
44662306a36Sopenharmony_ci		*opt |= (def ? flag : 0);
44762306a36Sopenharmony_ci	else if (val < 0 || val > 1) {
44862306a36Sopenharmony_ci		pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n",
44962306a36Sopenharmony_ci			  name, 0, 1);
45062306a36Sopenharmony_ci		*opt |= (def ? flag : 0);
45162306a36Sopenharmony_ci	} else {
45262306a36Sopenharmony_ci		pr_info("set parameter %s to %s\n",
45362306a36Sopenharmony_ci			name, val ? "TRUE" : "FALSE");
45462306a36Sopenharmony_ci		*opt |= (val ? flag : 0);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/**
45962306a36Sopenharmony_ci *	velocity_get_options	-	set options on device
46062306a36Sopenharmony_ci *	@opts: option structure for the device
46162306a36Sopenharmony_ci *	@index: index of option to use in module options array
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci *	Turn the module and command options into a single structure
46462306a36Sopenharmony_ci *	for the current device
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_cistatic void velocity_get_options(struct velocity_opt *opts, int index)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	velocity_set_int_opt(&opts->rx_thresh, rx_thresh[index],
47062306a36Sopenharmony_ci			     RX_THRESH_MIN, RX_THRESH_MAX, RX_THRESH_DEF,
47162306a36Sopenharmony_ci			     "rx_thresh");
47262306a36Sopenharmony_ci	velocity_set_int_opt(&opts->DMA_length, DMA_length[index],
47362306a36Sopenharmony_ci			     DMA_LENGTH_MIN, DMA_LENGTH_MAX, DMA_LENGTH_DEF,
47462306a36Sopenharmony_ci			     "DMA_length");
47562306a36Sopenharmony_ci	velocity_set_int_opt(&opts->numrx, RxDescriptors[index],
47662306a36Sopenharmony_ci			     RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF,
47762306a36Sopenharmony_ci			     "RxDescriptors");
47862306a36Sopenharmony_ci	velocity_set_int_opt(&opts->numtx, TxDescriptors[index],
47962306a36Sopenharmony_ci			     TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF,
48062306a36Sopenharmony_ci			     "TxDescriptors");
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	velocity_set_int_opt(&opts->flow_cntl, flow_control[index],
48362306a36Sopenharmony_ci			     FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF,
48462306a36Sopenharmony_ci			     "flow_control");
48562306a36Sopenharmony_ci	velocity_set_bool_opt(&opts->flags, IP_byte_align[index],
48662306a36Sopenharmony_ci			      IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN,
48762306a36Sopenharmony_ci			      "IP_byte_align");
48862306a36Sopenharmony_ci	velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index],
48962306a36Sopenharmony_ci			     MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF,
49062306a36Sopenharmony_ci			     "Media link mode");
49162306a36Sopenharmony_ci	velocity_set_int_opt(&opts->wol_opts, wol_opts[index],
49262306a36Sopenharmony_ci			     WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF,
49362306a36Sopenharmony_ci			     "Wake On Lan options");
49462306a36Sopenharmony_ci	opts->numrx = (opts->numrx & ~3);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/**
49862306a36Sopenharmony_ci *	velocity_init_cam_filter	-	initialise CAM
49962306a36Sopenharmony_ci *	@vptr: velocity to program
50062306a36Sopenharmony_ci *
50162306a36Sopenharmony_ci *	Initialize the content addressable memory used for filters. Load
50262306a36Sopenharmony_ci *	appropriately according to the presence of VLAN
50362306a36Sopenharmony_ci */
50462306a36Sopenharmony_cistatic void velocity_init_cam_filter(struct velocity_info *vptr)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
50762306a36Sopenharmony_ci	unsigned int vid, i = 0;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */
51062306a36Sopenharmony_ci	WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, &regs->MCFG);
51162306a36Sopenharmony_ci	WORD_REG_BITS_ON(MCFG_VIDFR, &regs->MCFG);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* Disable all CAMs */
51462306a36Sopenharmony_ci	memset(vptr->vCAMmask, 0, sizeof(u8) * 8);
51562306a36Sopenharmony_ci	memset(vptr->mCAMmask, 0, sizeof(u8) * 8);
51662306a36Sopenharmony_ci	mac_set_vlan_cam_mask(regs, vptr->vCAMmask);
51762306a36Sopenharmony_ci	mac_set_cam_mask(regs, vptr->mCAMmask);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* Enable VCAMs */
52062306a36Sopenharmony_ci	for_each_set_bit(vid, vptr->active_vlans, VLAN_N_VID) {
52162306a36Sopenharmony_ci		mac_set_vlan_cam(regs, i, (u8 *) &vid);
52262306a36Sopenharmony_ci		vptr->vCAMmask[i / 8] |= 0x1 << (i % 8);
52362306a36Sopenharmony_ci		if (++i >= VCAM_SIZE)
52462306a36Sopenharmony_ci			break;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	mac_set_vlan_cam_mask(regs, vptr->vCAMmask);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int velocity_vlan_rx_add_vid(struct net_device *dev,
53062306a36Sopenharmony_ci				    __be16 proto, u16 vid)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	spin_lock_irq(&vptr->lock);
53562306a36Sopenharmony_ci	set_bit(vid, vptr->active_vlans);
53662306a36Sopenharmony_ci	velocity_init_cam_filter(vptr);
53762306a36Sopenharmony_ci	spin_unlock_irq(&vptr->lock);
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int velocity_vlan_rx_kill_vid(struct net_device *dev,
54262306a36Sopenharmony_ci				     __be16 proto, u16 vid)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	spin_lock_irq(&vptr->lock);
54762306a36Sopenharmony_ci	clear_bit(vid, vptr->active_vlans);
54862306a36Sopenharmony_ci	velocity_init_cam_filter(vptr);
54962306a36Sopenharmony_ci	spin_unlock_irq(&vptr->lock);
55062306a36Sopenharmony_ci	return 0;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic void velocity_init_rx_ring_indexes(struct velocity_info *vptr)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	vptr->rx.dirty = vptr->rx.filled = vptr->rx.curr = 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/**
55962306a36Sopenharmony_ci *	velocity_rx_reset	-	handle a receive reset
56062306a36Sopenharmony_ci *	@vptr: velocity we are resetting
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci *	Reset the ownership and status for the receive ring side.
56362306a36Sopenharmony_ci *	Hand all the receive queue to the NIC.
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic void velocity_rx_reset(struct velocity_info *vptr)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
56962306a36Sopenharmony_ci	int i;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	velocity_init_rx_ring_indexes(vptr);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/*
57462306a36Sopenharmony_ci	 *	Init state, all RD entries belong to the NIC
57562306a36Sopenharmony_ci	 */
57662306a36Sopenharmony_ci	for (i = 0; i < vptr->options.numrx; ++i)
57762306a36Sopenharmony_ci		vptr->rx.ring[i].rdesc0.len |= OWNED_BY_NIC;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	writew(vptr->options.numrx, &regs->RBRDU);
58062306a36Sopenharmony_ci	writel(vptr->rx.pool_dma, &regs->RDBaseLo);
58162306a36Sopenharmony_ci	writew(0, &regs->RDIdx);
58262306a36Sopenharmony_ci	writew(vptr->options.numrx - 1, &regs->RDCSize);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci *	velocity_get_opt_media_mode	-	get media selection
58762306a36Sopenharmony_ci *	@vptr: velocity adapter
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci *	Get the media mode stored in EEPROM or module options and load
59062306a36Sopenharmony_ci *	mii_status accordingly. The requested link state information
59162306a36Sopenharmony_ci *	is also returned.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic u32 velocity_get_opt_media_mode(struct velocity_info *vptr)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	u32 status = 0;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	switch (vptr->options.spd_dpx) {
59862306a36Sopenharmony_ci	case SPD_DPX_AUTO:
59962306a36Sopenharmony_ci		status = VELOCITY_AUTONEG_ENABLE;
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case SPD_DPX_100_FULL:
60262306a36Sopenharmony_ci		status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL;
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case SPD_DPX_10_FULL:
60562306a36Sopenharmony_ci		status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL;
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci	case SPD_DPX_100_HALF:
60862306a36Sopenharmony_ci		status = VELOCITY_SPEED_100;
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case SPD_DPX_10_HALF:
61162306a36Sopenharmony_ci		status = VELOCITY_SPEED_10;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case SPD_DPX_1000_FULL:
61462306a36Sopenharmony_ci		status = VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL;
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	vptr->mii_status = status;
61862306a36Sopenharmony_ci	return status;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci *	safe_disable_mii_autopoll	-	autopoll off
62362306a36Sopenharmony_ci *	@regs: velocity registers
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci *	Turn off the autopoll and wait for it to disable on the chip
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic void safe_disable_mii_autopoll(struct mac_regs __iomem *regs)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	u16 ww;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/*  turn off MAUTO */
63262306a36Sopenharmony_ci	writeb(0, &regs->MIICR);
63362306a36Sopenharmony_ci	for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
63462306a36Sopenharmony_ci		udelay(1);
63562306a36Sopenharmony_ci		if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/**
64162306a36Sopenharmony_ci *	enable_mii_autopoll	-	turn on autopolling
64262306a36Sopenharmony_ci *	@regs: velocity registers
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci *	Enable the MII link status autopoll feature on the Velocity
64562306a36Sopenharmony_ci *	hardware. Wait for it to enable.
64662306a36Sopenharmony_ci */
64762306a36Sopenharmony_cistatic void enable_mii_autopoll(struct mac_regs __iomem *regs)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int ii;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	writeb(0, &(regs->MIICR));
65262306a36Sopenharmony_ci	writeb(MIIADR_SWMPL, &regs->MIIADR);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	for (ii = 0; ii < W_MAX_TIMEOUT; ii++) {
65562306a36Sopenharmony_ci		udelay(1);
65662306a36Sopenharmony_ci		if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	writeb(MIICR_MAUTO, &regs->MIICR);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	for (ii = 0; ii < W_MAX_TIMEOUT; ii++) {
66362306a36Sopenharmony_ci		udelay(1);
66462306a36Sopenharmony_ci		if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, &regs->MIISR))
66562306a36Sopenharmony_ci			break;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/**
67162306a36Sopenharmony_ci *	velocity_mii_read	-	read MII data
67262306a36Sopenharmony_ci *	@regs: velocity registers
67362306a36Sopenharmony_ci *	@index: MII register index
67462306a36Sopenharmony_ci *	@data: buffer for received data
67562306a36Sopenharmony_ci *
67662306a36Sopenharmony_ci *	Perform a single read of an MII 16bit register. Returns zero
67762306a36Sopenharmony_ci *	on success or -ETIMEDOUT if the PHY did not respond.
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_cistatic int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	u16 ww;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/*
68462306a36Sopenharmony_ci	 *	Disable MIICR_MAUTO, so that mii addr can be set normally
68562306a36Sopenharmony_ci	 */
68662306a36Sopenharmony_ci	safe_disable_mii_autopoll(regs);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	writeb(index, &regs->MIIADR);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	BYTE_REG_BITS_ON(MIICR_RCMD, &regs->MIICR);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
69362306a36Sopenharmony_ci		if (!(readb(&regs->MIICR) & MIICR_RCMD))
69462306a36Sopenharmony_ci			break;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	*data = readw(&regs->MIIDATA);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	enable_mii_autopoll(regs);
70062306a36Sopenharmony_ci	if (ww == W_MAX_TIMEOUT)
70162306a36Sopenharmony_ci		return -ETIMEDOUT;
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/**
70662306a36Sopenharmony_ci *	mii_check_media_mode	-	check media state
70762306a36Sopenharmony_ci *	@regs: velocity registers
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci *	Check the current MII status and determine the link status
71062306a36Sopenharmony_ci *	accordingly
71162306a36Sopenharmony_ci */
71262306a36Sopenharmony_cistatic u32 mii_check_media_mode(struct mac_regs __iomem *regs)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	u32 status = 0;
71562306a36Sopenharmony_ci	u16 ANAR;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (!MII_REG_BITS_IS_ON(BMSR_LSTATUS, MII_BMSR, regs))
71862306a36Sopenharmony_ci		status |= VELOCITY_LINK_FAIL;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (MII_REG_BITS_IS_ON(ADVERTISE_1000FULL, MII_CTRL1000, regs))
72162306a36Sopenharmony_ci		status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL;
72262306a36Sopenharmony_ci	else if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF, MII_CTRL1000, regs))
72362306a36Sopenharmony_ci		status |= (VELOCITY_SPEED_1000);
72462306a36Sopenharmony_ci	else {
72562306a36Sopenharmony_ci		velocity_mii_read(regs, MII_ADVERTISE, &ANAR);
72662306a36Sopenharmony_ci		if (ANAR & ADVERTISE_100FULL)
72762306a36Sopenharmony_ci			status |= (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL);
72862306a36Sopenharmony_ci		else if (ANAR & ADVERTISE_100HALF)
72962306a36Sopenharmony_ci			status |= VELOCITY_SPEED_100;
73062306a36Sopenharmony_ci		else if (ANAR & ADVERTISE_10FULL)
73162306a36Sopenharmony_ci			status |= (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL);
73262306a36Sopenharmony_ci		else
73362306a36Sopenharmony_ci			status |= (VELOCITY_SPEED_10);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) {
73762306a36Sopenharmony_ci		velocity_mii_read(regs, MII_ADVERTISE, &ANAR);
73862306a36Sopenharmony_ci		if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF))
73962306a36Sopenharmony_ci		    == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) {
74062306a36Sopenharmony_ci			if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs))
74162306a36Sopenharmony_ci				status |= VELOCITY_AUTONEG_ENABLE;
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return status;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/**
74962306a36Sopenharmony_ci *	velocity_mii_write	-	write MII data
75062306a36Sopenharmony_ci *	@regs: velocity registers
75162306a36Sopenharmony_ci *	@mii_addr: MII register index
75262306a36Sopenharmony_ci *	@data: 16bit data for the MII register
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci *	Perform a single write to an MII 16bit register. Returns zero
75562306a36Sopenharmony_ci *	on success or -ETIMEDOUT if the PHY did not respond.
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_cistatic int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	u16 ww;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 *	Disable MIICR_MAUTO, so that mii addr can be set normally
76362306a36Sopenharmony_ci	 */
76462306a36Sopenharmony_ci	safe_disable_mii_autopoll(regs);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* MII reg offset */
76762306a36Sopenharmony_ci	writeb(mii_addr, &regs->MIIADR);
76862306a36Sopenharmony_ci	/* set MII data */
76962306a36Sopenharmony_ci	writew(data, &regs->MIIDATA);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* turn on MIICR_WCMD */
77262306a36Sopenharmony_ci	BYTE_REG_BITS_ON(MIICR_WCMD, &regs->MIICR);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* W_MAX_TIMEOUT is the timeout period */
77562306a36Sopenharmony_ci	for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
77662306a36Sopenharmony_ci		udelay(5);
77762306a36Sopenharmony_ci		if (!(readb(&regs->MIICR) & MIICR_WCMD))
77862306a36Sopenharmony_ci			break;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci	enable_mii_autopoll(regs);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (ww == W_MAX_TIMEOUT)
78362306a36Sopenharmony_ci		return -ETIMEDOUT;
78462306a36Sopenharmony_ci	return 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/**
78862306a36Sopenharmony_ci *	set_mii_flow_control	-	flow control setup
78962306a36Sopenharmony_ci *	@vptr: velocity interface
79062306a36Sopenharmony_ci *
79162306a36Sopenharmony_ci *	Set up the flow control on this interface according to
79262306a36Sopenharmony_ci *	the supplied user/eeprom options.
79362306a36Sopenharmony_ci */
79462306a36Sopenharmony_cistatic void set_mii_flow_control(struct velocity_info *vptr)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	/*Enable or Disable PAUSE in ANAR */
79762306a36Sopenharmony_ci	switch (vptr->options.flow_cntl) {
79862306a36Sopenharmony_ci	case FLOW_CNTL_TX:
79962306a36Sopenharmony_ci		MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs);
80062306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs);
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	case FLOW_CNTL_RX:
80462306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs);
80562306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs);
80662306a36Sopenharmony_ci		break;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	case FLOW_CNTL_TX_RX:
80962306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs);
81062306a36Sopenharmony_ci		MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs);
81162306a36Sopenharmony_ci		break;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	case FLOW_CNTL_DISABLE:
81462306a36Sopenharmony_ci		MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs);
81562306a36Sopenharmony_ci		MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs);
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	default:
81862306a36Sopenharmony_ci		break;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/**
82362306a36Sopenharmony_ci *	mii_set_auto_on		-	autonegotiate on
82462306a36Sopenharmony_ci *	@vptr: velocity
82562306a36Sopenharmony_ci *
82662306a36Sopenharmony_ci *	Enable autonegotation on this interface
82762306a36Sopenharmony_ci */
82862306a36Sopenharmony_cistatic void mii_set_auto_on(struct velocity_info *vptr)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs))
83162306a36Sopenharmony_ci		MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs);
83262306a36Sopenharmony_ci	else
83362306a36Sopenharmony_ci		MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs);
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic u32 check_connection_type(struct mac_regs __iomem *regs)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	u32 status = 0;
83962306a36Sopenharmony_ci	u8 PHYSR0;
84062306a36Sopenharmony_ci	u16 ANAR;
84162306a36Sopenharmony_ci	PHYSR0 = readb(&regs->PHYSR0);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	   if (!(PHYSR0 & PHYSR0_LINKGD))
84562306a36Sopenharmony_ci	   status|=VELOCITY_LINK_FAIL;
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (PHYSR0 & PHYSR0_FDPX)
84962306a36Sopenharmony_ci		status |= VELOCITY_DUPLEX_FULL;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (PHYSR0 & PHYSR0_SPDG)
85262306a36Sopenharmony_ci		status |= VELOCITY_SPEED_1000;
85362306a36Sopenharmony_ci	else if (PHYSR0 & PHYSR0_SPD10)
85462306a36Sopenharmony_ci		status |= VELOCITY_SPEED_10;
85562306a36Sopenharmony_ci	else
85662306a36Sopenharmony_ci		status |= VELOCITY_SPEED_100;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) {
85962306a36Sopenharmony_ci		velocity_mii_read(regs, MII_ADVERTISE, &ANAR);
86062306a36Sopenharmony_ci		if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF))
86162306a36Sopenharmony_ci		    == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) {
86262306a36Sopenharmony_ci			if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs))
86362306a36Sopenharmony_ci				status |= VELOCITY_AUTONEG_ENABLE;
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return status;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci/**
87162306a36Sopenharmony_ci *	velocity_set_media_mode		-	set media mode
87262306a36Sopenharmony_ci *	@vptr: velocity adapter
87362306a36Sopenharmony_ci *	@mii_status: old MII link state
87462306a36Sopenharmony_ci *
87562306a36Sopenharmony_ci *	Check the media link state and configure the flow control
87662306a36Sopenharmony_ci *	PHY and also velocity hardware setup accordingly. In particular
87762306a36Sopenharmony_ci *	we need to set up CD polling and frame bursting.
87862306a36Sopenharmony_ci */
87962306a36Sopenharmony_cistatic int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	vptr->mii_status = mii_check_media_mode(vptr->mac_regs);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* Set mii link status */
88662306a36Sopenharmony_ci	set_mii_flow_control(vptr);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201)
88962306a36Sopenharmony_ci		MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/*
89262306a36Sopenharmony_ci	 *	If connection type is AUTO
89362306a36Sopenharmony_ci	 */
89462306a36Sopenharmony_ci	if (mii_status & VELOCITY_AUTONEG_ENABLE) {
89562306a36Sopenharmony_ci		netdev_info(vptr->netdev, "Velocity is in AUTO mode\n");
89662306a36Sopenharmony_ci		/* clear force MAC mode bit */
89762306a36Sopenharmony_ci		BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, &regs->CHIPGCR);
89862306a36Sopenharmony_ci		/* set duplex mode of MAC according to duplex mode of MII */
89962306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF, MII_ADVERTISE, vptr->mac_regs);
90062306a36Sopenharmony_ci		MII_REG_BITS_ON(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs);
90162306a36Sopenharmony_ci		MII_REG_BITS_ON(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		/* enable AUTO-NEGO mode */
90462306a36Sopenharmony_ci		mii_set_auto_on(vptr);
90562306a36Sopenharmony_ci	} else {
90662306a36Sopenharmony_ci		u16 CTRL1000;
90762306a36Sopenharmony_ci		u16 ANAR;
90862306a36Sopenharmony_ci		u8 CHIPGCR;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		/*
91162306a36Sopenharmony_ci		 * 1. if it's 3119, disable frame bursting in halfduplex mode
91262306a36Sopenharmony_ci		 *    and enable it in fullduplex mode
91362306a36Sopenharmony_ci		 * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR
91462306a36Sopenharmony_ci		 * 3. only enable CD heart beat counter in 10HD mode
91562306a36Sopenharmony_ci		 */
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		/* set force MAC mode bit */
91862306a36Sopenharmony_ci		BYTE_REG_BITS_ON(CHIPGCR_FCMODE, &regs->CHIPGCR);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		CHIPGCR = readb(&regs->CHIPGCR);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (mii_status & VELOCITY_SPEED_1000)
92362306a36Sopenharmony_ci			CHIPGCR |= CHIPGCR_FCGMII;
92462306a36Sopenharmony_ci		else
92562306a36Sopenharmony_ci			CHIPGCR &= ~CHIPGCR_FCGMII;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		if (mii_status & VELOCITY_DUPLEX_FULL) {
92862306a36Sopenharmony_ci			CHIPGCR |= CHIPGCR_FCFDX;
92962306a36Sopenharmony_ci			writeb(CHIPGCR, &regs->CHIPGCR);
93062306a36Sopenharmony_ci			netdev_info(vptr->netdev,
93162306a36Sopenharmony_ci				    "set Velocity to forced full mode\n");
93262306a36Sopenharmony_ci			if (vptr->rev_id < REV_ID_VT3216_A0)
93362306a36Sopenharmony_ci				BYTE_REG_BITS_OFF(TCR_TB2BDIS, &regs->TCR);
93462306a36Sopenharmony_ci		} else {
93562306a36Sopenharmony_ci			CHIPGCR &= ~CHIPGCR_FCFDX;
93662306a36Sopenharmony_ci			netdev_info(vptr->netdev,
93762306a36Sopenharmony_ci				    "set Velocity to forced half mode\n");
93862306a36Sopenharmony_ci			writeb(CHIPGCR, &regs->CHIPGCR);
93962306a36Sopenharmony_ci			if (vptr->rev_id < REV_ID_VT3216_A0)
94062306a36Sopenharmony_ci				BYTE_REG_BITS_ON(TCR_TB2BDIS, &regs->TCR);
94162306a36Sopenharmony_ci		}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		velocity_mii_read(vptr->mac_regs, MII_CTRL1000, &CTRL1000);
94462306a36Sopenharmony_ci		CTRL1000 &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
94562306a36Sopenharmony_ci		if ((mii_status & VELOCITY_SPEED_1000) &&
94662306a36Sopenharmony_ci		    (mii_status & VELOCITY_DUPLEX_FULL)) {
94762306a36Sopenharmony_ci			CTRL1000 |= ADVERTISE_1000FULL;
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci		velocity_mii_write(vptr->mac_regs, MII_CTRL1000, CTRL1000);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		if (!(mii_status & VELOCITY_DUPLEX_FULL) && (mii_status & VELOCITY_SPEED_10))
95262306a36Sopenharmony_ci			BYTE_REG_BITS_OFF(TESTCFG_HBDIS, &regs->TESTCFG);
95362306a36Sopenharmony_ci		else
95462306a36Sopenharmony_ci			BYTE_REG_BITS_ON(TESTCFG_HBDIS, &regs->TESTCFG);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci		/* MII_REG_BITS_OFF(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs); */
95762306a36Sopenharmony_ci		velocity_mii_read(vptr->mac_regs, MII_ADVERTISE, &ANAR);
95862306a36Sopenharmony_ci		ANAR &= (~(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF));
95962306a36Sopenharmony_ci		if (mii_status & VELOCITY_SPEED_100) {
96062306a36Sopenharmony_ci			if (mii_status & VELOCITY_DUPLEX_FULL)
96162306a36Sopenharmony_ci				ANAR |= ADVERTISE_100FULL;
96262306a36Sopenharmony_ci			else
96362306a36Sopenharmony_ci				ANAR |= ADVERTISE_100HALF;
96462306a36Sopenharmony_ci		} else if (mii_status & VELOCITY_SPEED_10) {
96562306a36Sopenharmony_ci			if (mii_status & VELOCITY_DUPLEX_FULL)
96662306a36Sopenharmony_ci				ANAR |= ADVERTISE_10FULL;
96762306a36Sopenharmony_ci			else
96862306a36Sopenharmony_ci				ANAR |= ADVERTISE_10HALF;
96962306a36Sopenharmony_ci		}
97062306a36Sopenharmony_ci		velocity_mii_write(vptr->mac_regs, MII_ADVERTISE, ANAR);
97162306a36Sopenharmony_ci		/* enable AUTO-NEGO mode */
97262306a36Sopenharmony_ci		mii_set_auto_on(vptr);
97362306a36Sopenharmony_ci		/* MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs); */
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	/* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */
97662306a36Sopenharmony_ci	/* vptr->mii_status=check_connection_type(vptr->mac_regs); */
97762306a36Sopenharmony_ci	return VELOCITY_LINK_CHANGE;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci *	velocity_print_link_status	-	link status reporting
98262306a36Sopenharmony_ci *	@vptr: velocity to report on
98362306a36Sopenharmony_ci *
98462306a36Sopenharmony_ci *	Turn the link status of the velocity card into a kernel log
98562306a36Sopenharmony_ci *	description of the new link state, detailing speed and duplex
98662306a36Sopenharmony_ci *	status
98762306a36Sopenharmony_ci */
98862306a36Sopenharmony_cistatic void velocity_print_link_status(struct velocity_info *vptr)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	const char *link;
99162306a36Sopenharmony_ci	const char *speed;
99262306a36Sopenharmony_ci	const char *duplex;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (vptr->mii_status & VELOCITY_LINK_FAIL) {
99562306a36Sopenharmony_ci		netdev_notice(vptr->netdev, "failed to detect cable link\n");
99662306a36Sopenharmony_ci		return;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (vptr->options.spd_dpx == SPD_DPX_AUTO) {
100062306a36Sopenharmony_ci		link = "auto-negotiation";
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_SPEED_1000)
100362306a36Sopenharmony_ci			speed = "1000";
100462306a36Sopenharmony_ci		else if (vptr->mii_status & VELOCITY_SPEED_100)
100562306a36Sopenharmony_ci			speed = "100";
100662306a36Sopenharmony_ci		else
100762306a36Sopenharmony_ci			speed = "10";
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
101062306a36Sopenharmony_ci			duplex = "full";
101162306a36Sopenharmony_ci		else
101262306a36Sopenharmony_ci			duplex = "half";
101362306a36Sopenharmony_ci	} else {
101462306a36Sopenharmony_ci		link = "forced";
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		switch (vptr->options.spd_dpx) {
101762306a36Sopenharmony_ci		case SPD_DPX_1000_FULL:
101862306a36Sopenharmony_ci			speed = "1000";
101962306a36Sopenharmony_ci			duplex = "full";
102062306a36Sopenharmony_ci			break;
102162306a36Sopenharmony_ci		case SPD_DPX_100_HALF:
102262306a36Sopenharmony_ci			speed = "100";
102362306a36Sopenharmony_ci			duplex = "half";
102462306a36Sopenharmony_ci			break;
102562306a36Sopenharmony_ci		case SPD_DPX_100_FULL:
102662306a36Sopenharmony_ci			speed = "100";
102762306a36Sopenharmony_ci			duplex = "full";
102862306a36Sopenharmony_ci			break;
102962306a36Sopenharmony_ci		case SPD_DPX_10_HALF:
103062306a36Sopenharmony_ci			speed = "10";
103162306a36Sopenharmony_ci			duplex = "half";
103262306a36Sopenharmony_ci			break;
103362306a36Sopenharmony_ci		case SPD_DPX_10_FULL:
103462306a36Sopenharmony_ci			speed = "10";
103562306a36Sopenharmony_ci			duplex = "full";
103662306a36Sopenharmony_ci			break;
103762306a36Sopenharmony_ci		default:
103862306a36Sopenharmony_ci			speed = "unknown";
103962306a36Sopenharmony_ci			duplex = "unknown";
104062306a36Sopenharmony_ci			break;
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci	netdev_notice(vptr->netdev, "Link %s speed %sM bps %s duplex\n",
104462306a36Sopenharmony_ci		      link, speed, duplex);
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci/**
104862306a36Sopenharmony_ci *	enable_flow_control_ability	-	flow control
104962306a36Sopenharmony_ci *	@vptr: veloity to configure
105062306a36Sopenharmony_ci *
105162306a36Sopenharmony_ci *	Set up flow control according to the flow control options
105262306a36Sopenharmony_ci *	determined by the eeprom/configuration.
105362306a36Sopenharmony_ci */
105462306a36Sopenharmony_cistatic void enable_flow_control_ability(struct velocity_info *vptr)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	switch (vptr->options.flow_cntl) {
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	case FLOW_CNTL_DEFAULT:
106262306a36Sopenharmony_ci		if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, &regs->PHYSR0))
106362306a36Sopenharmony_ci			writel(CR0_FDXRFCEN, &regs->CR0Set);
106462306a36Sopenharmony_ci		else
106562306a36Sopenharmony_ci			writel(CR0_FDXRFCEN, &regs->CR0Clr);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, &regs->PHYSR0))
106862306a36Sopenharmony_ci			writel(CR0_FDXTFCEN, &regs->CR0Set);
106962306a36Sopenharmony_ci		else
107062306a36Sopenharmony_ci			writel(CR0_FDXTFCEN, &regs->CR0Clr);
107162306a36Sopenharmony_ci		break;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	case FLOW_CNTL_TX:
107462306a36Sopenharmony_ci		writel(CR0_FDXTFCEN, &regs->CR0Set);
107562306a36Sopenharmony_ci		writel(CR0_FDXRFCEN, &regs->CR0Clr);
107662306a36Sopenharmony_ci		break;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	case FLOW_CNTL_RX:
107962306a36Sopenharmony_ci		writel(CR0_FDXRFCEN, &regs->CR0Set);
108062306a36Sopenharmony_ci		writel(CR0_FDXTFCEN, &regs->CR0Clr);
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	case FLOW_CNTL_TX_RX:
108462306a36Sopenharmony_ci		writel(CR0_FDXTFCEN, &regs->CR0Set);
108562306a36Sopenharmony_ci		writel(CR0_FDXRFCEN, &regs->CR0Set);
108662306a36Sopenharmony_ci		break;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	case FLOW_CNTL_DISABLE:
108962306a36Sopenharmony_ci		writel(CR0_FDXRFCEN, &regs->CR0Clr);
109062306a36Sopenharmony_ci		writel(CR0_FDXTFCEN, &regs->CR0Clr);
109162306a36Sopenharmony_ci		break;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	default:
109462306a36Sopenharmony_ci		break;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/**
110062306a36Sopenharmony_ci *	velocity_soft_reset	-	soft reset
110162306a36Sopenharmony_ci *	@vptr: velocity to reset
110262306a36Sopenharmony_ci *
110362306a36Sopenharmony_ci *	Kick off a soft reset of the velocity adapter and then poll
110462306a36Sopenharmony_ci *	until the reset sequence has completed before returning.
110562306a36Sopenharmony_ci */
110662306a36Sopenharmony_cistatic int velocity_soft_reset(struct velocity_info *vptr)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
110962306a36Sopenharmony_ci	int i = 0;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	writel(CR0_SFRST, &regs->CR0Set);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	for (i = 0; i < W_MAX_TIMEOUT; i++) {
111462306a36Sopenharmony_ci		udelay(5);
111562306a36Sopenharmony_ci		if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, &regs->CR0Set))
111662306a36Sopenharmony_ci			break;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (i == W_MAX_TIMEOUT) {
112062306a36Sopenharmony_ci		writel(CR0_FORSRST, &regs->CR0Set);
112162306a36Sopenharmony_ci		/* FIXME: PCI POSTING */
112262306a36Sopenharmony_ci		/* delay 2ms */
112362306a36Sopenharmony_ci		mdelay(2);
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	return 0;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci/**
112962306a36Sopenharmony_ci *	velocity_set_multi	-	filter list change callback
113062306a36Sopenharmony_ci *	@dev: network device
113162306a36Sopenharmony_ci *
113262306a36Sopenharmony_ci *	Called by the network layer when the filter lists need to change
113362306a36Sopenharmony_ci *	for a velocity adapter. Reload the CAMs with the new address
113462306a36Sopenharmony_ci *	filter ruleset.
113562306a36Sopenharmony_ci */
113662306a36Sopenharmony_cistatic void velocity_set_multi(struct net_device *dev)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
113962306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
114062306a36Sopenharmony_ci	u8 rx_mode;
114162306a36Sopenharmony_ci	int i;
114262306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {	/* Set promiscuous. */
114562306a36Sopenharmony_ci		writel(0xffffffff, &regs->MARCAM[0]);
114662306a36Sopenharmony_ci		writel(0xffffffff, &regs->MARCAM[4]);
114762306a36Sopenharmony_ci		rx_mode = (RCR_AM | RCR_AB | RCR_PROM);
114862306a36Sopenharmony_ci	} else if ((netdev_mc_count(dev) > vptr->multicast_limit) ||
114962306a36Sopenharmony_ci		   (dev->flags & IFF_ALLMULTI)) {
115062306a36Sopenharmony_ci		writel(0xffffffff, &regs->MARCAM[0]);
115162306a36Sopenharmony_ci		writel(0xffffffff, &regs->MARCAM[4]);
115262306a36Sopenharmony_ci		rx_mode = (RCR_AM | RCR_AB);
115362306a36Sopenharmony_ci	} else {
115462306a36Sopenharmony_ci		int offset = MCAM_SIZE - vptr->multicast_limit;
115562306a36Sopenharmony_ci		mac_get_cam_mask(regs, vptr->mCAMmask);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		i = 0;
115862306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
115962306a36Sopenharmony_ci			mac_set_cam(regs, i + offset, ha->addr);
116062306a36Sopenharmony_ci			vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7);
116162306a36Sopenharmony_ci			i++;
116262306a36Sopenharmony_ci		}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci		mac_set_cam_mask(regs, vptr->mCAMmask);
116562306a36Sopenharmony_ci		rx_mode = RCR_AM | RCR_AB | RCR_AP;
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci	if (dev->mtu > 1500)
116862306a36Sopenharmony_ci		rx_mode |= RCR_AL;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	BYTE_REG_BITS_ON(rx_mode, &regs->RCR);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/*
117562306a36Sopenharmony_ci * MII access , media link mode setting functions
117662306a36Sopenharmony_ci */
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/**
117962306a36Sopenharmony_ci *	mii_init	-	set up MII
118062306a36Sopenharmony_ci *	@vptr: velocity adapter
118162306a36Sopenharmony_ci *	@mii_status:  links tatus
118262306a36Sopenharmony_ci *
118362306a36Sopenharmony_ci *	Set up the PHY for the current link state.
118462306a36Sopenharmony_ci */
118562306a36Sopenharmony_cistatic void mii_init(struct velocity_info *vptr, u32 mii_status)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	u16 BMCR;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	switch (PHYID_GET_PHY_ID(vptr->phy_id)) {
119062306a36Sopenharmony_ci	case PHYID_ICPLUS_IP101A:
119162306a36Sopenharmony_ci		MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP),
119262306a36Sopenharmony_ci						MII_ADVERTISE, vptr->mac_regs);
119362306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
119462306a36Sopenharmony_ci			MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION,
119562306a36Sopenharmony_ci								vptr->mac_regs);
119662306a36Sopenharmony_ci		else
119762306a36Sopenharmony_ci			MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION,
119862306a36Sopenharmony_ci								vptr->mac_regs);
119962306a36Sopenharmony_ci		MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs);
120062306a36Sopenharmony_ci		break;
120162306a36Sopenharmony_ci	case PHYID_CICADA_CS8201:
120262306a36Sopenharmony_ci		/*
120362306a36Sopenharmony_ci		 *	Reset to hardware default
120462306a36Sopenharmony_ci		 */
120562306a36Sopenharmony_ci		MII_REG_BITS_OFF((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs);
120662306a36Sopenharmony_ci		/*
120762306a36Sopenharmony_ci		 *	Turn on ECHODIS bit in NWay-forced full mode and turn it
120862306a36Sopenharmony_ci		 *	off it in NWay-forced half mode for NWay-forced v.s.
120962306a36Sopenharmony_ci		 *	legacy-forced issue.
121062306a36Sopenharmony_ci		 */
121162306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
121262306a36Sopenharmony_ci			MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs);
121362306a36Sopenharmony_ci		else
121462306a36Sopenharmony_ci			MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs);
121562306a36Sopenharmony_ci		/*
121662306a36Sopenharmony_ci		 *	Turn on Link/Activity LED enable bit for CIS8201
121762306a36Sopenharmony_ci		 */
121862306a36Sopenharmony_ci		MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs);
121962306a36Sopenharmony_ci		break;
122062306a36Sopenharmony_ci	case PHYID_VT3216_32BIT:
122162306a36Sopenharmony_ci	case PHYID_VT3216_64BIT:
122262306a36Sopenharmony_ci		/*
122362306a36Sopenharmony_ci		 *	Reset to hardware default
122462306a36Sopenharmony_ci		 */
122562306a36Sopenharmony_ci		MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs);
122662306a36Sopenharmony_ci		/*
122762306a36Sopenharmony_ci		 *	Turn on ECHODIS bit in NWay-forced full mode and turn it
122862306a36Sopenharmony_ci		 *	off it in NWay-forced half mode for NWay-forced v.s.
122962306a36Sopenharmony_ci		 *	legacy-forced issue
123062306a36Sopenharmony_ci		 */
123162306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
123262306a36Sopenharmony_ci			MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs);
123362306a36Sopenharmony_ci		else
123462306a36Sopenharmony_ci			MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs);
123562306a36Sopenharmony_ci		break;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	case PHYID_MARVELL_1000:
123862306a36Sopenharmony_ci	case PHYID_MARVELL_1000S:
123962306a36Sopenharmony_ci		/*
124062306a36Sopenharmony_ci		 *	Assert CRS on Transmit
124162306a36Sopenharmony_ci		 */
124262306a36Sopenharmony_ci		MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs);
124362306a36Sopenharmony_ci		/*
124462306a36Sopenharmony_ci		 *	Reset to hardware default
124562306a36Sopenharmony_ci		 */
124662306a36Sopenharmony_ci		MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs);
124762306a36Sopenharmony_ci		break;
124862306a36Sopenharmony_ci	default:
124962306a36Sopenharmony_ci		;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci	velocity_mii_read(vptr->mac_regs, MII_BMCR, &BMCR);
125262306a36Sopenharmony_ci	if (BMCR & BMCR_ISOLATE) {
125362306a36Sopenharmony_ci		BMCR &= ~BMCR_ISOLATE;
125462306a36Sopenharmony_ci		velocity_mii_write(vptr->mac_regs, MII_BMCR, BMCR);
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci/**
125962306a36Sopenharmony_ci * setup_queue_timers	-	Setup interrupt timers
126062306a36Sopenharmony_ci * @vptr: velocity adapter
126162306a36Sopenharmony_ci *
126262306a36Sopenharmony_ci * Setup interrupt frequency during suppression (timeout if the frame
126362306a36Sopenharmony_ci * count isn't filled).
126462306a36Sopenharmony_ci */
126562306a36Sopenharmony_cistatic void setup_queue_timers(struct velocity_info *vptr)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	/* Only for newer revisions */
126862306a36Sopenharmony_ci	if (vptr->rev_id >= REV_ID_VT3216_A0) {
126962306a36Sopenharmony_ci		u8 txqueue_timer = 0;
127062306a36Sopenharmony_ci		u8 rxqueue_timer = 0;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci		if (vptr->mii_status & (VELOCITY_SPEED_1000 |
127362306a36Sopenharmony_ci				VELOCITY_SPEED_100)) {
127462306a36Sopenharmony_ci			txqueue_timer = vptr->options.txqueue_timer;
127562306a36Sopenharmony_ci			rxqueue_timer = vptr->options.rxqueue_timer;
127662306a36Sopenharmony_ci		}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		writeb(txqueue_timer, &vptr->mac_regs->TQETMR);
127962306a36Sopenharmony_ci		writeb(rxqueue_timer, &vptr->mac_regs->RQETMR);
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci/**
128462306a36Sopenharmony_ci * setup_adaptive_interrupts  -  Setup interrupt suppression
128562306a36Sopenharmony_ci * @vptr: velocity adapter
128662306a36Sopenharmony_ci *
128762306a36Sopenharmony_ci * The velocity is able to suppress interrupt during high interrupt load.
128862306a36Sopenharmony_ci * This function turns on that feature.
128962306a36Sopenharmony_ci */
129062306a36Sopenharmony_cistatic void setup_adaptive_interrupts(struct velocity_info *vptr)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
129362306a36Sopenharmony_ci	u16 tx_intsup = vptr->options.tx_intsup;
129462306a36Sopenharmony_ci	u16 rx_intsup = vptr->options.rx_intsup;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/* Setup default interrupt mask (will be changed below) */
129762306a36Sopenharmony_ci	vptr->int_mask = INT_MASK_DEF;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	/* Set Tx Interrupt Suppression Threshold */
130062306a36Sopenharmony_ci	writeb(CAMCR_PS0, &regs->CAMCR);
130162306a36Sopenharmony_ci	if (tx_intsup != 0) {
130262306a36Sopenharmony_ci		vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I |
130362306a36Sopenharmony_ci				ISR_PTX2I | ISR_PTX3I);
130462306a36Sopenharmony_ci		writew(tx_intsup, &regs->ISRCTL);
130562306a36Sopenharmony_ci	} else
130662306a36Sopenharmony_ci		writew(ISRCTL_TSUPDIS, &regs->ISRCTL);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	/* Set Rx Interrupt Suppression Threshold */
130962306a36Sopenharmony_ci	writeb(CAMCR_PS1, &regs->CAMCR);
131062306a36Sopenharmony_ci	if (rx_intsup != 0) {
131162306a36Sopenharmony_ci		vptr->int_mask &= ~ISR_PRXI;
131262306a36Sopenharmony_ci		writew(rx_intsup, &regs->ISRCTL);
131362306a36Sopenharmony_ci	} else
131462306a36Sopenharmony_ci		writew(ISRCTL_RSUPDIS, &regs->ISRCTL);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* Select page to interrupt hold timer */
131762306a36Sopenharmony_ci	writeb(0, &regs->CAMCR);
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci/**
132162306a36Sopenharmony_ci *	velocity_init_registers	-	initialise MAC registers
132262306a36Sopenharmony_ci *	@vptr: velocity to init
132362306a36Sopenharmony_ci *	@type: type of initialisation (hot or cold)
132462306a36Sopenharmony_ci *
132562306a36Sopenharmony_ci *	Initialise the MAC on a reset or on first set up on the
132662306a36Sopenharmony_ci *	hardware.
132762306a36Sopenharmony_ci */
132862306a36Sopenharmony_cistatic void velocity_init_registers(struct velocity_info *vptr,
132962306a36Sopenharmony_ci				    enum velocity_init_type type)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
133262306a36Sopenharmony_ci	struct net_device *netdev = vptr->netdev;
133362306a36Sopenharmony_ci	int i, mii_status;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	mac_wol_reset(regs);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	switch (type) {
133862306a36Sopenharmony_ci	case VELOCITY_INIT_RESET:
133962306a36Sopenharmony_ci	case VELOCITY_INIT_WOL:
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci		netif_stop_queue(netdev);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		/*
134462306a36Sopenharmony_ci		 *	Reset RX to prevent RX pointer not on the 4X location
134562306a36Sopenharmony_ci		 */
134662306a36Sopenharmony_ci		velocity_rx_reset(vptr);
134762306a36Sopenharmony_ci		mac_rx_queue_run(regs);
134862306a36Sopenharmony_ci		mac_rx_queue_wake(regs);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		mii_status = velocity_get_opt_media_mode(vptr);
135162306a36Sopenharmony_ci		if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) {
135262306a36Sopenharmony_ci			velocity_print_link_status(vptr);
135362306a36Sopenharmony_ci			if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
135462306a36Sopenharmony_ci				netif_wake_queue(netdev);
135562306a36Sopenharmony_ci		}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		enable_flow_control_ability(vptr);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci		mac_clear_isr(regs);
136062306a36Sopenharmony_ci		writel(CR0_STOP, &regs->CR0Clr);
136162306a36Sopenharmony_ci		writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT),
136262306a36Sopenharmony_ci							&regs->CR0Set);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	case VELOCITY_INIT_COLD:
136762306a36Sopenharmony_ci	default:
136862306a36Sopenharmony_ci		/*
136962306a36Sopenharmony_ci		 *	Do reset
137062306a36Sopenharmony_ci		 */
137162306a36Sopenharmony_ci		velocity_soft_reset(vptr);
137262306a36Sopenharmony_ci		mdelay(5);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci		if (!vptr->no_eeprom) {
137562306a36Sopenharmony_ci			mac_eeprom_reload(regs);
137662306a36Sopenharmony_ci			for (i = 0; i < 6; i++)
137762306a36Sopenharmony_ci				writeb(netdev->dev_addr[i], regs->PAR + i);
137862306a36Sopenharmony_ci		}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		/*
138162306a36Sopenharmony_ci		 *	clear Pre_ACPI bit.
138262306a36Sopenharmony_ci		 */
138362306a36Sopenharmony_ci		BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA));
138462306a36Sopenharmony_ci		mac_set_rx_thresh(regs, vptr->options.rx_thresh);
138562306a36Sopenharmony_ci		mac_set_dma_length(regs, vptr->options.DMA_length);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		writeb(WOLCFG_SAM | WOLCFG_SAB, &regs->WOLCFGSet);
138862306a36Sopenharmony_ci		/*
138962306a36Sopenharmony_ci		 *	Back off algorithm use original IEEE standard
139062306a36Sopenharmony_ci		 */
139162306a36Sopenharmony_ci		BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), &regs->CFGB);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		/*
139462306a36Sopenharmony_ci		 *	Init CAM filter
139562306a36Sopenharmony_ci		 */
139662306a36Sopenharmony_ci		velocity_init_cam_filter(vptr);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		/*
139962306a36Sopenharmony_ci		 *	Set packet filter: Receive directed and broadcast address
140062306a36Sopenharmony_ci		 */
140162306a36Sopenharmony_ci		velocity_set_multi(netdev);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		/*
140462306a36Sopenharmony_ci		 *	Enable MII auto-polling
140562306a36Sopenharmony_ci		 */
140662306a36Sopenharmony_ci		enable_mii_autopoll(regs);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		setup_adaptive_interrupts(vptr);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		writel(vptr->rx.pool_dma, &regs->RDBaseLo);
141162306a36Sopenharmony_ci		writew(vptr->options.numrx - 1, &regs->RDCSize);
141262306a36Sopenharmony_ci		mac_rx_queue_run(regs);
141362306a36Sopenharmony_ci		mac_rx_queue_wake(regs);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		writew(vptr->options.numtx - 1, &regs->TDCSize);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		for (i = 0; i < vptr->tx.numq; i++) {
141862306a36Sopenharmony_ci			writel(vptr->tx.pool_dma[i], &regs->TDBaseLo[i]);
141962306a36Sopenharmony_ci			mac_tx_queue_run(regs, i);
142062306a36Sopenharmony_ci		}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci		init_flow_control_register(vptr);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		writel(CR0_STOP, &regs->CR0Clr);
142562306a36Sopenharmony_ci		writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), &regs->CR0Set);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		mii_status = velocity_get_opt_media_mode(vptr);
142862306a36Sopenharmony_ci		netif_stop_queue(netdev);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		mii_init(vptr, mii_status);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci		if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) {
143362306a36Sopenharmony_ci			velocity_print_link_status(vptr);
143462306a36Sopenharmony_ci			if (!(vptr->mii_status & VELOCITY_LINK_FAIL))
143562306a36Sopenharmony_ci				netif_wake_queue(netdev);
143662306a36Sopenharmony_ci		}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		enable_flow_control_ability(vptr);
143962306a36Sopenharmony_ci		mac_hw_mibs_init(regs);
144062306a36Sopenharmony_ci		mac_write_int_mask(vptr->int_mask, regs);
144162306a36Sopenharmony_ci		mac_clear_isr(regs);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic void velocity_give_many_rx_descs(struct velocity_info *vptr)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
144962306a36Sopenharmony_ci	int avail, dirty, unusable;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/*
145262306a36Sopenharmony_ci	 * RD number must be equal to 4X per hardware spec
145362306a36Sopenharmony_ci	 * (programming guide rev 1.20, p.13)
145462306a36Sopenharmony_ci	 */
145562306a36Sopenharmony_ci	if (vptr->rx.filled < 4)
145662306a36Sopenharmony_ci		return;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	wmb();
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	unusable = vptr->rx.filled & 0x0003;
146162306a36Sopenharmony_ci	dirty = vptr->rx.dirty - unusable;
146262306a36Sopenharmony_ci	for (avail = vptr->rx.filled & 0xfffc; avail; avail--) {
146362306a36Sopenharmony_ci		dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1;
146462306a36Sopenharmony_ci		vptr->rx.ring[dirty].rdesc0.len |= OWNED_BY_NIC;
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	writew(vptr->rx.filled & 0xfffc, &regs->RBRDU);
146862306a36Sopenharmony_ci	vptr->rx.filled = unusable;
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci/**
147262306a36Sopenharmony_ci *	velocity_init_dma_rings	-	set up DMA rings
147362306a36Sopenharmony_ci *	@vptr: Velocity to set up
147462306a36Sopenharmony_ci *
147562306a36Sopenharmony_ci *	Allocate PCI mapped DMA rings for the receive and transmit layer
147662306a36Sopenharmony_ci *	to use.
147762306a36Sopenharmony_ci */
147862306a36Sopenharmony_cistatic int velocity_init_dma_rings(struct velocity_info *vptr)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	struct velocity_opt *opt = &vptr->options;
148162306a36Sopenharmony_ci	const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc);
148262306a36Sopenharmony_ci	const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc);
148362306a36Sopenharmony_ci	dma_addr_t pool_dma;
148462306a36Sopenharmony_ci	void *pool;
148562306a36Sopenharmony_ci	unsigned int i;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	/*
148862306a36Sopenharmony_ci	 * Allocate all RD/TD rings a single pool.
148962306a36Sopenharmony_ci	 *
149062306a36Sopenharmony_ci	 * dma_alloc_coherent() fulfills the requirement for 64 bytes
149162306a36Sopenharmony_ci	 * alignment
149262306a36Sopenharmony_ci	 */
149362306a36Sopenharmony_ci	pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq +
149462306a36Sopenharmony_ci				    rx_ring_size, &pool_dma, GFP_ATOMIC);
149562306a36Sopenharmony_ci	if (!pool) {
149662306a36Sopenharmony_ci		dev_err(vptr->dev, "%s : DMA memory allocation failed.\n",
149762306a36Sopenharmony_ci			vptr->netdev->name);
149862306a36Sopenharmony_ci		return -ENOMEM;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	vptr->rx.ring = pool;
150262306a36Sopenharmony_ci	vptr->rx.pool_dma = pool_dma;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	pool += rx_ring_size;
150562306a36Sopenharmony_ci	pool_dma += rx_ring_size;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	for (i = 0; i < vptr->tx.numq; i++) {
150862306a36Sopenharmony_ci		vptr->tx.rings[i] = pool;
150962306a36Sopenharmony_ci		vptr->tx.pool_dma[i] = pool_dma;
151062306a36Sopenharmony_ci		pool += tx_ring_size;
151162306a36Sopenharmony_ci		pool_dma += tx_ring_size;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	return 0;
151562306a36Sopenharmony_ci}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_cistatic void velocity_set_rxbufsize(struct velocity_info *vptr, int mtu)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	vptr->rx.buf_sz = (mtu <= ETH_DATA_LEN) ? PKT_BUF_SZ : mtu + 32;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci/**
152362306a36Sopenharmony_ci *	velocity_alloc_rx_buf	-	allocate aligned receive buffer
152462306a36Sopenharmony_ci *	@vptr: velocity
152562306a36Sopenharmony_ci *	@idx: ring index
152662306a36Sopenharmony_ci *
152762306a36Sopenharmony_ci *	Allocate a new full sized buffer for the reception of a frame and
152862306a36Sopenharmony_ci *	map it into PCI space for the hardware to use. The hardware
152962306a36Sopenharmony_ci *	requires *64* byte alignment of the buffer which makes life
153062306a36Sopenharmony_ci *	less fun than would be ideal.
153162306a36Sopenharmony_ci */
153262306a36Sopenharmony_cistatic int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct rx_desc *rd = &(vptr->rx.ring[idx]);
153562306a36Sopenharmony_ci	struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64);
153862306a36Sopenharmony_ci	if (rd_info->skb == NULL)
153962306a36Sopenharmony_ci		return -ENOMEM;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/*
154262306a36Sopenharmony_ci	 *	Do the gymnastics to get the buffer head for data at
154362306a36Sopenharmony_ci	 *	64byte alignment.
154462306a36Sopenharmony_ci	 */
154562306a36Sopenharmony_ci	skb_reserve(rd_info->skb,
154662306a36Sopenharmony_ci			64 - ((unsigned long) rd_info->skb->data & 63));
154762306a36Sopenharmony_ci	rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data,
154862306a36Sopenharmony_ci					vptr->rx.buf_sz, DMA_FROM_DEVICE);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/*
155162306a36Sopenharmony_ci	 *	Fill in the descriptor to match
155262306a36Sopenharmony_ci	 */
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	*((u32 *) & (rd->rdesc0)) = 0;
155562306a36Sopenharmony_ci	rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN;
155662306a36Sopenharmony_ci	rd->pa_low = cpu_to_le32(rd_info->skb_dma);
155762306a36Sopenharmony_ci	rd->pa_high = 0;
155862306a36Sopenharmony_ci	return 0;
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cistatic int velocity_rx_refill(struct velocity_info *vptr)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	int dirty = vptr->rx.dirty, done = 0;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	do {
156762306a36Sopenharmony_ci		struct rx_desc *rd = vptr->rx.ring + dirty;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		/* Fine for an all zero Rx desc at init time as well */
157062306a36Sopenharmony_ci		if (rd->rdesc0.len & OWNED_BY_NIC)
157162306a36Sopenharmony_ci			break;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci		if (!vptr->rx.info[dirty].skb) {
157462306a36Sopenharmony_ci			if (velocity_alloc_rx_buf(vptr, dirty) < 0)
157562306a36Sopenharmony_ci				break;
157662306a36Sopenharmony_ci		}
157762306a36Sopenharmony_ci		done++;
157862306a36Sopenharmony_ci		dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0;
157962306a36Sopenharmony_ci	} while (dirty != vptr->rx.curr);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (done) {
158262306a36Sopenharmony_ci		vptr->rx.dirty = dirty;
158362306a36Sopenharmony_ci		vptr->rx.filled += done;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	return done;
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci/**
159062306a36Sopenharmony_ci *	velocity_free_rd_ring	-	free receive ring
159162306a36Sopenharmony_ci *	@vptr: velocity to clean up
159262306a36Sopenharmony_ci *
159362306a36Sopenharmony_ci *	Free the receive buffers for each ring slot and any
159462306a36Sopenharmony_ci *	attached socket buffers that need to go away.
159562306a36Sopenharmony_ci */
159662306a36Sopenharmony_cistatic void velocity_free_rd_ring(struct velocity_info *vptr)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	int i;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	if (vptr->rx.info == NULL)
160162306a36Sopenharmony_ci		return;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	for (i = 0; i < vptr->options.numrx; i++) {
160462306a36Sopenharmony_ci		struct velocity_rd_info *rd_info = &(vptr->rx.info[i]);
160562306a36Sopenharmony_ci		struct rx_desc *rd = vptr->rx.ring + i;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		memset(rd, 0, sizeof(*rd));
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci		if (!rd_info->skb)
161062306a36Sopenharmony_ci			continue;
161162306a36Sopenharmony_ci		dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz,
161262306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
161362306a36Sopenharmony_ci		rd_info->skb_dma = 0;
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci		dev_kfree_skb(rd_info->skb);
161662306a36Sopenharmony_ci		rd_info->skb = NULL;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	kfree(vptr->rx.info);
162062306a36Sopenharmony_ci	vptr->rx.info = NULL;
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci/**
162462306a36Sopenharmony_ci *	velocity_init_rd_ring	-	set up receive ring
162562306a36Sopenharmony_ci *	@vptr: velocity to configure
162662306a36Sopenharmony_ci *
162762306a36Sopenharmony_ci *	Allocate and set up the receive buffers for each ring slot and
162862306a36Sopenharmony_ci *	assign them to the network adapter.
162962306a36Sopenharmony_ci */
163062306a36Sopenharmony_cistatic int velocity_init_rd_ring(struct velocity_info *vptr)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	int ret = -ENOMEM;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	vptr->rx.info = kcalloc(vptr->options.numrx,
163562306a36Sopenharmony_ci				sizeof(struct velocity_rd_info), GFP_KERNEL);
163662306a36Sopenharmony_ci	if (!vptr->rx.info)
163762306a36Sopenharmony_ci		goto out;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	velocity_init_rx_ring_indexes(vptr);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if (velocity_rx_refill(vptr) != vptr->options.numrx) {
164262306a36Sopenharmony_ci		netdev_err(vptr->netdev, "failed to allocate RX buffer\n");
164362306a36Sopenharmony_ci		velocity_free_rd_ring(vptr);
164462306a36Sopenharmony_ci		goto out;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	ret = 0;
164862306a36Sopenharmony_ciout:
164962306a36Sopenharmony_ci	return ret;
165062306a36Sopenharmony_ci}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci/**
165362306a36Sopenharmony_ci *	velocity_init_td_ring	-	set up transmit ring
165462306a36Sopenharmony_ci *	@vptr:	velocity
165562306a36Sopenharmony_ci *
165662306a36Sopenharmony_ci *	Set up the transmit ring and chain the ring pointers together.
165762306a36Sopenharmony_ci *	Returns zero on success or a negative posix errno code for
165862306a36Sopenharmony_ci *	failure.
165962306a36Sopenharmony_ci */
166062306a36Sopenharmony_cistatic int velocity_init_td_ring(struct velocity_info *vptr)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	int j;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	/* Init the TD ring entries */
166562306a36Sopenharmony_ci	for (j = 0; j < vptr->tx.numq; j++) {
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci		vptr->tx.infos[j] = kcalloc(vptr->options.numtx,
166862306a36Sopenharmony_ci					    sizeof(struct velocity_td_info),
166962306a36Sopenharmony_ci					    GFP_KERNEL);
167062306a36Sopenharmony_ci		if (!vptr->tx.infos[j])	{
167162306a36Sopenharmony_ci			while (--j >= 0)
167262306a36Sopenharmony_ci				kfree(vptr->tx.infos[j]);
167362306a36Sopenharmony_ci			return -ENOMEM;
167462306a36Sopenharmony_ci		}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		vptr->tx.tail[j] = vptr->tx.curr[j] = vptr->tx.used[j] = 0;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci	return 0;
167962306a36Sopenharmony_ci}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci/**
168262306a36Sopenharmony_ci *	velocity_free_dma_rings	-	free PCI ring pointers
168362306a36Sopenharmony_ci *	@vptr: Velocity to free from
168462306a36Sopenharmony_ci *
168562306a36Sopenharmony_ci *	Clean up the PCI ring buffers allocated to this velocity.
168662306a36Sopenharmony_ci */
168762306a36Sopenharmony_cistatic void velocity_free_dma_rings(struct velocity_info *vptr)
168862306a36Sopenharmony_ci{
168962306a36Sopenharmony_ci	const int size = vptr->options.numrx * sizeof(struct rx_desc) +
169062306a36Sopenharmony_ci		vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma);
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic int velocity_init_rings(struct velocity_info *vptr, int mtu)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	int ret;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	velocity_set_rxbufsize(vptr, mtu);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	ret = velocity_init_dma_rings(vptr);
170262306a36Sopenharmony_ci	if (ret < 0)
170362306a36Sopenharmony_ci		goto out;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	ret = velocity_init_rd_ring(vptr);
170662306a36Sopenharmony_ci	if (ret < 0)
170762306a36Sopenharmony_ci		goto err_free_dma_rings_0;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	ret = velocity_init_td_ring(vptr);
171062306a36Sopenharmony_ci	if (ret < 0)
171162306a36Sopenharmony_ci		goto err_free_rd_ring_1;
171262306a36Sopenharmony_ciout:
171362306a36Sopenharmony_ci	return ret;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cierr_free_rd_ring_1:
171662306a36Sopenharmony_ci	velocity_free_rd_ring(vptr);
171762306a36Sopenharmony_cierr_free_dma_rings_0:
171862306a36Sopenharmony_ci	velocity_free_dma_rings(vptr);
171962306a36Sopenharmony_ci	goto out;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci/**
172362306a36Sopenharmony_ci *	velocity_free_tx_buf	-	free transmit buffer
172462306a36Sopenharmony_ci *	@vptr: velocity
172562306a36Sopenharmony_ci *	@tdinfo: buffer
172662306a36Sopenharmony_ci *	@td: transmit descriptor to free
172762306a36Sopenharmony_ci *
172862306a36Sopenharmony_ci *	Release an transmit buffer. If the buffer was preallocated then
172962306a36Sopenharmony_ci *	recycle it, if not then unmap the buffer.
173062306a36Sopenharmony_ci */
173162306a36Sopenharmony_cistatic void velocity_free_tx_buf(struct velocity_info *vptr,
173262306a36Sopenharmony_ci		struct velocity_td_info *tdinfo, struct tx_desc *td)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	struct sk_buff *skb = tdinfo->skb;
173562306a36Sopenharmony_ci	int i;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	/*
173862306a36Sopenharmony_ci	 *	Don't unmap the pre-allocated tx_bufs
173962306a36Sopenharmony_ci	 */
174062306a36Sopenharmony_ci	for (i = 0; i < tdinfo->nskb_dma; i++) {
174162306a36Sopenharmony_ci		size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		/* For scatter-gather */
174462306a36Sopenharmony_ci		if (skb_shinfo(skb)->nr_frags > 0)
174562306a36Sopenharmony_ci			pktlen = max_t(size_t, pktlen,
174662306a36Sopenharmony_ci				       td->td_buf[i].size & ~TD_QUEUE);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci		dma_unmap_single(vptr->dev, tdinfo->skb_dma[i],
174962306a36Sopenharmony_ci				 le16_to_cpu(pktlen), DMA_TO_DEVICE);
175062306a36Sopenharmony_ci	}
175162306a36Sopenharmony_ci	dev_consume_skb_irq(skb);
175262306a36Sopenharmony_ci	tdinfo->skb = NULL;
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci/*
175662306a36Sopenharmony_ci *	FIXME: could we merge this with velocity_free_tx_buf ?
175762306a36Sopenharmony_ci */
175862306a36Sopenharmony_cistatic void velocity_free_td_ring_entry(struct velocity_info *vptr,
175962306a36Sopenharmony_ci							 int q, int n)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct velocity_td_info *td_info = &(vptr->tx.infos[q][n]);
176262306a36Sopenharmony_ci	int i;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	if (td_info == NULL)
176562306a36Sopenharmony_ci		return;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (td_info->skb) {
176862306a36Sopenharmony_ci		for (i = 0; i < td_info->nskb_dma; i++) {
176962306a36Sopenharmony_ci			if (td_info->skb_dma[i]) {
177062306a36Sopenharmony_ci				dma_unmap_single(vptr->dev, td_info->skb_dma[i],
177162306a36Sopenharmony_ci					td_info->skb->len, DMA_TO_DEVICE);
177262306a36Sopenharmony_ci				td_info->skb_dma[i] = 0;
177362306a36Sopenharmony_ci			}
177462306a36Sopenharmony_ci		}
177562306a36Sopenharmony_ci		dev_kfree_skb(td_info->skb);
177662306a36Sopenharmony_ci		td_info->skb = NULL;
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci/**
178162306a36Sopenharmony_ci *	velocity_free_td_ring	-	free td ring
178262306a36Sopenharmony_ci *	@vptr: velocity
178362306a36Sopenharmony_ci *
178462306a36Sopenharmony_ci *	Free up the transmit ring for this particular velocity adapter.
178562306a36Sopenharmony_ci *	We free the ring contents but not the ring itself.
178662306a36Sopenharmony_ci */
178762306a36Sopenharmony_cistatic void velocity_free_td_ring(struct velocity_info *vptr)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	int i, j;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	for (j = 0; j < vptr->tx.numq; j++) {
179262306a36Sopenharmony_ci		if (vptr->tx.infos[j] == NULL)
179362306a36Sopenharmony_ci			continue;
179462306a36Sopenharmony_ci		for (i = 0; i < vptr->options.numtx; i++)
179562306a36Sopenharmony_ci			velocity_free_td_ring_entry(vptr, j, i);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci		kfree(vptr->tx.infos[j]);
179862306a36Sopenharmony_ci		vptr->tx.infos[j] = NULL;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_cistatic void velocity_free_rings(struct velocity_info *vptr)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	velocity_free_td_ring(vptr);
180562306a36Sopenharmony_ci	velocity_free_rd_ring(vptr);
180662306a36Sopenharmony_ci	velocity_free_dma_rings(vptr);
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci/**
181062306a36Sopenharmony_ci *	velocity_error	-	handle error from controller
181162306a36Sopenharmony_ci *	@vptr: velocity
181262306a36Sopenharmony_ci *	@status: card status
181362306a36Sopenharmony_ci *
181462306a36Sopenharmony_ci *	Process an error report from the hardware and attempt to recover
181562306a36Sopenharmony_ci *	the card itself. At the moment we cannot recover from some
181662306a36Sopenharmony_ci *	theoretically impossible errors but this could be fixed using
181762306a36Sopenharmony_ci *	the pci_device_failed logic to bounce the hardware
181862306a36Sopenharmony_ci *
181962306a36Sopenharmony_ci */
182062306a36Sopenharmony_cistatic void velocity_error(struct velocity_info *vptr, int status)
182162306a36Sopenharmony_ci{
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	if (status & ISR_TXSTLI) {
182462306a36Sopenharmony_ci		struct mac_regs __iomem *regs = vptr->mac_regs;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		netdev_err(vptr->netdev, "TD structure error TDindex=%hx\n",
182762306a36Sopenharmony_ci			   readw(&regs->TDIdx[0]));
182862306a36Sopenharmony_ci		BYTE_REG_BITS_ON(TXESR_TDSTR, &regs->TXESR);
182962306a36Sopenharmony_ci		writew(TRDCSR_RUN, &regs->TDCSRClr);
183062306a36Sopenharmony_ci		netif_stop_queue(vptr->netdev);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci		/* FIXME: port over the pci_device_failed code and use it
183362306a36Sopenharmony_ci		   here */
183462306a36Sopenharmony_ci	}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	if (status & ISR_SRCI) {
183762306a36Sopenharmony_ci		struct mac_regs __iomem *regs = vptr->mac_regs;
183862306a36Sopenharmony_ci		int linked;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci		if (vptr->options.spd_dpx == SPD_DPX_AUTO) {
184162306a36Sopenharmony_ci			vptr->mii_status = check_connection_type(regs);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci			/*
184462306a36Sopenharmony_ci			 *	If it is a 3119, disable frame bursting in
184562306a36Sopenharmony_ci			 *	halfduplex mode and enable it in fullduplex
184662306a36Sopenharmony_ci			 *	 mode
184762306a36Sopenharmony_ci			 */
184862306a36Sopenharmony_ci			if (vptr->rev_id < REV_ID_VT3216_A0) {
184962306a36Sopenharmony_ci				if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
185062306a36Sopenharmony_ci					BYTE_REG_BITS_ON(TCR_TB2BDIS, &regs->TCR);
185162306a36Sopenharmony_ci				else
185262306a36Sopenharmony_ci					BYTE_REG_BITS_OFF(TCR_TB2BDIS, &regs->TCR);
185362306a36Sopenharmony_ci			}
185462306a36Sopenharmony_ci			/*
185562306a36Sopenharmony_ci			 *	Only enable CD heart beat counter in 10HD mode
185662306a36Sopenharmony_ci			 */
185762306a36Sopenharmony_ci			if (!(vptr->mii_status & VELOCITY_DUPLEX_FULL) && (vptr->mii_status & VELOCITY_SPEED_10))
185862306a36Sopenharmony_ci				BYTE_REG_BITS_OFF(TESTCFG_HBDIS, &regs->TESTCFG);
185962306a36Sopenharmony_ci			else
186062306a36Sopenharmony_ci				BYTE_REG_BITS_ON(TESTCFG_HBDIS, &regs->TESTCFG);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci			setup_queue_timers(vptr);
186362306a36Sopenharmony_ci		}
186462306a36Sopenharmony_ci		/*
186562306a36Sopenharmony_ci		 *	Get link status from PHYSR0
186662306a36Sopenharmony_ci		 */
186762306a36Sopenharmony_ci		linked = readb(&regs->PHYSR0) & PHYSR0_LINKGD;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci		if (linked) {
187062306a36Sopenharmony_ci			vptr->mii_status &= ~VELOCITY_LINK_FAIL;
187162306a36Sopenharmony_ci			netif_carrier_on(vptr->netdev);
187262306a36Sopenharmony_ci		} else {
187362306a36Sopenharmony_ci			vptr->mii_status |= VELOCITY_LINK_FAIL;
187462306a36Sopenharmony_ci			netif_carrier_off(vptr->netdev);
187562306a36Sopenharmony_ci		}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci		velocity_print_link_status(vptr);
187862306a36Sopenharmony_ci		enable_flow_control_ability(vptr);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci		/*
188162306a36Sopenharmony_ci		 *	Re-enable auto-polling because SRCI will disable
188262306a36Sopenharmony_ci		 *	auto-polling
188362306a36Sopenharmony_ci		 */
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci		enable_mii_autopoll(regs);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci		if (vptr->mii_status & VELOCITY_LINK_FAIL)
188862306a36Sopenharmony_ci			netif_stop_queue(vptr->netdev);
188962306a36Sopenharmony_ci		else
189062306a36Sopenharmony_ci			netif_wake_queue(vptr->netdev);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci	if (status & ISR_MIBFI)
189462306a36Sopenharmony_ci		velocity_update_hw_mibs(vptr);
189562306a36Sopenharmony_ci	if (status & ISR_LSTEI)
189662306a36Sopenharmony_ci		mac_rx_queue_wake(vptr->mac_regs);
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci/**
190062306a36Sopenharmony_ci *	velocity_tx_srv		-	transmit interrupt service
190162306a36Sopenharmony_ci *	@vptr: Velocity
190262306a36Sopenharmony_ci *
190362306a36Sopenharmony_ci *	Scan the queues looking for transmitted packets that
190462306a36Sopenharmony_ci *	we can complete and clean up. Update any statistics as
190562306a36Sopenharmony_ci *	necessary/
190662306a36Sopenharmony_ci */
190762306a36Sopenharmony_cistatic int velocity_tx_srv(struct velocity_info *vptr)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	struct tx_desc *td;
191062306a36Sopenharmony_ci	int qnum;
191162306a36Sopenharmony_ci	int full = 0;
191262306a36Sopenharmony_ci	int idx;
191362306a36Sopenharmony_ci	int works = 0;
191462306a36Sopenharmony_ci	struct velocity_td_info *tdinfo;
191562306a36Sopenharmony_ci	struct net_device_stats *stats = &vptr->netdev->stats;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	for (qnum = 0; qnum < vptr->tx.numq; qnum++) {
191862306a36Sopenharmony_ci		for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0;
191962306a36Sopenharmony_ci			idx = (idx + 1) % vptr->options.numtx) {
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci			/*
192262306a36Sopenharmony_ci			 *	Get Tx Descriptor
192362306a36Sopenharmony_ci			 */
192462306a36Sopenharmony_ci			td = &(vptr->tx.rings[qnum][idx]);
192562306a36Sopenharmony_ci			tdinfo = &(vptr->tx.infos[qnum][idx]);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci			if (td->tdesc0.len & OWNED_BY_NIC)
192862306a36Sopenharmony_ci				break;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci			if ((works++ > 15))
193162306a36Sopenharmony_ci				break;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci			if (td->tdesc0.TSR & TSR0_TERR) {
193462306a36Sopenharmony_ci				stats->tx_errors++;
193562306a36Sopenharmony_ci				stats->tx_dropped++;
193662306a36Sopenharmony_ci				if (td->tdesc0.TSR & TSR0_CDH)
193762306a36Sopenharmony_ci					stats->tx_heartbeat_errors++;
193862306a36Sopenharmony_ci				if (td->tdesc0.TSR & TSR0_CRS)
193962306a36Sopenharmony_ci					stats->tx_carrier_errors++;
194062306a36Sopenharmony_ci				if (td->tdesc0.TSR & TSR0_ABT)
194162306a36Sopenharmony_ci					stats->tx_aborted_errors++;
194262306a36Sopenharmony_ci				if (td->tdesc0.TSR & TSR0_OWC)
194362306a36Sopenharmony_ci					stats->tx_window_errors++;
194462306a36Sopenharmony_ci			} else {
194562306a36Sopenharmony_ci				stats->tx_packets++;
194662306a36Sopenharmony_ci				stats->tx_bytes += tdinfo->skb->len;
194762306a36Sopenharmony_ci			}
194862306a36Sopenharmony_ci			velocity_free_tx_buf(vptr, tdinfo, td);
194962306a36Sopenharmony_ci			vptr->tx.used[qnum]--;
195062306a36Sopenharmony_ci		}
195162306a36Sopenharmony_ci		vptr->tx.tail[qnum] = idx;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci		if (AVAIL_TD(vptr, qnum) < 1)
195462306a36Sopenharmony_ci			full = 1;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci	/*
195762306a36Sopenharmony_ci	 *	Look to see if we should kick the transmit network
195862306a36Sopenharmony_ci	 *	layer for more work.
195962306a36Sopenharmony_ci	 */
196062306a36Sopenharmony_ci	if (netif_queue_stopped(vptr->netdev) && (full == 0) &&
196162306a36Sopenharmony_ci	    (!(vptr->mii_status & VELOCITY_LINK_FAIL))) {
196262306a36Sopenharmony_ci		netif_wake_queue(vptr->netdev);
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci	return works;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/**
196862306a36Sopenharmony_ci *	velocity_rx_csum	-	checksum process
196962306a36Sopenharmony_ci *	@rd: receive packet descriptor
197062306a36Sopenharmony_ci *	@skb: network layer packet buffer
197162306a36Sopenharmony_ci *
197262306a36Sopenharmony_ci *	Process the status bits for the received packet and determine
197362306a36Sopenharmony_ci *	if the checksum was computed and verified by the hardware
197462306a36Sopenharmony_ci */
197562306a36Sopenharmony_cistatic inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb)
197662306a36Sopenharmony_ci{
197762306a36Sopenharmony_ci	skb_checksum_none_assert(skb);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (rd->rdesc1.CSM & CSM_IPKT) {
198062306a36Sopenharmony_ci		if (rd->rdesc1.CSM & CSM_IPOK) {
198162306a36Sopenharmony_ci			if ((rd->rdesc1.CSM & CSM_TCPKT) ||
198262306a36Sopenharmony_ci					(rd->rdesc1.CSM & CSM_UDPKT)) {
198362306a36Sopenharmony_ci				if (!(rd->rdesc1.CSM & CSM_TUPOK))
198462306a36Sopenharmony_ci					return;
198562306a36Sopenharmony_ci			}
198662306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
198762306a36Sopenharmony_ci		}
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci/**
199262306a36Sopenharmony_ci *	velocity_rx_copy	-	in place Rx copy for small packets
199362306a36Sopenharmony_ci *	@rx_skb: network layer packet buffer candidate
199462306a36Sopenharmony_ci *	@pkt_size: received data size
199562306a36Sopenharmony_ci *	@vptr: velocity adapter
199662306a36Sopenharmony_ci *
199762306a36Sopenharmony_ci *	Replace the current skb that is scheduled for Rx processing by a
199862306a36Sopenharmony_ci *	shorter, immediately allocated skb, if the received packet is small
199962306a36Sopenharmony_ci *	enough. This function returns a negative value if the received
200062306a36Sopenharmony_ci *	packet is too big or if memory is exhausted.
200162306a36Sopenharmony_ci */
200262306a36Sopenharmony_cistatic int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size,
200362306a36Sopenharmony_ci			    struct velocity_info *vptr)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	int ret = -1;
200662306a36Sopenharmony_ci	if (pkt_size < rx_copybreak) {
200762306a36Sopenharmony_ci		struct sk_buff *new_skb;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci		new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size);
201062306a36Sopenharmony_ci		if (new_skb) {
201162306a36Sopenharmony_ci			new_skb->ip_summed = rx_skb[0]->ip_summed;
201262306a36Sopenharmony_ci			skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size);
201362306a36Sopenharmony_ci			*rx_skb = new_skb;
201462306a36Sopenharmony_ci			ret = 0;
201562306a36Sopenharmony_ci		}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci	return ret;
201962306a36Sopenharmony_ci}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci/**
202262306a36Sopenharmony_ci *	velocity_iph_realign	-	IP header alignment
202362306a36Sopenharmony_ci *	@vptr: velocity we are handling
202462306a36Sopenharmony_ci *	@skb: network layer packet buffer
202562306a36Sopenharmony_ci *	@pkt_size: received data size
202662306a36Sopenharmony_ci *
202762306a36Sopenharmony_ci *	Align IP header on a 2 bytes boundary. This behavior can be
202862306a36Sopenharmony_ci *	configured by the user.
202962306a36Sopenharmony_ci */
203062306a36Sopenharmony_cistatic inline void velocity_iph_realign(struct velocity_info *vptr,
203162306a36Sopenharmony_ci					struct sk_buff *skb, int pkt_size)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) {
203462306a36Sopenharmony_ci		memmove(skb->data + 2, skb->data, pkt_size);
203562306a36Sopenharmony_ci		skb_reserve(skb, 2);
203662306a36Sopenharmony_ci	}
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci/**
204062306a36Sopenharmony_ci *	velocity_receive_frame	-	received packet processor
204162306a36Sopenharmony_ci *	@vptr: velocity we are handling
204262306a36Sopenharmony_ci *	@idx: ring index
204362306a36Sopenharmony_ci *
204462306a36Sopenharmony_ci *	A packet has arrived. We process the packet and if appropriate
204562306a36Sopenharmony_ci *	pass the frame up the network stack
204662306a36Sopenharmony_ci */
204762306a36Sopenharmony_cistatic int velocity_receive_frame(struct velocity_info *vptr, int idx)
204862306a36Sopenharmony_ci{
204962306a36Sopenharmony_ci	struct net_device_stats *stats = &vptr->netdev->stats;
205062306a36Sopenharmony_ci	struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]);
205162306a36Sopenharmony_ci	struct rx_desc *rd = &(vptr->rx.ring[idx]);
205262306a36Sopenharmony_ci	int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff;
205362306a36Sopenharmony_ci	struct sk_buff *skb;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (unlikely(rd->rdesc0.RSR & (RSR_STP | RSR_EDP | RSR_RL))) {
205662306a36Sopenharmony_ci		if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP))
205762306a36Sopenharmony_ci			netdev_err(vptr->netdev, "received frame spans multiple RDs\n");
205862306a36Sopenharmony_ci		stats->rx_length_errors++;
205962306a36Sopenharmony_ci		return -EINVAL;
206062306a36Sopenharmony_ci	}
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	if (rd->rdesc0.RSR & RSR_MAR)
206362306a36Sopenharmony_ci		stats->multicast++;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	skb = rd_info->skb;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma,
206862306a36Sopenharmony_ci				    vptr->rx.buf_sz, DMA_FROM_DEVICE);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	velocity_rx_csum(rd, skb);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) {
207362306a36Sopenharmony_ci		velocity_iph_realign(vptr, skb, pkt_len);
207462306a36Sopenharmony_ci		rd_info->skb = NULL;
207562306a36Sopenharmony_ci		dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz,
207662306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
207762306a36Sopenharmony_ci	} else {
207862306a36Sopenharmony_ci		dma_sync_single_for_device(vptr->dev, rd_info->skb_dma,
207962306a36Sopenharmony_ci					   vptr->rx.buf_sz, DMA_FROM_DEVICE);
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	skb_put(skb, pkt_len - 4);
208362306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, vptr->netdev);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (rd->rdesc0.RSR & RSR_DETAG) {
208662306a36Sopenharmony_ci		u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG));
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
208962306a36Sopenharmony_ci	}
209062306a36Sopenharmony_ci	netif_receive_skb(skb);
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	stats->rx_bytes += pkt_len;
209362306a36Sopenharmony_ci	stats->rx_packets++;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	return 0;
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci/**
209962306a36Sopenharmony_ci *	velocity_rx_srv		-	service RX interrupt
210062306a36Sopenharmony_ci *	@vptr: velocity
210162306a36Sopenharmony_ci *	@budget_left: remaining budget
210262306a36Sopenharmony_ci *
210362306a36Sopenharmony_ci *	Walk the receive ring of the velocity adapter and remove
210462306a36Sopenharmony_ci *	any received packets from the receive queue. Hand the ring
210562306a36Sopenharmony_ci *	slots back to the adapter for reuse.
210662306a36Sopenharmony_ci */
210762306a36Sopenharmony_cistatic int velocity_rx_srv(struct velocity_info *vptr, int budget_left)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	struct net_device_stats *stats = &vptr->netdev->stats;
211062306a36Sopenharmony_ci	int rd_curr = vptr->rx.curr;
211162306a36Sopenharmony_ci	int works = 0;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	while (works < budget_left) {
211462306a36Sopenharmony_ci		struct rx_desc *rd = vptr->rx.ring + rd_curr;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci		if (!vptr->rx.info[rd_curr].skb)
211762306a36Sopenharmony_ci			break;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci		if (rd->rdesc0.len & OWNED_BY_NIC)
212062306a36Sopenharmony_ci			break;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci		rmb();
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci		/*
212562306a36Sopenharmony_ci		 *	Don't drop CE or RL error frame although RXOK is off
212662306a36Sopenharmony_ci		 */
212762306a36Sopenharmony_ci		if (rd->rdesc0.RSR & (RSR_RXOK | RSR_CE | RSR_RL)) {
212862306a36Sopenharmony_ci			if (velocity_receive_frame(vptr, rd_curr) < 0)
212962306a36Sopenharmony_ci				stats->rx_dropped++;
213062306a36Sopenharmony_ci		} else {
213162306a36Sopenharmony_ci			if (rd->rdesc0.RSR & RSR_CRC)
213262306a36Sopenharmony_ci				stats->rx_crc_errors++;
213362306a36Sopenharmony_ci			if (rd->rdesc0.RSR & RSR_FAE)
213462306a36Sopenharmony_ci				stats->rx_frame_errors++;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci			stats->rx_dropped++;
213762306a36Sopenharmony_ci		}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		rd->size |= RX_INTEN;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci		rd_curr++;
214262306a36Sopenharmony_ci		if (rd_curr >= vptr->options.numrx)
214362306a36Sopenharmony_ci			rd_curr = 0;
214462306a36Sopenharmony_ci		works++;
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	vptr->rx.curr = rd_curr;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	if ((works > 0) && (velocity_rx_refill(vptr) > 0))
215062306a36Sopenharmony_ci		velocity_give_many_rx_descs(vptr);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	VAR_USED(stats);
215362306a36Sopenharmony_ci	return works;
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_cistatic int velocity_poll(struct napi_struct *napi, int budget)
215762306a36Sopenharmony_ci{
215862306a36Sopenharmony_ci	struct velocity_info *vptr = container_of(napi,
215962306a36Sopenharmony_ci			struct velocity_info, napi);
216062306a36Sopenharmony_ci	unsigned int rx_done;
216162306a36Sopenharmony_ci	unsigned long flags;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	/*
216462306a36Sopenharmony_ci	 * Do rx and tx twice for performance (taken from the VIA
216562306a36Sopenharmony_ci	 * out-of-tree driver).
216662306a36Sopenharmony_ci	 */
216762306a36Sopenharmony_ci	rx_done = velocity_rx_srv(vptr, budget);
216862306a36Sopenharmony_ci	spin_lock_irqsave(&vptr->lock, flags);
216962306a36Sopenharmony_ci	velocity_tx_srv(vptr);
217062306a36Sopenharmony_ci	/* If budget not fully consumed, exit the polling mode */
217162306a36Sopenharmony_ci	if (rx_done < budget) {
217262306a36Sopenharmony_ci		napi_complete_done(napi, rx_done);
217362306a36Sopenharmony_ci		mac_enable_int(vptr->mac_regs);
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vptr->lock, flags);
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	return rx_done;
217862306a36Sopenharmony_ci}
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci/**
218162306a36Sopenharmony_ci *	velocity_intr		-	interrupt callback
218262306a36Sopenharmony_ci *	@irq: interrupt number
218362306a36Sopenharmony_ci *	@dev_instance: interrupting device
218462306a36Sopenharmony_ci *
218562306a36Sopenharmony_ci *	Called whenever an interrupt is generated by the velocity
218662306a36Sopenharmony_ci *	adapter IRQ line. We may not be the source of the interrupt
218762306a36Sopenharmony_ci *	and need to identify initially if we are, and if not exit as
218862306a36Sopenharmony_ci *	efficiently as possible.
218962306a36Sopenharmony_ci */
219062306a36Sopenharmony_cistatic irqreturn_t velocity_intr(int irq, void *dev_instance)
219162306a36Sopenharmony_ci{
219262306a36Sopenharmony_ci	struct net_device *dev = dev_instance;
219362306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
219462306a36Sopenharmony_ci	u32 isr_status;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	spin_lock(&vptr->lock);
219762306a36Sopenharmony_ci	isr_status = mac_read_isr(vptr->mac_regs);
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	/* Not us ? */
220062306a36Sopenharmony_ci	if (isr_status == 0) {
220162306a36Sopenharmony_ci		spin_unlock(&vptr->lock);
220262306a36Sopenharmony_ci		return IRQ_NONE;
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	/* Ack the interrupt */
220662306a36Sopenharmony_ci	mac_write_isr(vptr->mac_regs, isr_status);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	if (likely(napi_schedule_prep(&vptr->napi))) {
220962306a36Sopenharmony_ci		mac_disable_int(vptr->mac_regs);
221062306a36Sopenharmony_ci		__napi_schedule(&vptr->napi);
221162306a36Sopenharmony_ci	}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
221462306a36Sopenharmony_ci		velocity_error(vptr, isr_status);
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	spin_unlock(&vptr->lock);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	return IRQ_HANDLED;
221962306a36Sopenharmony_ci}
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci/**
222262306a36Sopenharmony_ci *	velocity_open		-	interface activation callback
222362306a36Sopenharmony_ci *	@dev: network layer device to open
222462306a36Sopenharmony_ci *
222562306a36Sopenharmony_ci *	Called when the network layer brings the interface up. Returns
222662306a36Sopenharmony_ci *	a negative posix error code on failure, or zero on success.
222762306a36Sopenharmony_ci *
222862306a36Sopenharmony_ci *	All the ring allocation and set up is done on open for this
222962306a36Sopenharmony_ci *	adapter to minimise memory usage when inactive
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_cistatic int velocity_open(struct net_device *dev)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
223462306a36Sopenharmony_ci	int ret;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	ret = velocity_init_rings(vptr, dev->mtu);
223762306a36Sopenharmony_ci	if (ret < 0)
223862306a36Sopenharmony_ci		goto out;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	/* Ensure chip is running */
224162306a36Sopenharmony_ci	velocity_set_power_state(vptr, PCI_D0);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	velocity_init_registers(vptr, VELOCITY_INIT_COLD);
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED,
224662306a36Sopenharmony_ci			  dev->name, dev);
224762306a36Sopenharmony_ci	if (ret < 0) {
224862306a36Sopenharmony_ci		/* Power down the chip */
224962306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D3hot);
225062306a36Sopenharmony_ci		velocity_free_rings(vptr);
225162306a36Sopenharmony_ci		goto out;
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	velocity_give_many_rx_descs(vptr);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	mac_enable_int(vptr->mac_regs);
225762306a36Sopenharmony_ci	netif_start_queue(dev);
225862306a36Sopenharmony_ci	napi_enable(&vptr->napi);
225962306a36Sopenharmony_ci	vptr->flags |= VELOCITY_FLAGS_OPENED;
226062306a36Sopenharmony_ciout:
226162306a36Sopenharmony_ci	return ret;
226262306a36Sopenharmony_ci}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci/**
226562306a36Sopenharmony_ci *	velocity_shutdown	-	shut down the chip
226662306a36Sopenharmony_ci *	@vptr: velocity to deactivate
226762306a36Sopenharmony_ci *
226862306a36Sopenharmony_ci *	Shuts down the internal operations of the velocity and
226962306a36Sopenharmony_ci *	disables interrupts, autopolling, transmit and receive
227062306a36Sopenharmony_ci */
227162306a36Sopenharmony_cistatic void velocity_shutdown(struct velocity_info *vptr)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
227462306a36Sopenharmony_ci	mac_disable_int(regs);
227562306a36Sopenharmony_ci	writel(CR0_STOP, &regs->CR0Set);
227662306a36Sopenharmony_ci	writew(0xFFFF, &regs->TDCSRClr);
227762306a36Sopenharmony_ci	writeb(0xFF, &regs->RDCSRClr);
227862306a36Sopenharmony_ci	safe_disable_mii_autopoll(regs);
227962306a36Sopenharmony_ci	mac_clear_isr(regs);
228062306a36Sopenharmony_ci}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci/**
228362306a36Sopenharmony_ci *	velocity_change_mtu	-	MTU change callback
228462306a36Sopenharmony_ci *	@dev: network device
228562306a36Sopenharmony_ci *	@new_mtu: desired MTU
228662306a36Sopenharmony_ci *
228762306a36Sopenharmony_ci *	Handle requests from the networking layer for MTU change on
228862306a36Sopenharmony_ci *	this interface. It gets called on a change by the network layer.
228962306a36Sopenharmony_ci *	Return zero for success or negative posix error code.
229062306a36Sopenharmony_ci */
229162306a36Sopenharmony_cistatic int velocity_change_mtu(struct net_device *dev, int new_mtu)
229262306a36Sopenharmony_ci{
229362306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
229462306a36Sopenharmony_ci	int ret = 0;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	if (!netif_running(dev)) {
229762306a36Sopenharmony_ci		dev->mtu = new_mtu;
229862306a36Sopenharmony_ci		goto out_0;
229962306a36Sopenharmony_ci	}
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	if (dev->mtu != new_mtu) {
230262306a36Sopenharmony_ci		struct velocity_info *tmp_vptr;
230362306a36Sopenharmony_ci		unsigned long flags;
230462306a36Sopenharmony_ci		struct rx_info rx;
230562306a36Sopenharmony_ci		struct tx_info tx;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci		tmp_vptr = kzalloc(sizeof(*tmp_vptr), GFP_KERNEL);
230862306a36Sopenharmony_ci		if (!tmp_vptr) {
230962306a36Sopenharmony_ci			ret = -ENOMEM;
231062306a36Sopenharmony_ci			goto out_0;
231162306a36Sopenharmony_ci		}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		tmp_vptr->netdev = dev;
231462306a36Sopenharmony_ci		tmp_vptr->pdev = vptr->pdev;
231562306a36Sopenharmony_ci		tmp_vptr->dev = vptr->dev;
231662306a36Sopenharmony_ci		tmp_vptr->options = vptr->options;
231762306a36Sopenharmony_ci		tmp_vptr->tx.numq = vptr->tx.numq;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci		ret = velocity_init_rings(tmp_vptr, new_mtu);
232062306a36Sopenharmony_ci		if (ret < 0)
232162306a36Sopenharmony_ci			goto out_free_tmp_vptr_1;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci		napi_disable(&vptr->napi);
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci		spin_lock_irqsave(&vptr->lock, flags);
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci		netif_stop_queue(dev);
232862306a36Sopenharmony_ci		velocity_shutdown(vptr);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci		rx = vptr->rx;
233162306a36Sopenharmony_ci		tx = vptr->tx;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci		vptr->rx = tmp_vptr->rx;
233462306a36Sopenharmony_ci		vptr->tx = tmp_vptr->tx;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci		tmp_vptr->rx = rx;
233762306a36Sopenharmony_ci		tmp_vptr->tx = tx;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci		dev->mtu = new_mtu;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci		velocity_init_registers(vptr, VELOCITY_INIT_COLD);
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci		velocity_give_many_rx_descs(vptr);
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci		napi_enable(&vptr->napi);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci		mac_enable_int(vptr->mac_regs);
234862306a36Sopenharmony_ci		netif_start_queue(dev);
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vptr->lock, flags);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci		velocity_free_rings(tmp_vptr);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ciout_free_tmp_vptr_1:
235562306a36Sopenharmony_ci		kfree(tmp_vptr);
235662306a36Sopenharmony_ci	}
235762306a36Sopenharmony_ciout_0:
235862306a36Sopenharmony_ci	return ret;
235962306a36Sopenharmony_ci}
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
236262306a36Sopenharmony_ci/**
236362306a36Sopenharmony_ci *  velocity_poll_controller		-	Velocity Poll controller function
236462306a36Sopenharmony_ci *  @dev: network device
236562306a36Sopenharmony_ci *
236662306a36Sopenharmony_ci *
236762306a36Sopenharmony_ci *  Used by NETCONSOLE and other diagnostic tools to allow network I/P
236862306a36Sopenharmony_ci *  with interrupts disabled.
236962306a36Sopenharmony_ci */
237062306a36Sopenharmony_cistatic void velocity_poll_controller(struct net_device *dev)
237162306a36Sopenharmony_ci{
237262306a36Sopenharmony_ci	disable_irq(dev->irq);
237362306a36Sopenharmony_ci	velocity_intr(dev->irq, dev);
237462306a36Sopenharmony_ci	enable_irq(dev->irq);
237562306a36Sopenharmony_ci}
237662306a36Sopenharmony_ci#endif
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci/**
237962306a36Sopenharmony_ci *	velocity_mii_ioctl		-	MII ioctl handler
238062306a36Sopenharmony_ci *	@dev: network device
238162306a36Sopenharmony_ci *	@ifr: the ifreq block for the ioctl
238262306a36Sopenharmony_ci *	@cmd: the command
238362306a36Sopenharmony_ci *
238462306a36Sopenharmony_ci *	Process MII requests made via ioctl from the network layer. These
238562306a36Sopenharmony_ci *	are used by tools like kudzu to interrogate the link state of the
238662306a36Sopenharmony_ci *	hardware
238762306a36Sopenharmony_ci */
238862306a36Sopenharmony_cistatic int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
238962306a36Sopenharmony_ci{
239062306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
239162306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
239262306a36Sopenharmony_ci	unsigned long flags;
239362306a36Sopenharmony_ci	struct mii_ioctl_data *miidata = if_mii(ifr);
239462306a36Sopenharmony_ci	int err;
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	switch (cmd) {
239762306a36Sopenharmony_ci	case SIOCGMIIPHY:
239862306a36Sopenharmony_ci		miidata->phy_id = readb(&regs->MIIADR) & 0x1f;
239962306a36Sopenharmony_ci		break;
240062306a36Sopenharmony_ci	case SIOCGMIIREG:
240162306a36Sopenharmony_ci		if (velocity_mii_read(vptr->mac_regs, miidata->reg_num & 0x1f, &(miidata->val_out)) < 0)
240262306a36Sopenharmony_ci			return -ETIMEDOUT;
240362306a36Sopenharmony_ci		break;
240462306a36Sopenharmony_ci	case SIOCSMIIREG:
240562306a36Sopenharmony_ci		spin_lock_irqsave(&vptr->lock, flags);
240662306a36Sopenharmony_ci		err = velocity_mii_write(vptr->mac_regs, miidata->reg_num & 0x1f, miidata->val_in);
240762306a36Sopenharmony_ci		spin_unlock_irqrestore(&vptr->lock, flags);
240862306a36Sopenharmony_ci		check_connection_type(vptr->mac_regs);
240962306a36Sopenharmony_ci		if (err)
241062306a36Sopenharmony_ci			return err;
241162306a36Sopenharmony_ci		break;
241262306a36Sopenharmony_ci	default:
241362306a36Sopenharmony_ci		return -EOPNOTSUPP;
241462306a36Sopenharmony_ci	}
241562306a36Sopenharmony_ci	return 0;
241662306a36Sopenharmony_ci}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci/**
241962306a36Sopenharmony_ci *	velocity_ioctl		-	ioctl entry point
242062306a36Sopenharmony_ci *	@dev: network device
242162306a36Sopenharmony_ci *	@rq: interface request ioctl
242262306a36Sopenharmony_ci *	@cmd: command code
242362306a36Sopenharmony_ci *
242462306a36Sopenharmony_ci *	Called when the user issues an ioctl request to the network
242562306a36Sopenharmony_ci *	device in question. The velocity interface supports MII.
242662306a36Sopenharmony_ci */
242762306a36Sopenharmony_cistatic int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
242862306a36Sopenharmony_ci{
242962306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
243062306a36Sopenharmony_ci	int ret;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	/* If we are asked for information and the device is power
243362306a36Sopenharmony_ci	   saving then we need to bring the device back up to talk to it */
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (!netif_running(dev))
243662306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D0);
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	switch (cmd) {
243962306a36Sopenharmony_ci	case SIOCGMIIPHY:	/* Get address of MII PHY in use. */
244062306a36Sopenharmony_ci	case SIOCGMIIREG:	/* Read MII PHY register. */
244162306a36Sopenharmony_ci	case SIOCSMIIREG:	/* Write to MII PHY register. */
244262306a36Sopenharmony_ci		ret = velocity_mii_ioctl(dev, rq, cmd);
244362306a36Sopenharmony_ci		break;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	default:
244662306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
244762306a36Sopenharmony_ci	}
244862306a36Sopenharmony_ci	if (!netif_running(dev))
244962306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D3hot);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	return ret;
245362306a36Sopenharmony_ci}
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci/**
245662306a36Sopenharmony_ci *	velocity_get_stats	-	statistics callback
245762306a36Sopenharmony_ci *	@dev: network device
245862306a36Sopenharmony_ci *
245962306a36Sopenharmony_ci *	Callback from the network layer to allow driver statistics
246062306a36Sopenharmony_ci *	to be resynchronized with hardware collected state. In the
246162306a36Sopenharmony_ci *	case of the velocity we need to pull the MIB counters from
246262306a36Sopenharmony_ci *	the hardware into the counters before letting the network
246362306a36Sopenharmony_ci *	layer display them.
246462306a36Sopenharmony_ci */
246562306a36Sopenharmony_cistatic struct net_device_stats *velocity_get_stats(struct net_device *dev)
246662306a36Sopenharmony_ci{
246762306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	/* If the hardware is down, don't touch MII */
247062306a36Sopenharmony_ci	if (!netif_running(dev))
247162306a36Sopenharmony_ci		return &dev->stats;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	spin_lock_irq(&vptr->lock);
247462306a36Sopenharmony_ci	velocity_update_hw_mibs(vptr);
247562306a36Sopenharmony_ci	spin_unlock_irq(&vptr->lock);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	dev->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts];
247862306a36Sopenharmony_ci	dev->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts];
247962306a36Sopenharmony_ci	dev->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors];
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci//  unsigned long   rx_dropped;     /* no space in linux buffers    */
248262306a36Sopenharmony_ci	dev->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions];
248362306a36Sopenharmony_ci	/* detailed rx_errors: */
248462306a36Sopenharmony_ci//  unsigned long   rx_length_errors;
248562306a36Sopenharmony_ci//  unsigned long   rx_over_errors;     /* receiver ring buff overflow  */
248662306a36Sopenharmony_ci	dev->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE];
248762306a36Sopenharmony_ci//  unsigned long   rx_frame_errors;    /* recv'd frame alignment error */
248862306a36Sopenharmony_ci//  unsigned long   rx_fifo_errors;     /* recv'r fifo overrun      */
248962306a36Sopenharmony_ci//  unsigned long   rx_missed_errors;   /* receiver missed packet   */
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/* detailed tx_errors */
249262306a36Sopenharmony_ci//  unsigned long   tx_fifo_errors;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	return &dev->stats;
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci/**
249862306a36Sopenharmony_ci *	velocity_close		-	close adapter callback
249962306a36Sopenharmony_ci *	@dev: network device
250062306a36Sopenharmony_ci *
250162306a36Sopenharmony_ci *	Callback from the network layer when the velocity is being
250262306a36Sopenharmony_ci *	deactivated by the network layer
250362306a36Sopenharmony_ci */
250462306a36Sopenharmony_cistatic int velocity_close(struct net_device *dev)
250562306a36Sopenharmony_ci{
250662306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	napi_disable(&vptr->napi);
250962306a36Sopenharmony_ci	netif_stop_queue(dev);
251062306a36Sopenharmony_ci	velocity_shutdown(vptr);
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED)
251362306a36Sopenharmony_ci		velocity_get_ip(vptr);
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	free_irq(dev->irq, dev);
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	velocity_free_rings(vptr);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	vptr->flags &= (~VELOCITY_FLAGS_OPENED);
252062306a36Sopenharmony_ci	return 0;
252162306a36Sopenharmony_ci}
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci/**
252462306a36Sopenharmony_ci *	velocity_xmit		-	transmit packet callback
252562306a36Sopenharmony_ci *	@skb: buffer to transmit
252662306a36Sopenharmony_ci *	@dev: network device
252762306a36Sopenharmony_ci *
252862306a36Sopenharmony_ci *	Called by the network layer to request a packet is queued to
252962306a36Sopenharmony_ci *	the velocity. Returns zero on success.
253062306a36Sopenharmony_ci */
253162306a36Sopenharmony_cistatic netdev_tx_t velocity_xmit(struct sk_buff *skb,
253262306a36Sopenharmony_ci				 struct net_device *dev)
253362306a36Sopenharmony_ci{
253462306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
253562306a36Sopenharmony_ci	int qnum = 0;
253662306a36Sopenharmony_ci	struct tx_desc *td_ptr;
253762306a36Sopenharmony_ci	struct velocity_td_info *tdinfo;
253862306a36Sopenharmony_ci	unsigned long flags;
253962306a36Sopenharmony_ci	int pktlen;
254062306a36Sopenharmony_ci	int index, prev;
254162306a36Sopenharmony_ci	int i = 0;
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	if (skb_padto(skb, ETH_ZLEN))
254462306a36Sopenharmony_ci		goto out;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	/* The hardware can handle at most 7 memory segments, so merge
254762306a36Sopenharmony_ci	 * the skb if there are more */
254862306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) {
254962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
255062306a36Sopenharmony_ci		return NETDEV_TX_OK;
255162306a36Sopenharmony_ci	}
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	pktlen = skb_shinfo(skb)->nr_frags == 0 ?
255462306a36Sopenharmony_ci			max_t(unsigned int, skb->len, ETH_ZLEN) :
255562306a36Sopenharmony_ci				skb_headlen(skb);
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	spin_lock_irqsave(&vptr->lock, flags);
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	index = vptr->tx.curr[qnum];
256062306a36Sopenharmony_ci	td_ptr = &(vptr->tx.rings[qnum][index]);
256162306a36Sopenharmony_ci	tdinfo = &(vptr->tx.infos[qnum][index]);
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	td_ptr->tdesc1.TCR = TCR0_TIC;
256462306a36Sopenharmony_ci	td_ptr->td_buf[0].size &= ~TD_QUEUE;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	/*
256762306a36Sopenharmony_ci	 *	Map the linear network buffer into PCI space and
256862306a36Sopenharmony_ci	 *	add it to the transmit ring.
256962306a36Sopenharmony_ci	 */
257062306a36Sopenharmony_ci	tdinfo->skb = skb;
257162306a36Sopenharmony_ci	tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen,
257262306a36Sopenharmony_ci								DMA_TO_DEVICE);
257362306a36Sopenharmony_ci	td_ptr->tdesc0.len = cpu_to_le16(pktlen);
257462306a36Sopenharmony_ci	td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]);
257562306a36Sopenharmony_ci	td_ptr->td_buf[0].pa_high = 0;
257662306a36Sopenharmony_ci	td_ptr->td_buf[0].size = cpu_to_le16(pktlen);
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	/* Handle fragments */
257962306a36Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
258062306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci		tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev,
258362306a36Sopenharmony_ci							  frag, 0,
258462306a36Sopenharmony_ci							  skb_frag_size(frag),
258562306a36Sopenharmony_ci							  DMA_TO_DEVICE);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci		td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]);
258862306a36Sopenharmony_ci		td_ptr->td_buf[i + 1].pa_high = 0;
258962306a36Sopenharmony_ci		td_ptr->td_buf[i + 1].size = cpu_to_le16(skb_frag_size(frag));
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci	tdinfo->nskb_dma = i + 1;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16;
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
259662306a36Sopenharmony_ci		td_ptr->tdesc1.vlan = cpu_to_le16(skb_vlan_tag_get(skb));
259762306a36Sopenharmony_ci		td_ptr->tdesc1.TCR |= TCR0_VETAG;
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	/*
260162306a36Sopenharmony_ci	 *	Handle hardware checksum
260262306a36Sopenharmony_ci	 */
260362306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
260462306a36Sopenharmony_ci		const struct iphdr *ip = ip_hdr(skb);
260562306a36Sopenharmony_ci		if (ip->protocol == IPPROTO_TCP)
260662306a36Sopenharmony_ci			td_ptr->tdesc1.TCR |= TCR0_TCPCK;
260762306a36Sopenharmony_ci		else if (ip->protocol == IPPROTO_UDP)
260862306a36Sopenharmony_ci			td_ptr->tdesc1.TCR |= (TCR0_UDPCK);
260962306a36Sopenharmony_ci		td_ptr->tdesc1.TCR |= TCR0_IPCK;
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	prev = index - 1;
261362306a36Sopenharmony_ci	if (prev < 0)
261462306a36Sopenharmony_ci		prev = vptr->options.numtx - 1;
261562306a36Sopenharmony_ci	td_ptr->tdesc0.len |= OWNED_BY_NIC;
261662306a36Sopenharmony_ci	vptr->tx.used[qnum]++;
261762306a36Sopenharmony_ci	vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	if (AVAIL_TD(vptr, qnum) < 1)
262062306a36Sopenharmony_ci		netif_stop_queue(dev);
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	td_ptr = &(vptr->tx.rings[qnum][prev]);
262362306a36Sopenharmony_ci	td_ptr->td_buf[0].size |= TD_QUEUE;
262462306a36Sopenharmony_ci	mac_tx_queue_wake(vptr->mac_regs, qnum);
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vptr->lock, flags);
262762306a36Sopenharmony_ciout:
262862306a36Sopenharmony_ci	return NETDEV_TX_OK;
262962306a36Sopenharmony_ci}
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_cistatic const struct net_device_ops velocity_netdev_ops = {
263262306a36Sopenharmony_ci	.ndo_open		= velocity_open,
263362306a36Sopenharmony_ci	.ndo_stop		= velocity_close,
263462306a36Sopenharmony_ci	.ndo_start_xmit		= velocity_xmit,
263562306a36Sopenharmony_ci	.ndo_get_stats		= velocity_get_stats,
263662306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
263762306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
263862306a36Sopenharmony_ci	.ndo_set_rx_mode	= velocity_set_multi,
263962306a36Sopenharmony_ci	.ndo_change_mtu		= velocity_change_mtu,
264062306a36Sopenharmony_ci	.ndo_eth_ioctl		= velocity_ioctl,
264162306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= velocity_vlan_rx_add_vid,
264262306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= velocity_vlan_rx_kill_vid,
264362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
264462306a36Sopenharmony_ci	.ndo_poll_controller = velocity_poll_controller,
264562306a36Sopenharmony_ci#endif
264662306a36Sopenharmony_ci};
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci/**
264962306a36Sopenharmony_ci *	velocity_init_info	-	init private data
265062306a36Sopenharmony_ci *	@vptr: Velocity info
265162306a36Sopenharmony_ci *	@info: Board type
265262306a36Sopenharmony_ci *
265362306a36Sopenharmony_ci *	Set up the initial velocity_info struct for the device that has been
265462306a36Sopenharmony_ci *	discovered.
265562306a36Sopenharmony_ci */
265662306a36Sopenharmony_cistatic void velocity_init_info(struct velocity_info *vptr,
265762306a36Sopenharmony_ci				const struct velocity_info_tbl *info)
265862306a36Sopenharmony_ci{
265962306a36Sopenharmony_ci	vptr->chip_id = info->chip_id;
266062306a36Sopenharmony_ci	vptr->tx.numq = info->txqueue;
266162306a36Sopenharmony_ci	vptr->multicast_limit = MCAM_SIZE;
266262306a36Sopenharmony_ci	spin_lock_init(&vptr->lock);
266362306a36Sopenharmony_ci}
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci/**
266662306a36Sopenharmony_ci *	velocity_get_pci_info	-	retrieve PCI info for device
266762306a36Sopenharmony_ci *	@vptr: velocity device
266862306a36Sopenharmony_ci *
266962306a36Sopenharmony_ci *	Retrieve the PCI configuration space data that interests us from
267062306a36Sopenharmony_ci *	the kernel PCI layer
267162306a36Sopenharmony_ci */
267262306a36Sopenharmony_cistatic int velocity_get_pci_info(struct velocity_info *vptr)
267362306a36Sopenharmony_ci{
267462306a36Sopenharmony_ci	struct pci_dev *pdev = vptr->pdev;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	pci_set_master(pdev);
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	vptr->ioaddr = pci_resource_start(pdev, 0);
267962306a36Sopenharmony_ci	vptr->memaddr = pci_resource_start(pdev, 1);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
268262306a36Sopenharmony_ci		dev_err(&pdev->dev,
268362306a36Sopenharmony_ci			   "region #0 is not an I/O resource, aborting.\n");
268462306a36Sopenharmony_ci		return -EINVAL;
268562306a36Sopenharmony_ci	}
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) {
268862306a36Sopenharmony_ci		dev_err(&pdev->dev,
268962306a36Sopenharmony_ci			   "region #1 is an I/O resource, aborting.\n");
269062306a36Sopenharmony_ci		return -EINVAL;
269162306a36Sopenharmony_ci	}
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) {
269462306a36Sopenharmony_ci		dev_err(&pdev->dev, "region #1 is too small.\n");
269562306a36Sopenharmony_ci		return -EINVAL;
269662306a36Sopenharmony_ci	}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	return 0;
269962306a36Sopenharmony_ci}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci/**
270262306a36Sopenharmony_ci *	velocity_get_platform_info - retrieve platform info for device
270362306a36Sopenharmony_ci *	@vptr: velocity device
270462306a36Sopenharmony_ci *
270562306a36Sopenharmony_ci *	Retrieve the Platform configuration data that interests us
270662306a36Sopenharmony_ci */
270762306a36Sopenharmony_cistatic int velocity_get_platform_info(struct velocity_info *vptr)
270862306a36Sopenharmony_ci{
270962306a36Sopenharmony_ci	struct resource res;
271062306a36Sopenharmony_ci	int ret;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	vptr->no_eeprom = of_property_read_bool(vptr->dev->of_node, "no-eeprom");
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	ret = of_address_to_resource(vptr->dev->of_node, 0, &res);
271562306a36Sopenharmony_ci	if (ret) {
271662306a36Sopenharmony_ci		dev_err(vptr->dev, "unable to find memory address\n");
271762306a36Sopenharmony_ci		return ret;
271862306a36Sopenharmony_ci	}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	vptr->memaddr = res.start;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	if (resource_size(&res) < VELOCITY_IO_SIZE) {
272362306a36Sopenharmony_ci		dev_err(vptr->dev, "memory region is too small.\n");
272462306a36Sopenharmony_ci		return -EINVAL;
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	return 0;
272862306a36Sopenharmony_ci}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci/**
273162306a36Sopenharmony_ci *	velocity_print_info	-	per driver data
273262306a36Sopenharmony_ci *	@vptr: velocity
273362306a36Sopenharmony_ci *
273462306a36Sopenharmony_ci *	Print per driver data as the kernel driver finds Velocity
273562306a36Sopenharmony_ci *	hardware
273662306a36Sopenharmony_ci */
273762306a36Sopenharmony_cistatic void velocity_print_info(struct velocity_info *vptr)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	netdev_info(vptr->netdev, "%s - Ethernet Address: %pM\n",
274062306a36Sopenharmony_ci		    get_chip_name(vptr->chip_id), vptr->netdev->dev_addr);
274162306a36Sopenharmony_ci}
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_cistatic u32 velocity_get_link(struct net_device *dev)
274462306a36Sopenharmony_ci{
274562306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
274662306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
274762306a36Sopenharmony_ci	return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, &regs->PHYSR0) ? 1 : 0;
274862306a36Sopenharmony_ci}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci/**
275162306a36Sopenharmony_ci *	velocity_probe - set up discovered velocity device
275262306a36Sopenharmony_ci *	@dev: PCI device
275362306a36Sopenharmony_ci *	@info: table of match
275462306a36Sopenharmony_ci *	@irq: interrupt info
275562306a36Sopenharmony_ci *	@bustype: bus that device is connected to
275662306a36Sopenharmony_ci *
275762306a36Sopenharmony_ci *	Configure a discovered adapter from scratch. Return a negative
275862306a36Sopenharmony_ci *	errno error code on failure paths.
275962306a36Sopenharmony_ci */
276062306a36Sopenharmony_cistatic int velocity_probe(struct device *dev, int irq,
276162306a36Sopenharmony_ci			   const struct velocity_info_tbl *info,
276262306a36Sopenharmony_ci			   enum velocity_bus_type bustype)
276362306a36Sopenharmony_ci{
276462306a36Sopenharmony_ci	struct net_device *netdev;
276562306a36Sopenharmony_ci	int i;
276662306a36Sopenharmony_ci	struct velocity_info *vptr;
276762306a36Sopenharmony_ci	struct mac_regs __iomem *regs;
276862306a36Sopenharmony_ci	int ret = -ENOMEM;
276962306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	/* FIXME: this driver, like almost all other ethernet drivers,
277262306a36Sopenharmony_ci	 * can support more than MAX_UNITS.
277362306a36Sopenharmony_ci	 */
277462306a36Sopenharmony_ci	if (velocity_nics >= MAX_UNITS) {
277562306a36Sopenharmony_ci		dev_notice(dev, "already found %d NICs.\n", velocity_nics);
277662306a36Sopenharmony_ci		return -ENODEV;
277762306a36Sopenharmony_ci	}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct velocity_info));
278062306a36Sopenharmony_ci	if (!netdev)
278162306a36Sopenharmony_ci		goto out;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	/* Chain it all together */
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, dev);
278662306a36Sopenharmony_ci	vptr = netdev_priv(netdev);
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	pr_info_once("%s Ver. %s\n", VELOCITY_FULL_DRV_NAM, VELOCITY_VERSION);
278962306a36Sopenharmony_ci	pr_info_once("Copyright (c) 2002, 2003 VIA Networking Technologies, Inc.\n");
279062306a36Sopenharmony_ci	pr_info_once("Copyright (c) 2004 Red Hat Inc.\n");
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	netdev->irq = irq;
279362306a36Sopenharmony_ci	vptr->netdev = netdev;
279462306a36Sopenharmony_ci	vptr->dev = dev;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	velocity_init_info(vptr, info);
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	if (bustype == BUS_PCI) {
279962306a36Sopenharmony_ci		vptr->pdev = to_pci_dev(dev);
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci		ret = velocity_get_pci_info(vptr);
280262306a36Sopenharmony_ci		if (ret < 0)
280362306a36Sopenharmony_ci			goto err_free_dev;
280462306a36Sopenharmony_ci	} else {
280562306a36Sopenharmony_ci		vptr->pdev = NULL;
280662306a36Sopenharmony_ci		ret = velocity_get_platform_info(vptr);
280762306a36Sopenharmony_ci		if (ret < 0)
280862306a36Sopenharmony_ci			goto err_free_dev;
280962306a36Sopenharmony_ci	}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE);
281262306a36Sopenharmony_ci	if (regs == NULL) {
281362306a36Sopenharmony_ci		ret = -EIO;
281462306a36Sopenharmony_ci		goto err_free_dev;
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	vptr->mac_regs = regs;
281862306a36Sopenharmony_ci	vptr->rev_id = readb(&regs->rev_id);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	mac_wol_reset(regs);
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
282362306a36Sopenharmony_ci		addr[i] = readb(&regs->PAR[i]);
282462306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr);
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	velocity_get_options(&vptr->options, velocity_nics);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	/*
283062306a36Sopenharmony_ci	 *	Mask out the options cannot be set to the chip
283162306a36Sopenharmony_ci	 */
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	vptr->options.flags &= info->flags;
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	/*
283662306a36Sopenharmony_ci	 *	Enable the chip specified capbilities
283762306a36Sopenharmony_ci	 */
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	vptr->flags = vptr->options.flags | (info->flags & 0xFF000000UL);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	vptr->wol_opts = vptr->options.wol_opts;
284262306a36Sopenharmony_ci	vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs);
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci	netdev->netdev_ops = &velocity_netdev_ops;
284762306a36Sopenharmony_ci	netdev->ethtool_ops = &velocity_ethtool_ops;
284862306a36Sopenharmony_ci	netif_napi_add(netdev, &vptr->napi, velocity_poll);
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
285162306a36Sopenharmony_ci			   NETIF_F_HW_VLAN_CTAG_TX;
285262306a36Sopenharmony_ci	netdev->features |= NETIF_F_HW_VLAN_CTAG_TX |
285362306a36Sopenharmony_ci			NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX |
285462306a36Sopenharmony_ci			NETIF_F_IP_CSUM;
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	/* MTU range: 64 - 9000 */
285762306a36Sopenharmony_ci	netdev->min_mtu = VELOCITY_MIN_MTU;
285862306a36Sopenharmony_ci	netdev->max_mtu = VELOCITY_MAX_MTU;
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	ret = register_netdev(netdev);
286162306a36Sopenharmony_ci	if (ret < 0)
286262306a36Sopenharmony_ci		goto err_iounmap;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	if (!velocity_get_link(netdev)) {
286562306a36Sopenharmony_ci		netif_carrier_off(netdev);
286662306a36Sopenharmony_ci		vptr->mii_status |= VELOCITY_LINK_FAIL;
286762306a36Sopenharmony_ci	}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	velocity_print_info(vptr);
287062306a36Sopenharmony_ci	dev_set_drvdata(vptr->dev, netdev);
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	/* and leave the chip powered down */
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	velocity_set_power_state(vptr, PCI_D3hot);
287562306a36Sopenharmony_ci	velocity_nics++;
287662306a36Sopenharmony_ciout:
287762306a36Sopenharmony_ci	return ret;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_cierr_iounmap:
288062306a36Sopenharmony_ci	netif_napi_del(&vptr->napi);
288162306a36Sopenharmony_ci	iounmap(regs);
288262306a36Sopenharmony_cierr_free_dev:
288362306a36Sopenharmony_ci	free_netdev(netdev);
288462306a36Sopenharmony_ci	goto out;
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci/**
288862306a36Sopenharmony_ci *	velocity_remove	- device unplug
288962306a36Sopenharmony_ci *	@dev: device being removed
289062306a36Sopenharmony_ci *
289162306a36Sopenharmony_ci *	Device unload callback. Called on an unplug or on module
289262306a36Sopenharmony_ci *	unload for each active device that is present. Disconnects
289362306a36Sopenharmony_ci *	the device from the network layer and frees all the resources
289462306a36Sopenharmony_ci */
289562306a36Sopenharmony_cistatic int velocity_remove(struct device *dev)
289662306a36Sopenharmony_ci{
289762306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
289862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(netdev);
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	unregister_netdev(netdev);
290162306a36Sopenharmony_ci	netif_napi_del(&vptr->napi);
290262306a36Sopenharmony_ci	iounmap(vptr->mac_regs);
290362306a36Sopenharmony_ci	free_netdev(netdev);
290462306a36Sopenharmony_ci	velocity_nics--;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	return 0;
290762306a36Sopenharmony_ci}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_cistatic int velocity_pci_probe(struct pci_dev *pdev,
291062306a36Sopenharmony_ci			       const struct pci_device_id *ent)
291162306a36Sopenharmony_ci{
291262306a36Sopenharmony_ci	const struct velocity_info_tbl *info =
291362306a36Sopenharmony_ci					&chip_info_table[ent->driver_data];
291462306a36Sopenharmony_ci	int ret;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
291762306a36Sopenharmony_ci	if (ret < 0)
291862306a36Sopenharmony_ci		return ret;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	ret = pci_request_regions(pdev, VELOCITY_NAME);
292162306a36Sopenharmony_ci	if (ret < 0) {
292262306a36Sopenharmony_ci		dev_err(&pdev->dev, "No PCI resources.\n");
292362306a36Sopenharmony_ci		goto fail1;
292462306a36Sopenharmony_ci	}
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI);
292762306a36Sopenharmony_ci	if (ret == 0)
292862306a36Sopenharmony_ci		return 0;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	pci_release_regions(pdev);
293162306a36Sopenharmony_cifail1:
293262306a36Sopenharmony_ci	pci_disable_device(pdev);
293362306a36Sopenharmony_ci	return ret;
293462306a36Sopenharmony_ci}
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_cistatic void velocity_pci_remove(struct pci_dev *pdev)
293762306a36Sopenharmony_ci{
293862306a36Sopenharmony_ci	velocity_remove(&pdev->dev);
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	pci_release_regions(pdev);
294162306a36Sopenharmony_ci	pci_disable_device(pdev);
294262306a36Sopenharmony_ci}
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_cistatic int velocity_platform_probe(struct platform_device *pdev)
294562306a36Sopenharmony_ci{
294662306a36Sopenharmony_ci	const struct velocity_info_tbl *info;
294762306a36Sopenharmony_ci	int irq;
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci	info = of_device_get_match_data(&pdev->dev);
295062306a36Sopenharmony_ci	if (!info)
295162306a36Sopenharmony_ci		return -EINVAL;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
295462306a36Sopenharmony_ci	if (!irq)
295562306a36Sopenharmony_ci		return -EINVAL;
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM);
295862306a36Sopenharmony_ci}
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_cistatic int velocity_platform_remove(struct platform_device *pdev)
296162306a36Sopenharmony_ci{
296262306a36Sopenharmony_ci	velocity_remove(&pdev->dev);
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	return 0;
296562306a36Sopenharmony_ci}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
296862306a36Sopenharmony_ci/**
296962306a36Sopenharmony_ci *	wol_calc_crc		-	WOL CRC
297062306a36Sopenharmony_ci *	@size: size of the wake mask
297162306a36Sopenharmony_ci *	@pattern: data pattern
297262306a36Sopenharmony_ci *	@mask_pattern: mask
297362306a36Sopenharmony_ci *
297462306a36Sopenharmony_ci *	Compute the wake on lan crc hashes for the packet header
297562306a36Sopenharmony_ci *	we are interested in.
297662306a36Sopenharmony_ci */
297762306a36Sopenharmony_cistatic u16 wol_calc_crc(int size, u8 *pattern, u8 *mask_pattern)
297862306a36Sopenharmony_ci{
297962306a36Sopenharmony_ci	u16 crc = 0xFFFF;
298062306a36Sopenharmony_ci	u8 mask;
298162306a36Sopenharmony_ci	int i, j;
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
298462306a36Sopenharmony_ci		mask = mask_pattern[i];
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci		/* Skip this loop if the mask equals to zero */
298762306a36Sopenharmony_ci		if (mask == 0x00)
298862306a36Sopenharmony_ci			continue;
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci		for (j = 0; j < 8; j++) {
299162306a36Sopenharmony_ci			if ((mask & 0x01) == 0) {
299262306a36Sopenharmony_ci				mask >>= 1;
299362306a36Sopenharmony_ci				continue;
299462306a36Sopenharmony_ci			}
299562306a36Sopenharmony_ci			mask >>= 1;
299662306a36Sopenharmony_ci			crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1);
299762306a36Sopenharmony_ci		}
299862306a36Sopenharmony_ci	}
299962306a36Sopenharmony_ci	/*	Finally, invert the result once to get the correct data */
300062306a36Sopenharmony_ci	crc = ~crc;
300162306a36Sopenharmony_ci	return bitrev32(crc) >> 16;
300262306a36Sopenharmony_ci}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci/**
300562306a36Sopenharmony_ci *	velocity_set_wol	-	set up for wake on lan
300662306a36Sopenharmony_ci *	@vptr: velocity to set WOL status on
300762306a36Sopenharmony_ci *
300862306a36Sopenharmony_ci *	Set a card up for wake on lan either by unicast or by
300962306a36Sopenharmony_ci *	ARP packet.
301062306a36Sopenharmony_ci *
301162306a36Sopenharmony_ci *	FIXME: check static buffer is safe here
301262306a36Sopenharmony_ci */
301362306a36Sopenharmony_cistatic int velocity_set_wol(struct velocity_info *vptr)
301462306a36Sopenharmony_ci{
301562306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
301662306a36Sopenharmony_ci	enum speed_opt spd_dpx = vptr->options.spd_dpx;
301762306a36Sopenharmony_ci	static u8 buf[256];
301862306a36Sopenharmony_ci	int i;
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	static u32 mask_pattern[2][4] = {
302162306a36Sopenharmony_ci		{0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */
302262306a36Sopenharmony_ci		{0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff}	 /* Magic Packet */
302362306a36Sopenharmony_ci	};
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	writew(0xFFFF, &regs->WOLCRClr);
302662306a36Sopenharmony_ci	writeb(WOLCFG_SAB | WOLCFG_SAM, &regs->WOLCFGSet);
302762306a36Sopenharmony_ci	writew(WOLCR_MAGIC_EN, &regs->WOLCRSet);
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	/*
303062306a36Sopenharmony_ci	   if (vptr->wol_opts & VELOCITY_WOL_PHY)
303162306a36Sopenharmony_ci	   writew((WOLCR_LINKON_EN|WOLCR_LINKOFF_EN), &regs->WOLCRSet);
303262306a36Sopenharmony_ci	 */
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	if (vptr->wol_opts & VELOCITY_WOL_UCAST)
303562306a36Sopenharmony_ci		writew(WOLCR_UNICAST_EN, &regs->WOLCRSet);
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	if (vptr->wol_opts & VELOCITY_WOL_ARP) {
303862306a36Sopenharmony_ci		struct arp_packet *arp = (struct arp_packet *) buf;
303962306a36Sopenharmony_ci		u16 crc;
304062306a36Sopenharmony_ci		memset(buf, 0, sizeof(struct arp_packet) + 7);
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
304362306a36Sopenharmony_ci			writel(mask_pattern[0][i], &regs->ByteMask[0][i]);
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_ci		arp->type = htons(ETH_P_ARP);
304662306a36Sopenharmony_ci		arp->ar_op = htons(1);
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci		memcpy(arp->ar_tip, vptr->ip_addr, 4);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci		crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf,
305162306a36Sopenharmony_ci				(u8 *) & mask_pattern[0][0]);
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci		writew(crc, &regs->PatternCRC[0]);
305462306a36Sopenharmony_ci		writew(WOLCR_ARP_EN, &regs->WOLCRSet);
305562306a36Sopenharmony_ci	}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	BYTE_REG_BITS_ON(PWCFG_WOLTYPE, &regs->PWCFGSet);
305862306a36Sopenharmony_ci	BYTE_REG_BITS_ON(PWCFG_LEGACY_WOLEN, &regs->PWCFGSet);
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci	writew(0x0FFF, &regs->WOLSRClr);
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	if (spd_dpx == SPD_DPX_1000_FULL)
306362306a36Sopenharmony_ci		goto mac_done;
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci	if (spd_dpx != SPD_DPX_AUTO)
306662306a36Sopenharmony_ci		goto advertise_done;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) {
306962306a36Sopenharmony_ci		if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201)
307062306a36Sopenharmony_ci			MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs);
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci		MII_REG_BITS_OFF(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs);
307362306a36Sopenharmony_ci	}
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	if (vptr->mii_status & VELOCITY_SPEED_1000)
307662306a36Sopenharmony_ci		MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs);
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ciadvertise_done:
307962306a36Sopenharmony_ci	BYTE_REG_BITS_ON(CHIPGCR_FCMODE, &regs->CHIPGCR);
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci	{
308262306a36Sopenharmony_ci		u8 GCR;
308362306a36Sopenharmony_ci		GCR = readb(&regs->CHIPGCR);
308462306a36Sopenharmony_ci		GCR = (GCR & ~CHIPGCR_FCGMII) | CHIPGCR_FCFDX;
308562306a36Sopenharmony_ci		writeb(GCR, &regs->CHIPGCR);
308662306a36Sopenharmony_ci	}
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_cimac_done:
308962306a36Sopenharmony_ci	BYTE_REG_BITS_OFF(ISR_PWEI, &regs->ISR);
309062306a36Sopenharmony_ci	/* Turn on SWPTAG just before entering power mode */
309162306a36Sopenharmony_ci	BYTE_REG_BITS_ON(STICKHW_SWPTAG, &regs->STICKHW);
309262306a36Sopenharmony_ci	/* Go to bed ..... */
309362306a36Sopenharmony_ci	BYTE_REG_BITS_ON((STICKHW_DS1 | STICKHW_DS0), &regs->STICKHW);
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	return 0;
309662306a36Sopenharmony_ci}
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci/**
309962306a36Sopenharmony_ci *	velocity_save_context	-	save registers
310062306a36Sopenharmony_ci *	@vptr: velocity
310162306a36Sopenharmony_ci *	@context: buffer for stored context
310262306a36Sopenharmony_ci *
310362306a36Sopenharmony_ci *	Retrieve the current configuration from the velocity hardware
310462306a36Sopenharmony_ci *	and stash it in the context structure, for use by the context
310562306a36Sopenharmony_ci *	restore functions. This allows us to save things we need across
310662306a36Sopenharmony_ci *	power down states
310762306a36Sopenharmony_ci */
310862306a36Sopenharmony_cistatic void velocity_save_context(struct velocity_info *vptr, struct velocity_context *context)
310962306a36Sopenharmony_ci{
311062306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
311162306a36Sopenharmony_ci	u16 i;
311262306a36Sopenharmony_ci	u8 __iomem *ptr = (u8 __iomem *)regs;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	for (i = MAC_REG_PAR; i < MAC_REG_CR0_CLR; i += 4)
311562306a36Sopenharmony_ci		*((u32 *) (context->mac_reg + i)) = readl(ptr + i);
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci	for (i = MAC_REG_MAR; i < MAC_REG_TDCSR_CLR; i += 4)
311862306a36Sopenharmony_ci		*((u32 *) (context->mac_reg + i)) = readl(ptr + i);
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4)
312162306a36Sopenharmony_ci		*((u32 *) (context->mac_reg + i)) = readl(ptr + i);
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci}
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_cistatic int velocity_suspend(struct device *dev)
312662306a36Sopenharmony_ci{
312762306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
312862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(netdev);
312962306a36Sopenharmony_ci	unsigned long flags;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	if (!netif_running(vptr->netdev))
313262306a36Sopenharmony_ci		return 0;
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	netif_device_detach(vptr->netdev);
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	spin_lock_irqsave(&vptr->lock, flags);
313762306a36Sopenharmony_ci	if (vptr->pdev)
313862306a36Sopenharmony_ci		pci_save_state(vptr->pdev);
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) {
314162306a36Sopenharmony_ci		velocity_get_ip(vptr);
314262306a36Sopenharmony_ci		velocity_save_context(vptr, &vptr->context);
314362306a36Sopenharmony_ci		velocity_shutdown(vptr);
314462306a36Sopenharmony_ci		velocity_set_wol(vptr);
314562306a36Sopenharmony_ci		if (vptr->pdev)
314662306a36Sopenharmony_ci			pci_enable_wake(vptr->pdev, PCI_D3hot, 1);
314762306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D3hot);
314862306a36Sopenharmony_ci	} else {
314962306a36Sopenharmony_ci		velocity_save_context(vptr, &vptr->context);
315062306a36Sopenharmony_ci		velocity_shutdown(vptr);
315162306a36Sopenharmony_ci		if (vptr->pdev)
315262306a36Sopenharmony_ci			pci_disable_device(vptr->pdev);
315362306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D3hot);
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vptr->lock, flags);
315762306a36Sopenharmony_ci	return 0;
315862306a36Sopenharmony_ci}
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci/**
316162306a36Sopenharmony_ci *	velocity_restore_context	-	restore registers
316262306a36Sopenharmony_ci *	@vptr: velocity
316362306a36Sopenharmony_ci *	@context: buffer for stored context
316462306a36Sopenharmony_ci *
316562306a36Sopenharmony_ci *	Reload the register configuration from the velocity context
316662306a36Sopenharmony_ci *	created by velocity_save_context.
316762306a36Sopenharmony_ci */
316862306a36Sopenharmony_cistatic void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context)
316962306a36Sopenharmony_ci{
317062306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
317162306a36Sopenharmony_ci	int i;
317262306a36Sopenharmony_ci	u8 __iomem *ptr = (u8 __iomem *)regs;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	for (i = MAC_REG_PAR; i < MAC_REG_CR0_SET; i += 4)
317562306a36Sopenharmony_ci		writel(*((u32 *) (context->mac_reg + i)), ptr + i);
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	/* Just skip cr0 */
317862306a36Sopenharmony_ci	for (i = MAC_REG_CR1_SET; i < MAC_REG_CR0_CLR; i++) {
317962306a36Sopenharmony_ci		/* Clear */
318062306a36Sopenharmony_ci		writeb(~(*((u8 *) (context->mac_reg + i))), ptr + i + 4);
318162306a36Sopenharmony_ci		/* Set */
318262306a36Sopenharmony_ci		writeb(*((u8 *) (context->mac_reg + i)), ptr + i);
318362306a36Sopenharmony_ci	}
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	for (i = MAC_REG_MAR; i < MAC_REG_IMR; i += 4)
318662306a36Sopenharmony_ci		writel(*((u32 *) (context->mac_reg + i)), ptr + i);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4)
318962306a36Sopenharmony_ci		writel(*((u32 *) (context->mac_reg + i)), ptr + i);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	for (i = MAC_REG_TDCSR_SET; i <= MAC_REG_RDCSR_SET; i++)
319262306a36Sopenharmony_ci		writeb(*((u8 *) (context->mac_reg + i)), ptr + i);
319362306a36Sopenharmony_ci}
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_cistatic int velocity_resume(struct device *dev)
319662306a36Sopenharmony_ci{
319762306a36Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(dev);
319862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(netdev);
319962306a36Sopenharmony_ci	unsigned long flags;
320062306a36Sopenharmony_ci	int i;
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	if (!netif_running(vptr->netdev))
320362306a36Sopenharmony_ci		return 0;
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	velocity_set_power_state(vptr, PCI_D0);
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	if (vptr->pdev) {
320862306a36Sopenharmony_ci		pci_enable_wake(vptr->pdev, PCI_D0, 0);
320962306a36Sopenharmony_ci		pci_restore_state(vptr->pdev);
321062306a36Sopenharmony_ci	}
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	mac_wol_reset(vptr->mac_regs);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	spin_lock_irqsave(&vptr->lock, flags);
321562306a36Sopenharmony_ci	velocity_restore_context(vptr, &vptr->context);
321662306a36Sopenharmony_ci	velocity_init_registers(vptr, VELOCITY_INIT_WOL);
321762306a36Sopenharmony_ci	mac_disable_int(vptr->mac_regs);
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	velocity_tx_srv(vptr);
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	for (i = 0; i < vptr->tx.numq; i++) {
322262306a36Sopenharmony_ci		if (vptr->tx.used[i])
322362306a36Sopenharmony_ci			mac_tx_queue_wake(vptr->mac_regs, i);
322462306a36Sopenharmony_ci	}
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	mac_enable_int(vptr->mac_regs);
322762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vptr->lock, flags);
322862306a36Sopenharmony_ci	netif_device_attach(vptr->netdev);
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	return 0;
323162306a36Sopenharmony_ci}
323262306a36Sopenharmony_ci#endif	/* CONFIG_PM_SLEEP */
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume);
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci/*
323762306a36Sopenharmony_ci *	Definition for our device driver. The PCI layer interface
323862306a36Sopenharmony_ci *	uses this to handle all our card discover and plugging
323962306a36Sopenharmony_ci */
324062306a36Sopenharmony_cistatic struct pci_driver velocity_pci_driver = {
324162306a36Sopenharmony_ci	.name		= VELOCITY_NAME,
324262306a36Sopenharmony_ci	.id_table	= velocity_pci_id_table,
324362306a36Sopenharmony_ci	.probe		= velocity_pci_probe,
324462306a36Sopenharmony_ci	.remove		= velocity_pci_remove,
324562306a36Sopenharmony_ci	.driver = {
324662306a36Sopenharmony_ci		.pm = &velocity_pm_ops,
324762306a36Sopenharmony_ci	},
324862306a36Sopenharmony_ci};
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_cistatic struct platform_driver velocity_platform_driver = {
325162306a36Sopenharmony_ci	.probe		= velocity_platform_probe,
325262306a36Sopenharmony_ci	.remove		= velocity_platform_remove,
325362306a36Sopenharmony_ci	.driver = {
325462306a36Sopenharmony_ci		.name = "via-velocity",
325562306a36Sopenharmony_ci		.of_match_table = velocity_of_ids,
325662306a36Sopenharmony_ci		.pm = &velocity_pm_ops,
325762306a36Sopenharmony_ci	},
325862306a36Sopenharmony_ci};
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci/**
326162306a36Sopenharmony_ci *	velocity_ethtool_up	-	pre hook for ethtool
326262306a36Sopenharmony_ci *	@dev: network device
326362306a36Sopenharmony_ci *
326462306a36Sopenharmony_ci *	Called before an ethtool operation. We need to make sure the
326562306a36Sopenharmony_ci *	chip is out of D3 state before we poke at it. In case of ethtool
326662306a36Sopenharmony_ci *	ops nesting, only wake the device up in the outermost block.
326762306a36Sopenharmony_ci */
326862306a36Sopenharmony_cistatic int velocity_ethtool_up(struct net_device *dev)
326962306a36Sopenharmony_ci{
327062306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	if (vptr->ethtool_ops_nesting == U32_MAX)
327362306a36Sopenharmony_ci		return -EBUSY;
327462306a36Sopenharmony_ci	if (!vptr->ethtool_ops_nesting++ && !netif_running(dev))
327562306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D0);
327662306a36Sopenharmony_ci	return 0;
327762306a36Sopenharmony_ci}
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci/**
328062306a36Sopenharmony_ci *	velocity_ethtool_down	-	post hook for ethtool
328162306a36Sopenharmony_ci *	@dev: network device
328262306a36Sopenharmony_ci *
328362306a36Sopenharmony_ci *	Called after an ethtool operation. Restore the chip back to D3
328462306a36Sopenharmony_ci *	state if it isn't running. In case of ethtool ops nesting, only
328562306a36Sopenharmony_ci *	put the device to sleep in the outermost block.
328662306a36Sopenharmony_ci */
328762306a36Sopenharmony_cistatic void velocity_ethtool_down(struct net_device *dev)
328862306a36Sopenharmony_ci{
328962306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci	if (!--vptr->ethtool_ops_nesting && !netif_running(dev))
329262306a36Sopenharmony_ci		velocity_set_power_state(vptr, PCI_D3hot);
329362306a36Sopenharmony_ci}
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_cistatic int velocity_get_link_ksettings(struct net_device *dev,
329662306a36Sopenharmony_ci				       struct ethtool_link_ksettings *cmd)
329762306a36Sopenharmony_ci{
329862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
329962306a36Sopenharmony_ci	struct mac_regs __iomem *regs = vptr->mac_regs;
330062306a36Sopenharmony_ci	u32 status;
330162306a36Sopenharmony_ci	u32 supported, advertising;
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	status = check_connection_type(vptr->mac_regs);
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	supported = SUPPORTED_TP |
330662306a36Sopenharmony_ci			SUPPORTED_Autoneg |
330762306a36Sopenharmony_ci			SUPPORTED_10baseT_Half |
330862306a36Sopenharmony_ci			SUPPORTED_10baseT_Full |
330962306a36Sopenharmony_ci			SUPPORTED_100baseT_Half |
331062306a36Sopenharmony_ci			SUPPORTED_100baseT_Full |
331162306a36Sopenharmony_ci			SUPPORTED_1000baseT_Half |
331262306a36Sopenharmony_ci			SUPPORTED_1000baseT_Full;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	advertising = ADVERTISED_TP | ADVERTISED_Autoneg;
331562306a36Sopenharmony_ci	if (vptr->options.spd_dpx == SPD_DPX_AUTO) {
331662306a36Sopenharmony_ci		advertising |=
331762306a36Sopenharmony_ci			ADVERTISED_10baseT_Half |
331862306a36Sopenharmony_ci			ADVERTISED_10baseT_Full |
331962306a36Sopenharmony_ci			ADVERTISED_100baseT_Half |
332062306a36Sopenharmony_ci			ADVERTISED_100baseT_Full |
332162306a36Sopenharmony_ci			ADVERTISED_1000baseT_Half |
332262306a36Sopenharmony_ci			ADVERTISED_1000baseT_Full;
332362306a36Sopenharmony_ci	} else {
332462306a36Sopenharmony_ci		switch (vptr->options.spd_dpx) {
332562306a36Sopenharmony_ci		case SPD_DPX_1000_FULL:
332662306a36Sopenharmony_ci			advertising |= ADVERTISED_1000baseT_Full;
332762306a36Sopenharmony_ci			break;
332862306a36Sopenharmony_ci		case SPD_DPX_100_HALF:
332962306a36Sopenharmony_ci			advertising |= ADVERTISED_100baseT_Half;
333062306a36Sopenharmony_ci			break;
333162306a36Sopenharmony_ci		case SPD_DPX_100_FULL:
333262306a36Sopenharmony_ci			advertising |= ADVERTISED_100baseT_Full;
333362306a36Sopenharmony_ci			break;
333462306a36Sopenharmony_ci		case SPD_DPX_10_HALF:
333562306a36Sopenharmony_ci			advertising |= ADVERTISED_10baseT_Half;
333662306a36Sopenharmony_ci			break;
333762306a36Sopenharmony_ci		case SPD_DPX_10_FULL:
333862306a36Sopenharmony_ci			advertising |= ADVERTISED_10baseT_Full;
333962306a36Sopenharmony_ci			break;
334062306a36Sopenharmony_ci		default:
334162306a36Sopenharmony_ci			break;
334262306a36Sopenharmony_ci		}
334362306a36Sopenharmony_ci	}
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	if (status & VELOCITY_SPEED_1000)
334662306a36Sopenharmony_ci		cmd->base.speed = SPEED_1000;
334762306a36Sopenharmony_ci	else if (status & VELOCITY_SPEED_100)
334862306a36Sopenharmony_ci		cmd->base.speed = SPEED_100;
334962306a36Sopenharmony_ci	else
335062306a36Sopenharmony_ci		cmd->base.speed = SPEED_10;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	cmd->base.autoneg = (status & VELOCITY_AUTONEG_ENABLE) ?
335362306a36Sopenharmony_ci		AUTONEG_ENABLE : AUTONEG_DISABLE;
335462306a36Sopenharmony_ci	cmd->base.port = PORT_TP;
335562306a36Sopenharmony_ci	cmd->base.phy_address = readb(&regs->MIIADR) & 0x1F;
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	if (status & VELOCITY_DUPLEX_FULL)
335862306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_FULL;
335962306a36Sopenharmony_ci	else
336062306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_HALF;
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
336362306a36Sopenharmony_ci						supported);
336462306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
336562306a36Sopenharmony_ci						advertising);
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	return 0;
336862306a36Sopenharmony_ci}
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_cistatic int velocity_set_link_ksettings(struct net_device *dev,
337162306a36Sopenharmony_ci				       const struct ethtool_link_ksettings *cmd)
337262306a36Sopenharmony_ci{
337362306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
337462306a36Sopenharmony_ci	u32 speed = cmd->base.speed;
337562306a36Sopenharmony_ci	u32 curr_status;
337662306a36Sopenharmony_ci	u32 new_status = 0;
337762306a36Sopenharmony_ci	int ret = 0;
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	curr_status = check_connection_type(vptr->mac_regs);
338062306a36Sopenharmony_ci	curr_status &= (~VELOCITY_LINK_FAIL);
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	new_status |= ((cmd->base.autoneg) ? VELOCITY_AUTONEG_ENABLE : 0);
338362306a36Sopenharmony_ci	new_status |= ((speed == SPEED_1000) ? VELOCITY_SPEED_1000 : 0);
338462306a36Sopenharmony_ci	new_status |= ((speed == SPEED_100) ? VELOCITY_SPEED_100 : 0);
338562306a36Sopenharmony_ci	new_status |= ((speed == SPEED_10) ? VELOCITY_SPEED_10 : 0);
338662306a36Sopenharmony_ci	new_status |= ((cmd->base.duplex == DUPLEX_FULL) ?
338762306a36Sopenharmony_ci		       VELOCITY_DUPLEX_FULL : 0);
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	if ((new_status & VELOCITY_AUTONEG_ENABLE) &&
339062306a36Sopenharmony_ci	    (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) {
339162306a36Sopenharmony_ci		ret = -EINVAL;
339262306a36Sopenharmony_ci	} else {
339362306a36Sopenharmony_ci		enum speed_opt spd_dpx;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci		if (new_status & VELOCITY_AUTONEG_ENABLE)
339662306a36Sopenharmony_ci			spd_dpx = SPD_DPX_AUTO;
339762306a36Sopenharmony_ci		else if ((new_status & VELOCITY_SPEED_1000) &&
339862306a36Sopenharmony_ci			 (new_status & VELOCITY_DUPLEX_FULL)) {
339962306a36Sopenharmony_ci			spd_dpx = SPD_DPX_1000_FULL;
340062306a36Sopenharmony_ci		} else if (new_status & VELOCITY_SPEED_100)
340162306a36Sopenharmony_ci			spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ?
340262306a36Sopenharmony_ci				SPD_DPX_100_FULL : SPD_DPX_100_HALF;
340362306a36Sopenharmony_ci		else if (new_status & VELOCITY_SPEED_10)
340462306a36Sopenharmony_ci			spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ?
340562306a36Sopenharmony_ci				SPD_DPX_10_FULL : SPD_DPX_10_HALF;
340662306a36Sopenharmony_ci		else
340762306a36Sopenharmony_ci			return -EOPNOTSUPP;
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci		vptr->options.spd_dpx = spd_dpx;
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci		velocity_set_media_mode(vptr, new_status);
341262306a36Sopenharmony_ci	}
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	return ret;
341562306a36Sopenharmony_ci}
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_cistatic void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
341862306a36Sopenharmony_ci{
341962306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	strscpy(info->driver, VELOCITY_NAME, sizeof(info->driver));
342262306a36Sopenharmony_ci	strscpy(info->version, VELOCITY_VERSION, sizeof(info->version));
342362306a36Sopenharmony_ci	if (vptr->pdev)
342462306a36Sopenharmony_ci		strscpy(info->bus_info, pci_name(vptr->pdev),
342562306a36Sopenharmony_ci						sizeof(info->bus_info));
342662306a36Sopenharmony_ci	else
342762306a36Sopenharmony_ci		strscpy(info->bus_info, "platform", sizeof(info->bus_info));
342862306a36Sopenharmony_ci}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_cistatic void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
343162306a36Sopenharmony_ci{
343262306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
343362306a36Sopenharmony_ci	wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP;
343462306a36Sopenharmony_ci	wol->wolopts |= WAKE_MAGIC;
343562306a36Sopenharmony_ci	/*
343662306a36Sopenharmony_ci	   if (vptr->wol_opts & VELOCITY_WOL_PHY)
343762306a36Sopenharmony_ci		   wol.wolopts|=WAKE_PHY;
343862306a36Sopenharmony_ci			 */
343962306a36Sopenharmony_ci	if (vptr->wol_opts & VELOCITY_WOL_UCAST)
344062306a36Sopenharmony_ci		wol->wolopts |= WAKE_UCAST;
344162306a36Sopenharmony_ci	if (vptr->wol_opts & VELOCITY_WOL_ARP)
344262306a36Sopenharmony_ci		wol->wolopts |= WAKE_ARP;
344362306a36Sopenharmony_ci	memcpy(&wol->sopass, vptr->wol_passwd, 6);
344462306a36Sopenharmony_ci}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_cistatic int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
344762306a36Sopenharmony_ci{
344862306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP)))
345162306a36Sopenharmony_ci		return -EFAULT;
345262306a36Sopenharmony_ci	vptr->wol_opts = VELOCITY_WOL_MAGIC;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	/*
345562306a36Sopenharmony_ci	   if (wol.wolopts & WAKE_PHY) {
345662306a36Sopenharmony_ci	   vptr->wol_opts|=VELOCITY_WOL_PHY;
345762306a36Sopenharmony_ci	   vptr->flags |=VELOCITY_FLAGS_WOL_ENABLED;
345862306a36Sopenharmony_ci	   }
345962306a36Sopenharmony_ci	 */
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC) {
346262306a36Sopenharmony_ci		vptr->wol_opts |= VELOCITY_WOL_MAGIC;
346362306a36Sopenharmony_ci		vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED;
346462306a36Sopenharmony_ci	}
346562306a36Sopenharmony_ci	if (wol->wolopts & WAKE_UCAST) {
346662306a36Sopenharmony_ci		vptr->wol_opts |= VELOCITY_WOL_UCAST;
346762306a36Sopenharmony_ci		vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED;
346862306a36Sopenharmony_ci	}
346962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_ARP) {
347062306a36Sopenharmony_ci		vptr->wol_opts |= VELOCITY_WOL_ARP;
347162306a36Sopenharmony_ci		vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED;
347262306a36Sopenharmony_ci	}
347362306a36Sopenharmony_ci	memcpy(vptr->wol_passwd, wol->sopass, 6);
347462306a36Sopenharmony_ci	return 0;
347562306a36Sopenharmony_ci}
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_cistatic int get_pending_timer_val(int val)
347862306a36Sopenharmony_ci{
347962306a36Sopenharmony_ci	int mult_bits = val >> 6;
348062306a36Sopenharmony_ci	int mult = 1;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	switch (mult_bits)
348362306a36Sopenharmony_ci	{
348462306a36Sopenharmony_ci	case 1:
348562306a36Sopenharmony_ci		mult = 4; break;
348662306a36Sopenharmony_ci	case 2:
348762306a36Sopenharmony_ci		mult = 16; break;
348862306a36Sopenharmony_ci	case 3:
348962306a36Sopenharmony_ci		mult = 64; break;
349062306a36Sopenharmony_ci	case 0:
349162306a36Sopenharmony_ci	default:
349262306a36Sopenharmony_ci		break;
349362306a36Sopenharmony_ci	}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	return (val & 0x3f) * mult;
349662306a36Sopenharmony_ci}
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_cistatic void set_pending_timer_val(int *val, u32 us)
349962306a36Sopenharmony_ci{
350062306a36Sopenharmony_ci	u8 mult = 0;
350162306a36Sopenharmony_ci	u8 shift = 0;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	if (us >= 0x3f) {
350462306a36Sopenharmony_ci		mult = 1; /* mult with 4 */
350562306a36Sopenharmony_ci		shift = 2;
350662306a36Sopenharmony_ci	}
350762306a36Sopenharmony_ci	if (us >= 0x3f * 4) {
350862306a36Sopenharmony_ci		mult = 2; /* mult with 16 */
350962306a36Sopenharmony_ci		shift = 4;
351062306a36Sopenharmony_ci	}
351162306a36Sopenharmony_ci	if (us >= 0x3f * 16) {
351262306a36Sopenharmony_ci		mult = 3; /* mult with 64 */
351362306a36Sopenharmony_ci		shift = 6;
351462306a36Sopenharmony_ci	}
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	*val = (mult << 6) | ((us >> shift) & 0x3f);
351762306a36Sopenharmony_ci}
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_cistatic int velocity_get_coalesce(struct net_device *dev,
352162306a36Sopenharmony_ci				 struct ethtool_coalesce *ecmd,
352262306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
352362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
352462306a36Sopenharmony_ci{
352562306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup;
352862306a36Sopenharmony_ci	ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup;
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci	ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer);
353162306a36Sopenharmony_ci	ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer);
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci	return 0;
353462306a36Sopenharmony_ci}
353562306a36Sopenharmony_ci
353662306a36Sopenharmony_cistatic int velocity_set_coalesce(struct net_device *dev,
353762306a36Sopenharmony_ci				 struct ethtool_coalesce *ecmd,
353862306a36Sopenharmony_ci				 struct kernel_ethtool_coalesce *kernel_coal,
353962306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
354062306a36Sopenharmony_ci{
354162306a36Sopenharmony_ci	struct velocity_info *vptr = netdev_priv(dev);
354262306a36Sopenharmony_ci	int max_us = 0x3f * 64;
354362306a36Sopenharmony_ci	unsigned long flags;
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	/* 6 bits of  */
354662306a36Sopenharmony_ci	if (ecmd->tx_coalesce_usecs > max_us)
354762306a36Sopenharmony_ci		return -EINVAL;
354862306a36Sopenharmony_ci	if (ecmd->rx_coalesce_usecs > max_us)
354962306a36Sopenharmony_ci		return -EINVAL;
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	if (ecmd->tx_max_coalesced_frames > 0xff)
355262306a36Sopenharmony_ci		return -EINVAL;
355362306a36Sopenharmony_ci	if (ecmd->rx_max_coalesced_frames > 0xff)
355462306a36Sopenharmony_ci		return -EINVAL;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames;
355762306a36Sopenharmony_ci	vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames;
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_ci	set_pending_timer_val(&vptr->options.rxqueue_timer,
356062306a36Sopenharmony_ci			ecmd->rx_coalesce_usecs);
356162306a36Sopenharmony_ci	set_pending_timer_val(&vptr->options.txqueue_timer,
356262306a36Sopenharmony_ci			ecmd->tx_coalesce_usecs);
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	/* Setup the interrupt suppression and queue timers */
356562306a36Sopenharmony_ci	spin_lock_irqsave(&vptr->lock, flags);
356662306a36Sopenharmony_ci	mac_disable_int(vptr->mac_regs);
356762306a36Sopenharmony_ci	setup_adaptive_interrupts(vptr);
356862306a36Sopenharmony_ci	setup_queue_timers(vptr);
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci	mac_write_int_mask(vptr->int_mask, vptr->mac_regs);
357162306a36Sopenharmony_ci	mac_clear_isr(vptr->mac_regs);
357262306a36Sopenharmony_ci	mac_enable_int(vptr->mac_regs);
357362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vptr->lock, flags);
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci	return 0;
357662306a36Sopenharmony_ci}
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_cistatic const char velocity_gstrings[][ETH_GSTRING_LEN] = {
357962306a36Sopenharmony_ci	"rx_all",
358062306a36Sopenharmony_ci	"rx_ok",
358162306a36Sopenharmony_ci	"tx_ok",
358262306a36Sopenharmony_ci	"rx_error",
358362306a36Sopenharmony_ci	"rx_runt_ok",
358462306a36Sopenharmony_ci	"rx_runt_err",
358562306a36Sopenharmony_ci	"rx_64",
358662306a36Sopenharmony_ci	"tx_64",
358762306a36Sopenharmony_ci	"rx_65_to_127",
358862306a36Sopenharmony_ci	"tx_65_to_127",
358962306a36Sopenharmony_ci	"rx_128_to_255",
359062306a36Sopenharmony_ci	"tx_128_to_255",
359162306a36Sopenharmony_ci	"rx_256_to_511",
359262306a36Sopenharmony_ci	"tx_256_to_511",
359362306a36Sopenharmony_ci	"rx_512_to_1023",
359462306a36Sopenharmony_ci	"tx_512_to_1023",
359562306a36Sopenharmony_ci	"rx_1024_to_1518",
359662306a36Sopenharmony_ci	"tx_1024_to_1518",
359762306a36Sopenharmony_ci	"tx_ether_collisions",
359862306a36Sopenharmony_ci	"rx_crc_errors",
359962306a36Sopenharmony_ci	"rx_jumbo",
360062306a36Sopenharmony_ci	"tx_jumbo",
360162306a36Sopenharmony_ci	"rx_mac_control_frames",
360262306a36Sopenharmony_ci	"tx_mac_control_frames",
360362306a36Sopenharmony_ci	"rx_frame_alignment_errors",
360462306a36Sopenharmony_ci	"rx_long_ok",
360562306a36Sopenharmony_ci	"rx_long_err",
360662306a36Sopenharmony_ci	"tx_sqe_errors",
360762306a36Sopenharmony_ci	"rx_no_buf",
360862306a36Sopenharmony_ci	"rx_symbol_errors",
360962306a36Sopenharmony_ci	"in_range_length_errors",
361062306a36Sopenharmony_ci	"late_collisions"
361162306a36Sopenharmony_ci};
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_cistatic void velocity_get_strings(struct net_device *dev, u32 sset, u8 *data)
361462306a36Sopenharmony_ci{
361562306a36Sopenharmony_ci	switch (sset) {
361662306a36Sopenharmony_ci	case ETH_SS_STATS:
361762306a36Sopenharmony_ci		memcpy(data, *velocity_gstrings, sizeof(velocity_gstrings));
361862306a36Sopenharmony_ci		break;
361962306a36Sopenharmony_ci	}
362062306a36Sopenharmony_ci}
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_cistatic int velocity_get_sset_count(struct net_device *dev, int sset)
362362306a36Sopenharmony_ci{
362462306a36Sopenharmony_ci	switch (sset) {
362562306a36Sopenharmony_ci	case ETH_SS_STATS:
362662306a36Sopenharmony_ci		return ARRAY_SIZE(velocity_gstrings);
362762306a36Sopenharmony_ci	default:
362862306a36Sopenharmony_ci		return -EOPNOTSUPP;
362962306a36Sopenharmony_ci	}
363062306a36Sopenharmony_ci}
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_cistatic void velocity_get_ethtool_stats(struct net_device *dev,
363362306a36Sopenharmony_ci				       struct ethtool_stats *stats, u64 *data)
363462306a36Sopenharmony_ci{
363562306a36Sopenharmony_ci	if (netif_running(dev)) {
363662306a36Sopenharmony_ci		struct velocity_info *vptr = netdev_priv(dev);
363762306a36Sopenharmony_ci		u32 *p = vptr->mib_counter;
363862306a36Sopenharmony_ci		int i;
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci		spin_lock_irq(&vptr->lock);
364162306a36Sopenharmony_ci		velocity_update_hw_mibs(vptr);
364262306a36Sopenharmony_ci		spin_unlock_irq(&vptr->lock);
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(velocity_gstrings); i++)
364562306a36Sopenharmony_ci			*data++ = *p++;
364662306a36Sopenharmony_ci	}
364762306a36Sopenharmony_ci}
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops = {
365062306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
365162306a36Sopenharmony_ci				     ETHTOOL_COALESCE_MAX_FRAMES,
365262306a36Sopenharmony_ci	.get_drvinfo		= velocity_get_drvinfo,
365362306a36Sopenharmony_ci	.get_wol		= velocity_ethtool_get_wol,
365462306a36Sopenharmony_ci	.set_wol		= velocity_ethtool_set_wol,
365562306a36Sopenharmony_ci	.get_link		= velocity_get_link,
365662306a36Sopenharmony_ci	.get_strings		= velocity_get_strings,
365762306a36Sopenharmony_ci	.get_sset_count		= velocity_get_sset_count,
365862306a36Sopenharmony_ci	.get_ethtool_stats	= velocity_get_ethtool_stats,
365962306a36Sopenharmony_ci	.get_coalesce		= velocity_get_coalesce,
366062306a36Sopenharmony_ci	.set_coalesce		= velocity_set_coalesce,
366162306a36Sopenharmony_ci	.begin			= velocity_ethtool_up,
366262306a36Sopenharmony_ci	.complete		= velocity_ethtool_down,
366362306a36Sopenharmony_ci	.get_link_ksettings	= velocity_get_link_ksettings,
366462306a36Sopenharmony_ci	.set_link_ksettings	= velocity_set_link_ksettings,
366562306a36Sopenharmony_ci};
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_INET)
366862306a36Sopenharmony_cistatic int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr)
366962306a36Sopenharmony_ci{
367062306a36Sopenharmony_ci	struct in_ifaddr *ifa = ptr;
367162306a36Sopenharmony_ci	struct net_device *dev = ifa->ifa_dev->dev;
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci	if (dev_net(dev) == &init_net &&
367462306a36Sopenharmony_ci	    dev->netdev_ops == &velocity_netdev_ops)
367562306a36Sopenharmony_ci		velocity_get_ip(netdev_priv(dev));
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci	return NOTIFY_DONE;
367862306a36Sopenharmony_ci}
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_cistatic struct notifier_block velocity_inetaddr_notifier = {
368162306a36Sopenharmony_ci	.notifier_call	= velocity_netdev_event,
368262306a36Sopenharmony_ci};
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_cistatic void velocity_register_notifier(void)
368562306a36Sopenharmony_ci{
368662306a36Sopenharmony_ci	register_inetaddr_notifier(&velocity_inetaddr_notifier);
368762306a36Sopenharmony_ci}
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_cistatic void velocity_unregister_notifier(void)
369062306a36Sopenharmony_ci{
369162306a36Sopenharmony_ci	unregister_inetaddr_notifier(&velocity_inetaddr_notifier);
369262306a36Sopenharmony_ci}
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci#else
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci#define velocity_register_notifier()	do {} while (0)
369762306a36Sopenharmony_ci#define velocity_unregister_notifier()	do {} while (0)
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci#endif	/* defined(CONFIG_PM) && defined(CONFIG_INET) */
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci/**
370262306a36Sopenharmony_ci *	velocity_init_module	-	load time function
370362306a36Sopenharmony_ci *
370462306a36Sopenharmony_ci *	Called when the velocity module is loaded. The PCI driver
370562306a36Sopenharmony_ci *	is registered with the PCI layer, and in turn will call
370662306a36Sopenharmony_ci *	the probe functions for each velocity adapter installed
370762306a36Sopenharmony_ci *	in the system.
370862306a36Sopenharmony_ci */
370962306a36Sopenharmony_cistatic int __init velocity_init_module(void)
371062306a36Sopenharmony_ci{
371162306a36Sopenharmony_ci	int ret_pci, ret_platform;
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci	velocity_register_notifier();
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	ret_pci = pci_register_driver(&velocity_pci_driver);
371662306a36Sopenharmony_ci	ret_platform = platform_driver_register(&velocity_platform_driver);
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	/* if both_registers failed, remove the notifier */
371962306a36Sopenharmony_ci	if ((ret_pci < 0) && (ret_platform < 0)) {
372062306a36Sopenharmony_ci		velocity_unregister_notifier();
372162306a36Sopenharmony_ci		return ret_pci;
372262306a36Sopenharmony_ci	}
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_ci	return 0;
372562306a36Sopenharmony_ci}
372662306a36Sopenharmony_ci
372762306a36Sopenharmony_ci/**
372862306a36Sopenharmony_ci *	velocity_cleanup_module		-	module unload
372962306a36Sopenharmony_ci *
373062306a36Sopenharmony_ci *	When the velocity hardware is unloaded this function is called.
373162306a36Sopenharmony_ci *	It will clean up the notifiers and the unregister the PCI
373262306a36Sopenharmony_ci *	driver interface for this hardware. This in turn cleans up
373362306a36Sopenharmony_ci *	all discovered interfaces before returning from the function
373462306a36Sopenharmony_ci */
373562306a36Sopenharmony_cistatic void __exit velocity_cleanup_module(void)
373662306a36Sopenharmony_ci{
373762306a36Sopenharmony_ci	velocity_unregister_notifier();
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	pci_unregister_driver(&velocity_pci_driver);
374062306a36Sopenharmony_ci	platform_driver_unregister(&velocity_platform_driver);
374162306a36Sopenharmony_ci}
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_cimodule_init(velocity_init_module);
374462306a36Sopenharmony_cimodule_exit(velocity_cleanup_module);
3745