18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci*/
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define DRV_NAME	"uli526x"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/timer.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/ioport.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/pci.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
238c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
248c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
258c2ecf20Sopenharmony_ci#include <linux/delay.h>
268c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
278c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
288c2ecf20Sopenharmony_ci#include <linux/bitops.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/processor.h>
318c2ecf20Sopenharmony_ci#include <asm/io.h>
328c2ecf20Sopenharmony_ci#include <asm/dma.h>
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define uw32(reg, val)	iowrite32(val, ioaddr + (reg))
368c2ecf20Sopenharmony_ci#define ur32(reg)	ioread32(ioaddr + (reg))
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Board/System/Debug information/definition ---------------- */
398c2ecf20Sopenharmony_ci#define PCI_ULI5261_ID  0x526110B9	/* ULi M5261 ID*/
408c2ecf20Sopenharmony_ci#define PCI_ULI5263_ID  0x526310B9	/* ULi M5263 ID*/
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define ULI526X_IO_SIZE 0x100
438c2ecf20Sopenharmony_ci#define TX_DESC_CNT     0x20            /* Allocated Tx descriptors */
448c2ecf20Sopenharmony_ci#define RX_DESC_CNT     0x30            /* Allocated Rx descriptors */
458c2ecf20Sopenharmony_ci#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2)	/* Max TX packet count */
468c2ecf20Sopenharmony_ci#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3)	/* TX wakeup count */
478c2ecf20Sopenharmony_ci#define DESC_ALL_CNT    (TX_DESC_CNT + RX_DESC_CNT)
488c2ecf20Sopenharmony_ci#define TX_BUF_ALLOC    0x600
498c2ecf20Sopenharmony_ci#define RX_ALLOC_SIZE   0x620
508c2ecf20Sopenharmony_ci#define ULI526X_RESET    1
518c2ecf20Sopenharmony_ci#define CR0_DEFAULT     0
528c2ecf20Sopenharmony_ci#define CR6_DEFAULT     0x22200000
538c2ecf20Sopenharmony_ci#define CR7_DEFAULT     0x180c1
548c2ecf20Sopenharmony_ci#define CR15_DEFAULT    0x06            /* TxJabber RxWatchdog */
558c2ecf20Sopenharmony_ci#define TDES0_ERR_MASK  0x4302          /* TXJT, LC, EC, FUE */
568c2ecf20Sopenharmony_ci#define MAX_PACKET_SIZE 1514
578c2ecf20Sopenharmony_ci#define ULI5261_MAX_MULTICAST 14
588c2ecf20Sopenharmony_ci#define RX_COPY_SIZE	100
598c2ecf20Sopenharmony_ci#define MAX_CHECK_PACKET 0x8000
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define ULI526X_10MHF      0
628c2ecf20Sopenharmony_ci#define ULI526X_100MHF     1
638c2ecf20Sopenharmony_ci#define ULI526X_10MFD      4
648c2ecf20Sopenharmony_ci#define ULI526X_100MFD     5
658c2ecf20Sopenharmony_ci#define ULI526X_AUTO       8
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define ULI526X_TXTH_72	0x400000	/* TX TH 72 byte */
688c2ecf20Sopenharmony_ci#define ULI526X_TXTH_96	0x404000	/* TX TH 96 byte */
698c2ecf20Sopenharmony_ci#define ULI526X_TXTH_128	0x0000		/* TX TH 128 byte */
708c2ecf20Sopenharmony_ci#define ULI526X_TXTH_256	0x4000		/* TX TH 256 byte */
718c2ecf20Sopenharmony_ci#define ULI526X_TXTH_512	0x8000		/* TX TH 512 byte */
728c2ecf20Sopenharmony_ci#define ULI526X_TXTH_1K	0xC000		/* TX TH 1K  byte */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define ULI526X_TIMER_WUT  (jiffies + HZ * 1)/* timer wakeup time : 1 second */
758c2ecf20Sopenharmony_ci#define ULI526X_TX_TIMEOUT ((16*HZ)/2)	/* tx packet time-out time 8 s" */
768c2ecf20Sopenharmony_ci#define ULI526X_TX_KICK 	(4*HZ/2)	/* tx packet Kick-out time 2 s" */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define ULI526X_DBUG(dbug_now, msg, value)			\
798c2ecf20Sopenharmony_cido {								\
808c2ecf20Sopenharmony_ci	if (uli526x_debug || (dbug_now))			\
818c2ecf20Sopenharmony_ci		pr_err("%s %lx\n", (msg), (long) (value));	\
828c2ecf20Sopenharmony_ci} while (0)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define SHOW_MEDIA_TYPE(mode)					\
858c2ecf20Sopenharmony_ci	pr_err("Change Speed to %sMhz %s duplex\n",		\
868c2ecf20Sopenharmony_ci	       mode & 1 ? "100" : "10",				\
878c2ecf20Sopenharmony_ci	       mode & 4 ? "full" : "half");
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* CR9 definition: SROM/MII */
918c2ecf20Sopenharmony_ci#define CR9_SROM_READ   0x4800
928c2ecf20Sopenharmony_ci#define CR9_SRCS        0x1
938c2ecf20Sopenharmony_ci#define CR9_SRCLK       0x2
948c2ecf20Sopenharmony_ci#define CR9_CRDOUT      0x8
958c2ecf20Sopenharmony_ci#define SROM_DATA_0     0x0
968c2ecf20Sopenharmony_ci#define SROM_DATA_1     0x4
978c2ecf20Sopenharmony_ci#define PHY_DATA_1      0x20000
988c2ecf20Sopenharmony_ci#define PHY_DATA_0      0x00000
998c2ecf20Sopenharmony_ci#define MDCLKH          0x10000
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#define PHY_POWER_DOWN	0x800
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define SROM_V41_CODE   0x14
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Structure/enum declaration ------------------------------- */
1068c2ecf20Sopenharmony_cistruct tx_desc {
1078c2ecf20Sopenharmony_ci        __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
1088c2ecf20Sopenharmony_ci        char *tx_buf_ptr;               /* Data for us */
1098c2ecf20Sopenharmony_ci        struct tx_desc *next_tx_desc;
1108c2ecf20Sopenharmony_ci} __attribute__(( aligned(32) ));
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistruct rx_desc {
1138c2ecf20Sopenharmony_ci	__le32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
1148c2ecf20Sopenharmony_ci	struct sk_buff *rx_skb_ptr;	/* Data for us */
1158c2ecf20Sopenharmony_ci	struct rx_desc *next_rx_desc;
1168c2ecf20Sopenharmony_ci} __attribute__(( aligned(32) ));
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct uli526x_board_info {
1198c2ecf20Sopenharmony_ci	struct uli_phy_ops {
1208c2ecf20Sopenharmony_ci		void (*write)(struct uli526x_board_info *, u8, u8, u16);
1218c2ecf20Sopenharmony_ci		u16 (*read)(struct uli526x_board_info *, u8, u8);
1228c2ecf20Sopenharmony_ci	} phy;
1238c2ecf20Sopenharmony_ci	struct net_device *next_dev;	/* next device */
1248c2ecf20Sopenharmony_ci	struct pci_dev *pdev;		/* PCI device */
1258c2ecf20Sopenharmony_ci	spinlock_t lock;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	void __iomem *ioaddr;		/* I/O base address */
1288c2ecf20Sopenharmony_ci	u32 cr0_data;
1298c2ecf20Sopenharmony_ci	u32 cr5_data;
1308c2ecf20Sopenharmony_ci	u32 cr6_data;
1318c2ecf20Sopenharmony_ci	u32 cr7_data;
1328c2ecf20Sopenharmony_ci	u32 cr15_data;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* pointer for memory physical address */
1358c2ecf20Sopenharmony_ci	dma_addr_t buf_pool_dma_ptr;	/* Tx buffer pool memory */
1368c2ecf20Sopenharmony_ci	dma_addr_t buf_pool_dma_start;	/* Tx buffer pool align dword */
1378c2ecf20Sopenharmony_ci	dma_addr_t desc_pool_dma_ptr;	/* descriptor pool memory */
1388c2ecf20Sopenharmony_ci	dma_addr_t first_tx_desc_dma;
1398c2ecf20Sopenharmony_ci	dma_addr_t first_rx_desc_dma;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* descriptor pointer */
1428c2ecf20Sopenharmony_ci	unsigned char *buf_pool_ptr;	/* Tx buffer pool memory */
1438c2ecf20Sopenharmony_ci	unsigned char *buf_pool_start;	/* Tx buffer pool align dword */
1448c2ecf20Sopenharmony_ci	unsigned char *desc_pool_ptr;	/* descriptor pool memory */
1458c2ecf20Sopenharmony_ci	struct tx_desc *first_tx_desc;
1468c2ecf20Sopenharmony_ci	struct tx_desc *tx_insert_ptr;
1478c2ecf20Sopenharmony_ci	struct tx_desc *tx_remove_ptr;
1488c2ecf20Sopenharmony_ci	struct rx_desc *first_rx_desc;
1498c2ecf20Sopenharmony_ci	struct rx_desc *rx_insert_ptr;
1508c2ecf20Sopenharmony_ci	struct rx_desc *rx_ready_ptr;	/* packet come pointer */
1518c2ecf20Sopenharmony_ci	unsigned long tx_packet_cnt;	/* transmitted packet count */
1528c2ecf20Sopenharmony_ci	unsigned long rx_avail_cnt;	/* available rx descriptor count */
1538c2ecf20Sopenharmony_ci	unsigned long interval_rx_cnt;	/* rx packet count a callback time */
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	u16 dbug_cnt;
1568c2ecf20Sopenharmony_ci	u16 NIC_capability;		/* NIC media capability */
1578c2ecf20Sopenharmony_ci	u16 PHY_reg4;			/* Saved Phyxcer register 4 value */
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	u8 media_mode;			/* user specify media mode */
1608c2ecf20Sopenharmony_ci	u8 op_mode;			/* real work media mode */
1618c2ecf20Sopenharmony_ci	u8 phy_addr;
1628c2ecf20Sopenharmony_ci	u8 link_failed;			/* Ever link failed */
1638c2ecf20Sopenharmony_ci	u8 wait_reset;			/* Hardware failed, need to reset */
1648c2ecf20Sopenharmony_ci	struct timer_list timer;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Driver defined statistic counter */
1678c2ecf20Sopenharmony_ci	unsigned long tx_fifo_underrun;
1688c2ecf20Sopenharmony_ci	unsigned long tx_loss_carrier;
1698c2ecf20Sopenharmony_ci	unsigned long tx_no_carrier;
1708c2ecf20Sopenharmony_ci	unsigned long tx_late_collision;
1718c2ecf20Sopenharmony_ci	unsigned long tx_excessive_collision;
1728c2ecf20Sopenharmony_ci	unsigned long tx_jabber_timeout;
1738c2ecf20Sopenharmony_ci	unsigned long reset_count;
1748c2ecf20Sopenharmony_ci	unsigned long reset_cr8;
1758c2ecf20Sopenharmony_ci	unsigned long reset_fatal;
1768c2ecf20Sopenharmony_ci	unsigned long reset_TXtimeout;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* NIC SROM data */
1798c2ecf20Sopenharmony_ci	unsigned char srom[128];
1808c2ecf20Sopenharmony_ci	u8 init;
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cienum uli526x_offsets {
1848c2ecf20Sopenharmony_ci	DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
1858c2ecf20Sopenharmony_ci	DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
1868c2ecf20Sopenharmony_ci	DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70,
1878c2ecf20Sopenharmony_ci	DCR15 = 0x78
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cienum uli526x_CR6_bits {
1918c2ecf20Sopenharmony_ci	CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
1928c2ecf20Sopenharmony_ci	CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
1938c2ecf20Sopenharmony_ci	CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/* Global variable declaration ----------------------------- */
1978c2ecf20Sopenharmony_cistatic int uli526x_debug;
1988c2ecf20Sopenharmony_cistatic unsigned char uli526x_media_mode = ULI526X_AUTO;
1998c2ecf20Sopenharmony_cistatic u32 uli526x_cr6_user_set;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/* For module input parameter */
2028c2ecf20Sopenharmony_cistatic int debug;
2038c2ecf20Sopenharmony_cistatic u32 cr6set;
2048c2ecf20Sopenharmony_cistatic int mode = 8;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* function declaration ------------------------------------- */
2078c2ecf20Sopenharmony_cistatic int uli526x_open(struct net_device *);
2088c2ecf20Sopenharmony_cistatic netdev_tx_t uli526x_start_xmit(struct sk_buff *,
2098c2ecf20Sopenharmony_ci					    struct net_device *);
2108c2ecf20Sopenharmony_cistatic int uli526x_stop(struct net_device *);
2118c2ecf20Sopenharmony_cistatic void uli526x_set_filter_mode(struct net_device *);
2128c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
2138c2ecf20Sopenharmony_cistatic u16 read_srom_word(struct uli526x_board_info *, int);
2148c2ecf20Sopenharmony_cistatic irqreturn_t uli526x_interrupt(int, void *);
2158c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
2168c2ecf20Sopenharmony_cistatic void uli526x_poll(struct net_device *dev);
2178c2ecf20Sopenharmony_ci#endif
2188c2ecf20Sopenharmony_cistatic void uli526x_descriptor_init(struct net_device *, void __iomem *);
2198c2ecf20Sopenharmony_cistatic void allocate_rx_buffer(struct net_device *);
2208c2ecf20Sopenharmony_cistatic void update_cr6(u32, void __iomem *);
2218c2ecf20Sopenharmony_cistatic void send_filter_frame(struct net_device *, int);
2228c2ecf20Sopenharmony_cistatic u16 phy_readby_cr9(struct uli526x_board_info *, u8, u8);
2238c2ecf20Sopenharmony_cistatic u16 phy_readby_cr10(struct uli526x_board_info *, u8, u8);
2248c2ecf20Sopenharmony_cistatic void phy_writeby_cr9(struct uli526x_board_info *, u8, u8, u16);
2258c2ecf20Sopenharmony_cistatic void phy_writeby_cr10(struct uli526x_board_info *, u8, u8, u16);
2268c2ecf20Sopenharmony_cistatic void phy_write_1bit(struct uli526x_board_info *db, u32);
2278c2ecf20Sopenharmony_cistatic u16 phy_read_1bit(struct uli526x_board_info *db);
2288c2ecf20Sopenharmony_cistatic u8 uli526x_sense_speed(struct uli526x_board_info *);
2298c2ecf20Sopenharmony_cistatic void uli526x_process_mode(struct uli526x_board_info *);
2308c2ecf20Sopenharmony_cistatic void uli526x_timer(struct timer_list *t);
2318c2ecf20Sopenharmony_cistatic void uli526x_rx_packet(struct net_device *, struct uli526x_board_info *);
2328c2ecf20Sopenharmony_cistatic void uli526x_free_tx_pkt(struct net_device *, struct uli526x_board_info *);
2338c2ecf20Sopenharmony_cistatic void uli526x_reuse_skb(struct uli526x_board_info *, struct sk_buff *);
2348c2ecf20Sopenharmony_cistatic void uli526x_dynamic_reset(struct net_device *);
2358c2ecf20Sopenharmony_cistatic void uli526x_free_rxbuffer(struct uli526x_board_info *);
2368c2ecf20Sopenharmony_cistatic void uli526x_init(struct net_device *);
2378c2ecf20Sopenharmony_cistatic void uli526x_set_phyxcer(struct uli526x_board_info *);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void srom_clk_write(struct uli526x_board_info *db, u32 data)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS);
2448c2ecf20Sopenharmony_ci	udelay(5);
2458c2ecf20Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS | CR9_SRCLK);
2468c2ecf20Sopenharmony_ci	udelay(5);
2478c2ecf20Sopenharmony_ci	uw32(DCR9, data | CR9_SROM_READ | CR9_SRCS);
2488c2ecf20Sopenharmony_ci	udelay(5);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* ULI526X network board routine ---------------------------- */
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
2548c2ecf20Sopenharmony_ci	.ndo_open		= uli526x_open,
2558c2ecf20Sopenharmony_ci	.ndo_stop		= uli526x_stop,
2568c2ecf20Sopenharmony_ci	.ndo_start_xmit		= uli526x_start_xmit,
2578c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= uli526x_set_filter_mode,
2588c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
2598c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
2608c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
2618c2ecf20Sopenharmony_ci	.ndo_poll_controller 	= uli526x_poll,
2628c2ecf20Sopenharmony_ci#endif
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/*
2668c2ecf20Sopenharmony_ci *	Search ULI526X board, allocate space and register it
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int uli526x_init_one(struct pci_dev *pdev,
2708c2ecf20Sopenharmony_ci			    const struct pci_device_id *ent)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct uli526x_board_info *db;	/* board information structure */
2738c2ecf20Sopenharmony_ci	struct net_device *dev;
2748c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
2758c2ecf20Sopenharmony_ci	int i, err;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_init_one()", 0);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Init network device */
2808c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(*db));
2818c2ecf20Sopenharmony_ci	if (dev == NULL)
2828c2ecf20Sopenharmony_ci		return -ENOMEM;
2838c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
2868c2ecf20Sopenharmony_ci		pr_warn("32-bit PCI DMA not available\n");
2878c2ecf20Sopenharmony_ci		err = -ENODEV;
2888c2ecf20Sopenharmony_ci		goto err_out_free;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Enable Master/IO access, Disable memory access */
2928c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
2938c2ecf20Sopenharmony_ci	if (err)
2948c2ecf20Sopenharmony_ci		goto err_out_free;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (!pci_resource_start(pdev, 0)) {
2978c2ecf20Sopenharmony_ci		pr_err("I/O base is zero\n");
2988c2ecf20Sopenharmony_ci		err = -ENODEV;
2998c2ecf20Sopenharmony_ci		goto err_out_disable;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (pci_resource_len(pdev, 0) < (ULI526X_IO_SIZE) ) {
3038c2ecf20Sopenharmony_ci		pr_err("Allocated I/O size too small\n");
3048c2ecf20Sopenharmony_ci		err = -ENODEV;
3058c2ecf20Sopenharmony_ci		goto err_out_disable;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
3098c2ecf20Sopenharmony_ci	if (err < 0) {
3108c2ecf20Sopenharmony_ci		pr_err("Failed to request PCI regions\n");
3118c2ecf20Sopenharmony_ci		goto err_out_disable;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Init system & device */
3158c2ecf20Sopenharmony_ci	db = netdev_priv(dev);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* Allocate Tx/Rx descriptor memory */
3188c2ecf20Sopenharmony_ci	err = -ENOMEM;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	db->desc_pool_ptr = dma_alloc_coherent(&pdev->dev,
3218c2ecf20Sopenharmony_ci					       sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
3228c2ecf20Sopenharmony_ci					       &db->desc_pool_dma_ptr, GFP_KERNEL);
3238c2ecf20Sopenharmony_ci	if (!db->desc_pool_ptr)
3248c2ecf20Sopenharmony_ci		goto err_out_release;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	db->buf_pool_ptr = dma_alloc_coherent(&pdev->dev,
3278c2ecf20Sopenharmony_ci					      TX_BUF_ALLOC * TX_DESC_CNT + 4,
3288c2ecf20Sopenharmony_ci					      &db->buf_pool_dma_ptr, GFP_KERNEL);
3298c2ecf20Sopenharmony_ci	if (!db->buf_pool_ptr)
3308c2ecf20Sopenharmony_ci		goto err_out_free_tx_desc;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
3338c2ecf20Sopenharmony_ci	db->first_tx_desc_dma = db->desc_pool_dma_ptr;
3348c2ecf20Sopenharmony_ci	db->buf_pool_start = db->buf_pool_ptr;
3358c2ecf20Sopenharmony_ci	db->buf_pool_dma_start = db->buf_pool_dma_ptr;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	switch (ent->driver_data) {
3388c2ecf20Sopenharmony_ci	case PCI_ULI5263_ID:
3398c2ecf20Sopenharmony_ci		db->phy.write	= phy_writeby_cr10;
3408c2ecf20Sopenharmony_ci		db->phy.read	= phy_readby_cr10;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	default:
3438c2ecf20Sopenharmony_ci		db->phy.write	= phy_writeby_cr9;
3448c2ecf20Sopenharmony_ci		db->phy.read	= phy_readby_cr9;
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* IO region. */
3498c2ecf20Sopenharmony_ci	ioaddr = pci_iomap(pdev, 0, 0);
3508c2ecf20Sopenharmony_ci	if (!ioaddr)
3518c2ecf20Sopenharmony_ci		goto err_out_free_tx_buf;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	db->ioaddr = ioaddr;
3548c2ecf20Sopenharmony_ci	db->pdev = pdev;
3558c2ecf20Sopenharmony_ci	db->init = 1;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Register some necessary functions */
3608c2ecf20Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
3618c2ecf20Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	spin_lock_init(&db->lock);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* read 64 word srom data */
3678c2ecf20Sopenharmony_ci	for (i = 0; i < 64; i++)
3688c2ecf20Sopenharmony_ci		((__le16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db, i));
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Set Node address */
3718c2ecf20Sopenharmony_ci	if(((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0)		/* SROM absent, so read MAC address from ID Table */
3728c2ecf20Sopenharmony_ci	{
3738c2ecf20Sopenharmony_ci		uw32(DCR0, 0x10000);	//Diagnosis mode
3748c2ecf20Sopenharmony_ci		uw32(DCR13, 0x1c0);	//Reset dianostic pointer port
3758c2ecf20Sopenharmony_ci		uw32(DCR14, 0);		//Clear reset port
3768c2ecf20Sopenharmony_ci		uw32(DCR14, 0x10);	//Reset ID Table pointer
3778c2ecf20Sopenharmony_ci		uw32(DCR14, 0);		//Clear reset port
3788c2ecf20Sopenharmony_ci		uw32(DCR13, 0);		//Clear CR13
3798c2ecf20Sopenharmony_ci		uw32(DCR13, 0x1b0);	//Select ID Table access port
3808c2ecf20Sopenharmony_ci		//Read MAC address from CR14
3818c2ecf20Sopenharmony_ci		for (i = 0; i < 6; i++)
3828c2ecf20Sopenharmony_ci			dev->dev_addr[i] = ur32(DCR14);
3838c2ecf20Sopenharmony_ci		//Read end
3848c2ecf20Sopenharmony_ci		uw32(DCR13, 0);		//Clear CR13
3858c2ecf20Sopenharmony_ci		uw32(DCR0, 0);		//Clear CR0
3868c2ecf20Sopenharmony_ci		udelay(10);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	else		/*Exist SROM*/
3898c2ecf20Sopenharmony_ci	{
3908c2ecf20Sopenharmony_ci		for (i = 0; i < 6; i++)
3918c2ecf20Sopenharmony_ci			dev->dev_addr[i] = db->srom[20 + i];
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	err = register_netdev (dev);
3948c2ecf20Sopenharmony_ci	if (err)
3958c2ecf20Sopenharmony_ci		goto err_out_unmap;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	netdev_info(dev, "ULi M%04lx at pci%s, %pM, irq %d\n",
3988c2ecf20Sopenharmony_ci		    ent->driver_data >> 16, pci_name(pdev),
3998c2ecf20Sopenharmony_ci		    dev->dev_addr, pdev->irq);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	pci_set_master(pdev);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cierr_out_unmap:
4068c2ecf20Sopenharmony_ci	pci_iounmap(pdev, db->ioaddr);
4078c2ecf20Sopenharmony_cierr_out_free_tx_buf:
4088c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
4098c2ecf20Sopenharmony_ci			  db->buf_pool_ptr, db->buf_pool_dma_ptr);
4108c2ecf20Sopenharmony_cierr_out_free_tx_desc:
4118c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev,
4128c2ecf20Sopenharmony_ci			  sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
4138c2ecf20Sopenharmony_ci			  db->desc_pool_ptr, db->desc_pool_dma_ptr);
4148c2ecf20Sopenharmony_cierr_out_release:
4158c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
4168c2ecf20Sopenharmony_cierr_out_disable:
4178c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
4188c2ecf20Sopenharmony_cierr_out_free:
4198c2ecf20Sopenharmony_ci	free_netdev(dev);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return err;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void uli526x_remove_one(struct pci_dev *pdev)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
4288c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	unregister_netdev(dev);
4318c2ecf20Sopenharmony_ci	pci_iounmap(pdev, db->ioaddr);
4328c2ecf20Sopenharmony_ci	dma_free_coherent(&db->pdev->dev,
4338c2ecf20Sopenharmony_ci			  sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20,
4348c2ecf20Sopenharmony_ci			  db->desc_pool_ptr, db->desc_pool_dma_ptr);
4358c2ecf20Sopenharmony_ci	dma_free_coherent(&db->pdev->dev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
4368c2ecf20Sopenharmony_ci			  db->buf_pool_ptr, db->buf_pool_dma_ptr);
4378c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
4388c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
4398c2ecf20Sopenharmony_ci	free_netdev(dev);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/*
4448c2ecf20Sopenharmony_ci *	Open the interface.
4458c2ecf20Sopenharmony_ci *	The interface is opened whenever "ifconfig" activates it.
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int uli526x_open(struct net_device *dev)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	int ret;
4518c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_open", 0);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* system variable init */
4568c2ecf20Sopenharmony_ci	db->cr6_data = CR6_DEFAULT | uli526x_cr6_user_set;
4578c2ecf20Sopenharmony_ci	db->tx_packet_cnt = 0;
4588c2ecf20Sopenharmony_ci	db->rx_avail_cnt = 0;
4598c2ecf20Sopenharmony_ci	db->link_failed = 1;
4608c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
4618c2ecf20Sopenharmony_ci	db->wait_reset = 0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	db->NIC_capability = 0xf;	/* All capability*/
4648c2ecf20Sopenharmony_ci	db->PHY_reg4 = 0x1e0;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* CR6 operation mode decision */
4678c2ecf20Sopenharmony_ci	db->cr6_data |= ULI526X_TXTH_256;
4688c2ecf20Sopenharmony_ci	db->cr0_data = CR0_DEFAULT;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Initialize ULI526X board */
4718c2ecf20Sopenharmony_ci	uli526x_init(dev);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ret = request_irq(db->pdev->irq, uli526x_interrupt, IRQF_SHARED,
4748c2ecf20Sopenharmony_ci			  dev->name, dev);
4758c2ecf20Sopenharmony_ci	if (ret)
4768c2ecf20Sopenharmony_ci		return ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* Active System Interface */
4798c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* set and active a timer process */
4828c2ecf20Sopenharmony_ci	timer_setup(&db->timer, uli526x_timer, 0);
4838c2ecf20Sopenharmony_ci	db->timer.expires = ULI526X_TIMER_WUT + HZ * 2;
4848c2ecf20Sopenharmony_ci	add_timer(&db->timer);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/*	Initialize ULI526X board
4918c2ecf20Sopenharmony_ci *	Reset ULI526X board
4928c2ecf20Sopenharmony_ci *	Initialize TX/Rx descriptor chain structure
4938c2ecf20Sopenharmony_ci *	Send the set-up frame
4948c2ecf20Sopenharmony_ci *	Enable Tx/Rx machine
4958c2ecf20Sopenharmony_ci */
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void uli526x_init(struct net_device *dev)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
5008c2ecf20Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
5018c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
5028c2ecf20Sopenharmony_ci	u8	phy_tmp;
5038c2ecf20Sopenharmony_ci	u8	timeout;
5048c2ecf20Sopenharmony_ci	u16 phy_reg_reset;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_init()", 0);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* Reset M526x MAC controller */
5108c2ecf20Sopenharmony_ci	uw32(DCR0, ULI526X_RESET);	/* RESET MAC */
5118c2ecf20Sopenharmony_ci	udelay(100);
5128c2ecf20Sopenharmony_ci	uw32(DCR0, db->cr0_data);
5138c2ecf20Sopenharmony_ci	udelay(5);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* Phy addr : In some boards,M5261/M5263 phy address != 1 */
5168c2ecf20Sopenharmony_ci	db->phy_addr = 1;
5178c2ecf20Sopenharmony_ci	for (phy_tmp = 0; phy_tmp < 32; phy_tmp++) {
5188c2ecf20Sopenharmony_ci		u16 phy_value;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		phy_value = phy->read(db, phy_tmp, 3);	//peer add
5218c2ecf20Sopenharmony_ci		if (phy_value != 0xffff && phy_value != 0) {
5228c2ecf20Sopenharmony_ci			db->phy_addr = phy_tmp;
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (phy_tmp == 32)
5288c2ecf20Sopenharmony_ci		pr_warn("Can not find the phy address!!!\n");
5298c2ecf20Sopenharmony_ci	/* Parser SROM and media mode */
5308c2ecf20Sopenharmony_ci	db->media_mode = uli526x_media_mode;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	/* phyxcer capability setting */
5338c2ecf20Sopenharmony_ci	phy_reg_reset = phy->read(db, db->phy_addr, 0);
5348c2ecf20Sopenharmony_ci	phy_reg_reset = (phy_reg_reset | 0x8000);
5358c2ecf20Sopenharmony_ci	phy->write(db, db->phy_addr, 0, phy_reg_reset);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management
5388c2ecf20Sopenharmony_ci	 * functions") or phy data sheet for details on phy reset
5398c2ecf20Sopenharmony_ci	 */
5408c2ecf20Sopenharmony_ci	udelay(500);
5418c2ecf20Sopenharmony_ci	timeout = 10;
5428c2ecf20Sopenharmony_ci	while (timeout-- && phy->read(db, db->phy_addr, 0) & 0x8000)
5438c2ecf20Sopenharmony_ci		udelay(100);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Process Phyxcer Media Mode */
5468c2ecf20Sopenharmony_ci	uli526x_set_phyxcer(db);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Media Mode Process */
5498c2ecf20Sopenharmony_ci	if ( !(db->media_mode & ULI526X_AUTO) )
5508c2ecf20Sopenharmony_ci		db->op_mode = db->media_mode;		/* Force Mode */
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* Initialize Transmit/Receive descriptor and CR3/4 */
5538c2ecf20Sopenharmony_ci	uli526x_descriptor_init(dev, ioaddr);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* Init CR6 to program M526X operation */
5568c2ecf20Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* Send setup frame */
5598c2ecf20Sopenharmony_ci	send_filter_frame(dev, netdev_mc_count(dev));	/* M5261/M5263 */
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* Init CR7, interrupt active bit */
5628c2ecf20Sopenharmony_ci	db->cr7_data = CR7_DEFAULT;
5638c2ecf20Sopenharmony_ci	uw32(DCR7, db->cr7_data);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* Init CR15, Tx jabber and Rx watchdog timer */
5668c2ecf20Sopenharmony_ci	uw32(DCR15, db->cr15_data);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* Enable ULI526X Tx/Rx function */
5698c2ecf20Sopenharmony_ci	db->cr6_data |= CR6_RXSC | CR6_TXSC;
5708c2ecf20Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci/*
5758c2ecf20Sopenharmony_ci *	Hardware start transmission.
5768c2ecf20Sopenharmony_ci *	Send a packet to media from the upper layer.
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic netdev_tx_t uli526x_start_xmit(struct sk_buff *skb,
5808c2ecf20Sopenharmony_ci					    struct net_device *dev)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
5838c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
5848c2ecf20Sopenharmony_ci	struct tx_desc *txptr;
5858c2ecf20Sopenharmony_ci	unsigned long flags;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_start_xmit", 0);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* Resource flag check */
5908c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* Too large packet check */
5938c2ecf20Sopenharmony_ci	if (skb->len > MAX_PACKET_SIZE) {
5948c2ecf20Sopenharmony_ci		netdev_err(dev, "big packet = %d\n", (u16)skb->len);
5958c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
5968c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* No Tx resource check, it never happen nromally */
6028c2ecf20Sopenharmony_ci	if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
6038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
6048c2ecf20Sopenharmony_ci		netdev_err(dev, "No Tx resource %ld\n", db->tx_packet_cnt);
6058c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* Disable NIC interrupt */
6098c2ecf20Sopenharmony_ci	uw32(DCR7, 0);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* transmit this packet */
6128c2ecf20Sopenharmony_ci	txptr = db->tx_insert_ptr;
6138c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
6148c2ecf20Sopenharmony_ci	txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/* Point to next transmit free descriptor */
6178c2ecf20Sopenharmony_ci	db->tx_insert_ptr = txptr->next_tx_desc;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* Transmit Packet Process */
6208c2ecf20Sopenharmony_ci	if (db->tx_packet_cnt < TX_DESC_CNT) {
6218c2ecf20Sopenharmony_ci		txptr->tdes0 = cpu_to_le32(0x80000000);	/* Set owner bit */
6228c2ecf20Sopenharmony_ci		db->tx_packet_cnt++;			/* Ready to send */
6238c2ecf20Sopenharmony_ci		uw32(DCR1, 0x1);			/* Issue Tx polling */
6248c2ecf20Sopenharmony_ci		netif_trans_update(dev);		/* saved time stamp */
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* Tx resource check */
6288c2ecf20Sopenharmony_ci	if ( db->tx_packet_cnt < TX_FREE_DESC_CNT )
6298c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* Restore CR7 to enable interrupt */
6328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
6338c2ecf20Sopenharmony_ci	uw32(DCR7, db->cr7_data);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	/* free this SKB */
6368c2ecf20Sopenharmony_ci	dev_consume_skb_any(skb);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/*
6438c2ecf20Sopenharmony_ci *	Stop the interface.
6448c2ecf20Sopenharmony_ci *	The interface is stopped when it is brought.
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int uli526x_stop(struct net_device *dev)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
6508c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	/* disable system */
6538c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	/* deleted timer */
6568c2ecf20Sopenharmony_ci	del_timer_sync(&db->timer);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* Reset & stop ULI526X board */
6598c2ecf20Sopenharmony_ci	uw32(DCR0, ULI526X_RESET);
6608c2ecf20Sopenharmony_ci	udelay(5);
6618c2ecf20Sopenharmony_ci	db->phy.write(db, db->phy_addr, 0, 0x8000);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* free interrupt */
6648c2ecf20Sopenharmony_ci	free_irq(db->pdev->irq, dev);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* free allocated rx buffer */
6678c2ecf20Sopenharmony_ci	uli526x_free_rxbuffer(db);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return 0;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/*
6748c2ecf20Sopenharmony_ci *	M5261/M5263 insterrupt handler
6758c2ecf20Sopenharmony_ci *	receive the packet to upper layer, free the transmitted packet
6768c2ecf20Sopenharmony_ci */
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic irqreturn_t uli526x_interrupt(int irq, void *dev_id)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
6818c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
6828c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
6838c2ecf20Sopenharmony_ci	unsigned long flags;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
6868c2ecf20Sopenharmony_ci	uw32(DCR7, 0);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* Got ULI526X status */
6898c2ecf20Sopenharmony_ci	db->cr5_data = ur32(DCR5);
6908c2ecf20Sopenharmony_ci	uw32(DCR5, db->cr5_data);
6918c2ecf20Sopenharmony_ci	if ( !(db->cr5_data & 0x180c1) ) {
6928c2ecf20Sopenharmony_ci		/* Restore CR7 to enable interrupt mask */
6938c2ecf20Sopenharmony_ci		uw32(DCR7, db->cr7_data);
6948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
6958c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	/* Check system status */
6998c2ecf20Sopenharmony_ci	if (db->cr5_data & 0x2000) {
7008c2ecf20Sopenharmony_ci		/* system bus error happen */
7018c2ecf20Sopenharmony_ci		ULI526X_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
7028c2ecf20Sopenharmony_ci		db->reset_fatal++;
7038c2ecf20Sopenharmony_ci		db->wait_reset = 1;	/* Need to RESET */
7048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
7058c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	 /* Received the coming packet */
7098c2ecf20Sopenharmony_ci	if ( (db->cr5_data & 0x40) && db->rx_avail_cnt )
7108c2ecf20Sopenharmony_ci		uli526x_rx_packet(dev, db);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* reallocate rx descriptor buffer */
7138c2ecf20Sopenharmony_ci	if (db->rx_avail_cnt<RX_DESC_CNT)
7148c2ecf20Sopenharmony_ci		allocate_rx_buffer(dev);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* Free the transmitted descriptor */
7178c2ecf20Sopenharmony_ci	if ( db->cr5_data & 0x01)
7188c2ecf20Sopenharmony_ci		uli526x_free_tx_pkt(dev, db);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Restore CR7 to enable interrupt mask */
7218c2ecf20Sopenharmony_ci	uw32(DCR7, db->cr7_data);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
7248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
7288c2ecf20Sopenharmony_cistatic void uli526x_poll(struct net_device *dev)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* ISR grabs the irqsave lock, so this should be safe */
7338c2ecf20Sopenharmony_ci	uli526x_interrupt(db->pdev->irq, dev);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci#endif
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/*
7388c2ecf20Sopenharmony_ci *	Free TX resource after TX complete
7398c2ecf20Sopenharmony_ci */
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic void uli526x_free_tx_pkt(struct net_device *dev,
7428c2ecf20Sopenharmony_ci				struct uli526x_board_info * db)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct tx_desc *txptr;
7458c2ecf20Sopenharmony_ci	u32 tdes0;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	txptr = db->tx_remove_ptr;
7488c2ecf20Sopenharmony_ci	while(db->tx_packet_cnt) {
7498c2ecf20Sopenharmony_ci		tdes0 = le32_to_cpu(txptr->tdes0);
7508c2ecf20Sopenharmony_ci		if (tdes0 & 0x80000000)
7518c2ecf20Sopenharmony_ci			break;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		/* A packet sent completed */
7548c2ecf20Sopenharmony_ci		db->tx_packet_cnt--;
7558c2ecf20Sopenharmony_ci		dev->stats.tx_packets++;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		/* Transmit statistic counter */
7588c2ecf20Sopenharmony_ci		if ( tdes0 != 0x7fffffff ) {
7598c2ecf20Sopenharmony_ci			dev->stats.collisions += (tdes0 >> 3) & 0xf;
7608c2ecf20Sopenharmony_ci			dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff;
7618c2ecf20Sopenharmony_ci			if (tdes0 & TDES0_ERR_MASK) {
7628c2ecf20Sopenharmony_ci				dev->stats.tx_errors++;
7638c2ecf20Sopenharmony_ci				if (tdes0 & 0x0002) {	/* UnderRun */
7648c2ecf20Sopenharmony_ci					db->tx_fifo_underrun++;
7658c2ecf20Sopenharmony_ci					if ( !(db->cr6_data & CR6_SFT) ) {
7668c2ecf20Sopenharmony_ci						db->cr6_data = db->cr6_data | CR6_SFT;
7678c2ecf20Sopenharmony_ci						update_cr6(db->cr6_data, db->ioaddr);
7688c2ecf20Sopenharmony_ci					}
7698c2ecf20Sopenharmony_ci				}
7708c2ecf20Sopenharmony_ci				if (tdes0 & 0x0100)
7718c2ecf20Sopenharmony_ci					db->tx_excessive_collision++;
7728c2ecf20Sopenharmony_ci				if (tdes0 & 0x0200)
7738c2ecf20Sopenharmony_ci					db->tx_late_collision++;
7748c2ecf20Sopenharmony_ci				if (tdes0 & 0x0400)
7758c2ecf20Sopenharmony_ci					db->tx_no_carrier++;
7768c2ecf20Sopenharmony_ci				if (tdes0 & 0x0800)
7778c2ecf20Sopenharmony_ci					db->tx_loss_carrier++;
7788c2ecf20Sopenharmony_ci				if (tdes0 & 0x4000)
7798c2ecf20Sopenharmony_ci					db->tx_jabber_timeout++;
7808c2ecf20Sopenharmony_ci			}
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci    		txptr = txptr->next_tx_desc;
7848c2ecf20Sopenharmony_ci	}/* End of while */
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* Update TX remove pointer to next */
7878c2ecf20Sopenharmony_ci	db->tx_remove_ptr = txptr;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* Resource available check */
7908c2ecf20Sopenharmony_ci	if ( db->tx_packet_cnt < TX_WAKE_DESC_CNT )
7918c2ecf20Sopenharmony_ci		netif_wake_queue(dev);	/* Active upper layer, send again */
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci/*
7968c2ecf20Sopenharmony_ci *	Receive the come packet and pass to upper layer
7978c2ecf20Sopenharmony_ci */
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info * db)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct rx_desc *rxptr;
8028c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8038c2ecf20Sopenharmony_ci	int rxlen;
8048c2ecf20Sopenharmony_ci	u32 rdes0;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	rxptr = db->rx_ready_ptr;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	while(db->rx_avail_cnt) {
8098c2ecf20Sopenharmony_ci		rdes0 = le32_to_cpu(rxptr->rdes0);
8108c2ecf20Sopenharmony_ci		if (rdes0 & 0x80000000)	/* packet owner check */
8118c2ecf20Sopenharmony_ci		{
8128c2ecf20Sopenharmony_ci			break;
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		db->rx_avail_cnt--;
8168c2ecf20Sopenharmony_ci		db->interval_rx_cnt++;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		dma_unmap_single(&db->pdev->dev, le32_to_cpu(rxptr->rdes2),
8198c2ecf20Sopenharmony_ci				 RX_ALLOC_SIZE, DMA_FROM_DEVICE);
8208c2ecf20Sopenharmony_ci		if ( (rdes0 & 0x300) != 0x300) {
8218c2ecf20Sopenharmony_ci			/* A packet without First/Last flag */
8228c2ecf20Sopenharmony_ci			/* reuse this SKB */
8238c2ecf20Sopenharmony_ci			ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
8248c2ecf20Sopenharmony_ci			uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
8258c2ecf20Sopenharmony_ci		} else {
8268c2ecf20Sopenharmony_ci			/* A packet with First/Last flag */
8278c2ecf20Sopenharmony_ci			rxlen = ( (rdes0 >> 16) & 0x3fff) - 4;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci			/* error summary bit check */
8308c2ecf20Sopenharmony_ci			if (rdes0 & 0x8000) {
8318c2ecf20Sopenharmony_ci				/* This is a error packet */
8328c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;
8338c2ecf20Sopenharmony_ci				if (rdes0 & 1)
8348c2ecf20Sopenharmony_ci					dev->stats.rx_fifo_errors++;
8358c2ecf20Sopenharmony_ci				if (rdes0 & 2)
8368c2ecf20Sopenharmony_ci					dev->stats.rx_crc_errors++;
8378c2ecf20Sopenharmony_ci				if (rdes0 & 0x80)
8388c2ecf20Sopenharmony_ci					dev->stats.rx_length_errors++;
8398c2ecf20Sopenharmony_ci			}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci			if ( !(rdes0 & 0x8000) ||
8428c2ecf20Sopenharmony_ci				((db->cr6_data & CR6_PM) && (rxlen>6)) ) {
8438c2ecf20Sopenharmony_ci				struct sk_buff *new_skb = NULL;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci				skb = rxptr->rx_skb_ptr;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci				/* Good packet, send to upper layer */
8488c2ecf20Sopenharmony_ci				/* Shorst packet used new SKB */
8498c2ecf20Sopenharmony_ci				if ((rxlen < RX_COPY_SIZE) &&
8508c2ecf20Sopenharmony_ci				    (((new_skb = netdev_alloc_skb(dev, rxlen + 2)) != NULL))) {
8518c2ecf20Sopenharmony_ci					skb = new_skb;
8528c2ecf20Sopenharmony_ci					/* size less than COPY_SIZE, allocate a rxlen SKB */
8538c2ecf20Sopenharmony_ci					skb_reserve(skb, 2); /* 16byte align */
8548c2ecf20Sopenharmony_ci					skb_put_data(skb,
8558c2ecf20Sopenharmony_ci						     skb_tail_pointer(rxptr->rx_skb_ptr),
8568c2ecf20Sopenharmony_ci						     rxlen);
8578c2ecf20Sopenharmony_ci					uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
8588c2ecf20Sopenharmony_ci				} else
8598c2ecf20Sopenharmony_ci					skb_put(skb, rxlen);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci				skb->protocol = eth_type_trans(skb, dev);
8628c2ecf20Sopenharmony_ci				netif_rx(skb);
8638c2ecf20Sopenharmony_ci				dev->stats.rx_packets++;
8648c2ecf20Sopenharmony_ci				dev->stats.rx_bytes += rxlen;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci			} else {
8678c2ecf20Sopenharmony_ci				/* Reuse SKB buffer when the packet is error */
8688c2ecf20Sopenharmony_ci				ULI526X_DBUG(0, "Reuse SK buffer, rdes0", rdes0);
8698c2ecf20Sopenharmony_ci				uli526x_reuse_skb(db, rxptr->rx_skb_ptr);
8708c2ecf20Sopenharmony_ci			}
8718c2ecf20Sopenharmony_ci		}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		rxptr = rxptr->next_rx_desc;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	db->rx_ready_ptr = rxptr;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/*
8818c2ecf20Sopenharmony_ci * Set ULI526X multicast address
8828c2ecf20Sopenharmony_ci */
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_cistatic void uli526x_set_filter_mode(struct net_device * dev)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
8878c2ecf20Sopenharmony_ci	unsigned long flags;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0);
8908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
8938c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "Enable PROM Mode", 0);
8948c2ecf20Sopenharmony_ci		db->cr6_data |= CR6_PM | CR6_PBF;
8958c2ecf20Sopenharmony_ci		update_cr6(db->cr6_data, db->ioaddr);
8968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
8978c2ecf20Sopenharmony_ci		return;
8988c2ecf20Sopenharmony_ci	}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI ||
9018c2ecf20Sopenharmony_ci	    netdev_mc_count(dev) > ULI5261_MAX_MULTICAST) {
9028c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "Pass all multicast address",
9038c2ecf20Sopenharmony_ci			     netdev_mc_count(dev));
9048c2ecf20Sopenharmony_ci		db->cr6_data &= ~(CR6_PM | CR6_PBF);
9058c2ecf20Sopenharmony_ci		db->cr6_data |= CR6_PAM;
9068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
9078c2ecf20Sopenharmony_ci		return;
9088c2ecf20Sopenharmony_ci	}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "Set multicast address", netdev_mc_count(dev));
9118c2ecf20Sopenharmony_ci	send_filter_frame(dev, netdev_mc_count(dev)); 	/* M5261/M5263 */
9128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic void
9168c2ecf20Sopenharmony_ciULi_ethtool_get_link_ksettings(struct uli526x_board_info *db,
9178c2ecf20Sopenharmony_ci			       struct ethtool_link_ksettings *cmd)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	u32 supported, advertising;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	supported = (SUPPORTED_10baseT_Half |
9228c2ecf20Sopenharmony_ci	                   SUPPORTED_10baseT_Full |
9238c2ecf20Sopenharmony_ci	                   SUPPORTED_100baseT_Half |
9248c2ecf20Sopenharmony_ci	                   SUPPORTED_100baseT_Full |
9258c2ecf20Sopenharmony_ci	                   SUPPORTED_Autoneg |
9268c2ecf20Sopenharmony_ci	                   SUPPORTED_MII);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	advertising = (ADVERTISED_10baseT_Half |
9298c2ecf20Sopenharmony_ci	                   ADVERTISED_10baseT_Full |
9308c2ecf20Sopenharmony_ci	                   ADVERTISED_100baseT_Half |
9318c2ecf20Sopenharmony_ci	                   ADVERTISED_100baseT_Full |
9328c2ecf20Sopenharmony_ci	                   ADVERTISED_Autoneg |
9338c2ecf20Sopenharmony_ci	                   ADVERTISED_MII);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
9368c2ecf20Sopenharmony_ci						supported);
9378c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
9388c2ecf20Sopenharmony_ci						advertising);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	cmd->base.port = PORT_MII;
9418c2ecf20Sopenharmony_ci	cmd->base.phy_address = db->phy_addr;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	cmd->base.speed = SPEED_10;
9448c2ecf20Sopenharmony_ci	cmd->base.duplex = DUPLEX_HALF;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if(db->op_mode==ULI526X_100MHF || db->op_mode==ULI526X_100MFD)
9478c2ecf20Sopenharmony_ci	{
9488c2ecf20Sopenharmony_ci		cmd->base.speed = SPEED_100;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci	if(db->op_mode==ULI526X_10MFD || db->op_mode==ULI526X_100MFD)
9518c2ecf20Sopenharmony_ci	{
9528c2ecf20Sopenharmony_ci		cmd->base.duplex = DUPLEX_FULL;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci	if(db->link_failed)
9558c2ecf20Sopenharmony_ci	{
9568c2ecf20Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
9578c2ecf20Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (db->media_mode & ULI526X_AUTO)
9618c2ecf20Sopenharmony_ci	{
9628c2ecf20Sopenharmony_ci		cmd->base.autoneg = AUTONEG_ENABLE;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev,
9678c2ecf20Sopenharmony_ci			       struct ethtool_drvinfo *info)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
9728c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info));
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
9768c2ecf20Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	ULi_ethtool_get_link_ksettings(np, cmd);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	return 0;
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev) {
9868c2ecf20Sopenharmony_ci	struct uli526x_board_info *np = netdev_priv(dev);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	if(np->link_failed)
9898c2ecf20Sopenharmony_ci		return 0;
9908c2ecf20Sopenharmony_ci	else
9918c2ecf20Sopenharmony_ci		return 1;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic void uli526x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	wol->supported = WAKE_PHY | WAKE_MAGIC;
9978c2ecf20Sopenharmony_ci	wol->wolopts = 0;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
10018c2ecf20Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
10028c2ecf20Sopenharmony_ci	.get_link		= netdev_get_link,
10038c2ecf20Sopenharmony_ci	.get_wol		= uli526x_get_wol,
10048c2ecf20Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
10058c2ecf20Sopenharmony_ci};
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci/*
10088c2ecf20Sopenharmony_ci *	A periodic timer routine
10098c2ecf20Sopenharmony_ci *	Dynamic media sense, allocate Rx buffer...
10108c2ecf20Sopenharmony_ci */
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cistatic void uli526x_timer(struct timer_list *t)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = from_timer(db, t, timer);
10158c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(db->pdev);
10168c2ecf20Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
10178c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
10188c2ecf20Sopenharmony_ci 	unsigned long flags;
10198c2ecf20Sopenharmony_ci	u8 tmp_cr12 = 0;
10208c2ecf20Sopenharmony_ci	u32 tmp_cr8;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	//ULI526X_DBUG(0, "uli526x_timer()", 0);
10238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&db->lock, flags);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* Dynamic reset ULI526X : system error or transmit time-out */
10278c2ecf20Sopenharmony_ci	tmp_cr8 = ur32(DCR8);
10288c2ecf20Sopenharmony_ci	if ( (db->interval_rx_cnt==0) && (tmp_cr8) ) {
10298c2ecf20Sopenharmony_ci		db->reset_cr8++;
10308c2ecf20Sopenharmony_ci		db->wait_reset = 1;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci	db->interval_rx_cnt = 0;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/* TX polling kick monitor */
10358c2ecf20Sopenharmony_ci	if ( db->tx_packet_cnt &&
10368c2ecf20Sopenharmony_ci	     time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_KICK) ) {
10378c2ecf20Sopenharmony_ci		uw32(DCR1, 0x1);   // Tx polling again
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		// TX Timeout
10408c2ecf20Sopenharmony_ci		if ( time_after(jiffies, dev_trans_start(dev) + ULI526X_TX_TIMEOUT) ) {
10418c2ecf20Sopenharmony_ci			db->reset_TXtimeout++;
10428c2ecf20Sopenharmony_ci			db->wait_reset = 1;
10438c2ecf20Sopenharmony_ci			netdev_err(dev, " Tx timeout - resetting\n");
10448c2ecf20Sopenharmony_ci		}
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	if (db->wait_reset) {
10488c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "Dynamic Reset device", db->tx_packet_cnt);
10498c2ecf20Sopenharmony_ci		db->reset_count++;
10508c2ecf20Sopenharmony_ci		uli526x_dynamic_reset(dev);
10518c2ecf20Sopenharmony_ci		db->timer.expires = ULI526X_TIMER_WUT;
10528c2ecf20Sopenharmony_ci		add_timer(&db->timer);
10538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&db->lock, flags);
10548c2ecf20Sopenharmony_ci		return;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* Link status check, Dynamic media type change */
10588c2ecf20Sopenharmony_ci	if ((phy->read(db, db->phy_addr, 5) & 0x01e0)!=0)
10598c2ecf20Sopenharmony_ci		tmp_cr12 = 3;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	if ( !(tmp_cr12 & 0x3) && !db->link_failed ) {
10628c2ecf20Sopenharmony_ci		/* Link Failed */
10638c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "Link Failed", tmp_cr12);
10648c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
10658c2ecf20Sopenharmony_ci		netdev_info(dev, "NIC Link is Down\n");
10668c2ecf20Sopenharmony_ci		db->link_failed = 1;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci		/* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */
10698c2ecf20Sopenharmony_ci		/* AUTO don't need */
10708c2ecf20Sopenharmony_ci		if ( !(db->media_mode & 0x8) )
10718c2ecf20Sopenharmony_ci			phy->write(db, db->phy_addr, 0, 0x1000);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		/* AUTO mode, if INT phyxcer link failed, select EXT device */
10748c2ecf20Sopenharmony_ci		if (db->media_mode & ULI526X_AUTO) {
10758c2ecf20Sopenharmony_ci			db->cr6_data&=~0x00000200;	/* bit9=0, HD mode */
10768c2ecf20Sopenharmony_ci			update_cr6(db->cr6_data, db->ioaddr);
10778c2ecf20Sopenharmony_ci		}
10788c2ecf20Sopenharmony_ci	} else
10798c2ecf20Sopenharmony_ci		if ((tmp_cr12 & 0x3) && db->link_failed) {
10808c2ecf20Sopenharmony_ci			ULI526X_DBUG(0, "Link link OK", tmp_cr12);
10818c2ecf20Sopenharmony_ci			db->link_failed = 0;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci			/* Auto Sense Speed */
10848c2ecf20Sopenharmony_ci			if ( (db->media_mode & ULI526X_AUTO) &&
10858c2ecf20Sopenharmony_ci				uli526x_sense_speed(db) )
10868c2ecf20Sopenharmony_ci				db->link_failed = 1;
10878c2ecf20Sopenharmony_ci			uli526x_process_mode(db);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci			if(db->link_failed==0)
10908c2ecf20Sopenharmony_ci			{
10918c2ecf20Sopenharmony_ci				netdev_info(dev, "NIC Link is Up %d Mbps %s duplex\n",
10928c2ecf20Sopenharmony_ci					    (db->op_mode == ULI526X_100MHF ||
10938c2ecf20Sopenharmony_ci					     db->op_mode == ULI526X_100MFD)
10948c2ecf20Sopenharmony_ci					    ? 100 : 10,
10958c2ecf20Sopenharmony_ci					    (db->op_mode == ULI526X_10MFD ||
10968c2ecf20Sopenharmony_ci					     db->op_mode == ULI526X_100MFD)
10978c2ecf20Sopenharmony_ci					    ? "Full" : "Half");
10988c2ecf20Sopenharmony_ci				netif_carrier_on(dev);
10998c2ecf20Sopenharmony_ci			}
11008c2ecf20Sopenharmony_ci			/* SHOW_MEDIA_TYPE(db->op_mode); */
11018c2ecf20Sopenharmony_ci		}
11028c2ecf20Sopenharmony_ci		else if(!(tmp_cr12 & 0x3) && db->link_failed)
11038c2ecf20Sopenharmony_ci		{
11048c2ecf20Sopenharmony_ci			if(db->init==1)
11058c2ecf20Sopenharmony_ci			{
11068c2ecf20Sopenharmony_ci				netdev_info(dev, "NIC Link is Down\n");
11078c2ecf20Sopenharmony_ci				netif_carrier_off(dev);
11088c2ecf20Sopenharmony_ci			}
11098c2ecf20Sopenharmony_ci		}
11108c2ecf20Sopenharmony_ci	db->init = 0;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* Timer active again */
11138c2ecf20Sopenharmony_ci	db->timer.expires = ULI526X_TIMER_WUT;
11148c2ecf20Sopenharmony_ci	add_timer(&db->timer);
11158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&db->lock, flags);
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci/*
11208c2ecf20Sopenharmony_ci *	Stop ULI526X board
11218c2ecf20Sopenharmony_ci *	Free Tx/Rx allocated memory
11228c2ecf20Sopenharmony_ci *	Init system variable
11238c2ecf20Sopenharmony_ci */
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic void uli526x_reset_prepare(struct net_device *dev)
11268c2ecf20Sopenharmony_ci{
11278c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
11288c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* Sopt MAC controller */
11318c2ecf20Sopenharmony_ci	db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);	/* Disable Tx/Rx */
11328c2ecf20Sopenharmony_ci	update_cr6(db->cr6_data, ioaddr);
11338c2ecf20Sopenharmony_ci	uw32(DCR7, 0);				/* Disable Interrupt */
11348c2ecf20Sopenharmony_ci	uw32(DCR5, ur32(DCR5));
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	/* Disable upper layer interface */
11378c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Free Rx Allocate buffer */
11408c2ecf20Sopenharmony_ci	uli526x_free_rxbuffer(db);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	/* system variable init */
11438c2ecf20Sopenharmony_ci	db->tx_packet_cnt = 0;
11448c2ecf20Sopenharmony_ci	db->rx_avail_cnt = 0;
11458c2ecf20Sopenharmony_ci	db->link_failed = 1;
11468c2ecf20Sopenharmony_ci	db->init=1;
11478c2ecf20Sopenharmony_ci	db->wait_reset = 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci/*
11528c2ecf20Sopenharmony_ci *	Dynamic reset the ULI526X board
11538c2ecf20Sopenharmony_ci *	Stop ULI526X board
11548c2ecf20Sopenharmony_ci *	Free Tx/Rx allocated memory
11558c2ecf20Sopenharmony_ci *	Reset ULI526X board
11568c2ecf20Sopenharmony_ci *	Re-initialize ULI526X board
11578c2ecf20Sopenharmony_ci */
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic void uli526x_dynamic_reset(struct net_device *dev)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_dynamic_reset()", 0);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	uli526x_reset_prepare(dev);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	/* Re-initialize ULI526X board */
11668c2ecf20Sopenharmony_ci	uli526x_init(dev);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* Restart upper layer interface */
11698c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci/*
11738c2ecf20Sopenharmony_ci *	Suspend the interface.
11748c2ecf20Sopenharmony_ci */
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic int __maybe_unused uli526x_suspend(struct device *dev_d)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_suspend", 0);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	if (!netif_running(dev))
11838c2ecf20Sopenharmony_ci		return 0;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	netif_device_detach(dev);
11868c2ecf20Sopenharmony_ci	uli526x_reset_prepare(dev);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	device_set_wakeup_enable(dev_d, 0);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	return 0;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci/*
11948c2ecf20Sopenharmony_ci *	Resume the interface.
11958c2ecf20Sopenharmony_ci */
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_cistatic int __maybe_unused uli526x_resume(struct device *dev_d)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_resume", 0);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	if (!netif_running(dev))
12058c2ecf20Sopenharmony_ci		return 0;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	netif_device_attach(dev);
12088c2ecf20Sopenharmony_ci	/* Re-initialize ULI526X board */
12098c2ecf20Sopenharmony_ci	uli526x_init(dev);
12108c2ecf20Sopenharmony_ci	/* Restart upper layer interface */
12118c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	return 0;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci/*
12178c2ecf20Sopenharmony_ci *	free all allocated rx buffer
12188c2ecf20Sopenharmony_ci */
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_cistatic void uli526x_free_rxbuffer(struct uli526x_board_info * db)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_free_rxbuffer()", 0);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	/* free allocated rx buffer */
12258c2ecf20Sopenharmony_ci	while (db->rx_avail_cnt) {
12268c2ecf20Sopenharmony_ci		dev_kfree_skb(db->rx_ready_ptr->rx_skb_ptr);
12278c2ecf20Sopenharmony_ci		db->rx_ready_ptr = db->rx_ready_ptr->next_rx_desc;
12288c2ecf20Sopenharmony_ci		db->rx_avail_cnt--;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci/*
12348c2ecf20Sopenharmony_ci *	Reuse the SK buffer
12358c2ecf20Sopenharmony_ci */
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * skb)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct rx_desc *rxptr = db->rx_insert_ptr;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) {
12428c2ecf20Sopenharmony_ci		rxptr->rx_skb_ptr = skb;
12438c2ecf20Sopenharmony_ci		rxptr->rdes2 = cpu_to_le32(dma_map_single(&db->pdev->dev, skb_tail_pointer(skb),
12448c2ecf20Sopenharmony_ci							  RX_ALLOC_SIZE, DMA_FROM_DEVICE));
12458c2ecf20Sopenharmony_ci		wmb();
12468c2ecf20Sopenharmony_ci		rxptr->rdes0 = cpu_to_le32(0x80000000);
12478c2ecf20Sopenharmony_ci		db->rx_avail_cnt++;
12488c2ecf20Sopenharmony_ci		db->rx_insert_ptr = rxptr->next_rx_desc;
12498c2ecf20Sopenharmony_ci	} else
12508c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "SK Buffer reuse method error", db->rx_avail_cnt);
12518c2ecf20Sopenharmony_ci}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci/*
12558c2ecf20Sopenharmony_ci *	Initialize transmit/Receive descriptor
12568c2ecf20Sopenharmony_ci *	Using Chain structure, and allocate Tx/Rx buffer
12578c2ecf20Sopenharmony_ci */
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cistatic void uli526x_descriptor_init(struct net_device *dev, void __iomem *ioaddr)
12608c2ecf20Sopenharmony_ci{
12618c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
12628c2ecf20Sopenharmony_ci	struct tx_desc *tmp_tx;
12638c2ecf20Sopenharmony_ci	struct rx_desc *tmp_rx;
12648c2ecf20Sopenharmony_ci	unsigned char *tmp_buf;
12658c2ecf20Sopenharmony_ci	dma_addr_t tmp_tx_dma, tmp_rx_dma;
12668c2ecf20Sopenharmony_ci	dma_addr_t tmp_buf_dma;
12678c2ecf20Sopenharmony_ci	int i;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_descriptor_init()", 0);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	/* tx descriptor start pointer */
12728c2ecf20Sopenharmony_ci	db->tx_insert_ptr = db->first_tx_desc;
12738c2ecf20Sopenharmony_ci	db->tx_remove_ptr = db->first_tx_desc;
12748c2ecf20Sopenharmony_ci	uw32(DCR4, db->first_tx_desc_dma);	/* TX DESC address */
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* rx descriptor start pointer */
12778c2ecf20Sopenharmony_ci	db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT;
12788c2ecf20Sopenharmony_ci	db->first_rx_desc_dma =  db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT;
12798c2ecf20Sopenharmony_ci	db->rx_insert_ptr = db->first_rx_desc;
12808c2ecf20Sopenharmony_ci	db->rx_ready_ptr = db->first_rx_desc;
12818c2ecf20Sopenharmony_ci	uw32(DCR3, db->first_rx_desc_dma);	/* RX DESC address */
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/* Init Transmit chain */
12848c2ecf20Sopenharmony_ci	tmp_buf = db->buf_pool_start;
12858c2ecf20Sopenharmony_ci	tmp_buf_dma = db->buf_pool_dma_start;
12868c2ecf20Sopenharmony_ci	tmp_tx_dma = db->first_tx_desc_dma;
12878c2ecf20Sopenharmony_ci	for (tmp_tx = db->first_tx_desc, i = 0; i < TX_DESC_CNT; i++, tmp_tx++) {
12888c2ecf20Sopenharmony_ci		tmp_tx->tx_buf_ptr = tmp_buf;
12898c2ecf20Sopenharmony_ci		tmp_tx->tdes0 = cpu_to_le32(0);
12908c2ecf20Sopenharmony_ci		tmp_tx->tdes1 = cpu_to_le32(0x81000000);	/* IC, chain */
12918c2ecf20Sopenharmony_ci		tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma);
12928c2ecf20Sopenharmony_ci		tmp_tx_dma += sizeof(struct tx_desc);
12938c2ecf20Sopenharmony_ci		tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma);
12948c2ecf20Sopenharmony_ci		tmp_tx->next_tx_desc = tmp_tx + 1;
12958c2ecf20Sopenharmony_ci		tmp_buf = tmp_buf + TX_BUF_ALLOC;
12968c2ecf20Sopenharmony_ci		tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC;
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci	(--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma);
12998c2ecf20Sopenharmony_ci	tmp_tx->next_tx_desc = db->first_tx_desc;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	 /* Init Receive descriptor chain */
13028c2ecf20Sopenharmony_ci	tmp_rx_dma=db->first_rx_desc_dma;
13038c2ecf20Sopenharmony_ci	for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; i++, tmp_rx++) {
13048c2ecf20Sopenharmony_ci		tmp_rx->rdes0 = cpu_to_le32(0);
13058c2ecf20Sopenharmony_ci		tmp_rx->rdes1 = cpu_to_le32(0x01000600);
13068c2ecf20Sopenharmony_ci		tmp_rx_dma += sizeof(struct rx_desc);
13078c2ecf20Sopenharmony_ci		tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma);
13088c2ecf20Sopenharmony_ci		tmp_rx->next_rx_desc = tmp_rx + 1;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci	(--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma);
13118c2ecf20Sopenharmony_ci	tmp_rx->next_rx_desc = db->first_rx_desc;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/* pre-allocate Rx buffer */
13148c2ecf20Sopenharmony_ci	allocate_rx_buffer(dev);
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci/*
13198c2ecf20Sopenharmony_ci *	Update CR6 value
13208c2ecf20Sopenharmony_ci *	Firstly stop ULI526X, then written value and start
13218c2ecf20Sopenharmony_ci */
13228c2ecf20Sopenharmony_cistatic void update_cr6(u32 cr6_data, void __iomem *ioaddr)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	uw32(DCR6, cr6_data);
13258c2ecf20Sopenharmony_ci	udelay(5);
13268c2ecf20Sopenharmony_ci}
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci/*
13308c2ecf20Sopenharmony_ci *	Send a setup frame for M5261/M5263
13318c2ecf20Sopenharmony_ci *	This setup frame initialize ULI526X address filter mode
13328c2ecf20Sopenharmony_ci */
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
13358c2ecf20Sopenharmony_ci#define FLT_SHIFT 16
13368c2ecf20Sopenharmony_ci#else
13378c2ecf20Sopenharmony_ci#define FLT_SHIFT 0
13388c2ecf20Sopenharmony_ci#endif
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic void send_filter_frame(struct net_device *dev, int mc_cnt)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
13438c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
13448c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
13458c2ecf20Sopenharmony_ci	struct tx_desc *txptr;
13468c2ecf20Sopenharmony_ci	u16 * addrptr;
13478c2ecf20Sopenharmony_ci	u32 * suptr;
13488c2ecf20Sopenharmony_ci	int i;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "send_filter_frame()", 0);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	txptr = db->tx_insert_ptr;
13538c2ecf20Sopenharmony_ci	suptr = (u32 *) txptr->tx_buf_ptr;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	/* Node address */
13568c2ecf20Sopenharmony_ci	addrptr = (u16 *) dev->dev_addr;
13578c2ecf20Sopenharmony_ci	*suptr++ = addrptr[0] << FLT_SHIFT;
13588c2ecf20Sopenharmony_ci	*suptr++ = addrptr[1] << FLT_SHIFT;
13598c2ecf20Sopenharmony_ci	*suptr++ = addrptr[2] << FLT_SHIFT;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	/* broadcast address */
13628c2ecf20Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
13638c2ecf20Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
13648c2ecf20Sopenharmony_ci	*suptr++ = 0xffff << FLT_SHIFT;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* fit the multicast address */
13678c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev) {
13688c2ecf20Sopenharmony_ci		addrptr = (u16 *) ha->addr;
13698c2ecf20Sopenharmony_ci		*suptr++ = addrptr[0] << FLT_SHIFT;
13708c2ecf20Sopenharmony_ci		*suptr++ = addrptr[1] << FLT_SHIFT;
13718c2ecf20Sopenharmony_ci		*suptr++ = addrptr[2] << FLT_SHIFT;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	for (i = netdev_mc_count(dev); i < 14; i++) {
13758c2ecf20Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
13768c2ecf20Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
13778c2ecf20Sopenharmony_ci		*suptr++ = 0xffff << FLT_SHIFT;
13788c2ecf20Sopenharmony_ci	}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	/* prepare the setup frame */
13818c2ecf20Sopenharmony_ci	db->tx_insert_ptr = txptr->next_tx_desc;
13828c2ecf20Sopenharmony_ci	txptr->tdes1 = cpu_to_le32(0x890000c0);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* Resource Check and Send the setup packet */
13858c2ecf20Sopenharmony_ci	if (db->tx_packet_cnt < TX_DESC_CNT) {
13868c2ecf20Sopenharmony_ci		/* Resource Empty */
13878c2ecf20Sopenharmony_ci		db->tx_packet_cnt++;
13888c2ecf20Sopenharmony_ci		txptr->tdes0 = cpu_to_le32(0x80000000);
13898c2ecf20Sopenharmony_ci		update_cr6(db->cr6_data | 0x2000, ioaddr);
13908c2ecf20Sopenharmony_ci		uw32(DCR1, 0x1);	/* Issue Tx polling */
13918c2ecf20Sopenharmony_ci		update_cr6(db->cr6_data, ioaddr);
13928c2ecf20Sopenharmony_ci		netif_trans_update(dev);
13938c2ecf20Sopenharmony_ci	} else
13948c2ecf20Sopenharmony_ci		netdev_err(dev, "No Tx resource - Send_filter_frame!\n");
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci/*
13998c2ecf20Sopenharmony_ci *	Allocate rx buffer,
14008c2ecf20Sopenharmony_ci *	As possible as allocate maxiumn Rx buffer
14018c2ecf20Sopenharmony_ci */
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_cistatic void allocate_rx_buffer(struct net_device *dev)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct uli526x_board_info *db = netdev_priv(dev);
14068c2ecf20Sopenharmony_ci	struct rx_desc *rxptr;
14078c2ecf20Sopenharmony_ci	struct sk_buff *skb;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	rxptr = db->rx_insert_ptr;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	while(db->rx_avail_cnt < RX_DESC_CNT) {
14128c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb(dev, RX_ALLOC_SIZE);
14138c2ecf20Sopenharmony_ci		if (skb == NULL)
14148c2ecf20Sopenharmony_ci			break;
14158c2ecf20Sopenharmony_ci		rxptr->rx_skb_ptr = skb; /* FIXME (?) */
14168c2ecf20Sopenharmony_ci		rxptr->rdes2 = cpu_to_le32(dma_map_single(&db->pdev->dev, skb_tail_pointer(skb),
14178c2ecf20Sopenharmony_ci							  RX_ALLOC_SIZE, DMA_FROM_DEVICE));
14188c2ecf20Sopenharmony_ci		wmb();
14198c2ecf20Sopenharmony_ci		rxptr->rdes0 = cpu_to_le32(0x80000000);
14208c2ecf20Sopenharmony_ci		rxptr = rxptr->next_rx_desc;
14218c2ecf20Sopenharmony_ci		db->rx_avail_cnt++;
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	db->rx_insert_ptr = rxptr;
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci/*
14298c2ecf20Sopenharmony_ci *	Read one word data from the serial ROM
14308c2ecf20Sopenharmony_ci */
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_cistatic u16 read_srom_word(struct uli526x_board_info *db, int offset)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
14358c2ecf20Sopenharmony_ci	u16 srom_data = 0;
14368c2ecf20Sopenharmony_ci	int i;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ);
14398c2ecf20Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/* Send the Read Command 110b */
14428c2ecf20Sopenharmony_ci	srom_clk_write(db, SROM_DATA_1);
14438c2ecf20Sopenharmony_ci	srom_clk_write(db, SROM_DATA_1);
14448c2ecf20Sopenharmony_ci	srom_clk_write(db, SROM_DATA_0);
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	/* Send the offset */
14478c2ecf20Sopenharmony_ci	for (i = 5; i >= 0; i--) {
14488c2ecf20Sopenharmony_ci		srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
14498c2ecf20Sopenharmony_ci		srom_clk_write(db, srom_data);
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	for (i = 16; i > 0; i--) {
14558c2ecf20Sopenharmony_ci		uw32(DCR9, CR9_SROM_READ | CR9_SRCS | CR9_SRCLK);
14568c2ecf20Sopenharmony_ci		udelay(5);
14578c2ecf20Sopenharmony_ci		srom_data = (srom_data << 1) |
14588c2ecf20Sopenharmony_ci			    ((ur32(DCR9) & CR9_CRDOUT) ? 1 : 0);
14598c2ecf20Sopenharmony_ci		uw32(DCR9, CR9_SROM_READ | CR9_SRCS);
14608c2ecf20Sopenharmony_ci		udelay(5);
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	uw32(DCR9, CR9_SROM_READ);
14648c2ecf20Sopenharmony_ci	return srom_data;
14658c2ecf20Sopenharmony_ci}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci/*
14698c2ecf20Sopenharmony_ci *	Auto sense the media mode
14708c2ecf20Sopenharmony_ci */
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_cistatic u8 uli526x_sense_speed(struct uli526x_board_info * db)
14738c2ecf20Sopenharmony_ci{
14748c2ecf20Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
14758c2ecf20Sopenharmony_ci	u8 ErrFlag = 0;
14768c2ecf20Sopenharmony_ci	u16 phy_mode;
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	phy_mode = phy->read(db, db->phy_addr, 1);
14798c2ecf20Sopenharmony_ci	phy_mode = phy->read(db, db->phy_addr, 1);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	if ( (phy_mode & 0x24) == 0x24 ) {
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		phy_mode = ((phy->read(db, db->phy_addr, 5) & 0x01e0)<<7);
14848c2ecf20Sopenharmony_ci		if(phy_mode&0x8000)
14858c2ecf20Sopenharmony_ci			phy_mode = 0x8000;
14868c2ecf20Sopenharmony_ci		else if(phy_mode&0x4000)
14878c2ecf20Sopenharmony_ci			phy_mode = 0x4000;
14888c2ecf20Sopenharmony_ci		else if(phy_mode&0x2000)
14898c2ecf20Sopenharmony_ci			phy_mode = 0x2000;
14908c2ecf20Sopenharmony_ci		else
14918c2ecf20Sopenharmony_ci			phy_mode = 0x1000;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci		switch (phy_mode) {
14948c2ecf20Sopenharmony_ci		case 0x1000: db->op_mode = ULI526X_10MHF; break;
14958c2ecf20Sopenharmony_ci		case 0x2000: db->op_mode = ULI526X_10MFD; break;
14968c2ecf20Sopenharmony_ci		case 0x4000: db->op_mode = ULI526X_100MHF; break;
14978c2ecf20Sopenharmony_ci		case 0x8000: db->op_mode = ULI526X_100MFD; break;
14988c2ecf20Sopenharmony_ci		default: db->op_mode = ULI526X_10MHF; ErrFlag = 1; break;
14998c2ecf20Sopenharmony_ci		}
15008c2ecf20Sopenharmony_ci	} else {
15018c2ecf20Sopenharmony_ci		db->op_mode = ULI526X_10MHF;
15028c2ecf20Sopenharmony_ci		ULI526X_DBUG(0, "Link Failed :", phy_mode);
15038c2ecf20Sopenharmony_ci		ErrFlag = 1;
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	return ErrFlag;
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci/*
15118c2ecf20Sopenharmony_ci *	Set 10/100 phyxcer capability
15128c2ecf20Sopenharmony_ci *	AUTO mode : phyxcer register4 is NIC capability
15138c2ecf20Sopenharmony_ci *	Force mode: phyxcer register4 is the force media
15148c2ecf20Sopenharmony_ci */
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cistatic void uli526x_set_phyxcer(struct uli526x_board_info *db)
15178c2ecf20Sopenharmony_ci{
15188c2ecf20Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
15198c2ecf20Sopenharmony_ci	u16 phy_reg;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	/* Phyxcer capability setting */
15228c2ecf20Sopenharmony_ci	phy_reg = phy->read(db, db->phy_addr, 4) & ~0x01e0;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (db->media_mode & ULI526X_AUTO) {
15258c2ecf20Sopenharmony_ci		/* AUTO Mode */
15268c2ecf20Sopenharmony_ci		phy_reg |= db->PHY_reg4;
15278c2ecf20Sopenharmony_ci	} else {
15288c2ecf20Sopenharmony_ci		/* Force Mode */
15298c2ecf20Sopenharmony_ci		switch(db->media_mode) {
15308c2ecf20Sopenharmony_ci		case ULI526X_10MHF: phy_reg |= 0x20; break;
15318c2ecf20Sopenharmony_ci		case ULI526X_10MFD: phy_reg |= 0x40; break;
15328c2ecf20Sopenharmony_ci		case ULI526X_100MHF: phy_reg |= 0x80; break;
15338c2ecf20Sopenharmony_ci		case ULI526X_100MFD: phy_reg |= 0x100; break;
15348c2ecf20Sopenharmony_ci		}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci  	/* Write new capability to Phyxcer Reg4 */
15398c2ecf20Sopenharmony_ci	if ( !(phy_reg & 0x01e0)) {
15408c2ecf20Sopenharmony_ci		phy_reg|=db->PHY_reg4;
15418c2ecf20Sopenharmony_ci		db->media_mode|=ULI526X_AUTO;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci	phy->write(db, db->phy_addr, 4, phy_reg);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci 	/* Restart Auto-Negotiation */
15468c2ecf20Sopenharmony_ci	phy->write(db, db->phy_addr, 0, 0x1200);
15478c2ecf20Sopenharmony_ci	udelay(50);
15488c2ecf20Sopenharmony_ci}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci/*
15528c2ecf20Sopenharmony_ci *	Process op-mode
15538c2ecf20Sopenharmony_ci 	AUTO mode : PHY controller in Auto-negotiation Mode
15548c2ecf20Sopenharmony_ci *	Force mode: PHY controller in force mode with HUB
15558c2ecf20Sopenharmony_ci *			N-way force capability with SWITCH
15568c2ecf20Sopenharmony_ci */
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_cistatic void uli526x_process_mode(struct uli526x_board_info *db)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	struct uli_phy_ops *phy = &db->phy;
15618c2ecf20Sopenharmony_ci	u16 phy_reg;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	/* Full Duplex Mode Check */
15648c2ecf20Sopenharmony_ci	if (db->op_mode & 0x4)
15658c2ecf20Sopenharmony_ci		db->cr6_data |= CR6_FDM;	/* Set Full Duplex Bit */
15668c2ecf20Sopenharmony_ci	else
15678c2ecf20Sopenharmony_ci		db->cr6_data &= ~CR6_FDM;	/* Clear Full Duplex Bit */
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	update_cr6(db->cr6_data, db->ioaddr);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	/* 10/100M phyxcer force mode need */
15728c2ecf20Sopenharmony_ci	if (!(db->media_mode & 0x8)) {
15738c2ecf20Sopenharmony_ci		/* Forece Mode */
15748c2ecf20Sopenharmony_ci		phy_reg = phy->read(db, db->phy_addr, 6);
15758c2ecf20Sopenharmony_ci		if (!(phy_reg & 0x1)) {
15768c2ecf20Sopenharmony_ci			/* parter without N-Way capability */
15778c2ecf20Sopenharmony_ci			phy_reg = 0x0;
15788c2ecf20Sopenharmony_ci			switch(db->op_mode) {
15798c2ecf20Sopenharmony_ci			case ULI526X_10MHF: phy_reg = 0x0; break;
15808c2ecf20Sopenharmony_ci			case ULI526X_10MFD: phy_reg = 0x100; break;
15818c2ecf20Sopenharmony_ci			case ULI526X_100MHF: phy_reg = 0x2000; break;
15828c2ecf20Sopenharmony_ci			case ULI526X_100MFD: phy_reg = 0x2100; break;
15838c2ecf20Sopenharmony_ci			}
15848c2ecf20Sopenharmony_ci			phy->write(db, db->phy_addr, 0, phy_reg);
15858c2ecf20Sopenharmony_ci		}
15868c2ecf20Sopenharmony_ci	}
15878c2ecf20Sopenharmony_ci}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci/* M5261/M5263 Chip */
15918c2ecf20Sopenharmony_cistatic void phy_writeby_cr9(struct uli526x_board_info *db, u8 phy_addr,
15928c2ecf20Sopenharmony_ci			    u8 offset, u16 phy_data)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	u16 i;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	/* Send 33 synchronization clock to Phy controller */
15978c2ecf20Sopenharmony_ci	for (i = 0; i < 35; i++)
15988c2ecf20Sopenharmony_ci		phy_write_1bit(db, PHY_DATA_1);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	/* Send start command(01) to Phy */
16018c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
16028c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	/* Send write command(01) to Phy */
16058c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
16068c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	/* Send Phy address */
16098c2ecf20Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
16108c2ecf20Sopenharmony_ci		phy_write_1bit(db, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* Send register address */
16138c2ecf20Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
16148c2ecf20Sopenharmony_ci		phy_write_1bit(db, offset & i ? PHY_DATA_1 : PHY_DATA_0);
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	/* written trasnition */
16178c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
16188c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	/* Write a word data to PHY controller */
16218c2ecf20Sopenharmony_ci	for (i = 0x8000; i > 0; i >>= 1)
16228c2ecf20Sopenharmony_ci		phy_write_1bit(db, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistatic u16 phy_readby_cr9(struct uli526x_board_info *db, u8 phy_addr, u8 offset)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	u16 phy_data;
16288c2ecf20Sopenharmony_ci	int i;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	/* Send 33 synchronization clock to Phy controller */
16318c2ecf20Sopenharmony_ci	for (i = 0; i < 35; i++)
16328c2ecf20Sopenharmony_ci		phy_write_1bit(db, PHY_DATA_1);
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	/* Send start command(01) to Phy */
16358c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
16368c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	/* Send read command(10) to Phy */
16398c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_1);
16408c2ecf20Sopenharmony_ci	phy_write_1bit(db, PHY_DATA_0);
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	/* Send Phy address */
16438c2ecf20Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
16448c2ecf20Sopenharmony_ci		phy_write_1bit(db, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	/* Send register address */
16478c2ecf20Sopenharmony_ci	for (i = 0x10; i > 0; i = i >> 1)
16488c2ecf20Sopenharmony_ci		phy_write_1bit(db, offset & i ? PHY_DATA_1 : PHY_DATA_0);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	/* Skip transition state */
16518c2ecf20Sopenharmony_ci	phy_read_1bit(db);
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	/* read 16bit data */
16548c2ecf20Sopenharmony_ci	for (phy_data = 0, i = 0; i < 16; i++) {
16558c2ecf20Sopenharmony_ci		phy_data <<= 1;
16568c2ecf20Sopenharmony_ci		phy_data |= phy_read_1bit(db);
16578c2ecf20Sopenharmony_ci	}
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	return phy_data;
16608c2ecf20Sopenharmony_ci}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cistatic u16 phy_readby_cr10(struct uli526x_board_info *db, u8 phy_addr,
16638c2ecf20Sopenharmony_ci			   u8 offset)
16648c2ecf20Sopenharmony_ci{
16658c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
16668c2ecf20Sopenharmony_ci	u32 cr10_value = phy_addr;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	cr10_value = (cr10_value <<  5) + offset;
16698c2ecf20Sopenharmony_ci	cr10_value = (cr10_value << 16) + 0x08000000;
16708c2ecf20Sopenharmony_ci	uw32(DCR10, cr10_value);
16718c2ecf20Sopenharmony_ci	udelay(1);
16728c2ecf20Sopenharmony_ci	while (1) {
16738c2ecf20Sopenharmony_ci		cr10_value = ur32(DCR10);
16748c2ecf20Sopenharmony_ci		if (cr10_value & 0x10000000)
16758c2ecf20Sopenharmony_ci			break;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci	return cr10_value & 0x0ffff;
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_cistatic void phy_writeby_cr10(struct uli526x_board_info *db, u8 phy_addr,
16818c2ecf20Sopenharmony_ci			     u8 offset, u16 phy_data)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
16848c2ecf20Sopenharmony_ci	u32 cr10_value = phy_addr;
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	cr10_value = (cr10_value <<  5) + offset;
16878c2ecf20Sopenharmony_ci	cr10_value = (cr10_value << 16) + 0x04000000 + phy_data;
16888c2ecf20Sopenharmony_ci	uw32(DCR10, cr10_value);
16898c2ecf20Sopenharmony_ci	udelay(1);
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci/*
16928c2ecf20Sopenharmony_ci *	Write one bit data to Phy Controller
16938c2ecf20Sopenharmony_ci */
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_cistatic void phy_write_1bit(struct uli526x_board_info *db, u32 data)
16968c2ecf20Sopenharmony_ci{
16978c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	uw32(DCR9, data);		/* MII Clock Low */
17008c2ecf20Sopenharmony_ci	udelay(1);
17018c2ecf20Sopenharmony_ci	uw32(DCR9, data | MDCLKH);	/* MII Clock High */
17028c2ecf20Sopenharmony_ci	udelay(1);
17038c2ecf20Sopenharmony_ci	uw32(DCR9, data);		/* MII Clock Low */
17048c2ecf20Sopenharmony_ci	udelay(1);
17058c2ecf20Sopenharmony_ci}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci/*
17098c2ecf20Sopenharmony_ci *	Read one bit phy data from PHY controller
17108c2ecf20Sopenharmony_ci */
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_cistatic u16 phy_read_1bit(struct uli526x_board_info *db)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	void __iomem *ioaddr = db->ioaddr;
17158c2ecf20Sopenharmony_ci	u16 phy_data;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	uw32(DCR9, 0x50000);
17188c2ecf20Sopenharmony_ci	udelay(1);
17198c2ecf20Sopenharmony_ci	phy_data = (ur32(DCR9) >> 19) & 0x1;
17208c2ecf20Sopenharmony_ci	uw32(DCR9, 0x40000);
17218c2ecf20Sopenharmony_ci	udelay(1);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	return phy_data;
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_cistatic const struct pci_device_id uli526x_pci_tbl[] = {
17288c2ecf20Sopenharmony_ci	{ 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5261_ID },
17298c2ecf20Sopenharmony_ci	{ 0x10B9, 0x5263, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ULI5263_ID },
17308c2ecf20Sopenharmony_ci	{ 0, }
17318c2ecf20Sopenharmony_ci};
17328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, uli526x_pci_tbl);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(uli526x_pm_ops, uli526x_suspend, uli526x_resume);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_cistatic struct pci_driver uli526x_driver = {
17378c2ecf20Sopenharmony_ci	.name		= "uli526x",
17388c2ecf20Sopenharmony_ci	.id_table	= uli526x_pci_tbl,
17398c2ecf20Sopenharmony_ci	.probe		= uli526x_init_one,
17408c2ecf20Sopenharmony_ci	.remove		= uli526x_remove_one,
17418c2ecf20Sopenharmony_ci	.driver.pm	= &uli526x_pm_ops,
17428c2ecf20Sopenharmony_ci};
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peer Chen, peer.chen@uli.com.tw");
17458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ULi M5261/M5263 fast ethernet driver");
17468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
17498c2ecf20Sopenharmony_cimodule_param(mode, int, 0);
17508c2ecf20Sopenharmony_cimodule_param(cr6set, int, 0);
17518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "ULi M5261/M5263 enable debugging (0-1)");
17528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode, "ULi M5261/M5263: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA");
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci/*	Description:
17558c2ecf20Sopenharmony_ci *	when user used insmod to add module, system invoked init_module()
17568c2ecf20Sopenharmony_ci *	to register the services.
17578c2ecf20Sopenharmony_ci */
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_cistatic int __init uli526x_init_module(void)
17608c2ecf20Sopenharmony_ci{
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "init_module() ", debug);
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	if (debug)
17658c2ecf20Sopenharmony_ci		uli526x_debug = debug;	/* set debug flag */
17668c2ecf20Sopenharmony_ci	if (cr6set)
17678c2ecf20Sopenharmony_ci		uli526x_cr6_user_set = cr6set;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	switch (mode) {
17708c2ecf20Sopenharmony_ci	case ULI526X_10MHF:
17718c2ecf20Sopenharmony_ci	case ULI526X_100MHF:
17728c2ecf20Sopenharmony_ci	case ULI526X_10MFD:
17738c2ecf20Sopenharmony_ci	case ULI526X_100MFD:
17748c2ecf20Sopenharmony_ci		uli526x_media_mode = mode;
17758c2ecf20Sopenharmony_ci		break;
17768c2ecf20Sopenharmony_ci	default:
17778c2ecf20Sopenharmony_ci		uli526x_media_mode = ULI526X_AUTO;
17788c2ecf20Sopenharmony_ci		break;
17798c2ecf20Sopenharmony_ci	}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	return pci_register_driver(&uli526x_driver);
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci/*
17868c2ecf20Sopenharmony_ci *	Description:
17878c2ecf20Sopenharmony_ci *	when user used rmmod to delete module, system invoked clean_module()
17888c2ecf20Sopenharmony_ci *	to un-register all registered services.
17898c2ecf20Sopenharmony_ci */
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_cistatic void __exit uli526x_cleanup_module(void)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	ULI526X_DBUG(0, "uli526x_cleanup_module() ", debug);
17948c2ecf20Sopenharmony_ci	pci_unregister_driver(&uli526x_driver);
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_cimodule_init(uli526x_init_module);
17988c2ecf20Sopenharmony_cimodule_exit(uli526x_cleanup_module);
1799