162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci*/
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define DRV_NAME	"uli526x"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/timer.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/ioport.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/netdevice.h>
2262306a36Sopenharmony_ci#include <linux/etherdevice.h>
2362306a36Sopenharmony_ci#include <linux/ethtool.h>
2462306a36Sopenharmony_ci#include <linux/skbuff.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/spinlock.h>
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci#include <linux/bitops.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <asm/processor.h>
3162306a36Sopenharmony_ci#include <asm/io.h>
3262306a36Sopenharmony_ci#include <asm/dma.h>
3362306a36Sopenharmony_ci#include <linux/uaccess.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define uw32(reg, val)	iowrite32(val, ioaddr + (reg))
3662306a36Sopenharmony_ci#define ur32(reg)	ioread32(ioaddr + (reg))
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Board/System/Debug information/definition ---------------- */
3962306a36Sopenharmony_ci#define PCI_ULI5261_ID  0x526110B9	/* ULi M5261 ID*/
4062306a36Sopenharmony_ci#define PCI_ULI5263_ID  0x526310B9	/* ULi M5263 ID*/
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define ULI526X_IO_SIZE 0x100
4362306a36Sopenharmony_ci#define TX_DESC_CNT     0x20            /* Allocated Tx descriptors */
4462306a36Sopenharmony_ci#define RX_DESC_CNT     0x30            /* Allocated Rx descriptors */
4562306a36Sopenharmony_ci#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2)	/* Max TX packet count */
4662306a36Sopenharmony_ci#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3)	/* TX wakeup count */
4762306a36Sopenharmony_ci#define DESC_ALL_CNT    (TX_DESC_CNT + RX_DESC_CNT)
4862306a36Sopenharmony_ci#define TX_BUF_ALLOC    0x600
4962306a36Sopenharmony_ci#define RX_ALLOC_SIZE   0x620
5062306a36Sopenharmony_ci#define ULI526X_RESET    1
5162306a36Sopenharmony_ci#define CR0_DEFAULT     0
5262306a36Sopenharmony_ci#define CR6_DEFAULT     0x22200000
5362306a36Sopenharmony_ci#define CR7_DEFAULT     0x180c1
5462306a36Sopenharmony_ci#define CR15_DEFAULT    0x06            /* TxJabber RxWatchdog */
5562306a36Sopenharmony_ci#define TDES0_ERR_MASK  0x4302          /* TXJT, LC, EC, FUE */
5662306a36Sopenharmony_ci#define MAX_PACKET_SIZE 1514
5762306a36Sopenharmony_ci#define ULI5261_MAX_MULTICAST 14
5862306a36Sopenharmony_ci#define RX_COPY_SIZE	100
5962306a36Sopenharmony_ci#define MAX_CHECK_PACKET 0x8000
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define ULI526X_10MHF      0
6262306a36Sopenharmony_ci#define ULI526X_100MHF     1
6362306a36Sopenharmony_ci#define ULI526X_10MFD      4
6462306a36Sopenharmony_ci#define ULI526X_100MFD     5
6562306a36Sopenharmony_ci#define ULI526X_AUTO       8
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define ULI526X_TXTH_72	0x400000	/* TX TH 72 byte */
6862306a36Sopenharmony_ci#define ULI526X_TXTH_96	0x404000	/* TX TH 96 byte */
6962306a36Sopenharmony_ci#define ULI526X_TXTH_128	0x0000		/* TX TH 128 byte */
7062306a36Sopenharmony_ci#define ULI526X_TXTH_256	0x4000		/* TX TH 256 byte */
7162306a36Sopenharmony_ci#define ULI526X_TXTH_512	0x8000		/* TX TH 512 byte */
7262306a36Sopenharmony_ci#define ULI526X_TXTH_1K	0xC000		/* TX TH 1K  byte */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define ULI526X_TIMER_WUT  (jiffies + HZ * 1)/* timer wakeup time : 1 second */
7562306a36Sopenharmony_ci#define ULI526X_TX_TIMEOUT ((16*HZ)/2)	/* tx packet time-out time 8 s" */
7662306a36Sopenharmony_ci#define ULI526X_TX_KICK 	(4*HZ/2)	/* tx packet Kick-out time 2 s" */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define ULI526X_DBUG(dbug_now, msg, value)			\
7962306a36Sopenharmony_cido {								\
8062306a36Sopenharmony_ci	if (uli526x_debug || (dbug_now))			\
8162306a36Sopenharmony_ci		pr_err("%s %lx\n", (msg), (long) (value));	\
8262306a36Sopenharmony_ci} while (0)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define SHOW_MEDIA_TYPE(mode)					\
8562306a36Sopenharmony_ci	pr_err("Change Speed to %sMhz %s duplex\n",		\
8662306a36Sopenharmony_ci	       mode & 1 ? "100" : "10",				\
8762306a36Sopenharmony_ci	       mode & 4 ? "full" : "half");
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* CR9 definition: SROM/MII */
9162306a36Sopenharmony_ci#define CR9_SROM_READ   0x4800
9262306a36Sopenharmony_ci#define CR9_SRCS        0x1
9362306a36Sopenharmony_ci#define CR9_SRCLK       0x2
9462306a36Sopenharmony_ci#define CR9_CRDOUT      0x8
9562306a36Sopenharmony_ci#define SROM_DATA_0     0x0
9662306a36Sopenharmony_ci#define SROM_DATA_1     0x4
9762306a36Sopenharmony_ci#define PHY_DATA_1      0x20000
9862306a36Sopenharmony_ci#define PHY_DATA_0      0x00000
9962306a36Sopenharmony_ci#define MDCLKH          0x10000
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define PHY_POWER_DOWN	0x800
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define SROM_V41_CODE   0x14
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Structure/enum declaration ------------------------------- */
10662306a36Sopenharmony_cistruct tx_desc {
10762306a36Sopenharmony_ci        __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
10862306a36Sopenharmony_ci        char *tx_buf_ptr;               /* Data for us */
10962306a36Sopenharmony_ci        struct tx_desc *next_tx_desc;
11062306a36Sopenharmony_ci} __attribute__(( aligned(32) ));
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct rx_desc {
11362306a36Sopenharmony_ci	__le32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
11462306a36Sopenharmony_ci	struct sk_buff *rx_skb_ptr;	/* Data for us */
11562306a36Sopenharmony_ci	struct rx_desc *next_rx_desc;
11662306a36Sopenharmony_ci} __attribute__(( aligned(32) ));
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistruct uli526x_board_info {
11962306a36Sopenharmony_ci	struct uli_phy_ops {
12062306a36Sopenharmony_ci		void (*write)(struct uli526x_board_info *, u8, u8, u16);
12162306a36Sopenharmony_ci		u16 (*read)(struct uli526x_board_info *, u8, u8);
12262306a36Sopenharmony_ci	} phy;
12362306a36Sopenharmony_ci	struct net_device *next_dev;	/* next device */
12462306a36Sopenharmony_ci	struct pci_dev *pdev;		/* PCI device */
12562306a36Sopenharmony_ci	spinlock_t lock;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	void __iomem *ioaddr;		/* I/O base address */
12862306a36Sopenharmony_ci	u32 cr0_data;
12962306a36Sopenharmony_ci	u32 cr5_data;
13062306a36Sopenharmony_ci	u32 cr6_data;
13162306a36Sopenharmony_ci	u32 cr7_data;
13262306a36Sopenharmony_ci	u32 cr15_data;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* pointer for memory physical address */
13562306a36Sopenharmony_ci	dma_addr_t buf_pool_dma_ptr;	/* Tx buffer pool memory */
13662306a36Sopenharmony_ci	dma_addr_t buf_pool_dma_start;	/* Tx buffer pool align dword */
13762306a36Sopenharmony_ci	dma_addr_t desc_pool_dma_ptr;	/* descriptor pool memory */
13862306a36Sopenharmony_ci	dma_addr_t first_tx_desc_dma;
13962306a36Sopenharmony_ci	dma_addr_t first_rx_desc_dma;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* descriptor pointer */
14262306a36Sopenharmony_ci	unsigned char *buf_pool_ptr;	/* Tx buffer pool memory */
14362306a36Sopenharmony_ci	unsigned char *buf_pool_start;	/* Tx buffer pool align dword */
14462306a36Sopenharmony_ci	unsigned char *desc_pool_ptr;	/* descriptor pool memory */
14562306a36Sopenharmony_ci	struct tx_desc *first_tx_desc;
14662306a36Sopenharmony_ci	struct tx_desc *tx_insert_ptr;
14762306a36Sopenharmony_ci	struct tx_desc *tx_remove_ptr;
14862306a36Sopenharmony_ci	struct rx_desc *first_rx_desc;
14962306a36Sopenharmony_ci	struct rx_desc *rx_insert_ptr;
15062306a36Sopenharmony_ci	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
15162306a36Sopenharmony_ci	unsigned long tx_packet_cnt;	/* transmitted packet count */
15262306a36Sopenharmony_ci	unsigned long rx_avail_cnt;	/* available rx descriptor count */
15362306a36Sopenharmony_ci	unsigned long interval_rx_cnt;	/* rx packet count a callback time */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	u16 dbug_cnt;
15662306a36Sopenharmony_ci	u16 NIC_capability;		/* NIC media capability */
15762306a36Sopenharmony_ci	u16 PHY_reg4;			/* Saved Phyxcer register 4 value */
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	u8 media_mode;			/* user specify media mode */
16062306a36Sopenharmony_ci	u8 op_mode;			/* real work media mode */
16162306a36Sopenharmony_ci	u8 phy_addr;
16262306a36Sopenharmony_ci	u8 link_failed;			/* Ever link failed */
16362306a36Sopenharmony_ci	u8 wait_reset;			/* Hardware failed, need to reset */
16462306a36Sopenharmony_ci	struct timer_list timer;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Driver defined statistic counter */
16762306a36Sopenharmony_ci	unsigned long tx_fifo_underrun;
16862306a36Sopenharmony_ci	unsigned long tx_loss_carrier;
16962306a36Sopenharmony_ci	unsigned long tx_no_carrier;
17062306a36Sopenharmony_ci	unsigned long tx_late_collision;
17162306a36Sopenharmony_ci	unsigned long tx_excessive_collision;
17262306a36Sopenharmony_ci	unsigned long tx_jabber_timeout;
17362306a36Sopenharmony_ci	unsigned long reset_count;
17462306a36Sopenharmony_ci	unsigned long reset_cr8;
17562306a36Sopenharmony_ci	unsigned long reset_fatal;
17662306a36Sopenharmony_ci	unsigned long reset_TXtimeout;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* NIC SROM data */
17962306a36Sopenharmony_ci	unsigned char srom[128];
18062306a36Sopenharmony_ci	u8 init;
18162306a36Sopenharmony_ci};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cienum uli526x_offsets {
18462306a36Sopenharmony_ci	DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
18562306a36Sopenharmony_ci	DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
18662306a36Sopenharmony_ci	DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70,
18762306a36Sopenharmony_ci	DCR15 = 0x78
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cienum uli526x_CR6_bits {
19162306a36Sopenharmony_ci	CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
19262306a36Sopenharmony_ci	CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
19362306a36Sopenharmony_ci	CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/* Global variable declaration ----------------------------- */
19762306a36Sopenharmony_cistatic int uli526x_debug;
19862306a36Sopenharmony_cistatic unsigned char uli526x_media_mode = ULI526X_AUTO;
19962306a36Sopenharmony_cistatic u32 uli526x_cr6_user_set;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* For module input parameter */
20262306a36Sopenharmony_cistatic int debug;
20362306a36Sopenharmony_cistatic u32 cr6set;
20462306a36Sopenharmony_cistatic int mode = 8;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* function declaration ------------------------------------- */
20762306a36Sopenharmony_cistatic int uli526x_open(struct net_device *);
20862306a36Sopenharmony_cistatic netdev_tx_t uli526x_start_xmit(struct sk_buff *,
20962306a36Sopenharmony_ci					    struct net_device *);
21062306a36Sopenharmony_cistatic int uli526x_stop(struct net_device *);
21162306a36Sopenharmony_cistatic void uli526x_set_filter_mode(struct net_device *);
21262306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
21362306a36Sopenharmony_cistatic u16 read_srom_word(struct uli526x_board_info *, int);
21462306a36Sopenharmony_cistatic irqreturn_t uli526x_interrupt(int, void *);
21562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
21662306a36Sopenharmony_cistatic void uli526x_poll(struct net_device *dev);
21762306a36Sopenharmony_ci#endif
21862306a36Sopenharmony_cistatic void uli526x_descriptor_init(struct net_device *, void __iomem *);
21962306a36Sopenharmony_cistatic void allocate_rx_buffer(struct net_device *);
22062306a36Sopenharmony_cistatic void update_cr6(u32, void __iomem *);
22162306a36Sopenharmony_cistatic void send_filter_frame(struct net_device *, int);
22262306a36Sopenharmony_cistatic u16 phy_readby_cr9(struct uli526x_board_info *, u8, u8);
22362306a36Sopenharmony_cistatic u16 phy_readby_cr10(struct uli526x_board_info *, u8, u8);
22462306a36Sopenharmony_cistatic void phy_writeby_cr9(struct uli526x_board_info *, u8, u8, u16);
22562306a36Sopenharmony_cistatic void phy_writeby_cr10(struct uli526x_board_info *, u8, u8, u16);
22662306a36Sopenharmony_cistatic void phy_write_1bit(struct uli526x_board_info *db, u32);
22762306a36Sopenharmony_cistatic u16 phy_read_1bit(struct uli526x_board_info *db);
22862306a36Sopenharmony_cistatic u8 uli526x_sense_speed(struct uli526x_board_info *);
22962306a36Sopenharmony_cistatic void uli526x_process_mode(struct uli526x_board_info *);
23062306a36Sopenharmony_cistatic void uli526x_timer(struct timer_list *t);
23162306a36Sopenharmony_cistatic void uli526x_rx_packet(struct net_device *, struct uli526x_board_info *);
23262306a36Sopenharmony_cistatic void uli526x_free_tx_pkt(struct net_device *, struct uli526x_board_info *);
23362306a36Sopenharmony_cistatic void uli526x_reuse_skb(struct uli526x_board_info *, struct sk_buff *);
23462306a36Sopenharmony_cistatic void uli526x_dynamic_reset(struct net_device *);
23562306a36Sopenharmony_cistatic void uli526x_free_rxbuffer(struct uli526x_board_info *);
23662306a36Sopenharmony_cistatic void uli526x_init(struct net_device *);
23762306a36Sopenharmony_cistatic void uli526x_set_phyxcer(struct uli526x_board_info *);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void srom_clk_write(struct uli526x_board_info *db, u32 data)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS);
24462306a36Sopenharmony_ci	udelay(5);
24562306a36Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS | CR9_SRCLK);
24662306a36Sopenharmony_ci	udelay(5);
24762306a36Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS);
24862306a36Sopenharmony_ci	udelay(5);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* ULI526X network board routine ---------------------------- */
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
25462306a36Sopenharmony_ci	.ndo_open		= uli526x_open,
25562306a36Sopenharmony_ci	.ndo_stop		= uli526x_stop,
25662306a36Sopenharmony_ci	.ndo_start_xmit		= uli526x_start_xmit,
25762306a36Sopenharmony_ci	.ndo_set_rx_mode	= uli526x_set_filter_mode,
25862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
25962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
26062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
26162306a36Sopenharmony_ci	.ndo_poll_controller 	= uli526x_poll,
26262306a36Sopenharmony_ci#endif
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/*
26662306a36Sopenharmony_ci *	Search ULI526X board, allocate space and register it
26762306a36Sopenharmony_ci */
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int uli526x_init_one(struct pci_dev *pdev,
27062306a36Sopenharmony_ci			    const struct pci_device_id *ent)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct uli526x_board_info *db;	/* board information structure */
27362306a36Sopenharmony_ci	struct net_device *dev;
27462306a36Sopenharmony_ci	void __iomem *ioaddr;
27562306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
27662306a36Sopenharmony_ci	int i, err;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_init_one()", 0);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Init network device */
28162306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*db));
28262306a36Sopenharmony_ci	if (dev == NULL)
28362306a36Sopenharmony_ci		return -ENOMEM;
28462306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
28762306a36Sopenharmony_ci		pr_warn("32-bit PCI DMA not available\n");
28862306a36Sopenharmony_ci		err = -ENODEV;
28962306a36Sopenharmony_ci		goto err_out_free;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Enable Master/IO access, Disable memory access */
29362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
29462306a36Sopenharmony_ci	if (err)
29562306a36Sopenharmony_ci		goto err_out_free;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (!pci_resource_start(pdev, 0)) {
29862306a36Sopenharmony_ci		pr_err("I/O base is zero\n");
29962306a36Sopenharmony_ci		err = -ENODEV;
30062306a36Sopenharmony_ci		goto err_out_disable;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (pci_resource_len(pdev, 0) < (ULI526X_IO_SIZE) ) {
30462306a36Sopenharmony_ci		pr_err("Allocated I/O size too small\n");
30562306a36Sopenharmony_ci		err = -ENODEV;
30662306a36Sopenharmony_ci		goto err_out_disable;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
31062306a36Sopenharmony_ci	if (err < 0) {
31162306a36Sopenharmony_ci		pr_err("Failed to request PCI regions\n");
31262306a36Sopenharmony_ci		goto err_out_disable;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Init system & device */
31662306a36Sopenharmony_ci	db = netdev_priv(dev);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* Allocate Tx/Rx descriptor memory */
31962306a36Sopenharmony_ci	err = -ENOMEM;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	db->desc_pool_ptr = dma_alloc_coherent(&pdev->dev,
32262306a36Sopenharmony_ci					       sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
32362306a36Sopenharmony_ci					       &db->desc_pool_dma_ptr, GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!db->desc_pool_ptr)
32562306a36Sopenharmony_ci		goto err_out_release;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	db->buf_pool_ptr = dma_alloc_coherent(&pdev->dev,
32862306a36Sopenharmony_ci					      TX_BUF_ALLOC * TX_DESC_CNT + 4,
32962306a36Sopenharmony_ci					      &db->buf_pool_dma_ptr, GFP_KERNEL);
33062306a36Sopenharmony_ci	if (!db->buf_pool_ptr)
33162306a36Sopenharmony_ci		goto err_out_free_tx_desc;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
33462306a36Sopenharmony_ci	db->first_tx_desc_dma = db->desc_pool_dma_ptr;
33562306a36Sopenharmony_ci	db->buf_pool_start = db->buf_pool_ptr;
33662306a36Sopenharmony_ci	db->buf_pool_dma_start = db->buf_pool_dma_ptr;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	switch (ent->driver_data) {
33962306a36Sopenharmony_ci	case PCI_ULI5263_ID:
34062306a36Sopenharmony_ci		db->phy.write	= phy_writeby_cr10;
34162306a36Sopenharmony_ci		db->phy.read	= phy_readby_cr10;
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	default:
34462306a36Sopenharmony_ci		db->phy.write	= phy_writeby_cr9;
34562306a36Sopenharmony_ci		db->phy.read	= phy_readby_cr9;
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* IO region. */
35062306a36Sopenharmony_ci	ioaddr = pci_iomap(pdev, 0, 0);
35162306a36Sopenharmony_ci	if (!ioaddr)
35262306a36Sopenharmony_ci		goto err_out_free_tx_buf;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	db->ioaddr = ioaddr;
35562306a36Sopenharmony_ci	db->pdev = pdev;
35662306a36Sopenharmony_ci	db->init = 1;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Register some necessary functions */
36162306a36Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
36262306a36Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_lock_init(&db->lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* read 64 word srom data */
36862306a36Sopenharmony_ci	for (i = 0; i < 64; i++)
36962306a36Sopenharmony_ci		((__le16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db, i));
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* Set Node address */
37262306a36Sopenharmony_ci	if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0)		/* SROM absent, so read MAC address from ID Table */
37362306a36Sopenharmony_ci	{
37462306a36Sopenharmony_ci		uw32(DCR0, 0x10000);	//Diagnosis mode
37562306a36Sopenharmony_ci		uw32(DCR13, 0x1c0);	//Reset dianostic pointer port
37662306a36Sopenharmony_ci		uw32(DCR14, 0);		//Clear reset port
37762306a36Sopenharmony_ci		uw32(DCR14, 0x10);	//Reset ID Table pointer
37862306a36Sopenharmony_ci		uw32(DCR14, 0);		//Clear reset port
37962306a36Sopenharmony_ci		uw32(DCR13, 0);		//Clear CR13
38062306a36Sopenharmony_ci		uw32(DCR13, 0x1b0);	//Select ID Table access port
38162306a36Sopenharmony_ci		//Read MAC address from CR14
38262306a36Sopenharmony_ci		for (i = 0; i < 6; i++)
38362306a36Sopenharmony_ci			addr[i] = ur32(DCR14);
38462306a36Sopenharmony_ci		//Read end
38562306a36Sopenharmony_ci		uw32(DCR13, 0);		//Clear CR13
38662306a36Sopenharmony_ci		uw32(DCR0, 0);		//Clear CR0
38762306a36Sopenharmony_ci		udelay(10);
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	else		/*Exist SROM*/
39062306a36Sopenharmony_ci	{
39162306a36Sopenharmony_ci		for (i = 0; i < 6; i++)
39262306a36Sopenharmony_ci			addr[i] = db->srom[20 + i];
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	err = register_netdev (dev);
39762306a36Sopenharmony_ci	if (err)
39862306a36Sopenharmony_ci		goto err_out_unmap;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	netdev_info(dev, "ULi M%04lx at pci%s, %pM, irq %d\n",
40162306a36Sopenharmony_ci		    ent->driver_data >> 16, pci_name(pdev),
40262306a36Sopenharmony_ci		    dev->dev_addr, pdev->irq);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	pci_set_master(pdev);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cierr_out_unmap:
40962306a36Sopenharmony_ci	pci_iounmap(pdev, db->ioaddr);
41062306a36Sopenharmony_cierr_out_free_tx_buf:
41162306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
41262306a36Sopenharmony_ci			  db->buf_pool_ptr, db->buf_pool_dma_ptr);
41362306a36Sopenharmony_cierr_out_free_tx_desc:
41462306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev,
41562306a36Sopenharmony_ci			  sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
41662306a36Sopenharmony_ci			  db->desc_pool_ptr, db->desc_pool_dma_ptr);
41762306a36Sopenharmony_cierr_out_release:
41862306a36Sopenharmony_ci	pci_release_regions(pdev);
41962306a36Sopenharmony_cierr_out_disable:
42062306a36Sopenharmony_ci	pci_disable_device(pdev);
42162306a36Sopenharmony_cierr_out_free:
42262306a36Sopenharmony_ci	free_netdev(dev);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return err;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void uli526x_remove_one(struct pci_dev *pdev)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
43162306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	unregister_netdev(dev);
43462306a36Sopenharmony_ci	pci_iounmap(pdev, db->ioaddr);
43562306a36Sopenharmony_ci	dma_free_coherent(&db->pdev->dev,
43662306a36Sopenharmony_ci			  sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
43762306a36Sopenharmony_ci			  db->desc_pool_ptr, db->desc_pool_dma_ptr);
43862306a36Sopenharmony_ci	dma_free_coherent(&db->pdev->dev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
43962306a36Sopenharmony_ci			  db->buf_pool_ptr, db->buf_pool_dma_ptr);
44062306a36Sopenharmony_ci	pci_release_regions(pdev);
44162306a36Sopenharmony_ci	pci_disable_device(pdev);
44262306a36Sopenharmony_ci	free_netdev(dev);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/*
44762306a36Sopenharmony_ci *	Open the interface.
44862306a36Sopenharmony_ci *	The interface is opened whenever "ifconfig" activates it.
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int uli526x_open(struct net_device *dev)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int ret;
45462306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_open", 0);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* system variable init */
45962306a36Sopenharmony_ci	db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set;
46062306a36Sopenharmony_ci	db->tx_packet_cnt = 0;
46162306a36Sopenharmony_ci	db->rx_avail_cnt = 0;
46262306a36Sopenharmony_ci	db->link_failed = 1;
46362306a36Sopenharmony_ci	netif_carrier_off(dev);
46462306a36Sopenharmony_ci	db->wait_reset = 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	db->NIC_capability = 0xf;	/* All capability*/
46762306a36Sopenharmony_ci	db->PHY_reg4 = 0x1e0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* CR6 operation mode decision */
47062306a36Sopenharmony_ci	db->cr6_data |= ULI526X_TXTH_256;
47162306a36Sopenharmony_ci	db->cr0_data = CR0_DEFAULT;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Initialize ULI526X board */
47462306a36Sopenharmony_ci	uli526x_init(dev);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ret = request_irq(db->pdev->irq, uli526x_interrupt, IRQF_SHARED,
47762306a36Sopenharmony_ci			  dev->name, dev);
47862306a36Sopenharmony_ci	if (ret)
47962306a36Sopenharmony_ci		return ret;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Active System Interface */
48262306a36Sopenharmony_ci	netif_wake_queue(dev);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* set and active a timer process */
48562306a36Sopenharmony_ci	timer_setup(&db->timer, uli526x_timer, 0);
48662306a36Sopenharmony_ci	db->timer.expires = ULI526X_TIMER_WUT + HZ * 2;
48762306a36Sopenharmony_ci	add_timer(&db->timer);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return 0;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/*	Initialize ULI526X board
49462306a36Sopenharmony_ci *	Reset ULI526X board
49562306a36Sopenharmony_ci *	Initialize TX/Rx descriptor chain structure
49662306a36Sopenharmony_ci *	Send the set-up frame
49762306a36Sopenharmony_ci *	Enable Tx/Rx machine
49862306a36Sopenharmony_ci */
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void uli526x_init(struct net_device *dev)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
50362306a36Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
50462306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
50562306a36Sopenharmony_ci	u8	phy_tmp;
50662306a36Sopenharmony_ci	u8	timeout;
50762306a36Sopenharmony_ci	u16 phy_reg_reset;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_init()", 0);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Reset M526x MAC controller */
51362306a36Sopenharmony_ci	uw32(DCR0, ULI526X_RESET);	/* RESET MAC */
51462306a36Sopenharmony_ci	udelay(100);
51562306a36Sopenharmony_ci	uw32(DCR0, db->cr0_data);
51662306a36Sopenharmony_ci	udelay(5);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* Phy addr : In some boards,M5261/M5263 phy address != 1 */
51962306a36Sopenharmony_ci	db->phy_addr = 1;
52062306a36Sopenharmony_ci	for (phy_tmp = 0; phy_tmp < 32; phy_tmp++) {
52162306a36Sopenharmony_ci		u16 phy_value;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		phy_value = phy->read(db, phy_tmp, 3);	//peer add
52462306a36Sopenharmony_ci		if (phy_value != 0xffff && phy_value != 0) {
52562306a36Sopenharmony_ci			db->phy_addr = phy_tmp;
52662306a36Sopenharmony_ci			break;
52762306a36Sopenharmony_ci		}
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (phy_tmp == 32)
53162306a36Sopenharmony_ci		pr_warn("Can not find the phy address!!!\n");
53262306a36Sopenharmony_ci	/* Parser SROM and media mode */
53362306a36Sopenharmony_ci	db->media_mode = uli526x_media_mode;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* phyxcer capability setting */
53662306a36Sopenharmony_ci	phy_reg_reset = phy->read(db, db->phy_addr, 0);
53762306a36Sopenharmony_ci	phy_reg_reset = (phy_reg_reset | 0x8000);
53862306a36Sopenharmony_ci	phy->write(db, db->phy_addr, 0, phy_reg_reset);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management
54162306a36Sopenharmony_ci	 * functions") or phy data sheet for details on phy reset
54262306a36Sopenharmony_ci	 */
54362306a36Sopenharmony_ci	udelay(500);
54462306a36Sopenharmony_ci	timeout = 10;
54562306a36Sopenharmony_ci	while (timeout-- && phy->read(db, db->phy_addr, 0) & 0x8000)
54662306a36Sopenharmony_ci		udelay(100);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Process Phyxcer Media Mode */
54962306a36Sopenharmony_ci	uli526x_set_phyxcer(db);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Media Mode Process */
55262306a36Sopenharmony_ci	if ( !(db->media_mode & ULI526X_AUTO) )
55362306a36Sopenharmony_ci		db->op_mode = db->media_mode;		/* Force Mode */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Initialize Transmit/Receive descriptor and CR3/4 */
55662306a36Sopenharmony_ci	uli526x_descriptor_init(dev, ioaddr);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Init CR6 to program M526X operation */
55962306a36Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* Send setup frame */
56262306a36Sopenharmony_ci	send_filter_frame(dev, netdev_mc_count(dev));	/* M5261/M5263 */
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Init CR7, interrupt active bit */
56562306a36Sopenharmony_ci	db->cr7_data = CR7_DEFAULT;
56662306a36Sopenharmony_ci	uw32(DCR7, db->cr7_data);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* Init CR15, Tx jabber and Rx watchdog timer */
56962306a36Sopenharmony_ci	uw32(DCR15, db->cr15_data);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* Enable ULI526X Tx/Rx function */
57262306a36Sopenharmony_ci	db->cr6_data |= CR6_RXSC | CR6_TXSC;
57362306a36Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/*
57862306a36Sopenharmony_ci *	Hardware start transmission.
57962306a36Sopenharmony_ci *	Send a packet to media from the upper layer.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic netdev_tx_t uli526x_start_xmit(struct sk_buff *skb,
58362306a36Sopenharmony_ci					    struct net_device *dev)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
58662306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
58762306a36Sopenharmony_ci	struct tx_desc *txptr;
58862306a36Sopenharmony_ci	unsigned long flags;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_start_xmit", 0);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* Resource flag check */
59362306a36Sopenharmony_ci	netif_stop_queue(dev);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* Too large packet check */
59662306a36Sopenharmony_ci	if (skb->len > MAX_PACKET_SIZE) {
59762306a36Sopenharmony_ci		netdev_err(dev, "big packet = %d\n", (u16)skb->len);
59862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
59962306a36Sopenharmony_ci		return NETDEV_TX_OK;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* No Tx resource check, it never happen nromally */
60562306a36Sopenharmony_ci	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
60662306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
60762306a36Sopenharmony_ci		netdev_err(dev, "No Tx resource %ld\n", db->tx_packet_cnt);
60862306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Disable NIC interrupt */
61262306a36Sopenharmony_ci	uw32(DCR7, 0);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* transmit this packet */
61562306a36Sopenharmony_ci	txptr = db->tx_insert_ptr;
61662306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
61762306a36Sopenharmony_ci	txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* Point to next transmit free descriptor */
62062306a36Sopenharmony_ci	db->tx_insert_ptr = txptr->next_tx_desc;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* Transmit Packet Process */
62362306a36Sopenharmony_ci	if (db->tx_packet_cnt < TX_DESC_CNT) {
62462306a36Sopenharmony_ci		txptr->tdes0 = cpu_to_le32(0x80000000);	/* Set owner bit */
62562306a36Sopenharmony_ci		db->tx_packet_cnt++;			/* Ready to send */
62662306a36Sopenharmony_ci		uw32(DCR1, 0x1);			/* Issue Tx polling */
62762306a36Sopenharmony_ci		netif_trans_update(dev);		/* saved time stamp */
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* Tx resource check */
63162306a36Sopenharmony_ci	if ( db->tx_packet_cnt < TX_FREE_DESC_CNT )
63262306a36Sopenharmony_ci		netif_wake_queue(dev);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* Restore CR7 to enable interrupt */
63562306a36Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
63662306a36Sopenharmony_ci	uw32(DCR7, db->cr7_data);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/* free this SKB */
63962306a36Sopenharmony_ci	dev_consume_skb_any(skb);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return NETDEV_TX_OK;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/*
64662306a36Sopenharmony_ci *	Stop the interface.
64762306a36Sopenharmony_ci *	The interface is stopped when it is brought.
64862306a36Sopenharmony_ci */
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic int uli526x_stop(struct net_device *dev)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
65362306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* disable system */
65662306a36Sopenharmony_ci	netif_stop_queue(dev);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* deleted timer */
65962306a36Sopenharmony_ci	del_timer_sync(&db->timer);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Reset & stop ULI526X board */
66262306a36Sopenharmony_ci	uw32(DCR0, ULI526X_RESET);
66362306a36Sopenharmony_ci	udelay(5);
66462306a36Sopenharmony_ci	db->phy.write(db, db->phy_addr, 0, 0x8000);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* free interrupt */
66762306a36Sopenharmony_ci	free_irq(db->pdev->irq, dev);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* free allocated rx buffer */
67062306a36Sopenharmony_ci	uli526x_free_rxbuffer(db);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return 0;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/*
67762306a36Sopenharmony_ci *	M5261/M5263 insterrupt handler
67862306a36Sopenharmony_ci *	receive the packet to upper layer, free the transmitted packet
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic irqreturn_t uli526x_interrupt(int irq, void *dev_id)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct net_device *dev = dev_id;
68462306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
68562306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
68662306a36Sopenharmony_ci	unsigned long flags;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
68962306a36Sopenharmony_ci	uw32(DCR7, 0);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* Got ULI526X status */
69262306a36Sopenharmony_ci	db->cr5_data = ur32(DCR5);
69362306a36Sopenharmony_ci	uw32(DCR5, db->cr5_data);
69462306a36Sopenharmony_ci	if ( !(db->cr5_data & 0x180c1) ) {
69562306a36Sopenharmony_ci		/* Restore CR7 to enable interrupt mask */
69662306a36Sopenharmony_ci		uw32(DCR7, db->cr7_data);
69762306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
69862306a36Sopenharmony_ci		return IRQ_HANDLED;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* Check system status */
70262306a36Sopenharmony_ci	if (db->cr5_data & 0x2000) {
70362306a36Sopenharmony_ci		/* system bus error happen */
70462306a36Sopenharmony_ci		ULI526X_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
70562306a36Sopenharmony_ci		db->reset_fatal++;
70662306a36Sopenharmony_ci		db->wait_reset = 1;	/* Need to RESET */
70762306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
70862306a36Sopenharmony_ci		return IRQ_HANDLED;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	 /* Received the coming packet */
71262306a36Sopenharmony_ci	if ( (db->cr5_data & 0x40) && db->rx_avail_cnt )
71362306a36Sopenharmony_ci		uli526x_rx_packet(dev, db);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* reallocate rx descriptor buffer */
71662306a36Sopenharmony_ci	if (db->rx_avail_cnt<RX_DESC_CNT)
71762306a36Sopenharmony_ci		allocate_rx_buffer(dev);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Free the transmitted descriptor */
72062306a36Sopenharmony_ci	if ( db->cr5_data & 0x01)
72162306a36Sopenharmony_ci		uli526x_free_tx_pkt(dev, db);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Restore CR7 to enable interrupt mask */
72462306a36Sopenharmony_ci	uw32(DCR7, db->cr7_data);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
72762306a36Sopenharmony_ci	return IRQ_HANDLED;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
73162306a36Sopenharmony_cistatic void uli526x_poll(struct net_device *dev)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* ISR grabs the irqsave lock, so this should be safe */
73662306a36Sopenharmony_ci	uli526x_interrupt(db->pdev->irq, dev);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci#endif
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/*
74162306a36Sopenharmony_ci *	Free TX resource after TX complete
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic void uli526x_free_tx_pkt(struct net_device *dev,
74562306a36Sopenharmony_ci				struct uli526x_board_info * db)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct tx_desc *txptr;
74862306a36Sopenharmony_ci	u32 tdes0;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	txptr = db->tx_remove_ptr;
75162306a36Sopenharmony_ci	while(db->tx_packet_cnt) {
75262306a36Sopenharmony_ci		tdes0 = le32_to_cpu(txptr->tdes0);
75362306a36Sopenharmony_ci		if (tdes0 & 0x80000000)
75462306a36Sopenharmony_ci			break;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		/* A packet sent completed */
75762306a36Sopenharmony_ci		db->tx_packet_cnt--;
75862306a36Sopenharmony_ci		dev->stats.tx_packets++;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		/* Transmit statistic counter */
76162306a36Sopenharmony_ci		if ( tdes0 != 0x7fffffff ) {
76262306a36Sopenharmony_ci			dev->stats.collisions += (tdes0 >> 3) & 0xf;
76362306a36Sopenharmony_ci			dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff;
76462306a36Sopenharmony_ci			if (tdes0 & TDES0_ERR_MASK) {
76562306a36Sopenharmony_ci				dev->stats.tx_errors++;
76662306a36Sopenharmony_ci				if (tdes0 & 0x0002) {	/* UnderRun */
76762306a36Sopenharmony_ci					db->tx_fifo_underrun++;
76862306a36Sopenharmony_ci					if ( !(db->cr6_data & CR6_SFT) ) {
76962306a36Sopenharmony_ci						db->cr6_data = db->cr6_data | CR6_SFT;
77062306a36Sopenharmony_ci						update_cr6(db->cr6_data, db->ioaddr);
77162306a36Sopenharmony_ci					}
77262306a36Sopenharmony_ci				}
77362306a36Sopenharmony_ci				if (tdes0 & 0x0100)
77462306a36Sopenharmony_ci					db->tx_excessive_collision++;
77562306a36Sopenharmony_ci				if (tdes0 & 0x0200)
77662306a36Sopenharmony_ci					db->tx_late_collision++;
77762306a36Sopenharmony_ci				if (tdes0 & 0x0400)
77862306a36Sopenharmony_ci					db->tx_no_carrier++;
77962306a36Sopenharmony_ci				if (tdes0 & 0x0800)
78062306a36Sopenharmony_ci					db->tx_loss_carrier++;
78162306a36Sopenharmony_ci				if (tdes0 & 0x4000)
78262306a36Sopenharmony_ci					db->tx_jabber_timeout++;
78362306a36Sopenharmony_ci			}
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		txptr = txptr->next_tx_desc;
78762306a36Sopenharmony_ci	}/* End of while */
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Update TX remove pointer to next */
79062306a36Sopenharmony_ci	db->tx_remove_ptr = txptr;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* Resource available check */
79362306a36Sopenharmony_ci	if ( db->tx_packet_cnt < TX_WAKE_DESC_CNT )
79462306a36Sopenharmony_ci		netif_wake_queue(dev);	/* Active upper layer, send again */
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci/*
79962306a36Sopenharmony_ci *	Receive the come packet and pass to upper layer
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info * db)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct rx_desc *rxptr;
80562306a36Sopenharmony_ci	struct sk_buff *skb;
80662306a36Sopenharmony_ci	int rxlen;
80762306a36Sopenharmony_ci	u32 rdes0;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	rxptr = db->rx_ready_ptr;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	while(db->rx_avail_cnt) {
81262306a36Sopenharmony_ci		rdes0 = le32_to_cpu(rxptr->rdes0);
81362306a36Sopenharmony_ci		if (rdes0 & 0x80000000)	/* packet owner check */
81462306a36Sopenharmony_ci		{
81562306a36Sopenharmony_ci			break;
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		db->rx_avail_cnt--;
81962306a36Sopenharmony_ci		db->interval_rx_cnt++;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		dma_unmap_single(&db->pdev->dev, le32_to_cpu(rxptr->rdes2),
82262306a36Sopenharmony_ci				 RX_ALLOC_SIZE, DMA_FROM_DEVICE);
82362306a36Sopenharmony_ci		if ( (rdes0 & 0x300) != 0x300) {
82462306a36Sopenharmony_ci			/* A packet without First/Last flag */
82562306a36Sopenharmony_ci			/* reuse this SKB */
82662306a36Sopenharmony_ci			ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
82762306a36Sopenharmony_ci			uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
82862306a36Sopenharmony_ci		} else {
82962306a36Sopenharmony_ci			/* A packet with First/Last flag */
83062306a36Sopenharmony_ci			rxlen = ( (rdes0 >> 16) & 0x3fff) - 4;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci			/* error summary bit check */
83362306a36Sopenharmony_ci			if (rdes0 & 0x8000) {
83462306a36Sopenharmony_ci				/* This is a error packet */
83562306a36Sopenharmony_ci				dev->stats.rx_errors++;
83662306a36Sopenharmony_ci				if (rdes0 & 1)
83762306a36Sopenharmony_ci					dev->stats.rx_fifo_errors++;
83862306a36Sopenharmony_ci				if (rdes0 & 2)
83962306a36Sopenharmony_ci					dev->stats.rx_crc_errors++;
84062306a36Sopenharmony_ci				if (rdes0 & 0x80)
84162306a36Sopenharmony_ci					dev->stats.rx_length_errors++;
84262306a36Sopenharmony_ci			}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci			if ( !(rdes0 & 0x8000) ||
84562306a36Sopenharmony_ci				((db->cr6_data & CR6_PM) && (rxlen>6)) ) {
84662306a36Sopenharmony_ci				struct sk_buff *new_skb = NULL;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci				skb = rxptr->rx_skb_ptr;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci				/* Good packet, send to upper layer */
85162306a36Sopenharmony_ci				/* Shorst packet used new SKB */
85262306a36Sopenharmony_ci				if ((rxlen < RX_COPY_SIZE) &&
85362306a36Sopenharmony_ci				    (((new_skb = netdev_alloc_skb(dev, rxlen + 2)) != NULL))) {
85462306a36Sopenharmony_ci					skb = new_skb;
85562306a36Sopenharmony_ci					/* size less than COPY_SIZE, allocate a rxlen SKB */
85662306a36Sopenharmony_ci					skb_reserve(skb, 2); /* 16byte align */
85762306a36Sopenharmony_ci					skb_put_data(skb,
85862306a36Sopenharmony_ci						     skb_tail_pointer(rxptr->rx_skb_ptr),
85962306a36Sopenharmony_ci						     rxlen);
86062306a36Sopenharmony_ci					uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
86162306a36Sopenharmony_ci				} else
86262306a36Sopenharmony_ci					skb_put(skb, rxlen);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
86562306a36Sopenharmony_ci				netif_rx(skb);
86662306a36Sopenharmony_ci				dev->stats.rx_packets++;
86762306a36Sopenharmony_ci				dev->stats.rx_bytes += rxlen;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci			} else {
87062306a36Sopenharmony_ci				/* Reuse SKB buffer when the packet is error */
87162306a36Sopenharmony_ci				ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
87262306a36Sopenharmony_ci				uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
87362306a36Sopenharmony_ci			}
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		rxptr = rxptr->next_rx_desc;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	db->rx_ready_ptr = rxptr;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/*
88462306a36Sopenharmony_ci * Set ULI526X multicast address
88562306a36Sopenharmony_ci */
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void uli526x_set_filter_mode(struct net_device * dev)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
89062306a36Sopenharmony_ci	unsigned long flags;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0);
89362306a36Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
89662306a36Sopenharmony_ci		ULI526X_DBUG(0, "Enable PROM Mode", 0);
89762306a36Sopenharmony_ci		db->cr6_data |= CR6_PM | CR6_PBF;
89862306a36Sopenharmony_ci		update_cr6(db->cr6_data, db->ioaddr);
89962306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
90062306a36Sopenharmony_ci		return;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI ||
90462306a36Sopenharmony_ci	    netdev_mc_count(dev) > ULI5261_MAX_MULTICAST) {
90562306a36Sopenharmony_ci		ULI526X_DBUG(0, "Pass all multicast address",
90662306a36Sopenharmony_ci			     netdev_mc_count(dev));
90762306a36Sopenharmony_ci		db->cr6_data &= ~(CR6_PM | CR6_PBF);
90862306a36Sopenharmony_ci		db->cr6_data |= CR6_PAM;
90962306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
91062306a36Sopenharmony_ci		return;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	ULI526X_DBUG(0, "Set multicast address", netdev_mc_count(dev));
91462306a36Sopenharmony_ci	send_filter_frame(dev, netdev_mc_count(dev)); 	/* M5261/M5263 */
91562306a36Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic void
91962306a36Sopenharmony_ciULi_ethtool_get_link_ksettings(struct uli526x_board_info *db,
92062306a36Sopenharmony_ci			       struct ethtool_link_ksettings *cmd)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	u32 supported, advertising;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	supported = (SUPPORTED_10baseT_Half |
92562306a36Sopenharmony_ci	                   SUPPORTED_10baseT_Full |
92662306a36Sopenharmony_ci	                   SUPPORTED_100baseT_Half |
92762306a36Sopenharmony_ci	                   SUPPORTED_100baseT_Full |
92862306a36Sopenharmony_ci	                   SUPPORTED_Autoneg |
92962306a36Sopenharmony_ci	                   SUPPORTED_MII);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	advertising = (ADVERTISED_10baseT_Half |
93262306a36Sopenharmony_ci	                   ADVERTISED_10baseT_Full |
93362306a36Sopenharmony_ci	                   ADVERTISED_100baseT_Half |
93462306a36Sopenharmony_ci	                   ADVERTISED_100baseT_Full |
93562306a36Sopenharmony_ci	                   ADVERTISED_Autoneg |
93662306a36Sopenharmony_ci	                   ADVERTISED_MII);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
93962306a36Sopenharmony_ci						supported);
94062306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
94162306a36Sopenharmony_ci						advertising);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	cmd->base.port = PORT_MII;
94462306a36Sopenharmony_ci	cmd->base.phy_address = db->phy_addr;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	cmd->base.speed = SPEED_10;
94762306a36Sopenharmony_ci	cmd->base.duplex = DUPLEX_HALF;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if(db->op_mode==ULI526X_100MHF || db->op_mode==ULI526X_100MFD)
95062306a36Sopenharmony_ci	{
95162306a36Sopenharmony_ci		cmd->base.speed = SPEED_100;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	if(db->op_mode==ULI526X_10MFD || db->op_mode==ULI526X_100MFD)
95462306a36Sopenharmony_ci	{
95562306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_FULL;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	if(db->link_failed)
95862306a36Sopenharmony_ci	{
95962306a36Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
96062306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (db->media_mode & ULI526X_AUTO)
96462306a36Sopenharmony_ci	{
96562306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_ENABLE;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev,
97062306a36Sopenharmony_ci			       struct ethtool_drvinfo *info)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
97562306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info));
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
97962306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	ULi_ethtool_get_link_ksettings(np, cmd);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev) {
98962306a36Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	if(np->link_failed)
99262306a36Sopenharmony_ci		return 0;
99362306a36Sopenharmony_ci	else
99462306a36Sopenharmony_ci		return 1;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic void uli526x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	wol->supported = WAKE_PHY | WAKE_MAGIC;
100062306a36Sopenharmony_ci	wol->wolopts = 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
100462306a36Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
100562306a36Sopenharmony_ci	.get_link		= netdev_get_link,
100662306a36Sopenharmony_ci	.get_wol		= uli526x_get_wol,
100762306a36Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
100862306a36Sopenharmony_ci};
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci/*
101162306a36Sopenharmony_ci *	A periodic timer routine
101262306a36Sopenharmony_ci *	Dynamic media sense, allocate Rx buffer...
101362306a36Sopenharmony_ci */
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void uli526x_timer(struct timer_list *t)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct uli526x_board_info *db = from_timer(db, t, timer);
101862306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(db->pdev);
101962306a36Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
102062306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
102162306a36Sopenharmony_ci	unsigned long flags;
102262306a36Sopenharmony_ci	u8 tmp_cr12 = 0;
102362306a36Sopenharmony_ci	u32 tmp_cr8;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	//ULI526X_DBUG(0, "uli526x_timer()", 0);
102662306a36Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* Dynamic reset ULI526X : system error or transmit time-out */
103062306a36Sopenharmony_ci	tmp_cr8 = ur32(DCR8);
103162306a36Sopenharmony_ci	if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) {
103262306a36Sopenharmony_ci		db->reset_cr8++;
103362306a36Sopenharmony_ci		db->wait_reset = 1;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	db->interval_rx_cnt = 0;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/* TX polling kick monitor */
103862306a36Sopenharmony_ci	if ( db->tx_packet_cnt &&
103962306a36Sopenharmony_ci	     time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_KICK) ) {
104062306a36Sopenharmony_ci		uw32(DCR1, 0x1);   // Tx polling again
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		// TX Timeout
104362306a36Sopenharmony_ci		if ( time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_TIMEOUT) ) {
104462306a36Sopenharmony_ci			db->reset_TXtimeout++;
104562306a36Sopenharmony_ci			db->wait_reset = 1;
104662306a36Sopenharmony_ci			netdev_err(dev, " Tx timeout - resetting\n");
104762306a36Sopenharmony_ci		}
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (db->wait_reset) {
105162306a36Sopenharmony_ci		ULI526X_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt);
105262306a36Sopenharmony_ci		db->reset_count++;
105362306a36Sopenharmony_ci		uli526x_dynamic_reset(dev);
105462306a36Sopenharmony_ci		db->timer.expires = ULI526X_TIMER_WUT;
105562306a36Sopenharmony_ci		add_timer(&db->timer);
105662306a36Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
105762306a36Sopenharmony_ci		return;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* Link status check, Dynamic media type change */
106162306a36Sopenharmony_ci	if ((phy->read(db, db->phy_addr, 5) & 0x01e0)!=0)
106262306a36Sopenharmony_ci		tmp_cr12 = 3;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if ( !(tmp_cr12 & 0x3) && !db->link_failed ) {
106562306a36Sopenharmony_ci		/* Link Failed */
106662306a36Sopenharmony_ci		ULI526X_DBUG(0, "Link Failed", tmp_cr12);
106762306a36Sopenharmony_ci		netif_carrier_off(dev);
106862306a36Sopenharmony_ci		netdev_info(dev, "NIC Link is Down\n");
106962306a36Sopenharmony_ci		db->link_failed = 1;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		/* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */
107262306a36Sopenharmony_ci		/* AUTO don't need */
107362306a36Sopenharmony_ci		if ( !(db->media_mode & 0x8) )
107462306a36Sopenharmony_ci			phy->write(db, db->phy_addr, 0, 0x1000);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		/* AUTO mode, if INT phyxcer link failed, select EXT device */
107762306a36Sopenharmony_ci		if (db->media_mode & ULI526X_AUTO) {
107862306a36Sopenharmony_ci			db->cr6_data&=~0x00000200;	/* bit9=0, HD mode */
107962306a36Sopenharmony_ci			update_cr6(db->cr6_data, db->ioaddr);
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci	} else
108262306a36Sopenharmony_ci		if ((tmp_cr12 & 0x3) && db->link_failed) {
108362306a36Sopenharmony_ci			ULI526X_DBUG(0, "Link link OK", tmp_cr12);
108462306a36Sopenharmony_ci			db->link_failed = 0;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci			/* Auto Sense Speed */
108762306a36Sopenharmony_ci			if ( (db->media_mode & ULI526X_AUTO) &&
108862306a36Sopenharmony_ci				uli526x_sense_speed(db) )
108962306a36Sopenharmony_ci				db->link_failed = 1;
109062306a36Sopenharmony_ci			uli526x_process_mode(db);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci			if(db->link_failed==0)
109362306a36Sopenharmony_ci			{
109462306a36Sopenharmony_ci				netdev_info(dev, "NIC Link is Up %d Mbps %s duplex\n",
109562306a36Sopenharmony_ci					    (db->op_mode == ULI526X_100MHF ||
109662306a36Sopenharmony_ci					     db->op_mode == ULI526X_100MFD)
109762306a36Sopenharmony_ci					    ? 100 : 10,
109862306a36Sopenharmony_ci					    (db->op_mode == ULI526X_10MFD ||
109962306a36Sopenharmony_ci					     db->op_mode == ULI526X_100MFD)
110062306a36Sopenharmony_ci					    ? "Full" : "Half");
110162306a36Sopenharmony_ci				netif_carrier_on(dev);
110262306a36Sopenharmony_ci			}
110362306a36Sopenharmony_ci			/* SHOW_MEDIA_TYPE(db->op_mode); */
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci		else if(!(tmp_cr12 & 0x3) && db->link_failed)
110662306a36Sopenharmony_ci		{
110762306a36Sopenharmony_ci			if(db->init==1)
110862306a36Sopenharmony_ci			{
110962306a36Sopenharmony_ci				netdev_info(dev, "NIC Link is Down\n");
111062306a36Sopenharmony_ci				netif_carrier_off(dev);
111162306a36Sopenharmony_ci			}
111262306a36Sopenharmony_ci		}
111362306a36Sopenharmony_ci	db->init = 0;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* Timer active again */
111662306a36Sopenharmony_ci	db->timer.expires = ULI526X_TIMER_WUT;
111762306a36Sopenharmony_ci	add_timer(&db->timer);
111862306a36Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci/*
112362306a36Sopenharmony_ci *	Stop ULI526X board
112462306a36Sopenharmony_ci *	Free Tx/Rx allocated memory
112562306a36Sopenharmony_ci *	Init system variable
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic void uli526x_reset_prepare(struct net_device *dev)
112962306a36Sopenharmony_ci{
113062306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
113162306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* Sopt MAC controller */
113462306a36Sopenharmony_ci	db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);	/* Disable Tx/Rx */
113562306a36Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
113662306a36Sopenharmony_ci	uw32(DCR7, 0);				/* Disable Interrupt */
113762306a36Sopenharmony_ci	uw32(DCR5, ur32(DCR5));
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* Disable upper layer interface */
114062306a36Sopenharmony_ci	netif_stop_queue(dev);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* Free Rx Allocate buffer */
114362306a36Sopenharmony_ci	uli526x_free_rxbuffer(db);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* system variable init */
114662306a36Sopenharmony_ci	db->tx_packet_cnt = 0;
114762306a36Sopenharmony_ci	db->rx_avail_cnt = 0;
114862306a36Sopenharmony_ci	db->link_failed = 1;
114962306a36Sopenharmony_ci	db->init=1;
115062306a36Sopenharmony_ci	db->wait_reset = 0;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci/*
115562306a36Sopenharmony_ci *	Dynamic reset the ULI526X board
115662306a36Sopenharmony_ci *	Stop ULI526X board
115762306a36Sopenharmony_ci *	Free Tx/Rx allocated memory
115862306a36Sopenharmony_ci *	Reset ULI526X board
115962306a36Sopenharmony_ci *	Re-initialize ULI526X board
116062306a36Sopenharmony_ci */
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic void uli526x_dynamic_reset(struct net_device *dev)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_dynamic_reset()", 0);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	uli526x_reset_prepare(dev);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	/* Re-initialize ULI526X board */
116962306a36Sopenharmony_ci	uli526x_init(dev);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* Restart upper layer interface */
117262306a36Sopenharmony_ci	netif_wake_queue(dev);
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci/*
117662306a36Sopenharmony_ci *	Suspend the interface.
117762306a36Sopenharmony_ci */
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int __maybe_unused uli526x_suspend(struct device *dev_d)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_suspend", 0);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (!netif_running(dev))
118662306a36Sopenharmony_ci		return 0;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	netif_device_detach(dev);
118962306a36Sopenharmony_ci	uli526x_reset_prepare(dev);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	device_set_wakeup_enable(dev_d, 0);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return 0;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci/*
119762306a36Sopenharmony_ci *	Resume the interface.
119862306a36Sopenharmony_ci */
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic int __maybe_unused uli526x_resume(struct device *dev_d)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_resume", 0);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (!netif_running(dev))
120862306a36Sopenharmony_ci		return 0;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	netif_device_attach(dev);
121162306a36Sopenharmony_ci	/* Re-initialize ULI526X board */
121262306a36Sopenharmony_ci	uli526x_init(dev);
121362306a36Sopenharmony_ci	/* Restart upper layer interface */
121462306a36Sopenharmony_ci	netif_wake_queue(dev);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return 0;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci/*
122062306a36Sopenharmony_ci *	free all allocated rx buffer
122162306a36Sopenharmony_ci */
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic void uli526x_free_rxbuffer(struct uli526x_board_info * db)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/* free allocated rx buffer */
122862306a36Sopenharmony_ci	while (db->rx_avail_cnt) {
122962306a36Sopenharmony_ci		dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr);
123062306a36Sopenharmony_ci		db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc;
123162306a36Sopenharmony_ci		db->rx_avail_cnt--;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/*
123762306a36Sopenharmony_ci *	Reuse the SK buffer
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * skb)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct rx_desc *rxptr = db->rx_insert_ptr;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) {
124562306a36Sopenharmony_ci		rxptr->rx_skb_ptr = skb;
124662306a36Sopenharmony_ci		rxptr->rdes2 = cpu_to_le32(dma_map_single(&db->pdev->dev, skb_tail_pointer(skb),
124762306a36Sopenharmony_ci							  RX_ALLOC_SIZE, DMA_FROM_DEVICE));
124862306a36Sopenharmony_ci		wmb();
124962306a36Sopenharmony_ci		rxptr->rdes0 = cpu_to_le32(0x80000000);
125062306a36Sopenharmony_ci		db->rx_avail_cnt++;
125162306a36Sopenharmony_ci		db->rx_insert_ptr = rxptr->next_rx_desc;
125262306a36Sopenharmony_ci	} else
125362306a36Sopenharmony_ci		ULI526X_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci/*
125862306a36Sopenharmony_ci *	Initialize transmit/Receive descriptor
125962306a36Sopenharmony_ci *	Using Chain structure, and allocate Tx/Rx buffer
126062306a36Sopenharmony_ci */
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic void uli526x_descriptor_init(struct net_device *dev, void __iomem *ioaddr)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
126562306a36Sopenharmony_ci	struct tx_desc *tmp_tx;
126662306a36Sopenharmony_ci	struct rx_desc *tmp_rx;
126762306a36Sopenharmony_ci	unsigned char *tmp_buf;
126862306a36Sopenharmony_ci	dma_addr_t tmp_tx_dma, tmp_rx_dma;
126962306a36Sopenharmony_ci	dma_addr_t tmp_buf_dma;
127062306a36Sopenharmony_ci	int i;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_descriptor_init()", 0);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	/* tx descriptor start pointer */
127562306a36Sopenharmony_ci	db->tx_insert_ptr = db->first_tx_desc;
127662306a36Sopenharmony_ci	db->tx_remove_ptr = db->first_tx_desc;
127762306a36Sopenharmony_ci	uw32(DCR4, db->first_tx_desc_dma);	/* TX DESC address */
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/* rx descriptor start pointer */
128062306a36Sopenharmony_ci	db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT;
128162306a36Sopenharmony_ci	db->first_rx_desc_dma =  db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT;
128262306a36Sopenharmony_ci	db->rx_insert_ptr = db->first_rx_desc;
128362306a36Sopenharmony_ci	db->rx_ready_ptr = db->first_rx_desc;
128462306a36Sopenharmony_ci	uw32(DCR3, db->first_rx_desc_dma);	/* RX DESC address */
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/* Init Transmit chain */
128762306a36Sopenharmony_ci	tmp_buf = db->buf_pool_start;
128862306a36Sopenharmony_ci	tmp_buf_dma = db->buf_pool_dma_start;
128962306a36Sopenharmony_ci	tmp_tx_dma = db->first_tx_desc_dma;
129062306a36Sopenharmony_ci	for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) {
129162306a36Sopenharmony_ci		tmp_tx->tx_buf_ptr = tmp_buf;
129262306a36Sopenharmony_ci		tmp_tx->tdes0 = cpu_to_le32(0);
129362306a36Sopenharmony_ci		tmp_tx->tdes1 = cpu_to_le32(0x81000000);	/* IC, chain */
129462306a36Sopenharmony_ci		tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma);
129562306a36Sopenharmony_ci		tmp_tx_dma += sizeof(struct tx_desc);
129662306a36Sopenharmony_ci		tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma);
129762306a36Sopenharmony_ci		tmp_tx->next_tx_desc = tmp_tx + 1;
129862306a36Sopenharmony_ci		tmp_buf = tmp_buf + TX_BUF_ALLOC;
129962306a36Sopenharmony_ci		tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci	(--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma);
130262306a36Sopenharmony_ci	tmp_tx->next_tx_desc = db->first_tx_desc;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	 /* Init Receive descriptor chain */
130562306a36Sopenharmony_ci	tmp_rx_dma=db->first_rx_desc_dma;
130662306a36Sopenharmony_ci	for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) {
130762306a36Sopenharmony_ci		tmp_rx->rdes0 = cpu_to_le32(0);
130862306a36Sopenharmony_ci		tmp_rx->rdes1 = cpu_to_le32(0x01000600);
130962306a36Sopenharmony_ci		tmp_rx_dma += sizeof(struct rx_desc);
131062306a36Sopenharmony_ci		tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma);
131162306a36Sopenharmony_ci		tmp_rx->next_rx_desc = tmp_rx + 1;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci	(--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma);
131462306a36Sopenharmony_ci	tmp_rx->next_rx_desc = db->first_rx_desc;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* pre-allocate Rx buffer */
131762306a36Sopenharmony_ci	allocate_rx_buffer(dev);
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci/*
132262306a36Sopenharmony_ci *	Update CR6 value
132362306a36Sopenharmony_ci *	Firstly stop ULI526X, then written value and start
132462306a36Sopenharmony_ci */
132562306a36Sopenharmony_cistatic void update_cr6(u32 cr6_data, void __iomem *ioaddr)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	uw32(DCR6, cr6_data);
132862306a36Sopenharmony_ci	udelay(5);
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci/*
133362306a36Sopenharmony_ci *	Send a setup frame for M5261/M5263
133462306a36Sopenharmony_ci *	This setup frame initialize ULI526X address filter mode
133562306a36Sopenharmony_ci */
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
133862306a36Sopenharmony_ci#define FLT_SHIFT 16
133962306a36Sopenharmony_ci#else
134062306a36Sopenharmony_ci#define FLT_SHIFT 0
134162306a36Sopenharmony_ci#endif
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic void send_filter_frame(struct net_device *dev, int mc_cnt)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
134662306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
134762306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
134862306a36Sopenharmony_ci	struct tx_desc *txptr;
134962306a36Sopenharmony_ci	const u16 * addrptr;
135062306a36Sopenharmony_ci	u32 * suptr;
135162306a36Sopenharmony_ci	int i;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	ULI526X_DBUG(0, "send_filter_frame()", 0);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	txptr = db->tx_insert_ptr;
135662306a36Sopenharmony_ci	suptr = (u32 *) txptr->tx_buf_ptr;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/* Node address */
135962306a36Sopenharmony_ci	addrptr = (const u16 *) dev->dev_addr;
136062306a36Sopenharmony_ci	*suptr++ = addrptr[0] << FLT_SHIFT;
136162306a36Sopenharmony_ci	*suptr++ = addrptr[1] << FLT_SHIFT;
136262306a36Sopenharmony_ci	*suptr++ = addrptr[2] << FLT_SHIFT;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	/* broadcast address */
136562306a36Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
136662306a36Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
136762306a36Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* fit the multicast address */
137062306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
137162306a36Sopenharmony_ci		addrptr = (u16 *) ha->addr;
137262306a36Sopenharmony_ci		*suptr++ = addrptr[0] << FLT_SHIFT;
137362306a36Sopenharmony_ci		*suptr++ = addrptr[1] << FLT_SHIFT;
137462306a36Sopenharmony_ci		*suptr++ = addrptr[2] << FLT_SHIFT;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	for (i = netdev_mc_count(dev); i < 14; i++) {
137862306a36Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
137962306a36Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
138062306a36Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	/* prepare the setup frame */
138462306a36Sopenharmony_ci	db->tx_insert_ptr = txptr->next_tx_desc;
138562306a36Sopenharmony_ci	txptr->tdes1 = cpu_to_le32(0x890000c0);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	/* Resource Check and Send the setup packet */
138862306a36Sopenharmony_ci	if (db->tx_packet_cnt < TX_DESC_CNT) {
138962306a36Sopenharmony_ci		/* Resource Empty */
139062306a36Sopenharmony_ci		db->tx_packet_cnt++;
139162306a36Sopenharmony_ci		txptr->tdes0 = cpu_to_le32(0x80000000);
139262306a36Sopenharmony_ci		update_cr6(db->cr6_data | 0x2000, ioaddr);
139362306a36Sopenharmony_ci		uw32(DCR1, 0x1);	/* Issue Tx polling */
139462306a36Sopenharmony_ci		update_cr6(db->cr6_data, ioaddr);
139562306a36Sopenharmony_ci		netif_trans_update(dev);
139662306a36Sopenharmony_ci	} else
139762306a36Sopenharmony_ci		netdev_err(dev, "No Tx resource - Send_filter_frame!\n");
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci/*
140262306a36Sopenharmony_ci *	Allocate rx buffer,
140362306a36Sopenharmony_ci *	As possible as allocate maxiumn Rx buffer
140462306a36Sopenharmony_ci */
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic void allocate_rx_buffer(struct net_device *dev)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
140962306a36Sopenharmony_ci	struct rx_desc *rxptr;
141062306a36Sopenharmony_ci	struct sk_buff *skb;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	rxptr = db->rx_insert_ptr;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	while(db->rx_avail_cnt < RX_DESC_CNT) {
141562306a36Sopenharmony_ci		skb = netdev_alloc_skb(dev, RX_ALLOC_SIZE);
141662306a36Sopenharmony_ci		if (skb == NULL)
141762306a36Sopenharmony_ci			break;
141862306a36Sopenharmony_ci		rxptr->rx_skb_ptr = skb; /* FIXME (?) */
141962306a36Sopenharmony_ci		rxptr->rdes2 = cpu_to_le32(dma_map_single(&db->pdev->dev, skb_tail_pointer(skb),
142062306a36Sopenharmony_ci							  RX_ALLOC_SIZE, DMA_FROM_DEVICE));
142162306a36Sopenharmony_ci		wmb();
142262306a36Sopenharmony_ci		rxptr->rdes0 = cpu_to_le32(0x80000000);
142362306a36Sopenharmony_ci		rxptr = rxptr->next_rx_desc;
142462306a36Sopenharmony_ci		db->rx_avail_cnt++;
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	db->rx_insert_ptr = rxptr;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci/*
143262306a36Sopenharmony_ci *	Read one word data from the serial ROM
143362306a36Sopenharmony_ci */
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_cistatic u16 read_srom_word(struct uli526x_board_info *db, int offset)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
143862306a36Sopenharmony_ci	u16 srom_data = 0;
143962306a36Sopenharmony_ci	int i;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ);
144262306a36Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* Send the Read Command 110b */
144562306a36Sopenharmony_ci	srom_clk_write(db, SROM_DATA_1);
144662306a36Sopenharmony_ci	srom_clk_write(db, SROM_DATA_1);
144762306a36Sopenharmony_ci	srom_clk_write(db, SROM_DATA_0);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	/* Send the offset */
145062306a36Sopenharmony_ci	for (i = 5; i >= 0; i--) {
145162306a36Sopenharmony_ci		srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
145262306a36Sopenharmony_ci		srom_clk_write(db, srom_data);
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	for (i = 16; i > 0; i--) {
145862306a36Sopenharmony_ci		uw32(DCR9, CR9_SROM_READ | CR9_SRCS | CR9_SRCLK);
145962306a36Sopenharmony_ci		udelay(5);
146062306a36Sopenharmony_ci		srom_data = (srom_data << 1) |
146162306a36Sopenharmony_ci			    ((ur32(DCR9) & CR9_CRDOUT) ? 1 : 0);
146262306a36Sopenharmony_ci		uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
146362306a36Sopenharmony_ci		udelay(5);
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ);
146762306a36Sopenharmony_ci	return srom_data;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci/*
147262306a36Sopenharmony_ci *	Auto sense the media mode
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_cistatic u8 uli526x_sense_speed(struct uli526x_board_info * db)
147662306a36Sopenharmony_ci{
147762306a36Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
147862306a36Sopenharmony_ci	u8 ErrFlag = 0;
147962306a36Sopenharmony_ci	u16 phy_mode;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	phy_mode = phy->read(db, db->phy_addr, 1);
148262306a36Sopenharmony_ci	phy_mode = phy->read(db, db->phy_addr, 1);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if ( (phy_mode & 0x24) == 0x24 ) {
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci		phy_mode = ((phy->read(db, db->phy_addr, 5) & 0x01e0)<<7);
148762306a36Sopenharmony_ci		if(phy_mode&0x8000)
148862306a36Sopenharmony_ci			phy_mode = 0x8000;
148962306a36Sopenharmony_ci		else if(phy_mode&0x4000)
149062306a36Sopenharmony_ci			phy_mode = 0x4000;
149162306a36Sopenharmony_ci		else if(phy_mode&0x2000)
149262306a36Sopenharmony_ci			phy_mode = 0x2000;
149362306a36Sopenharmony_ci		else
149462306a36Sopenharmony_ci			phy_mode = 0x1000;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci		switch (phy_mode) {
149762306a36Sopenharmony_ci		case 0x1000: db->op_mode = ULI526X_10MHF; break;
149862306a36Sopenharmony_ci		case 0x2000: db->op_mode = ULI526X_10MFD; break;
149962306a36Sopenharmony_ci		case 0x4000: db->op_mode = ULI526X_100MHF; break;
150062306a36Sopenharmony_ci		case 0x8000: db->op_mode = ULI526X_100MFD; break;
150162306a36Sopenharmony_ci		default: db->op_mode = ULI526X_10MHF; ErrFlag = 1; break;
150262306a36Sopenharmony_ci		}
150362306a36Sopenharmony_ci	} else {
150462306a36Sopenharmony_ci		db->op_mode = ULI526X_10MHF;
150562306a36Sopenharmony_ci		ULI526X_DBUG(0, "Link Failed :", phy_mode);
150662306a36Sopenharmony_ci		ErrFlag = 1;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return ErrFlag;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci/*
151462306a36Sopenharmony_ci *	Set 10/100 phyxcer capability
151562306a36Sopenharmony_ci *	AUTO mode : phyxcer register4 is NIC capability
151662306a36Sopenharmony_ci *	Force mode: phyxcer register4 is the force media
151762306a36Sopenharmony_ci */
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic void uli526x_set_phyxcer(struct uli526x_board_info *db)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
152262306a36Sopenharmony_ci	u16 phy_reg;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* Phyxcer capability setting */
152562306a36Sopenharmony_ci	phy_reg = phy->read(db, db->phy_addr, 4) & ~0x01e0;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	if (db->media_mode & ULI526X_AUTO) {
152862306a36Sopenharmony_ci		/* AUTO Mode */
152962306a36Sopenharmony_ci		phy_reg |= db->PHY_reg4;
153062306a36Sopenharmony_ci	} else {
153162306a36Sopenharmony_ci		/* Force Mode */
153262306a36Sopenharmony_ci		switch(db->media_mode) {
153362306a36Sopenharmony_ci		case ULI526X_10MHF: phy_reg |= 0x20; break;
153462306a36Sopenharmony_ci		case ULI526X_10MFD: phy_reg |= 0x40; break;
153562306a36Sopenharmony_ci		case ULI526X_100MHF: phy_reg |= 0x80; break;
153662306a36Sopenharmony_ci		case ULI526X_100MFD: phy_reg |= 0x100; break;
153762306a36Sopenharmony_ci		}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* Write new capability to Phyxcer Reg4 */
154262306a36Sopenharmony_ci	if ( !(phy_reg & 0x01e0)) {
154362306a36Sopenharmony_ci		phy_reg|=db->PHY_reg4;
154462306a36Sopenharmony_ci		db->media_mode|=ULI526X_AUTO;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci	phy->write(db, db->phy_addr, 4, phy_reg);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	/* Restart Auto-Negotiation */
154962306a36Sopenharmony_ci	phy->write(db, db->phy_addr, 0, 0x1200);
155062306a36Sopenharmony_ci	udelay(50);
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci/*
155562306a36Sopenharmony_ci *	Process op-mode
155662306a36Sopenharmony_ci	AUTO mode : PHY controller in Auto-negotiation Mode
155762306a36Sopenharmony_ci *	Force mode: PHY controller in force mode with HUB
155862306a36Sopenharmony_ci *			N-way force capability with SWITCH
155962306a36Sopenharmony_ci */
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic void uli526x_process_mode(struct uli526x_board_info *db)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
156462306a36Sopenharmony_ci	u16 phy_reg;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/* Full Duplex Mode Check */
156762306a36Sopenharmony_ci	if (db->op_mode & 0x4)
156862306a36Sopenharmony_ci		db->cr6_data |= CR6_FDM;	/* Set Full Duplex Bit */
156962306a36Sopenharmony_ci	else
157062306a36Sopenharmony_ci		db->cr6_data &= ~CR6_FDM;	/* Clear Full Duplex Bit */
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	update_cr6(db->cr6_data, db->ioaddr);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	/* 10/100M phyxcer force mode need */
157562306a36Sopenharmony_ci	if (!(db->media_mode & 0x8)) {
157662306a36Sopenharmony_ci		/* Forece Mode */
157762306a36Sopenharmony_ci		phy_reg = phy->read(db, db->phy_addr, 6);
157862306a36Sopenharmony_ci		if (!(phy_reg & 0x1)) {
157962306a36Sopenharmony_ci			/* parter without N-Way capability */
158062306a36Sopenharmony_ci			phy_reg = 0x0;
158162306a36Sopenharmony_ci			switch(db->op_mode) {
158262306a36Sopenharmony_ci			case ULI526X_10MHF: phy_reg = 0x0; break;
158362306a36Sopenharmony_ci			case ULI526X_10MFD: phy_reg = 0x100; break;
158462306a36Sopenharmony_ci			case ULI526X_100MHF: phy_reg = 0x2000; break;
158562306a36Sopenharmony_ci			case ULI526X_100MFD: phy_reg = 0x2100; break;
158662306a36Sopenharmony_ci			}
158762306a36Sopenharmony_ci			phy->write(db, db->phy_addr, 0, phy_reg);
158862306a36Sopenharmony_ci		}
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci/* M5261/M5263 Chip */
159462306a36Sopenharmony_cistatic void phy_writeby_cr9(struct uli526x_board_info *db, u8 phy_addr,
159562306a36Sopenharmony_ci			    u8 offset, u16 phy_data)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	u16 i;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* Send 33 synchronization clock to Phy controller */
160062306a36Sopenharmony_ci	for (i = 0; i < 35; i++)
160162306a36Sopenharmony_ci		phy_write_1bit(db, PHY_DATA_1);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	/* Send start command(01) to Phy */
160462306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
160562306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* Send write command(01) to Phy */
160862306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
160962306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/* Send Phy address */
161262306a36Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
161362306a36Sopenharmony_ci		phy_write_1bit(db, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	/* Send register address */
161662306a36Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
161762306a36Sopenharmony_ci		phy_write_1bit(db, offset & i ? PHY_DATA_1 : PHY_DATA_0);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	/* written trasnition */
162062306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
162162306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	/* Write a word data to PHY controller */
162462306a36Sopenharmony_ci	for (i = 0x8000; i > 0; i >>= 1)
162562306a36Sopenharmony_ci		phy_write_1bit(db, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic u16 phy_readby_cr9(struct uli526x_board_info *db, u8 phy_addr, u8 offset)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	u16 phy_data;
163162306a36Sopenharmony_ci	int i;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	/* Send 33 synchronization clock to Phy controller */
163462306a36Sopenharmony_ci	for (i = 0; i < 35; i++)
163562306a36Sopenharmony_ci		phy_write_1bit(db, PHY_DATA_1);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/* Send start command(01) to Phy */
163862306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
163962306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	/* Send read command(10) to Phy */
164262306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
164362306a36Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	/* Send Phy address */
164662306a36Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
164762306a36Sopenharmony_ci		phy_write_1bit(db, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	/* Send register address */
165062306a36Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
165162306a36Sopenharmony_ci		phy_write_1bit(db, offset & i ? PHY_DATA_1 : PHY_DATA_0);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	/* Skip transition state */
165462306a36Sopenharmony_ci	phy_read_1bit(db);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* read 16bit data */
165762306a36Sopenharmony_ci	for (phy_data = 0, i = 0; i < 16; i++) {
165862306a36Sopenharmony_ci		phy_data <<= 1;
165962306a36Sopenharmony_ci		phy_data |= phy_read_1bit(db);
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	return phy_data;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic u16 phy_readby_cr10(struct uli526x_board_info *db, u8 phy_addr,
166662306a36Sopenharmony_ci			   u8 offset)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
166962306a36Sopenharmony_ci	u32 cr10_value = phy_addr;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	cr10_value = (cr10_value <<  5) + offset;
167262306a36Sopenharmony_ci	cr10_value = (cr10_value << 16) + 0x08000000;
167362306a36Sopenharmony_ci	uw32(DCR10, cr10_value);
167462306a36Sopenharmony_ci	udelay(1);
167562306a36Sopenharmony_ci	while (1) {
167662306a36Sopenharmony_ci		cr10_value = ur32(DCR10);
167762306a36Sopenharmony_ci		if (cr10_value & 0x10000000)
167862306a36Sopenharmony_ci			break;
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci	return cr10_value & 0x0ffff;
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic void phy_writeby_cr10(struct uli526x_board_info *db, u8 phy_addr,
168462306a36Sopenharmony_ci			     u8 offset, u16 phy_data)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
168762306a36Sopenharmony_ci	u32 cr10_value = phy_addr;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	cr10_value = (cr10_value <<  5) + offset;
169062306a36Sopenharmony_ci	cr10_value = (cr10_value << 16) + 0x04000000 + phy_data;
169162306a36Sopenharmony_ci	uw32(DCR10, cr10_value);
169262306a36Sopenharmony_ci	udelay(1);
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci/*
169562306a36Sopenharmony_ci *	Write one bit data to Phy Controller
169662306a36Sopenharmony_ci */
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic void phy_write_1bit(struct uli526x_board_info *db, u32 data)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	uw32(DCR9, data);		/* MII Clock Low */
170362306a36Sopenharmony_ci	udelay(1);
170462306a36Sopenharmony_ci	uw32(DCR9, data | MDCLKH);	/* MII Clock High */
170562306a36Sopenharmony_ci	udelay(1);
170662306a36Sopenharmony_ci	uw32(DCR9, data);		/* MII Clock Low */
170762306a36Sopenharmony_ci	udelay(1);
170862306a36Sopenharmony_ci}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci/*
171262306a36Sopenharmony_ci *	Read one bit phy data from PHY controller
171362306a36Sopenharmony_ci */
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic u16 phy_read_1bit(struct uli526x_board_info *db)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
171862306a36Sopenharmony_ci	u16 phy_data;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	uw32(DCR9, 0x50000);
172162306a36Sopenharmony_ci	udelay(1);
172262306a36Sopenharmony_ci	phy_data = (ur32(DCR9) >> 19) & 0x1;
172362306a36Sopenharmony_ci	uw32(DCR9, 0x40000);
172462306a36Sopenharmony_ci	udelay(1);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	return phy_data;
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic const struct pci_device_id uli526x_pci_tbl[] = {
173162306a36Sopenharmony_ci	{ 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID },
173262306a36Sopenharmony_ci	{ 0x10B9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5263_ID },
173362306a36Sopenharmony_ci	{ 0, }
173462306a36Sopenharmony_ci};
173562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, uli526x_pci_tbl);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(uli526x_pm_ops, uli526x_suspend, uli526x_resume);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_cistatic struct pci_driver uli526x_driver = {
174062306a36Sopenharmony_ci	.name		= "uli526x",
174162306a36Sopenharmony_ci	.id_table	= uli526x_pci_tbl,
174262306a36Sopenharmony_ci	.probe		= uli526x_init_one,
174362306a36Sopenharmony_ci	.remove		= uli526x_remove_one,
174462306a36Sopenharmony_ci	.driver.pm	= &uli526x_pm_ops,
174562306a36Sopenharmony_ci};
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ciMODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw");
174862306a36Sopenharmony_ciMODULE_DESCRIPTION("ULi M5261/M5263 fast ethernet driver");
174962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cimodule_param(debug, int, 0644);
175262306a36Sopenharmony_cimodule_param(mode, int, 0);
175362306a36Sopenharmony_cimodule_param(cr6set, int, 0);
175462306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "ULi M5261/M5263 enable debugging (0-1)");
175562306a36Sopenharmony_ciMODULE_PARM_DESC(mode, "ULi M5261/M5263: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA");
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci/*	Description:
175862306a36Sopenharmony_ci *	when user used insmod to add module, system invoked init_module()
175962306a36Sopenharmony_ci *	to register the services.
176062306a36Sopenharmony_ci */
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_cistatic int __init uli526x_init_module(void)
176362306a36Sopenharmony_ci{
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	ULI526X_DBUG(0, "init_module() ", debug);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (debug)
176862306a36Sopenharmony_ci		uli526x_debug = debug;	/* set debug flag */
176962306a36Sopenharmony_ci	if (cr6set)
177062306a36Sopenharmony_ci		uli526x_cr6_user_set = cr6set;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	switch (mode) {
177362306a36Sopenharmony_ci	case ULI526X_10MHF:
177462306a36Sopenharmony_ci	case ULI526X_100MHF:
177562306a36Sopenharmony_ci	case ULI526X_10MFD:
177662306a36Sopenharmony_ci	case ULI526X_100MFD:
177762306a36Sopenharmony_ci		uli526x_media_mode = mode;
177862306a36Sopenharmony_ci		break;
177962306a36Sopenharmony_ci	default:
178062306a36Sopenharmony_ci		uli526x_media_mode = ULI526X_AUTO;
178162306a36Sopenharmony_ci		break;
178262306a36Sopenharmony_ci	}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	return pci_register_driver(&uli526x_driver);
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/*
178962306a36Sopenharmony_ci *	Description:
179062306a36Sopenharmony_ci *	when user used rmmod to delete module, system invoked clean_module()
179162306a36Sopenharmony_ci *	to un-register all registered services.
179262306a36Sopenharmony_ci */
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic void __exit uli526x_cleanup_module(void)
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_cleanup_module() ", debug);
179762306a36Sopenharmony_ci	pci_unregister_driver(&uli526x_driver);
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cimodule_init(uli526x_init_module);
180162306a36Sopenharmony_cimodule_exit(uli526x_cleanup_module);
1802