📄 ax88796b.c
字号:
/*
==================================================================================
ax88796b.c: A SAMSUNG S3C2440 Linux2.6.x Ethernet device driver for ASIX AX88796B chips.
This program is free software; you can distrine_block_inputbute it and/or modify it under
the terms of the GNU General Public License (Version 2) as published by the Free Software
Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
This program is based on
ne.c: A general non-shared-memory NS8390 ethernet driver for linux
Written 1992-94 by Donald Becker.
8390.c: A general NS8390 ethernet driver core for linux.
Written 1992-94 by Donald Becker.
Version history:
Version 1.0.0 06/02/2006
* Initial release
Version 1.0.0 27/03/2006
* Fixups Transmit Queue functions.
Version 1.1.0 20/09/2006
* Ported to support kernel 2.6.x.
Version 1.2.0 09/09/2008
* 1. Added 8-bit support.
* 2. Added EEPROM support.
* 3. Added PHY power process function.
==================================================================================
Driver Overview
==================================================================================
ASIX AX88796B 3-in-1 Local Bus 8/16-bit Fast Ethernet Linux Driver
The AX88796B Ethernet controller is a high performance and highly integrated
local CPU bus Ethernet controllers with embedded 10/100Mbps PHY/Transceiver
16K bytes SRAM and supports both 8-bit and 16-bit local CPU interfaces for any
embedded systems.
If you look for more details, please visit ASIX's web site (http://www.asix.com.tw).
==================================================================================
COMPILING DRIVER
==================================================================================
Prepare:
AX88796B Linux Driver.
Linux Kernel source code.
Cross-Compiler.
Getting Start:
1.Extracting the AX88796B source file by executing the following command.
[root@localhost]# tar jxvf ax88796b-arm-linux2.4.tar.bz2
2.Edit the makefile to specifie the path of target platform Linux Kernel source.
EX:
KDIR := /work/linux-2.6.17.11
3.Executing 'make' command to compiler AX88796B Driver.
4.If the compilation well, the ax88796.ko will be created under the current directory.
==================================================================================
DRIVER PARAMETERS
==================================================================================
The following parameters can be set when using insmod.
EX: [root@localhost ax88796b]# insmod ax88796.ko mem=0x08000000
mem=0xNNNNNNNN
specifies the physical base address that AX88796B can be accessed.
Default value '0x08000000'.
irq=N
specifies the irq number. Default value '0x27'.
media=(0=auto, 1=100full, 2=100half, 3=10full, 4=10half)
Media mode control. Default value 'auto'.
*/
#include "ax88796b.h"
/* Local Function Prototypes */
static int ax_probe (struct net_device *dev);
static int ax_open (struct net_device *dev);
static int ax_close (struct net_device *dev);
static void ax_reset (struct net_device *dev);
static void ax_get_hdr (struct net_device *dev, struct ax_pkt_hdr *hdr,
int ring_page);
static void ax_block_input (struct net_device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void ax_block_output (struct net_device *dev, const int count,
const unsigned char *buf, const int start_page);
static int ethdev_init (struct net_device *dev);
static void ax_init (struct net_device *dev, int startp);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
# ifdef CONFIG_BOARD_S3C2440_SMDK
int set_external_irq (int irq, int edge, int pullup);
# endif
static void ax_interrupt (int irq, void *dev_id, struct pt_regs * regs);
#else
static irqreturn_t ax_interrupt (int irq, void *dev_id, struct pt_regs * regs);
#endif
static void ax_tx_intr (struct net_device *dev);
static void ax_tx_err (struct net_device *dev);
static void ax_receive (struct net_device *dev);
static void ax_rx_overrun (struct net_device *dev);
static void ax_trigger_send (struct net_device *dev, unsigned int length, int start_page);
static void set_multicast_list (struct net_device *dev);
static void do_set_multicast_list (struct net_device *dev);
static void ax_watchdog (unsigned long arg);
static void ax_vlan_rx_add_vid (struct net_device *netdev, u16 vid);
static void ax_vlan_rx_kill_vid (struct net_device *netdev, u16 vid);
static int mdio_read (struct net_device *dev, int phy_id, int loc);
static void mdio_write (struct net_device *dev, int phy_id, int loc, int value);
/* NAMING CONSTANT DECLARATIONS */
#define DRV_NAME "AX88796B"
#define ADP_NAME "ASIX AX88796B Ethernet Adapter"
#define DRV_VERSION "1.2.0"
#define PFX DRV_NAME ": "
#define PRINTK(flag, args...) if (flag & DEBUG_FLAGS) printk(args)
#define CONFIG_AX88796B_USE_MEMCPY 0
#define CONFIG_AX88796B_8BIT_WIDE 0
#define CONFIG_AX88796B_EEPROM_READ_WRITE 0
/* LOCAL VARIABLES DECLARATIONS */
static char version[] __devinitdata =
KERN_INFO ADP_NAME ":v" DRV_VERSION " " __TIME__ " " __DATE__ "\n"
KERN_INFO " http://www.asix.com.tw\n";
static unsigned int media = 0;
#ifdef MODULE
static struct net_device dev_ax;
static int mem;
static int irq;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MODULE_PARM (mem, "i");
MODULE_PARM (irq, "i");
MODULE_PARM (media, "i");
#else
module_param (mem, int, 0);
module_param (irq, int, 0);
module_param (media, int, 0);
#endif
MODULE_PARM_DESC (mem, "MEMORY base address(es),required");
MODULE_PARM_DESC (irq, "IRQ number(s)");
MODULE_PARM_DESC (media, "Media Mode(0=auto, 1=100full, 2=100half, 3=10full, 4=10half)");
MODULE_DESCRIPTION ("ASIX AX88796B Fast Ethernet driver");
MODULE_LICENSE ("GPL");
/*
* ----------------------------------------------------------------------------
* Function Name: init_module
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
int init_module (void)
{
struct net_device *dev = &dev_ax;
dev->irq = irq;
dev->base_addr = mem;
dev->init = ax_probe;
if (register_netdev (dev) == 0)
return 0;
if (mem != 0) {
PRINTK (WARNING_MSG, PFX " No AX88796B card found at memory = %#x\n", mem);
}
else {
PRINTK (WARNING_MSG, PFX " You must supply \"mem=0xNNNNNNN\" value(s) for AX88796B.\n");
}
return -ENXIO;
}
/*
* ----------------------------------------------------------------------------
* Function Name: cleanup_module
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
void cleanup_module (void)
{
struct net_device *dev = &dev_ax;
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
free_irq (dev->irq, dev);
iounmap (ax_base);
unregister_netdev (dev);
kfree (dev->priv);
dev->priv = NULL;
}
#endif /* MODULE */
#if (CONFIG_AX88796B_8BIT_WIDE == 1)
static inline u16 READ_FIFO (void *membase)
{
return (readb (membase) | (((u16)readb (membase)) << 8));
}
static inline void WRITE_FIFO (void *membase, u16 data)
{
writeb ((u8)data , membase);
writeb ((u8)(data >> 8) , membase);
}
#else
static inline u16 READ_FIFO (void *membase)
{
return readw (membase);
}
static inline void WRITE_FIFO (void *membase, u16 data)
{
writew (data, membase);
}
#endif
/*
* ----------------------------------------------------------------------------
* Function Name: config_2440_bank1
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void config_2440_bank1 (void)
{
/* Configure SAMSUNG S3C2440A controller for AX88796B operation */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
# ifdef CONFIG_BOARD_S3C2440_SMDK
# if (CONFIG_AX88796B_8BIT_WIDE == 1)
BWSCON = BWSCON & 0xFFFFFFCF;
# else
BWSCON = (BWSCON & 0xFFFFFFCF) | 0x00000010;
# endif
BANKCON1 = DEFAULT_125MHZ_BANKCON1;
set_external_irq (IRQ_EINT11, EXT_LOWLEVEL, GPIO_PULLUP_DIS);
EINTMASK &= ~EINT11_MASK;
EXTINT1 &= ~0xf000;
EXTINT1 |= FLTEN11_LOWLEVEL;
# endif /* CONFIG_BOARD_S3C2440_SMDK */
#else
# ifdef CONFIG_ARCH_S3C2410
{
unsigned long tmp;
tmp = __raw_readl (S3C2410_BWSCON);
# if (CONFIG_AX88796B_8BIT_WIDE == 1)
__raw_writel ((tmp & ~0x30) | S3C2410_BWSCON_DW1_8, S3C2410_BWSCON);
# else
__raw_writel ((tmp & ~0x30) | S3C2410_BWSCON_DW1_16, S3C2410_BWSCON);
# endif
__raw_writel (S3C2410_BANKCON_Tcah1 | S3C2410_BANKCON_Tacc8, S3C2410_BANKCON1);
}
# endif /* CONFIG_ARCH_S3C2410 */
#endif
}
/*
* ----------------------------------------------------------------------------
* Function Name: load_macaddr
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void
load_macaddr (struct net_device *dev, unsigned char *pMac)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
int i;
/* Read the 12 bytes of station address PROM. */
{
struct {unsigned char value, offset; } program_seq[] =
{
{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
{ax_local->bus_width, EN0_DCFG}, /* Set wide access. */
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR}, /* Mask completion irq. */
{0xFF, EN0_ISR},
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
{0x0C, EN0_RCNTLO}, /* 12 bytes of station address */
{0x00, EN0_RCNTHI},
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
{0x00, EN0_RSARHI},
{E8390_RREAD+E8390_START, E8390_CMD},
};
for (i = 0; i < sizeof (program_seq)/sizeof (program_seq[0]); i++)
writeb (program_seq[i].value, ax_base + program_seq[i].offset);
}
while (( readb (ax_base + EN0_SR) & 0x20) == 0);
for (i = 0; i < 6; i++) {
pMac[i] = (unsigned char) READ_FIFO (ax_base + EN0_DATAPORT);
}
writeb (ENISR_RDC, ax_base + EN0_ISR); /* Ack intr. */
}
/*
* ----------------------------------------------------------------------------
* Function Name: ax_probe
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static int ax_probe (struct net_device *dev)
{
int i;
int reg0, ret;
unsigned long base_addr;
void *address;
struct ax_device *ax_local;
PRINTK (INIT_MSG, PFX " probe start ..........\n");
SET_MODULE_OWNER (dev);
if (dev->base_addr == 0 )
dev->base_addr = AX88796B_BASE;
base_addr = dev->base_addr;
if (check_mem_region (base_addr , NE_IO_EXTENT))
return -ENODEV;
if (! request_mem_region (base_addr, NE_IO_EXTENT, DRV_NAME))
return -EBUSY;
address=ioremap_nocache (base_addr, NE_IO_EXTENT);
if (!address) {
PRINTK (ERROR_MSG, PFX " Unable to remap memory\n");
return -EBUSY;
}
config_2440_bank1 ();
reg0 = readb (address);
if (reg0 == 0xFF) {
ret = -ENODEV;
goto err_out;
}
/* Do a preliminary verification that we have a 8390. */
{
int regd;
writeb (E8390_NODMA+E8390_PAGE1+E8390_STOP, address + E8390_CMD);
regd = readb (address + EN0_COUNTER0);
writeb (0xff, address + EN0_COUNTER0);
writeb (E8390_NODMA+E8390_PAGE0, address + E8390_CMD);
readb (address + EN0_COUNTER0); /* Clear the counter by reading. */
if (readb (address + EN0_COUNTER0) != 0) {
writeb (reg0, address);
writeb (regd, address + EN0_COUNTER0); /* Restore the old values. */
ret = -ENODEV;
goto err_out;
}
}
printk (version);
/* Reset card. Who knows what dain-bramaged state it was left in. */
{
unsigned long reset_start_time = jiffies;
readb (address + EN0_RESET);
while ((readb (address + EN0_ISR) & ENISR_RESET) == 0) {
if (jiffies - reset_start_time > 2*HZ/100) {
PRINTK (ERROR_MSG, " not found (no reset ack).\n");
ret = -ENODEV;
goto err_out;
}
}
writeb (0xff, (address + EN0_ISR)); /* Ack all intr. */
}
if (dev->irq == 0)
dev->irq =IRQ_EINT11;
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init (dev))
{
PRINTK (ERROR_MSG, "unable to get memory for dev->priv.\n");
ret = -ENOMEM;
goto err_out;
}
ax_local = (struct ax_device *)dev->priv;
ax_local->name = DRV_NAME;
ax_local->membase = address;
ax_local->tx_start_page = NESM_START_PG;
ax_local->rx_start_page = NESM_RX_START_PG;
ax_local->stop_page = NESM_STOP_PG;
ax_local->media = media;
#if (CONFIG_AX88796B_8BIT_WIDE == 1)
ax_local->bus_width = 0;
#else
ax_local->bus_width = 1;
#endif
load_macaddr (dev, dev->dev_addr);
/* Support for No EEPROM */
if ( (dev->dev_addr[0] == 0) && (dev->dev_addr[1] == 0) &&
(dev->dev_addr[2] == 0) && (dev->dev_addr[3] == 0) &&
(dev->dev_addr[4] == 0) && (dev->dev_addr[5] == 0) )
{
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x88;
dev->dev_addr[2] = 0x88;
dev->dev_addr[3] = 0x77;
dev->dev_addr[4] = 0x99;
dev->dev_addr[5] = 0x66;
}
PRINTK (DRIVER_MSG, "%s: MAC ADDRESS ",DRV_NAME);
for (i = 0; i < ETHER_ADDR_LEN; i++) {
PRINTK (DRIVER_MSG, " %2.2x", dev->dev_addr[i]);
}
PRINTK (DRIVER_MSG, "\n%s: %s found at 0x%x, using IRQ %d.\n",
dev->name, DRV_NAME, AX88796B_BASE, dev->irq);
dev->open = &ax_open;
dev->stop = &ax_close;
dev->features |= NETIF_F_HW_VLAN_FILTER;
dev->vlan_rx_add_vid = ax_vlan_rx_add_vid;
dev->vlan_rx_kill_vid = ax_vlan_rx_kill_vid;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -