📄 sunhme.c
字号:
/* $Id: sunhme.c,v 1.105 2000/12/05 02:00:36 anton Exp $ * sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching, * auto carrier detecting ethernet driver. Also known as the * "Happy Meal Ethernet" found on SunSwift SBUS cards. * * Copyright (C) 1996, 1998, 1999 David S. Miller (davem@redhat.com) * * Changes : * 2000/11/11 Willy Tarreau <willy AT meta-x.org> * - port to non-sparc architectures. Tested only on x86 and * only currently works with QFE PCI cards. * - ability to specify the MAC address at module load time by passing this * argument : macaddr=0x00,0x10,0x20,0x30,0x40,0x50 */static char *version = "sunhme.c:v1.99 12/Sep/99 David S. Miller (davem@redhat.com)\n";#include <linux/module.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ethtool.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/errno.h>#include <asm/byteorder.h>#ifdef __sparc__#include <asm/idprom.h>#include <asm/sbus.h>#include <asm/openprom.h>#include <asm/oplib.h>#include <asm/auxio.h>#ifndef __sparc_v9__#include <asm/io-unit.h>#endif#endif#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#ifdef CONFIG_PCI#include <linux/pci.h>#ifdef __sparc__#include <asm/pbm.h>#endif#endif#include "sunhme.h"static int macaddr[6];/* accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */MODULE_PARM(macaddr, "6i");static struct happy_meal *root_happy_dev = NULL;#ifdef CONFIG_SBUSstatic struct quattro *qfe_sbus_list = NULL;#endif#ifdef CONFIG_PCIstatic struct quattro *qfe_pci_list = NULL;#endif#undef HMEDEBUG#undef SXDEBUG#undef RXDEBUG#undef TXDEBUG#undef TXLOGGING#ifdef TXLOGGINGstruct hme_tx_logent { unsigned int tstamp; int tx_new, tx_old; unsigned int action;#define TXLOG_ACTION_IRQ 0x01#define TXLOG_ACTION_TXMIT 0x02#define TXLOG_ACTION_TBUSY 0x04#define TXLOG_ACTION_NBUFS 0x08 unsigned int status;};#define TX_LOG_LEN 128static struct hme_tx_logent tx_log[TX_LOG_LEN];static int txlog_cur_entry = 0;static __inline__ void tx_add_log(struct happy_meal *hp, unsigned int a, unsigned int s){ struct hme_tx_logent *tlp; unsigned long flags; save_and_cli(flags); tlp = &tx_log[txlog_cur_entry]; tlp->tstamp = (unsigned int)jiffies; tlp->tx_new = hp->tx_new; tlp->tx_old = hp->tx_old; tlp->action = a; tlp->status = s; txlog_cur_entry = (txlog_cur_entry + 1) & (TX_LOG_LEN - 1); restore_flags(flags);}static __inline__ void tx_dump_log(void){ int i, this; this = txlog_cur_entry; for (i = 0; i < TX_LOG_LEN; i++) { printk("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i, tx_log[this].tstamp, tx_log[this].tx_new, tx_log[this].tx_old, tx_log[this].action, tx_log[this].status); this = (this + 1) & (TX_LOG_LEN - 1); }}static __inline__ void tx_dump_ring(struct happy_meal *hp){ struct hmeal_init_block *hb = hp->happy_block; struct happy_meal_txd *tp = &hb->happy_meal_txd[0]; int i; for (i = 0; i < TX_RING_SIZE; i+=4) { printk("TXD[%d..%d]: [%08x:%08x] [%08x:%08x] [%08x:%08x] [%08x:%08x]\n", i, i + 4, le32_to_cpu(tp[i].tx_flags), le32_to_cpu(tp[i].tx_addr), le32_to_cpu(tp[i + 1].tx_flags), le32_to_cpu(tp[i + 1].tx_addr), le32_to_cpu(tp[i + 2].tx_flags), le32_to_cpu(tp[i + 2].tx_addr), le32_to_cpu(tp[i + 3].tx_flags), le32_to_cpu(tp[i + 3].tx_addr)); }}#else#define tx_add_log(hp, a, s) do { } while(0)#define tx_dump_log() do { } while(0)#define tx_dump_ring(hp) do { } while(0)#endif#ifdef HMEDEBUG#define HMD(x) printk x#else#define HMD(x)#endif/* #define AUTO_SWITCH_DEBUG */#ifdef AUTO_SWITCH_DEBUG#define ASD(x) printk x#else#define ASD(x)#endif#define DEFAULT_IPG0 16 /* For lance-mode only */#define DEFAULT_IPG1 8 /* For all modes */#define DEFAULT_IPG2 4 /* For all modes */#define DEFAULT_JAMSIZE 4 /* Toe jam */#ifdef CONFIG_PCI/* This happy_pci_ids is declared __initdata because it is only used as an advisory to depmod. If this is ported to the new PCI interface where it could be referenced at any time due to hot plugging, it should be changed to __devinitdata. */struct pci_device_id happymeal_pci_ids[] __initdata = { { vendor: PCI_VENDOR_ID_SUN, device: PCI_DEVICE_ID_SUN_HAPPYMEAL, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(pci, happymeal_pci_ids);#endif/* NOTE: In the descriptor writes one _must_ write the address * member _first_. The card must not be allowed to see * the updated descriptor flags until the address is * correct. I've added a write memory barrier between * the two stores so that I can sleep well at night... -DaveM */#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)static void sbus_hme_write32(unsigned long reg, u32 val){ sbus_writel(val, reg);}static u32 sbus_hme_read32(unsigned long reg){ return sbus_readl(reg);}static void sbus_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr){ rxd->rx_addr = addr; wmb(); rxd->rx_flags = flags;}static void sbus_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr){ txd->tx_addr = addr; wmb(); txd->tx_flags = flags;}static u32 sbus_hme_read_desc32(u32 *p){ return *p;}static void pci_hme_write32(unsigned long reg, u32 val){ writel(val, reg);}static u32 pci_hme_read32(unsigned long reg){ return readl(reg);}static void pci_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr){ rxd->rx_addr = cpu_to_le32(addr); wmb(); rxd->rx_flags = cpu_to_le32(flags);}static void pci_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr){ txd->tx_addr = cpu_to_le32(addr); wmb(); txd->tx_flags = cpu_to_le32(flags);}static u32 pci_hme_read_desc32(u32 *p){ return cpu_to_le32p(p);}#define hme_write32(__hp, __reg, __val) \ ((__hp)->write32((__reg), (__val)))#define hme_read32(__hp, __reg) \ ((__hp)->read32(__reg))#define hme_write_rxd(__hp, __rxd, __flags, __addr) \ ((__hp)->write_rxd((__rxd), (__flags), (__addr)))#define hme_write_txd(__hp, __txd, __flags, __addr) \ ((__hp)->write_txd((__txd), (__flags), (__addr)))#define hme_read_desc32(__hp, __p) \ ((__hp)->read_desc32(__p))#define hme_dma_map(__hp, __ptr, __size, __dir) \ ((__hp)->dma_map((__hp)->happy_dev, (__ptr), (__size), (__dir)))#define hme_dma_unmap(__hp, __addr, __size, __dir) \ ((__hp)->dma_unmap((__hp)->happy_dev, (__addr), (__size), (__dir)))#define hme_dma_sync(__hp, __addr, __size, __dir) \ ((__hp)->dma_sync((__hp)->happy_dev, (__addr), (__size), (__dir)))#else#ifdef CONFIG_SBUS/* SBUS only compilation */#define hme_write32(__hp, __reg, __val) \ sbus_writel((__val), (__reg))#define hme_read32(__hp, __reg) \ sbus_readl(__reg)#define hme_write_rxd(__hp, __rxd, __flags, __addr) \do { (__rxd)->rx_addr = (__addr); \ wmb(); \ (__rxd)->rx_flags = (__flags); \} while(0)#define hme_write_txd(__hp, __txd, __flags, __addr) \do { (__txd)->tx_addr = (__addr); \ wmb(); \ (__txd)->tx_flags = (__flags); \} while(0)#define hme_read_desc32(__hp, __p) (*(__p))#define hme_dma_map(__hp, __ptr, __size, __dir) \ sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))#define hme_dma_unmap(__hp, __addr, __size, __dir) \ sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))#define hme_dma_sync(__hp, __addr, __size, __dir) \ sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))#else/* PCI only compilation */#define hme_write32(__hp, __reg, __val) \ writel((__val), (__reg))#define hme_read32(__hp, __reg) \ readl(__reg)#define hme_write_rxd(__hp, __rxd, __flags, __addr) \do { (__rxd)->rx_addr = cpu_to_le32(__addr); \ wmb(); \ (__rxd)->rx_flags = cpu_to_le32(__flags); \} while(0)#define hme_write_txd(__hp, __txd, __flags, __addr) \do { (__txd)->tx_addr = cpu_to_le32(__addr); \ wmb(); \ (__txd)->tx_flags = cpu_to_le32(__flags); \} while(0)#define hme_read_desc32(__hp, __p) cpu_to_le32p(__p)#define hme_dma_map(__hp, __ptr, __size, __dir) \ pci_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))#define hme_dma_unmap(__hp, __addr, __size, __dir) \ pci_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))#define hme_dma_sync(__hp, __addr, __size, __dir) \ pci_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))#endif#endif#ifdef SBUS_DMA_BIDIRECTIONAL# define DMA_BIDIRECTIONAL SBUS_DMA_BIDIRECTIONAL#else# define DMA_BIDIRECTIONAL 0#endif#ifdef SBUS_DMA_FROMDEVICE# define DMA_FROMDEVICE SBUS_DMA_FROMDEVICE#else# define DMA_TODEVICE 1#endif#ifdef SBUS_DMA_TODEVICE# define DMA_TODEVICE SBUS_DMA_TODEVICE#else# define DMA_FROMDEVICE 2#endif/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */static void BB_PUT_BIT(struct happy_meal *hp, unsigned long tregs, int bit){ hme_write32(hp, tregs + TCVR_BBDATA, bit); hme_write32(hp, tregs + TCVR_BBCLOCK, 0); hme_write32(hp, tregs + TCVR_BBCLOCK, 1);}#if 0static u32 BB_GET_BIT(struct happy_meal *hp, unsigned long tregs, int internal){ u32 ret; hme_write32(hp, tregs + TCVR_BBCLOCK, 0); hme_write32(hp, tregs + TCVR_BBCLOCK, 1); ret = hme_read32(hp, tregs + TCVR_CFG); if (internal) ret &= TCV_CFG_MDIO0; else ret &= TCV_CFG_MDIO1; return ret;}#endifstatic u32 BB_GET_BIT2(struct happy_meal *hp, unsigned long tregs, int internal){ u32 retval; hme_write32(hp, tregs + TCVR_BBCLOCK, 0); udelay(1); retval = hme_read32(hp, tregs + TCVR_CFG); if (internal) retval &= TCV_CFG_MDIO0; else retval &= TCV_CFG_MDIO1; hme_write32(hp, tregs + TCVR_BBCLOCK, 1); return retval;}#define TCVR_FAILURE 0x80000000 /* Impossible MIF read value */static int happy_meal_bb_read(struct happy_meal *hp, unsigned long tregs, int reg){ u32 tmp; int retval = 0; int i; ASD(("happy_meal_bb_read: reg=%d ", reg)); /* Enable the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 1); /* Force BitBang into the idle state. */ for (i = 0; i < 32; i++) BB_PUT_BIT(hp, tregs, 1); /* Give it the read sequence. */ BB_PUT_BIT(hp, tregs, 0); BB_PUT_BIT(hp, tregs, 1); BB_PUT_BIT(hp, tregs, 1); BB_PUT_BIT(hp, tregs, 0); /* Give it the PHY address. */ tmp = hp->paddr & 0xff; for (i = 4; i >= 0; i--) BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1)); /* Tell it what register we want to read. */ tmp = (reg & 0xff); for (i = 4; i >= 0; i--) BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1)); /* Close down the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 0); /* Now read in the value. */ (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); for (i = 15; i >= 0; i--) retval |= BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); ASD(("value=%x\n", retval)); return retval;}static void happy_meal_bb_write(struct happy_meal *hp, unsigned long tregs, int reg, unsigned short value){ u32 tmp; int i; ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value)); /* Enable the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 1); /* Force BitBang into the idle state. */ for (i = 0; i < 32; i++) BB_PUT_BIT(hp, tregs, 1); /* Give it write sequence. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -