📄 sunhme.c
字号:
/* 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, 2002, 2003, 2006 David S. Miller (davem@davemloft.net) * * 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 */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/crc32.h>#include <linux/random.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/mm.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#ifdef CONFIG_SPARC#include <asm/idprom.h>#include <asm/sbus.h>#include <asm/openprom.h>#include <asm/oplib.h>#include <asm/prom.h>#include <asm/auxio.h>#endif#include <asm/uaccess.h>#include <asm/pgtable.h>#include <asm/irq.h>#ifdef CONFIG_PCI#include <linux/pci.h>#endif#include "sunhme.h"#define DRV_NAME "sunhme"#define DRV_VERSION "3.00"#define DRV_RELDATE "June 23, 2006"#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"static char version[] = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";MODULE_VERSION(DRV_VERSION);MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION("Sun HappyMealEthernet(HME) 10/100baseT ethernet driver");MODULE_LICENSE("GPL");static int macaddr[6];/* accept MAC address of the form macaddr=0x08,0x00,0x20,0x30,0x40,0x50 */module_param_array(macaddr, int, NULL, 0);MODULE_PARM_DESC(macaddr, "Happy Meal MAC address to set");#ifdef CONFIG_SBUSstatic struct quattro *qfe_sbus_list;#endif#ifdef CONFIG_PCIstatic struct quattro *qfe_pci_list;#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;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 *//* 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(void __iomem *reg, u32 val){ sbus_writel(val, reg);}static u32 sbus_hme_read32(void __iomem *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(void __iomem *reg, u32 val){ writel(val, reg);}static u32 pci_hme_read32(void __iomem *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_for_cpu(__hp, __addr, __size, __dir) \ ((__hp)->dma_sync_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)))#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ ((__hp)->dma_sync_for_device((__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_for_cpu(__hp, __addr, __size, __dir) \ sbus_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ sbus_dma_sync_single_for_device((__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_for_cpu(__hp, __addr, __size, __dir) \ pci_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ pci_dma_sync_single_for_device((__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, void __iomem *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, void __iomem *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, void __iomem *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, void __iomem *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, void __iomem *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. */ BB_PUT_BIT(hp, tregs, 0); BB_PUT_BIT(hp, tregs, 1); BB_PUT_BIT(hp, tregs, 0); BB_PUT_BIT(hp, tregs, 1); /* Give it the PHY address. */ tmp = (hp->paddr & 0xff);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -