/* fa31x.c -- NETGEAR FA311/FA312 PCI Adapter Driver */
/* fa31x.c: Version 2.20 07/16/2001
*
* NETGEAR FA311/FA312 PCI Adaper Driver for Linux
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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.
*/
static const char *version = "fa31x.c: NETGEAR FA311/FA312 PCI Adapter v2.20\n";
/* Generic Kernel Module/Driver Headers */
#ifdef MODVERSIONS
#include
#endif
#include
#include
#include
#include
#include /* IO stuff */
#include
#include
/* Ethernet Driver Specific Headers */
#include
#include
#include
#include
#include
#include "fa31x.h"
/* Macros */
/*
* SWAP_BUS_TO_CPU_XX and SWAP_CPU_TO_BUS_XX macros swap 16 and 32 bit values
* between the PCI bus' little endian byte-order and the CPU's native
* byte-order.
*/
#ifdef __BIG_ENDIAN
#define BUS_TO_CPU_SWAP_16(X) swap_16(X)
#define BUS_TO_CPU_SWAP_32(X) swap_32(X)
#define CPU_TO_BUS_SWAP_16(X) swap_16(X)
#define CPU_TO_BUS_SWAP_32(X) swap_32(X)
#define CPU_TO_NET_SWAP_16(X) ((u16)(X))
#define CPU_TO_NET_SWAP_32(X) ((u32)(X))
#define NET_TO_CPU_SWAP_16(X) ((u16)(X))
#define NET_TO_CPU_SWAP_32(X) ((u32)(X))
#else
#define BUS_TO_CPU_SWAP_32(X) ((u32)(X))
#define BUS_TO_CPU_SWAP_16(X) ((u16)(X))
#define CPU_TO_BUS_SWAP_32(X) ((u32)(X))
#define CPU_TO_BUS_SWAP_16(X) ((u16)(X))
#define CPU_TO_NET_SWAP_16(X) swap_16(X)
#define CPU_TO_NET_SWAP_32(X) swap_32(X)
#define NET_TO_CPU_SWAP_16(X) swap_16(X)
#define NET_TO_CPU_SWAP_32(X) swap_32(X)
#endif
/* Macros translate addresses between PCI bus and CPU */
#define BUS_TO_CPU_ADDR_XLATE(X) BUS_TO_CPU_SWAP_32(bus_to_virt(X))
#define CPU_TO_BUS_ADDR_XLATE(X) CPU_TO_BUS_SWAP_32(virt_to_bus(X))
/* Macros to read/write 32/16 bit data to/from FA311/FA312 device registers. */
#define DP_REG32_WRITE(reg, val) io_write_32 (iobase+(reg), \
CPU_TO_BUS_SWAP_32(val))
#define DP_REG32_READ(reg) BUS_TO_CPU_SWAP_32(io_read_32 (iobase+(reg)))
#define DP_REG16_WRITE(reg, val) io_write_16 (iobase+(reg), \
CPU_TO_BUS_SWAP_16(val))
#define DP_REG16_READ(reg) BUS_TO_CPU_SWAP_16(io_read_16 (iobase+(reg)))
#define DP_REG32_SET(reg, val) DP_REG32_WRITE(reg,DP_REG32_READ(reg)|(val))
#define DP_REG32_CLR(reg, val) DP_REG32_WRITE(reg,DP_REG32_READ(reg)&~(val))
#define DP_REG16_SET(reg, val) DP_REG16_WRITE(reg,DP_REG16_READ(reg)|(val))
#define DP_REG16_CLR(reg, val) DP_REG16_WRITE(reg,DP_REG16_READ(reg)&~(val))
/* Debug Macros */
#define DP_DEBUG_PROBE 0x00000001
#define DP_DEBUG_OPEN 0x00000002
#define DP_DEBUG_CLOSE 0x00000004
#define DP_DEBUG_IOCTL 0x00000008
#define DP_DEBUG_TX 0x00000010
#define DP_DEBUG_RX 0x00000020
#define DP_DEBUG_MC 0x00000040
#define DP_DEBUG_ANEG 0x00000080
#define DP_DEBUG_INTR 0x00000100
#define DP_DEBUG_LOAD 0x00000200
#define DP_DEBUG_UNLOAD 0x00000400
#ifdef DEBUG
u32 dp_debug_level=DEBUG;
#define DP_DEBUG(level, X) if (level & dp_debug_level) printk X
#else
#define DP_DEBUG(level, X)
#endif
/* data types */
typedef u32 status; /* return status */
typedef volatile u8 * virt_addr; /* CPU virtual address */
typedef volatile u8 * bus_addr; /* BUS physical address */
typedef u8 bool;
#define OK 0 /* status: OK */
#define ERROR -1 /* status: ERROR */
#define TRUE 1
#define FALSE 0
/* Default Driver Parameters */
#define DP_DEFAULT_TXQ_SIZE 10
#define DP_DEFAULT_RXQ_SIZE 30
#define DP_RX_COPY_THRESHOLD 128 /* upper limit for rx packet copy */
#define DP_POLYNOMIAL 0x04C11DB6
/* Alignment and packet sizes */
#define ETH_CRC_LEN 4
#define ETH_MAX_PKT_SIZE (ETH_FRAME_LEN + ETH_CRC_LEN)
#define DP_ALIGN 4 /* word alignment */
/* Driver private descriptor macros */
#define DP_DESC_SKBPTR 0x0c /* SKB pointer offset */
#define DP_QUEUE_ELE_SIZE (DP_DESC_SIZE + 4)
#define DP_QUEUE_ELE_NEXT(q) \
q->cur_desc_addr = DP_QUEUE_ELE_NEXT_GET(q, q->cur_desc_addr)
#define DP_QUEUE_ELE_NEXT_GET(q, desc_addr) \
((desc_addr) == (q)->last_desc_addr) ? (q)->first_desc_addr : \
(desc_addr) + DP_QUEUE_ELE_SIZE
/* Macros to get/set the values of the descriptor fields */
#define DP_DESC_LNK_GET(ptr) *(u32 *)((virt_addr)ptr + DP_DESC_LNK)
#define DP_DESC_CMDSTS_GET(ptr) *(u32 *)((virt_addr)ptr + DP_DESC_CMDSTS)
#define DP_DESC_BUFPTR_GET(ptr) *(u32 *)((virt_addr)ptr + DP_DESC_BUFPTR)
#define DP_DESC_SKBPTR_GET(ptr) *(u32 *)((virt_addr)ptr + DP_DESC_SKBPTR)
#define DP_DESC_LNK_SET(ptr, val) DP_DESC_LNK_GET(ptr) = (val)
#define DP_DESC_CMDSTS_SET(ptr, val) DP_DESC_CMDSTS_GET(ptr) = (val)
#define DP_DESC_BUFPTR_SET(ptr,val) DP_DESC_BUFPTR_GET(ptr) = (val)
#define DP_DESC_SKBPTR_SET(ptr,val) DP_DESC_SKBPTR_GET(ptr) = (val)
/*
* Macros to get/set the values of descriptor fields with
* appropriate address and byte order translations
*/
#define DP_DESC_LNK_XLATE_GET(p) BUS_TO_CPU_ADDR_XLATE(DP_DESC_LNK_GET(p))
#define DP_DESC_CMDSTS_XLATE_GET(p) BUS_TO_CPU_SWAP_32(DP_DESC_CMDSTS_GET(p))
#define DP_DESC_BUFPTR_XLATE_GET(p) BUS_TO_CPU_ADDR_XLATE(DP_DESC_BUFPTR_GET(p))
#define DP_DESC_LNK_XLATE_SET(p, v) \
DP_DESC_LNK_SET(p, CPU_TO_BUS_ADDR_XLATE(v))
#define DP_DESC_CMDSTS_XLATE_SET(p, v) \
DP_DESC_CMDSTS_SET(p, CPU_TO_BUS_SWAP_32(v))
#define DP_DESC_BUFPTR_XLATE_SET(p,v) \
DP_DESC_BUFPTR_SET(p, CPU_TO_BUS_ADDR_XLATE(v))
/* Descriptor queue */
struct fa31x_queue {
virt_addr first_desc_addr; /* descriptor array address */
virt_addr last_desc_addr; /* last descriptor address */
virt_addr cur_desc_addr; /* current descriptor address */
virt_addr qbuf; /* allocated queue buffer */
u16 count; /* number of elements */
};
/* Queue types -- qtype */
#define DP_QUEUE_TYPE_TX 1 /* Transmit queue */
#define DP_QUEUE_TYPE_RX 2 /* Receive queue */
/* Device private data */
struct fa31x_priv {
struct device * next; /* Next fa31xdevice */
struct fa31x_queue tx_queue; /* Transmit Descriptor Queue */
struct fa31x_queue rx_queue; /* Receive Descriptor Queue */
struct enet_statistics stats; /* MIB data */
};
static struct device * fa31x_dev_list; /* List of fa31xdevices */
/* Linux Network Driver interface routines */
extern int fa31x_probe (struct device *dev);
static int fa31x_open (struct device *dev);
static int fa31x_close (struct device *dev);
static int fa31x_start_xmit (struct sk_buff *skb, struct device *dev);
static void fa31x_set_multicast_list (struct device *dev);
static int fa31x_ioctl (struct device *dev, struct ifreq *rq, int cmd);
static void fa31x_interrupt (int irq, void *dev_id, struct pt_regs *regs);
static struct enet_statistics *fa31x_get_stats (struct device *dev);
/* Support Functions */
/*static u16 swap_16 (u16 us);*/
/*static u32 swap_32 (u32 ui);*/
static u32 io_read_32 (u16 io_port);
static void io_write_32 (u16 io_port, u32 data);
static u16 io_read_16 (u16 io_port);
/*static void io_write_16 (u16 io_port, u16 data);*/
/* Driver Private Routines */
static void fa31x_mac_address_set (u32 iobase, char *mac_addr);
static void fa31x_mac_address_get (u32 iobase, char *mac_addr);
static status fa31x_dev_reset (u32 iobase);
static status fa31x_queue_create (struct fa31x_queue *q, int count, int qtype);
static status fa31x_queue_delete (struct fa31x_queue *q);
static virt_addr fa31x_tx_desc_get (struct device *dev);
static virt_addr fa31x_rx_desc_get (struct device *dev);
static status fa31x_phy_setup (struct device *dev);
static int fa31x_crc (char * mc_addr);
static void fa31x_tx_skb_reclaim (struct device *dev, virt_addr desc_addr);
static void ReadEEWord(u16 iobase,u32 wordOffset,u16* pEeData);
/* Driver Debug Routines */
#ifdef DEBUG
static void fa31x_regs_info (u16 iobase);
#endif
/*
* fa31x_probe - enumerate the PCI bus for instances of FA311/FA312
*
* This routine enumerates FA311/FA312 ethernet devices on the PCI bus, and
* registers each FA311/FA312 device found.
*/
int
fa31x_probe (struct device *dev)
{
int dev_count;
u8 bus;
u8 func;
u8 irq;
u32 iobase;
u32 version;
if (! pcibios_present())
return -ENODEV; /* No such device */
dev_count=0;
while (pcibios_find_device (PCI_VENDOR_ID_NS, PCI_VENDOR_ID_NS_83815,
dev_count, &bus, &func)
== PCIBIOS_SUCCESSFUL) {
/* Allocate, name the device and add to ethernet device list */
dev = init_etherdev(dev, sizeof (struct fa31x_priv));
if (dev == NULL) {
printk (KERN_INFO "%s: Failed to create device struct\n",
DP_DRV_NAME);
break;
}
/* Read PCI Configuration Registers */
pcibios_read_config_byte (bus, func, PCI_INTERRUPT_LINE, &irq);
pcibios_read_config_dword (bus, func, PCI_BASE_ADDRESS_0, &iobase);
iobase &= PCI_BASE_ADDRESS_IO_MASK;
/* Put the device in a quiescent state */
if (fa31x_dev_reset (iobase) != OK) {
printk (KERN_INFO "%s: Device Reset failed.\n",
DP_DRV_NAME);
kfree (dev);
continue; /* Try the next device */
}
DP_REG32_WRITE(DP_CCSR, 0x00008000);
DP_REG32_WRITE(DP_WCSR, 0x00000000);
/* Get the ethernet address */
fa31x_mac_address_get (iobase, dev->dev_addr);
/* If address is not set, set up a default one */
/* XXX needed only for pre-release versions of the hardware */
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)) {
u32 random = jiffies;
u8 * ptr = (u8 *) &random;
dev->dev_addr[0] = 0x08; /* National's Ethernet ID 0 */
// dev->dev_addr[1] = 0x00; /* National's Ethernet ID 1 */
// dev->dev_addr[2] = 0x17; /* National's Ethernet ID 2 */
dev->dev_addr[3] = *ptr++;
dev->dev_addr[4] = *ptr++;
dev->dev_addr[5] = *ptr;
}
/* initialize the device data */
dev->base_addr = iobase;
dev->irq = irq;
dev->open = fa31x_open;
dev->stop = fa31x_close;
dev->get_stats = fa31x_get_stats;
dev->do_ioctl = fa31x_ioctl;
dev->hard_start_xmit = fa31x_start_xmit;
dev->set_multicast_list = fa31x_set_multicast_list;
ether_setup (dev); /* initialize generic fields */
/* reserve IO region */
request_region (iobase, FA31x_PCI_IO_SIZE, dev->name);
/* Display board info */
version = DP_REG32_READ(DP_SRR);
printk (KERN_INFO "%s: bus=%d func=%d io=0x%x irq=%d ver=%d.%d\n",
dev->name, bus, func, iobase, irq,
(version & DP_SRR_MAJ) >> DP_SRR_MAJ_SHF,
(version & DP_SRR_MIN));
/* If address is not set, set up a default one */
/* XXX needed only for pre-release versions of the hardware */
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)) {
u32 random = jiffies;
u8 * ptr = (u8 *) &random;
dev->dev_addr[0] = 0x08; /* National's Ethernet ID 0 */
dev->dev_addr[1] = 0x00; /* National's Ethernet ID 1 */
dev->dev_addr[2] = 0x17; /* National's Ethernet ID 2 */
dev->dev_addr[3] = *ptr++;
dev->dev_addr[4] = *ptr++;
dev->dev_addr[5] = *ptr;
}
printk (KERN_INFO "%s: ethernet addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
dev->name,
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
/* Chain the device */
((struct fa31x_priv *)(dev->priv))->next = fa31x_dev_list;
fa31x_dev_list = dev;
#ifdef DEBUG
printk (KERN_INFO "%s: DebugInfo: dev=0x%x priv=0x%x "
"&dp_debug_level=0x%x\n",
dev->name, (u32)dev, (u32)dev->priv, (u32)&dp_debug_level);
#endif
/* Update Counters */
dev_count++;
dev=NULL;
}
return dev_count ? OK : -ENODEV;
}
/* fa31x_open - open and initialize a device */
static int
fa31x_open (struct device *dev)
{
u32 iobase = dev->base_addr;
struct fa31x_priv* priv = dev->priv;
/* Reset the device -- paranoia! */
if (fa31x_dev_reset (iobase) != OK)
return -EAGAIN;
/* Allocate Tx and Rx queues */
if (fa31x_queue_create (&priv->tx_queue, DP_DEFAULT_TXQ_SIZE,
DP_QUEUE_TYPE_TX) != OK) {
printk (KERN_INFO "%s: Failed to create tx queue\n", dev->name);
return -EAGAIN;
}
if (fa31x_queue_create (&priv->rx_queue, DP_DEFAULT_RXQ_SIZE,
DP_QUEUE_TYPE_RX) != OK) {
fa31x_queue_delete (&priv->tx_queue);
printk (KERN_INFO "%s: Failed to create rx queue\n", dev->name);
return -EAGAIN;
}
/* Install the Tx and Rx queues on the device */
DP_REG32_WRITE (DP_TXDP, virt_to_bus(priv->tx_queue.first_desc_addr));
DP_REG32_WRITE (DP_RXDP, virt_to_bus(priv->rx_queue.first_desc_addr));
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "dp: setting TXDP=0x%x RXDP=0x%x\n",
(u32)virt_to_bus (priv->tx_queue.first_desc_addr),
(u32)virt_to_bus (priv->rx_queue.first_desc_addr)));
/* Install interrupt vector */
if (request_irq (dev->irq, &fa31x_interrupt, SA_SHIRQ,
dev->name, dev) != OK) {
fa31x_queue_delete (&priv->tx_queue);
fa31x_queue_delete (&priv->rx_queue);
return -EAGAIN;
}
/* Setup phy capabilities */
if (fa31x_phy_setup (dev) != OK) {
printk (KERN_INFO "%s: Warning PHY setup did not complete. Check cable.\n",
dev->name);
}
/* Setup transmit control */
DP_REG32_WRITE (DP_TXCFG, (DP_TXCFG_DRTH_SET(0x30) |
DP_TXCFG_FLTH_SET(0x10) |
DP_TXCFG_MXDMA_32 |
DP_TXCFG_ATP));
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "dp: TXCFG set to 0x%x\n",
(DP_TXCFG_DRTH_SET(0x30) | DP_TXCFG_FLTH_SET(0x10) |
DP_TXCFG_MXDMA_32 | DP_TXCFG_ATP)));
/* Setup receive control */
DP_REG32_WRITE (DP_RXCFG, (DP_RXCFG_DRTH_SET(0x08) |
DP_RXCFG_MXDMA_32));
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "dp: RXCFG set to 0x%x\n",
(DP_RXCFG_DRTH_SET(0x08) | DP_RXCFG_MXDMA_32)));
/* Setup the ethernet address */
fa31x_mac_address_set (iobase, dev->dev_addr);
/* Receive perfect match and broadcast packets */
DP_REG32_WRITE (DP_RFCR, 0);
DP_REG32_WRITE (DP_RFCR, (DP_RFCR_AAB | /* all broadcast pkts */
DP_RFCR_APM | /* perfect match pkts */
DP_RFCR_RFEN));
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "dp: RFCR set to 0x%x\n",
DP_RFCR_RFEN | DP_RFCR_APM | DP_RFCR_AAB));
/* Turn on device interrupts */
DP_REG32_WRITE (DP_IMR, (DP_IMR_RXOK | DP_IMR_MIB |
DP_IMR_RTABT | DP_IMR_RMABT |
DP_IMR_SSERR | DP_IMR_PHY));
DP_REG32_WRITE (DP_IER, DP_IER_IE);
/* Enable Tx/Rx */
DP_REG32_WRITE (DP_CR, DP_CR_TXE | DP_CR_RXE);
/* Increment module reference count */
MOD_INC_USE_COUNT;
return OK;
}
/* fa31x_close - close a device, and reclaim resources */
static int
fa31x_close (struct device *dev)
{
u32 iobase = dev->base_addr;
struct fa31x_priv* priv = dev->priv;
/* Stop the Tx/Rx */
/* XXX Do we need to do this ??? */
DP_REG32_WRITE (DP_CR, (DP_CR_TXD | DP_CR_RXD));
/* Reset the device */
fa31x_dev_reset (iobase);
/* Uninstall the interrupt vector */
free_irq (dev->irq, dev);
/* Free the Tx and Rx queues */
fa31x_queue_delete (&priv->tx_queue);
fa31x_queue_delete (&priv->rx_queue);
/* Decrement module reference count */
MOD_DEC_USE_COUNT;
return OK;
}
/*
* fa31x_start_xmit - transmit an ethernet packet.
*
* This routine writes to a tx descriptor, sets the ownership bit of the
* CMDSTS, and signals the chip
*/
static int
fa31x_start_xmit (struct sk_buff *skb, struct device *dev)
{
u32 iobase = dev->base_addr;
u32 cmdsts;
virt_addr tx_desc;
struct enet_statistics *stats_p;
if (skb->len > ETH_MAX_PKT_SIZE)
return ERROR;
tx_desc = fa31x_tx_desc_get(dev);
if (tx_desc == NULL) {
set_bit (0, &dev->tbusy);
DP_REG32_SET (DP_IMR, DP_IMR_TXOK | DP_IMR_TXIDLE);
tx_desc = fa31x_tx_desc_get (dev);
}
stats_p = &((struct fa31x_priv *)(dev->priv))->stats;
/* Update tx_desc to point to SKB data, set CMDSTS, and signal the chip */
if (tx_desc != NULL) {
/* Reclaim SKBs from the TX queue */
if (DP_DESC_SKBPTR_GET (tx_desc))
fa31x_tx_skb_reclaim (dev, tx_desc);
/* update statistics of the previous transmit */
cmdsts = DP_DESC_CMDSTS_XLATE_GET (tx_desc);
if(DP_REG32_READ(DP_ISR) & DP_ISR_TXERR) { /*modified by Pam 2001/7/16 */
/* if (cmdsts & DP_DESC_CMDSTS_TX_ERRORS) { */
stats_p->tx_errors++;
/* Update individual counters */
stats_p->collisions += DP_DESC_CMDSTS_TX_COLLISIONS_GET(cmdsts);
if (cmdsts & DP_DESC_CMDSTS_TX_TXA) /* tx aborted */
stats_p->tx_packets--;
if (cmdsts & DP_DESC_CMDSTS_TX_TFU) /* fifo errors */
stats_p->tx_fifo_errors++;
if (cmdsts & DP_DESC_CMDSTS_TX_CRS) /* lost carrier */
stats_p->tx_carrier_errors++;
if (cmdsts & DP_DESC_CMDSTS_TX_OWC) /* out of window collisions */
stats_p->tx_window_errors++;
}
/* Update the descriptor */
DP_DESC_CMDSTS_XLATE_SET (tx_desc, DP_DESC_CMDSTS_OWN|skb->len);
DP_DESC_BUFPTR_XLATE_SET (tx_desc, skb->data);
DP_DESC_SKBPTR_SET (tx_desc, (u32)skb);
dev->trans_start = jiffies;
DP_REG32_SET (DP_CR, DP_CR_TXE);
DP_DEBUG (DP_DEBUG_TX,
(KERN_INFO "Tx: tx_desc=0x%x ", (u32)tx_desc));
stats_p->tx_packets++;
} else
stats_p->tx_dropped++;
return OK;
}
/*
* fa31x_start_receive - receive the data from a Rx Queue descriptor
*
* This routine receives the data from Rx queue as long as it gets a valid
* rx_descriptor and resets the descriptor's CMDSTS field back to the Buffer
* size, and updates the BUFPTR and SKBPTR fields to the newly allocated SKB.
*/
static int
fa31x_start_receive (struct device *dev)
{
u32 cmdsts;
int len;
bool do_copy;
virt_addr rx_desc;
struct sk_buff *cur_skb;
struct sk_buff *new_skb;
struct sk_buff *rx_skb;
struct enet_statistics *stats_p;
stats_p = &((struct fa31x_priv *)(dev->priv))->stats;
for (rx_desc = fa31x_rx_desc_get(dev);
(rx_desc != NULL);
rx_desc = fa31x_rx_desc_get(dev)) {
DP_DEBUG (DP_DEBUG_RX,
(KERN_INFO "Rx: rx_desc=0x%x, CMDSTS = 0x%x",
(u32)rx_desc, DP_DESC_CMDSTS_XLATE_GET(rx_desc)));
cmdsts = DP_DESC_CMDSTS_XLATE_GET (rx_desc);
/* Send the packet to the stack if no errors */
if ((cmdsts & DP_DESC_CMDSTS_RX_ERRORS) == 0) {
len = (cmdsts & DP_DESC_CMDSTS_SIZE) - ETH_CRC_LEN;
/*
* Allocate a new SKB
* small data packets less than DP_RX_COPY_THRESHOLD are copied
* into the new SKB, other allocate one to replace the current SKB.
*/
if (len < DP_RX_COPY_THRESHOLD) {
do_copy = TRUE;
new_skb = alloc_skb (len + 2, GFP_ATOMIC);
} else {
do_copy = FALSE;
new_skb = alloc_skb (ETH_MAX_PKT_SIZE, GFP_ATOMIC);
}
if (new_skb) {
cur_skb = (struct sk_buff *) DP_DESC_SKBPTR_GET (rx_desc);
if (do_copy) {
/* Copy data from current SKB and send the new SKB up */
rx_skb = new_skb;
skb_reserve (rx_skb, 2);
memcpy (skb_put(rx_skb, len), cur_skb->data, len);
} else {
/* Replace the the current SKB with the new SKB */
rx_skb = cur_skb;
DP_DESC_BUFPTR_XLATE_SET (rx_desc, new_skb->data);
DP_DESC_SKBPTR_SET (rx_desc, (u32) new_skb);
(void) skb_put(rx_skb, len);
}
/* update the SKB and set it up */
rx_skb->dev = dev;
rx_skb->protocol = eth_type_trans (rx_skb, dev);
netif_rx (rx_skb);
dev->last_rx = jiffies;
stats_p->rx_packets++;
} else
stats_p->rx_dropped++; /* no resources */
if (cmdsts & DP_DESC_CMDSTS_RX_DEST_MC)
stats_p->multicast++;
}
else {
stats_p->rx_errors++; /* bad packet */
/* Update individual counters */
if (cmdsts & (DP_DESC_CMDSTS_RX_RUNT |
DP_DESC_CMDSTS_RX_LONG))
stats_p->rx_length_errors++;
if (cmdsts & DP_DESC_CMDSTS_RX_CRCE)
stats_p->rx_crc_errors++;
if (cmdsts & DP_DESC_CMDSTS_RX_FAE)
stats_p->rx_frame_errors++;
if (cmdsts & DP_DESC_CMDSTS_RX_RXO)
stats_p->rx_fifo_errors++;
}
/* Cleanup the descriptor and make available for reception */
DP_DESC_CMDSTS_XLATE_SET (rx_desc, ETH_MAX_PKT_SIZE);
}
return OK;
}
/* fa31x_get_stats - get current device statistics */
static struct enet_statistics *
fa31x_get_stats (struct device *dev)
{
return &((struct fa31x_priv *)(dev->priv))->stats;
}
/* fa31x_set_multicast_list - sets multicast, & promiscuous mode */
static void
fa31x_set_multicast_list (struct device *dev)
{
u32 iobase = dev->base_addr;
u16 hash_table[32];
u32 rfcr_flags;
int i;
struct dev_mc_list * mc_list;
/* default RFCR mode */
rfcr_flags = DP_RFCR_APM | DP_RFCR_AAB | DP_RFCR_RFEN;
/* Setup promiscuous mode */
if (dev->flags & IFF_PROMISC) {
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "IFF_PROMISC\n"));
rfcr_flags = (DP_RFCR_AAU | DP_RFCR_AAM | DP_RFCR_AAB |
DP_RFCR_RFEN);
} else if (dev->flags & IFF_ALLMULTI) {
/* Receive all multicast packets */
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "IFF_ALLMULTI\n"));
rfcr_flags |= DP_RFCR_AAM;
} else {
/* Setup to receive programmed multicast packets */
memset (hash_table, 0, 32);
for (i=0, mc_list=dev->mc_list; mc_list && i < dev->mc_count;
i++, mc_list = mc_list->next) {
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "mc_addr=%p\n", mc_list->dmi_addr));
set_bit (fa31x_crc((char *)mc_list->dmi_addr) & 0x1ff, hash_table);
}
/* install the hash table */
for (i=0; i<32; i++) {
DP_REG32_WRITE (DP_RFCR, DP_RFCR_RFADDR_FMEM_LO + i*2);
DP_REG32_WRITE (DP_RFDR, (u32) hash_table[i]);
}
rfcr_flags |= DP_RFCR_MHEN;
}
DP_REG32_WRITE (DP_RFCR, 0);
DP_REG32_WRITE (DP_RFCR, rfcr_flags);
DP_DEBUG (DP_DEBUG_OPEN,
(KERN_INFO "MC Setup RFCR flags=0x%x\n", rfcr_flags));
return;
}
/* fa31x_crc - computer CRC for hash table entries */
static int
fa31x_crc (char * mc_addr)
{
u32 crc;
u8 cur_byte;
u8 msb;
u8 byte, bit;
crc = ~0;
for (byte=0; byte<6; byte++) {
cur_byte = *mc_addr++;
for (bit=0; bit<8; bit++) {
msb = crc >> 31;
crc <<= 1;
if (msb ^ (cur_byte & 1)) {
crc ^= DP_POLYNOMIAL;
crc |= 1;
}
cur_byte >>= 1;
}
}
crc >>= 23;
return (crc);
}
/* fa31x_interrupt - handle driver specific ioctls */
static int
fa31x_ioctl (struct device *dev, struct ifreq *rq, int cmd)
{
return -EOPNOTSUPP;
}
/* 02/15/2000 */
static u32 PreviousLinkStatus=0;
/* fa31x_interrupt - handle the interrupts */
static void
fa31x_interrupt (int irq, void *dev_id, struct pt_regs *regs)
{
struct device * dev = dev_id;
u16 iobase = dev->base_addr;
u32 reg_isr;
u32 CurrentLinkStatus;
CurrentLinkStatus= DP_REG32_READ(DP_PHYSTS);
#define HAL_PHY_SPEED10 0x00000002
if(PreviousLinkStatus!=CurrentLinkStatus)
{
if(CurrentLinkStatus & HAL_PHY_SPEED10)
{
DP_REG32_WRITE(DP_PHY_PAGE,0x0001);
DP_REG32_WRITE(DP_PHY_EXTCFG,0x0000);
DP_REG32_WRITE(DP_PHY_PAGE,0x0000);
}
else
{
DP_REG32_WRITE(DP_PHY_PAGE,0x0001);
DP_REG32_WRITE(DP_PHY_EXTCFG,0x0010);
DP_REG32_WRITE(DP_PHY_PAGE,0x0000);
}
PreviousLinkStatus=CurrentLinkStatus;
}
reg_isr = DP_REG32_READ (DP_ISR);
DP_DEBUG (DP_DEBUG_IOCTL,
(KERN_INFO "%s: intr_status=0x%x\n", dev->name,
reg_isr));
if (reg_isr & DP_ISR_RXOK)
fa31x_start_receive (dev);
if (reg_isr & (DP_ISR_TXOK | DP_ISR_TXIDLE)) {
DP_REG32_CLR (DP_IMR, (DP_IMR_TXOK|DP_IMR_TXIDLE));
clear_bit (0, &dev->tbusy);
mark_bh (NET_BH);
}
}
/* fa31x_mac_address_set - set the ethernet address */
static void
fa31x_mac_address_set (u32 iobase, char *mac_addr)
{
u16 * mac_addr_ptr;
int i;
for (i=0, mac_addr_ptr = (u16 *)mac_addr; i<3; i++, mac_addr_ptr++) {
DP_REG32_WRITE (DP_RFCR, DP_RFCR_RFADDR_PMATCH1 + i*2);
DP_REG32_WRITE (DP_RFDR, CPU_TO_BUS_SWAP_16 (*mac_addr_ptr));
}
}
/* fa31x_mac_address_get - get the ethernet address */
static void
fa31x_mac_address_get (u32 iobase, char *mac_addr)
{
u16 * mac_addr_ptr=(u16 *)mac_addr;
int i;
u16 mask;
u16 word1 = 0;
u16 word2 = 0;
u16 word3 = 0;
u16 word4 = 0;
u16 nicAddress[ 3 ];
//
// Read 16 bit words 6 - 9 from the EEProm. They contain the hardwares MAC
// address in a rather cryptic format.
//
ReadEEWord( iobase,
0x06,
&word1 );
ReadEEWord( iobase,
0x07,
&word2 );
ReadEEWord( iobase,
0x08,
&word3 );
ReadEEWord( iobase,
0x09,
&word4 );
//
// Decode the cryptic format into what we can use a word at a time.
//
nicAddress[ 0 ] = word1 & 1;
nicAddress[ 1 ] = word2 & 1;
nicAddress[ 2 ] = word3 & 1;
i = 15;
mask = 0x2;
while ( i-- ) {
if ( word2 & 0x8000 ) {
nicAddress[ 0 ] |= mask;
}
word2 = word2 << 1;
mask = mask << 1;
}
//printk("word0=%x\n",nicAddress[0]);
*mac_addr_ptr=nicAddress[0];
mac_addr_ptr++;
i = 15;
mask = 0x2;
while ( i-- ) {
if ( word3 & 0x8000 ) {
nicAddress[ 1 ] |= mask;
}
word3 = word3 << 1;
mask = mask << 1;
}
//printk("word1 =%x\n",nicAddress[1]);
*mac_addr_ptr=nicAddress[1];
mac_addr_ptr++;
i = 15;
mask = 0x2;
while ( i-- ) {
if ( word4 & 0x8000 ) {
nicAddress[ 2 ] |= mask;
}
word4 = word4 << 1;
mask = mask << 1;
}
//printk("word2 =%x\n",nicAddress[2]);
*mac_addr_ptr=nicAddress[2];
}
/* fa31x_dev_reset - soft reset FA311/FA312 */
static status
fa31x_dev_reset (u32 iobase)
{
int timeout = 100; /* XXX cleanup: use macro */
DP_REG32_WRITE (DP_CR, DP_CR_RST);
while (timeout--) {
if (DP_REG32_READ (DP_ISR) == 0x03008000) {
return (OK);
}
udelay (5);
}
return ERROR;
}
/*
* fa31x_queue_create - create a circular queue of descriptors
*
* This routine allocates a descriptor buffer array aligned on a word
* boundary, initializes and links the array to make a circular queue.
*/
static status
fa31x_queue_create (struct fa31x_queue *q, int count, int qtype)
{
virt_addr desc_addr;
int i;
struct sk_buff *skb;
/* allocate the desc buffer array */
q->qbuf = (virt_addr) kmalloc (DP_QUEUE_ELE_SIZE * count + DP_ALIGN,
GFP_DMA);
if (q->qbuf == (virt_addr) NULL)
return ERROR;
memset ((char *)q->qbuf, 0, DP_QUEUE_ELE_SIZE * count + DP_ALIGN);
/* Adjust alignment and Initialize queue data */
q->cur_desc_addr =
q->first_desc_addr =
(virt_addr)(((u32)q->qbuf + DP_ALIGN) & ~(DP_ALIGN - 1));
q->last_desc_addr = q->first_desc_addr + ((count -1) * DP_QUEUE_ELE_SIZE);
q->count = count;
/* Initialize each buffer descriptor, and link them into circular queue */
for (i=0, desc_addr=q->first_desc_addr; i
i++, desc_addr+=DP_QUEUE_ELE_SIZE) {
DP_DESC_LNK_XLATE_SET (desc_addr, desc_addr + DP_QUEUE_ELE_SIZE);
/* Update the size, BUFPTR, and SKBPTR fields for RX descriptors */
if (qtype == DP_QUEUE_TYPE_RX) {
skb = alloc_skb (ETH_MAX_PKT_SIZE, GFP_ATOMIC);
if (skb == NULL) {
fa31x_queue_delete (q);
return (ERROR);
}
DP_DESC_CMDSTS_XLATE_SET (desc_addr, ETH_MAX_PKT_SIZE);
DP_DESC_BUFPTR_XLATE_SET (desc_addr, skb->data);
DP_DESC_SKBPTR_SET (desc_addr, (u32) skb);
}
}
/* Make the queue circular */
DP_DESC_LNK_XLATE_SET (q->last_desc_addr, q->first_desc_addr);
return OK;
}
/* fa31x_queue_delete - frees an allocated descriptor queue */
static status
fa31x_queue_delete (struct fa31x_queue *q)
{
int i;
virt_addr desc_addr;
struct sk_buff *skb;
/* Free all SKBs in the queue */
for (i=0, desc_addr=q->first_desc_addr;
(i < q->count);
i++, desc_addr += DP_QUEUE_ELE_SIZE) {
skb = (struct sk_buff *) DP_DESC_SKBPTR_GET (desc_addr);
if (skb != NULL)
dev_kfree_skb (skb);
}
/* Free the queue buffer */
kfree ((char *)q->qbuf);
return (OK);
}
/*
* fa31x_tx_desc_get - get a valid transmit descriptor
*
* This routine returns the current descriptor from the tx_queue if driver is
* the owner, else returns NULL
*/
static virt_addr
fa31x_tx_desc_get (struct device *dev)
{
struct fa31x_queue * q;
virt_addr desc_addr = NULL;
q = &((struct fa31x_priv *)(dev->priv))->tx_queue;
/* Check if we own the descriptor */
if ((DP_DESC_CMDSTS_XLATE_GET (q->cur_desc_addr) & DP_DESC_CMDSTS_OWN)
== 0) {
desc_addr = q->cur_desc_addr;
DP_QUEUE_ELE_NEXT (q); /* Move to the next element */
}
return desc_addr;
}
/* fa31x_tx_skb_reclaim - reclaim SKBs in transmitted descriptors */
static void
fa31x_tx_skb_reclaim (struct device *dev, virt_addr desc_addr)
{
struct sk_buff * skb;
struct fa31x_queue * q;
/* Reclaim buffers from all descriptors we own. */
q = &((struct fa31x_priv *)(dev->priv))->tx_queue;
while (((DP_DESC_CMDSTS_XLATE_GET(desc_addr) & DP_DESC_CMDSTS_OWN) == 0) &&
((skb=(struct sk_buff *) DP_DESC_SKBPTR_GET(desc_addr)) != NULL)) {
dev_kfree_skb (skb);
DP_DESC_SKBPTR_SET (desc_addr, 0);
desc_addr = DP_QUEUE_ELE_NEXT_GET (q, desc_addr);
}
}
/*
* fa31x_rx_desc_get - get a valid receive descriptor
*
* This routine returns the current descriptor from the rx_queue if driver is
* the owner, else returns NULL
*/
static virt_addr
fa31x_rx_desc_get (struct device *dev)
{
struct fa31x_queue * q;
virt_addr desc_addr = NULL;
q = &((struct fa31x_priv *)(dev->priv))->rx_queue;
/* Check if we own the descriptor */
if (DP_DESC_CMDSTS_XLATE_GET (q->cur_desc_addr) & DP_DESC_CMDSTS_OWN) {
desc_addr = q->cur_desc_addr;
DP_QUEUE_ELE_NEXT(q); /* Move to the next element */
}
return desc_addr;
}
/* fa31x_phy_setup - reset and setup the PHY device */
int link_speed=0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
MODULE_PARM(link_speed, "i");
#endif
static status
fa31x_phy_setup (struct device *dev)
{
u32 iobase = dev->base_addr;
u32 dp_cfg_val;
u16 phy_status;
u16 timeout;
u32 lnk_status;
u32 lnk_check = 0;
dp_cfg_val = DP_REG32_READ(DP_CFG);
dp_cfg_val &= 0xFFFF1FFF; /* clear auto-negotiation select */
dp_cfg_val |= (DP_CFG_PESEL | /* parity error detect */
DP_CFG_PAUSE_ADV | /* pause capable */
DP_CFG_PINT_ACEN | /* phy intr auto clear */
0x00040000); /* phy config */
switch (link_speed) {
case 1: /* Force 10Mb Half duplex */
dp_cfg_val |= DP_CFG_ANEG_SEL_10_HD;
break;
case 2: /* Force 100Mb Half duplex */
dp_cfg_val |= DP_CFG_ANEG_SEL_100_HD;
break;
case 3: /* Force 10Mb Full duplex */
dp_cfg_val |= DP_CFG_ANEG_SEL_10_FD;
break;
case 4: /* Force 100Mb Full duplex */
dp_cfg_val |= DP_CFG_ANEG_SEL_100_FD;
break;
default:
printk (KERN_INFO "Usage: insmod fa31x link_speed=n (n=0...4)");
printk (KERN_INFO "Force to 10/100 full/half autonegotiate mode)");
case 0: /* Autonegotiate 10/100 full/half */
dp_cfg_val |= DP_CFG_ANEG_SEL_AUTO;
break;
}
DP_REG32_WRITE (DP_CFG, dp_cfg_val | DP_CFG_PHY_RST);
udelay (500);
DP_REG32_WRITE (DP_CFG, dp_cfg_val);
for (timeout=10000; timeout; timeout--) {
lnk_status = DP_REG32_READ (DP_CFG);
switch (link_speed) {
case 1: /* Force 10Mb Half duplex */
if (!(lnk_status & (DP_CFG_SPEED100 | DP_CFG_FDUP))) {
lnk_check = 1;
break;
}
udelay (500);
break;
case 2: /* Force 100Mb Half duplex */
if ((lnk_status & (DP_CFG_SPEED100 | DP_CFG_FDUP))
== DP_CFG_SPEED100) {
lnk_check = 1;
break;
}
udelay (500);
break;
case 3: /* Force 10Mb Full duplex */
if ((lnk_status & (DP_CFG_ANEG_DN | DP_CFG_SPEED100))
== DP_CFG_ANEG_DN) {
lnk_check = 1;
break;
}
udelay (500);
break;
case 4: /* Force 100Mb Full duplex */
if ((lnk_status & (DP_CFG_ANEG_DN | DP_CFG_SPEED100))
== (DP_CFG_ANEG_DN | DP_CFG_SPEED100)) {
lnk_check = 1;
break;
}
udelay (500);
break;
case 0: /* Autonegotiate 10/100 full/half */
default:
if (DP_REG32_READ (DP_CFG) & DP_CFG_ANEG_DN) {
lnk_check = 1;
break;
}
udelay (500);
}
if (lnk_check)
break;
}
if (timeout == 0) {
DP_DEBUG (DP_DEBUG_ANEG, (KERN_INFO "Phy Autonegotiate Failed\n"));
return (ERROR);
}
DP_REG32_WRITE (DP_PHY_PAGE, DP_PHY_PAGE_VAL);
DP_REG32_WRITE (DP_PHYCR, DP_PHYCR_PMDCSR_VAL);
DP_REG32_WRITE (DP_PHY_TDATA, DP_PHY_TDATA_VAL);
DP_REG32_WRITE (DP_PHY_DSPCFG, DP_PHY_DSPCFG_VAL);
DP_REG32_WRITE (DP_PHY_SDCFG, DP_PHY_SDCFG_VAL);
DP_REG32_WRITE (DP_PHY_PAGE, (u16) 0x0000);
phy_status = DP_REG16_READ (DP_PHYSTS);
printk (KERN_INFO "%s: speed=%d duplex=%s link=%s\n",
dev->name,
(phy_status & DP_PHYSTS_SPEED_10) ? 10 : 100,
(phy_status & DP_PHYSTS_FDX)? "full" : "half",
(phy_status & DP_PHYSTS_LNK_VALID) ? "up" : "down");
return (OK);
}
#if 0
/* swap_16 - swap a 16 bit value between little endian & big endian byteorder */
static u16 swap_16 (u16 us)
{
return ((us << 8) & 0xff00) | ((us >> 8) & 0x00ff);
}
#endif
#if 0 /* Not used */
/* swap_32 - swap a 32 bit value between little endian & big endian byteorder */
static u32 swap_32 (u32 ui)
{
return ((swap_16 (ui & 0x0000ffff) << 16) | swap_16 (ui >> 16));
}
#endif
/* io_read_16 - read the 16 bit value stored at given io address */
static u16 io_read_16 (u16 io_port)
{
return inw (io_port);
}
#if 0 /* Not used */
/* io_write_16 - write the 16 bit data at given io address */
static void io_write_16 (u16 io_port, u16 data)
{
outw (data, io_port);
}
#endif
/* io_read_32 - read the 32 bit value stored at given io address*/
static u32 io_read_32 (u16 io_port)
{
return inl (io_port);
}
/* io_write_32 - write the 32 bit data at given io address */
static void io_write_32 (u16 io_port, u32 data)
{
outl (data, io_port);
}
#ifdef DEBUG
/* fa31x_desc_info - print info on descriptors
*
* This routine displays values of the elements of fa31x_queue;
* if option is -1, prints info on all the descs and their field vals;
* and in all other cases prints info on the specified descriptor.
*/
static void fa31x_desc_info (struct fa31x_queue * q, int option)
{
virt_addr desc_addr;
u32 i;
/* Display info header */
printk ( KERN_DEBUG "fa31x_desc_info(): q=0x%x, "
"first_desc_addr=0x%x, last_desc_addr=0x%x cur_desc_addr=0x%x\n",
(u32)q, (u32)q->first_desc_addr,
(u32)q->last_desc_addr, (u32)q->cur_desc_addr);
/* Print requested element info */
switch (option) {
case -1: /* all elements */
desc_addr = q->first_desc_addr;
for (i=0; icount; i++) {
printk (KERN_DEBUG "desc_addr=0x%x link=0x%x "
"cmdsts=0x%x bufptr=0x%x\n",
(u32)desc_addr, DP_DESC_LNK_GET (desc_addr),
DP_DESC_CMDSTS_GET (desc_addr),
DP_DESC_BUFPTR_GET (desc_addr));
desc_addr += DP_QUEUE_ELE_SIZE;
}
break;
default: /* a specific element */
if (option > q->count) {
printk (KERN_DEBUG "Can't print info for ele=%d when count=%d\n",
option, q->count);
}
else {
desc_addr = q->first_desc_addr + (DP_QUEUE_ELE_SIZE * (option - 1));
printk (KERN_DEBUG "ele=%d desc_addr=0x%x link=0x%x "
"cmdsts=0x%x bufptr=0x%x\n",
option, (u32)desc_addr, DP_DESC_LNK_GET (desc_addr),
DP_DESC_CMDSTS_GET (desc_addr),
DP_DESC_BUFPTR_GET (desc_addr));
}
}
}
/* fa31x_regs_info - prints values of registers */
static void fa31x_regs_info (u16 iobase)
{
printk (KERN_INFO "dp(0x%x): CFG=0x%x IMR=0x%x IER=0x%x\n",
iobase, DP_REG32_READ (DP_CFG), DP_REG32_READ (DP_IMR),
DP_REG32_READ (DP_IER));
printk (KERN_INFO " ++ TXDP=0x%x TXCFG=0x%x RXDP=0x%x RXCFG=0x%x\n",
DP_REG32_READ (DP_TXDP), DP_REG32_READ (DP_TXCFG),
DP_REG32_READ (DP_RXDP), DP_REG32_READ (DP_RXCFG));
}
#endif
#ifdef MODULE
/*
* init_module - Initialize the driver module
*
* This routine is called to initialize the driver when the module in
* installed into a running kernel via insmod.
*
*/
int
init_module (void)
{
printk (KERN_INFO "%s", version);
return fa31x_probe (NULL);
}
/*
* cleanup_module - Deinstall the driver module.
*
* This routine is called when removing the driver module from a running
* kernel via rmmod.
*
*/
void
cleanup_module (void)
{
struct device *cur_dev;
cur_dev=fa31x_dev_list;
while (cur_dev) {
fa31x_dev_list = ((struct fa31x_priv *)(cur_dev->priv))->next;
unregister_netdev (cur_dev);
release_region (cur_dev->base_addr, FA31x_PCI_IO_SIZE);
kfree (cur_dev);
cur_dev = fa31x_dev_list;
}
}
#endif /* MODULE */
static void ReadEEWord(u16 iobase,u32 wordOffset,u16* pEeData)
{
#define DP_MEAR 0x08
#define EECLK 0x00000004
#define EECS 0x00000008
#define EEDI 0x00000001
#define EEDO 0x00000002
u32 i = 9;
u32 mask = 0x100;
u32 command = 0x180 | wordOffset;
u32 data;
u32 temp;
//
// Start the sequence with Chip Select 0 for a 4 microsecond cycle.
//
DP_REG32_WRITE (DP_MEAR, 0);
udelay( 2 );
DP_REG32_WRITE (DP_MEAR, EECLK );
udelay( 2 );
//
// Write out the read command, one bit at a time, insuring that each transition
// of the clock lasts at least 2 microseconds.
//
while ( i-- ) {
data = ( command & mask ) ? EECS | EEDI : EECS;
DP_REG32_WRITE(DP_MEAR, data );
udelay( 2 );
DP_REG32_WRITE(DP_MEAR, data | EECLK );
udelay( 2 );
mask = mask >> 1;
}
//
// Read in the 16 bits of data, one bit at a time, insuring that each
// transition of the clock lasts at least 2 microseconds.
//
i = 16;
mask = 0x8000;
data = 0;
while ( i--) {
DP_REG32_WRITE(DP_MEAR, EECS );
udelay( 2 );
DP_REG32_WRITE(DP_MEAR, EECS | EECLK );
udelay( 2 );
temp = DP_REG32_READ(DP_MEAR);
if ( temp & EEDO ) {
data |= mask;
}
mask = mask >> 1;
}
*pEeData = ( u16 )data;
}
/*
* Local variables:
*
* for Redhat 5.2/6.0/6.x
compile-command: "gcc -D__KERNEL__ -DMODULE -Wall -Wstrict-prototypes -O -m486 -c fa31x.c"
* for Redhat 7.0
compile-command: "kgcc -D__KERNEL__ -DMODULE -Wall -Wstrict-prototypes -O -m486 -c fa31x.c -I/usr/src/linux/include"
* version-control: t
* kept-new-versions: 5
* End:
*/ |
Social: