| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) 2001 Patton Electronics Company |
| * Copyright (C) 2002 Momentum Computer |
| * |
| * Copyright 2000 MontaVista Software Inc. |
| * Author: MontaVista Software, Inc. |
| * stevel@mvista.com or support@mvista.com |
| * |
| * This program is free software; you can distribute 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 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. |
| * |
| * Ethernet driver for the MIPS GT96100 Advanced Communication Controller. |
| * |
| * Modified for the Gallileo/Marvell GT-64240 Communication Controller. |
| * |
| * Support for Rx NAPI, Rx checksum offload, IOCTL and ETHTOOL added |
| * Manish Lachwani (lachwani@pmc-sierra.com) - 09/16/2003 |
| * |
| * Modified for later version of Linux 2.4 kernel |
| * Manish Lachwani (lachwani@pmc-sierra.com) - 04/29/2004 |
| */ |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/string.h> |
| #include <linux/timer.h> |
| #include <linux/errno.h> |
| #include <linux/in.h> |
| #include <linux/ioport.h> |
| #include <linux/slab.h> |
| #include <linux/interrupt.h> |
| #include <linux/pci.h> |
| #include <linux/init.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/ethtool.h> |
| #include <linux/skbuff.h> |
| #include <linux/delay.h> |
| #include <linux/ctype.h> |
| #include <linux/mii.h> |
| |
| #include <asm/irq.h> |
| #include <asm/bitops.h> |
| #include <asm/io.h> |
| #include <asm/uaccess.h> |
| |
| #define DESC_DATA_BE 1 |
| |
| #include "gt64240eth.h" |
| |
| // enable this port (set hash size to 1/2K) |
| //- #define PORT_CONFIG pcrHS |
| #define PORT_CONFIG (pcrHS | pcrHD) |
| //- #define PORT_CONFIG pcrHS |pcrPM |pcrPBF|pcrHDM |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS); |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS | pcrPM); |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS | pcrPM | 1<<pcrLPBKBit); |
| |
| // clear all the MIB ctr regs |
| #define EXT_CONFIG_CLEAR (pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrDPLXen | pcxrPRIOrxOverride | pcxrRMIIen) |
| |
| /* |
| * _debug level: |
| * <= 2 none. |
| * > 2 some warnings such as queue full, ..... |
| * > 3 lots of change-of-state messages. |
| * > 4 EXTENSIVE data/descriptor dumps. |
| */ |
| |
| #ifdef GT64240_DEBUG |
| static int gt64240_debug = GT64240_DEBUG; |
| #else |
| static int gt64240_debug = 0; |
| #endif |
| |
| static int debug = -1; |
| |
| #define GT64240_MSG_ENABLE (NETIF_MSG_DRV | \ |
| NETIF_MSG_PROBE | \ |
| NETIF_MSG_LINK) |
| |
| |
| /********************************************************/ |
| |
| // prototypes |
| static void *dmaalloc(size_t size, dma_addr_t * dma_handle); |
| static void dmafree(size_t size, void *vaddr); |
| static void gt64240_delay(int msec); |
| static int gt64240_add_hash_entry(struct net_device *dev, |
| unsigned char *addr); |
| static void read_mib_counters(struct gt64240_private *gp); |
| static int read_MII(struct net_device *dev, u32 reg); |
| static int write_MII(struct net_device *dev, u32 reg, u16 data); |
| #if 1 |
| static void dump_tx_ring(struct net_device *dev); |
| static void dump_rx_ring(struct net_device *dev); |
| #endif |
| static void dump_MII(struct net_device *dev); |
| static void dump_tx_desc(struct net_device *dev, int i); |
| static void dump_rx_desc(struct net_device *dev, int i); |
| static void dump_skb(struct net_device *dev, struct sk_buff *skb); |
| static void dump_hw_addr(unsigned char *addr_str); |
| static void update_stats(struct gt64240_private *gp); |
| static void abort(struct net_device *dev, u32 abort_bits); |
| static void hard_stop(struct net_device *dev); |
| static void enable_ether_irq(struct net_device *dev); |
| static void disable_ether_irq(struct net_device *dev); |
| static int __init gt64240_probe1(uint32_t ioaddr, int irq, int port_num); |
| static void reset_tx(struct net_device *dev); |
| static void reset_rx(struct net_device *dev); |
| static int gt64240_init(struct net_device *dev); |
| static int gt64240_open(struct net_device *dev); |
| static int gt64240_close(struct net_device *dev); |
| static int gt64240_tx(struct sk_buff *skb, struct net_device *dev); |
| #ifdef GT64240_NAPI |
| static int gt64240_poll(struct net_device *dev, int *budget); |
| static int gt64240_rx(struct net_device *dev, u32 status, int budget); |
| #else |
| static int gt64240_rx(struct net_device *dev, u32 status); |
| #endif |
| static void gt64240_interrupt(int irq, void *dev_id, struct pt_regs *regs); |
| static void gt64240_tx_timeout(struct net_device *dev); |
| static void gt64240_set_rx_mode(struct net_device *dev); |
| static struct net_device_stats *gt64240_get_stats(struct net_device *dev); |
| |
| extern char *__init prom_getcmdline(void); |
| extern int prom_get_mac_addrs(unsigned char |
| station_addr[NUM_INTERFACES][6]); |
| |
| static char version[] __devinitdata = |
| "gt64240eth.o: version 0.1, <www.patton.com>\n"; |
| |
| // PHY device addresses |
| static u32 gt64240_phy_addr[NUM_INTERFACES] __devinitdata = { 0x8, 0x1, 0xa }; |
| |
| // Need real Ethernet addresses -- in parse_mac_addr_options(), |
| // these will be replaced by prom_get_mac_addrs() and/or prom_getcmdline(). |
| static unsigned char gt64240_station_addr[NUM_INTERFACES][6] = { |
| {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, |
| {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, |
| {0x02, 0x03, 0x04, 0x05, 0x06, 0x07} |
| }; |
| |
| static int max_interrupt_work = 32; |
| |
| /* |
| * Base address and interupt of the GT64240 ethernet controllers |
| */ |
| static struct { |
| unsigned int port; |
| int irq; |
| } gt64240_iflist[NUM_INTERFACES] = { |
| { |
| GT64240_ETH0_BASE, 8}, { |
| GT64240_ETH1_BASE, 8}, { |
| GT64240_ETH2_BASE, 8} |
| }; |
| |
| /* |
| DMA memory allocation, derived from pci_alloc_consistent. |
| */ |
| static void *dmaalloc(size_t size, dma_addr_t * dma_handle) |
| { |
| void *ret; |
| |
| ret = |
| (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, |
| get_order(size)); |
| |
| if (ret != NULL) { |
| dma_cache_inv((unsigned long) ret, size); |
| if (dma_handle != NULL) |
| *dma_handle = virt_to_phys(ret); |
| |
| /* bump virtual address up to non-cached area */ |
| ret = (void *) KSEG1ADDR(ret); |
| } |
| |
| return ret; |
| } |
| |
| static void dmafree(size_t size, void *vaddr) |
| { |
| vaddr = (void *) KSEG0ADDR(vaddr); |
| free_pages((unsigned long) vaddr, get_order(size)); |
| } |
| |
| static void gt64240_delay(int ms) |
| { |
| if (in_interrupt()) |
| return; |
| else { |
| current->state = TASK_INTERRUPTIBLE; |
| schedule_timeout(ms * HZ / 1000); |
| } |
| } |
| |
| unsigned char prom_mac_addr_base[6]; |
| |
| int prom_get_mac_addrs(unsigned char station_addr[NUM_INTERFACES][6]) |
| { |
| memcpy(station_addr[0], prom_mac_addr_base, 6); |
| memcpy(station_addr[1], prom_mac_addr_base, 6); |
| memcpy(station_addr[2], prom_mac_addr_base, 6); |
| |
| station_addr[1][5] += 1; |
| station_addr[2][5] += 2; |
| |
| return 0; |
| } |
| |
| void parse_mac_addr_options(void) |
| { |
| prom_get_mac_addrs(gt64240_station_addr); |
| } |
| |
| static int read_MII(struct net_device *dev, u32 reg) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int timedout = 20; |
| u32 smir = smirOpCode | (gp->phy_addr << smirPhyAdBit) | |
| (reg << smirRegAdBit); |
| |
| // wait for last operation to complete |
| while ((GT64240_READ(GT64240_ETH_SMI_REG)) & smirBusy) { |
| // snooze for 1 msec and check again |
| gt64240_delay(1); |
| |
| if (--timedout == 0) { |
| printk("%s: read_MII busy timeout!!\n", dev->name); |
| return -1; |
| } |
| } |
| |
| GT64240_WRITE(GT64240_ETH_SMI_REG, smir); |
| |
| timedout = 20; |
| // wait for read to complete |
| while (! |
| ((smir = |
| GT64240_READ(GT64240_ETH_SMI_REG)) & smirReadValid)) { |
| // snooze for 1 msec and check again |
| gt64240_delay(1); |
| |
| if (--timedout == 0) { |
| printk("%s: read_MII timeout!!\n", dev->name); |
| return -1; |
| } |
| } |
| |
| return (int) (smir & smirDataMask); |
| } |
| |
| static void gp_get_drvinfo (struct net_device *dev, |
| struct ethtool_drvinfo *info) |
| { |
| strcpy(info->driver, "gt64260"); |
| strcpy(info->version, version); |
| } |
| |
| static int gp_get_settings(struct net_device *dev, |
| struct ethtool_cmd *cmd) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int rc; |
| |
| spin_lock_irq(&gp->lock); |
| rc = mii_ethtool_gset(&gp->mii_if, cmd); |
| spin_unlock_irq(&gp->lock); |
| return rc; |
| } |
| |
| static int gp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int rc; |
| |
| spin_lock_irq(&gp->lock); |
| rc = mii_ethtool_sset(&gp->mii_if, cmd); |
| spin_unlock_irq(&gp->lock); |
| return rc; |
| } |
| |
| static int gp_nway_reset(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| return mii_nway_restart(&gp->mii_if); |
| } |
| |
| static u32 gp_get_link(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| return mii_link_ok(&gp->mii_if); |
| } |
| |
| static u32 gp_get_msglevel(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| return gp->msg_enable; |
| } |
| |
| static void gp_set_msglevel(struct net_device *dev, u32 value) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| gp->msg_enable = value; |
| } |
| |
| static struct ethtool_ops gp_ethtool_ops = { |
| .get_drvinfo = gp_get_drvinfo, |
| .get_settings = gp_get_settings, |
| .set_settings = gp_set_settings, |
| .nway_reset = gp_nway_reset, |
| .get_link = gp_get_link, |
| .get_msglevel = gp_get_msglevel, |
| .set_msglevel = gp_set_msglevel, |
| }; |
| |
| static int gt64240_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| struct mii_ioctl_data *data = |
| (struct mii_ioctl_data *) &rq->ifr_data; |
| int retval; |
| |
| if (!netif_running(dev)) |
| return -EINVAL; |
| |
| spin_lock_irq(&gp->lock); |
| retval = generic_mii_ioctl(&gp->mii_if, data, cmd, NULL); |
| spin_unlock_irq(&gp->lock); |
| |
| return retval; |
| } |
| |
| static void dump_tx_desc(struct net_device *dev, int i) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| gt64240_td_t *td = &gp->tx_ring[i]; |
| |
| printk |
| ("%s:tx[%d]: self=%08x cmd=%08x, cnt=%4d. bufp=%08x, next=%08x\n", |
| dev->name, i, td, td->cmdstat, td->byte_cnt, td->buff_ptr, |
| td->next); |
| } |
| |
| static void dump_rx_desc(struct net_device *dev, int i) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| gt64240_rd_t *rd = &gp->rx_ring[i]; |
| |
| printk |
| ("%s:rx_dsc[%d]: self=%08x cst=%08x,size=%4d. cnt=%4d. bufp=%08x, next=%08x\n", |
| dev->name, i, rd, rd->cmdstat, rd->buff_sz, rd->byte_cnt, |
| rd->buff_ptr, rd->next); |
| } |
| |
| // These routines work, just disabled to avoid compile warnings |
| static int write_MII(struct net_device *dev, u32 reg, u16 data) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int timedout = 20; |
| u32 smir = |
| (gp->phy_addr << smirPhyAdBit) | (reg << smirRegAdBit) | data; |
| |
| // wait for last operation to complete |
| while (GT64240_READ(GT64240_ETH_SMI_REG) & smirBusy) { |
| // snooze for 1 msec and check again |
| gt64240_delay(1); |
| |
| if (--timedout == 0) { |
| printk("%s: write_MII busy timeout!!\n", |
| dev->name); |
| return -1; |
| } |
| } |
| |
| GT64240_WRITE(GT64240_ETH_SMI_REG, smir); |
| return 0; |
| } |
| |
| static void dump_tx_ring(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int i; |
| |
| printk("%s: dump_tx_ring: txno/txni/cnt=%d/%d/%d\n", |
| dev->name, gp->tx_next_out, gp->tx_next_in, gp->tx_count); |
| |
| for (i = 0; i < TX_RING_SIZE; i++) |
| dump_tx_desc(dev, i); |
| } |
| |
| static void dump_rx_ring(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int i; |
| |
| printk("%s: dump_rx_ring: rxno=%d\n", dev->name, gp->rx_next_out); |
| |
| for (i = 0; i < RX_RING_SIZE; i++) |
| dump_rx_desc(dev, i); |
| } |
| |
| static void dump_MII(struct net_device *dev) |
| { |
| int i, val; |
| |
| for (i = 0; i < 7; i++) { |
| if ((val = read_MII(dev, i)) >= 0) |
| printk("%s: MII Reg %d=%x\n", dev->name, i, val); |
| } |
| for (i = 16; i < 21; i++) { |
| if ((val = read_MII(dev, i)) >= 0) |
| printk("%s: MII Reg %d=%x\n", dev->name, i, val); |
| } |
| } |
| |
| |
| static void dump_hw_addr(unsigned char *addr_str) |
| { |
| int i; |
| for (i = 0; i < 6; i++) { |
| printk("%2.2x", addr_str[i]); |
| printk(i < 5 ? ":" : "\n"); |
| } |
| } |
| |
| |
| static void dump_skb(struct net_device *dev, struct sk_buff *skb) |
| { |
| int i; |
| unsigned char *skbdata; |
| |
| printk("%s: dump_skb: skb=%p, skb->data=%p, skb->len=%d.", |
| dev->name, skb, skb->data, skb->len); |
| |
| skbdata = (unsigned char *) KSEG1ADDR(skb->data); |
| |
| for (i = 0; i < skb->len; i++) { |
| if (!(i % 16)) |
| printk("\r\n %3.3x: %2.2x,", i, skbdata[i]); |
| else |
| printk("%2.2x,", skbdata[i]); |
| } |
| printk("\r\n"); |
| } |
| |
| |
| static void dump_data(struct net_device *dev, char *ptr, int len) |
| { |
| int i; |
| unsigned char *data; |
| |
| printk("%s: dump_data: ptr=%p, len=%d.", dev->name, ptr, len); |
| |
| data = (unsigned char *) KSEG1ADDR(ptr); |
| |
| for (i = 0; i < len; i++) { |
| if (!(i % 16)) |
| printk("\n %3.3x: %2.2x,", i, data[i]); |
| else |
| printk("%2.2x,", data[i]); |
| } |
| printk("\n"); |
| } |
| |
| |
| /*--------------------------------------------------------------*/ |
| /* A D D H A S H E N T R Y */ |
| /*--------------------------------------------------------------*/ |
| static int gt64240_add_hash_entry(struct net_device *dev, |
| unsigned char *addr) |
| { |
| struct gt64240_private *gp; |
| int i; |
| u32 value1, value0, *entry; |
| u16 hashResult; |
| unsigned char hash_ea[6]; |
| static int flag = 0; |
| static unsigned char swapped[256]; |
| |
| if (flag == 0) { /* Create table to swap bits in a byte */ |
| flag = 1; |
| for (i = 0; i < 256; i++) { |
| swapped[i] = (i & 0x01) << 7; |
| swapped[i] |= (i & 0x02) << 5; |
| swapped[i] |= (i & 0x04) << 3; |
| swapped[i] |= (i & 0x08) << 1; |
| swapped[i] |= (i & 0x10) >> 1; |
| swapped[i] |= (i & 0x20) >> 3; |
| swapped[i] |= (i & 0x40) >> 5; |
| swapped[i] |= (i & 0x80) >> 7; |
| } |
| } |
| |
| for (i = 0; i < 6; i++) { /* swap bits from mac to create hash mac */ |
| hash_ea[i] = swapped[addr[i]]; |
| } |
| |
| gp = (struct gt64240_private *) dev->priv; |
| |
| /* create hash entry address */ |
| hashResult = (((hash_ea[5] >> 2) & 0x3F) << 9) & 0x7E00; |
| hashResult |= ((hash_ea[4] & 0x7F) << 2) | (hash_ea[5] & 0x03); |
| hashResult ^= |
| ((hash_ea[3] & 0xFF) << 1) | ((hash_ea[4] >> 7) & 0x01); |
| hashResult ^= ((hash_ea[1] & 0x01) << 8) | (hash_ea[2] & 0xFF); |
| |
| value0 = hteValid | hteRD; /* Create hash table entry value */ |
| value0 |= (u32) addr[0] << 3; |
| value0 |= (u32) addr[1] << 11; |
| value0 |= (u32) addr[2] << 19; |
| value0 |= ((u32) addr[3] & 0x1f) << 27; |
| |
| value1 = ((u32) addr[3] >> 5) & 0x07; |
| value1 |= (u32) addr[4] << 3; |
| value1 |= (u32) addr[5] << 11; |
| |
| /* Inset entry value into hash table */ |
| for (i = 0; i < HASH_HOP_NUMBER; i++) { |
| entry = (u32 *) ((u32) gp->hash_table + |
| (((u32) hashResult & 0x07ff) << 3)); |
| if ((*entry & hteValid) && !(*entry & hteSkip)) { |
| hashResult += 2; /* oops, occupied, go to next entry */ |
| } else { |
| #ifdef __LITTLE_ENDIAN |
| entry[1] = value1; |
| entry[0] = value0; |
| #else |
| entry[0] = value1; |
| entry[1] = value0; |
| #endif |
| break; |
| } |
| } |
| if (i >= HASH_HOP_NUMBER) { |
| printk("%s: gt64240_add_hash_entry expired!\n", dev->name); |
| return (-1); |
| } |
| return (0); |
| } |
| |
| |
| static void read_mib_counters(struct gt64240_private *gp) |
| { |
| u32 *mib_regs = (u32 *) & gp->mib; |
| int i; |
| |
| for (i = 0; i < sizeof(mib_counters_t) / sizeof(u32); i++) |
| mib_regs[i] = |
| GT64240ETH_READ(gp, |
| GT64240_ETH_MIB_COUNT_BASE + |
| i * sizeof(u32)); |
| } |
| |
| |
| static void update_stats(struct gt64240_private *gp) |
| { |
| mib_counters_t *mib = &gp->mib; |
| struct net_device_stats *stats = &gp->stats; |
| |
| read_mib_counters(gp); |
| |
| stats->rx_packets = mib->totalFramesReceived; |
| stats->tx_packets = mib->framesSent; |
| stats->rx_bytes = mib->totalByteReceived; |
| stats->tx_bytes = mib->byteSent; |
| stats->rx_errors = mib->totalFramesReceived - mib->framesReceived; |
| //the tx error counters are incremented by the ISR |
| //rx_dropped incremented by gt64240_rx |
| //tx_dropped incremented by gt64240_tx |
| stats->multicast = mib->multicastFramesReceived; |
| // collisions incremented by gt64240_tx_complete |
| stats->rx_length_errors = mib->oversizeFrames + mib->fragments; |
| // The RxError condition means the Rx DMA encountered a |
| // CPU owned descriptor, which, if things are working as |
| // they should, means the Rx ring has overflowed. |
| stats->rx_over_errors = mib->macRxError; |
| stats->rx_crc_errors = mib->cRCError; |
| } |
| |
| static void abort(struct net_device *dev, u32 abort_bits) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int timedout = 100; // wait up to 100 msec for hard stop to complete |
| |
| if (gt64240_debug > 3) |
| printk("%s: abort\n", dev->name); |
| |
| // Return if neither Rx or Tx abort bits are set |
| if (!(abort_bits & (sdcmrAR | sdcmrAT))) |
| return; |
| |
| // make sure only the Rx/Tx abort bits are set |
| abort_bits &= (sdcmrAR | sdcmrAT); |
| |
| spin_lock(&gp->lock); |
| |
| // abort any Rx/Tx DMA immediately |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, abort_bits); |
| |
| if (gt64240_debug > 3) |
| printk("%s: abort: SDMA cmd = %x/%x\n", |
| dev->name, abort_bits, GT64240ETH_READ(gp, |
| GT64240_ETH_SDMA_COMM)); |
| |
| // wait for abort to complete |
| while ((GT64240ETH_READ(gp, GT64240_ETH_SDMA_COMM)) & abort_bits) { |
| // snooze for 20 msec and check again |
| gt64240_delay(1); |
| |
| if (--timedout == 0) { |
| printk("%s: abort timeout!!\n", dev->name); |
| break; |
| } |
| } |
| |
| spin_unlock(&gp->lock); |
| } |
| |
| |
| static void hard_stop(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| |
| if (gt64240_debug > 3) |
| printk("%s: hard stop\n", dev->name); |
| |
| disable_ether_irq(dev); |
| |
| abort(dev, sdcmrAR | sdcmrAT); |
| |
| // disable port |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, 0); |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_hard_stop: Port Config=%x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_PORT_CONFIG)); |
| |
| } |
| |
| |
| static void enable_ether_irq(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| u32 intMask; |
| |
| intMask = |
| icrTxBufferLow | icrTxEndLow | icrTxErrorLow | |
| icrTxBufferHigh | icrTxEndHigh | icrTxErrorHigh | icrTxUdr | |
| icrRxBuffer | icrRxOVR | icrRxError | icrMIIPhySTC | |
| icrEtherIntSum; |
| |
| |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0); /* CLEAR existing ints */ |
| // unmask device interrupts: |
| GT64240ETH_WRITE(gp, GT64240_ETH_INT_MASK, intMask); |
| |
| // now route ethernet interrupts to GT PCI1 (eth0 and eth1 will be |
| // sharing it). |
| GT_READ(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, &intMask); |
| intMask |= 1 << gp->port_num; |
| GT_WRITE(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, intMask); |
| } |
| |
| static void disable_ether_irq(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| u32 intMask; |
| |
| GT_READ(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, &intMask); |
| intMask &= ~(1 << gp->port_num); |
| GT_WRITE(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, intMask); |
| |
| // mask all device interrupts: |
| GT64240ETH_WRITE(gp, GT64240_ETH_INT_MASK, 0); |
| } |
| |
| /* |
| * Probe for a GT64240 ethernet controller. |
| */ |
| static int __init gt64240_probe(void) |
| { |
| unsigned int base_addr = 0; |
| int i; |
| int found = 0; |
| |
| if (gt64240_debug > 2) |
| printk("gt64240_probe at 0x%08x\n", base_addr); |
| |
| parse_mac_addr_options(); |
| |
| for (i = 0; i < NUM_INTERFACES; i++) { |
| int base_addr = gt64240_iflist[i].port; |
| |
| if (check_region(base_addr, GT64240_ETH_IO_SIZE)) { |
| printk("gt64240_probe: ioaddr 0x%lx taken?\n", |
| base_addr); |
| continue; |
| } |
| |
| if (gt64240_probe1(base_addr, gt64240_iflist[i].irq, i) == |
| 0) { |
| /* Does not seem to be the "traditional" way folks do this, */ |
| /* but I want to init both eth ports if at all possible! */ |
| /* So, until I find out the "correct" way to do this: */ |
| if (++found == NUM_INTERFACES) /* That's all of them! */ |
| return 0; |
| } |
| } |
| if (found) |
| return 0; /* as long as we found at least one! */ |
| return -ENODEV; |
| } |
| |
| module_init(gt64240_probe); |
| |
| static int __init gt64240_probe1(uint32_t ioaddr, int irq, int port_num) |
| { |
| struct net_device *dev = NULL; |
| static unsigned version_printed = 0; |
| struct gt64240_private *gp = NULL; |
| int retval; |
| u32 cpuConfig; |
| unsigned char chip_rev; |
| |
| dev = alloc_etherdev(sizeof(struct gt64240_private)); |
| if (!dev) |
| return -ENOMEM; |
| |
| if (irq < 0) { |
| printk |
| ("gt64240_probe1: irq unknown - probing not supported\n"); |
| return -ENODEV; |
| } |
| #if 1 /* KLUDGE Alert: no check on return value: */ |
| if (!request_region(ioaddr, GT64240_ETH_IO_SIZE, "gt64240eth")) |
| printk("*** request_region() failed!\n"); |
| #endif |
| |
| cpuConfig = GT64240_READ(CPU_CONFIGURATION); |
| printk("gt64240_probe1: cpu in %s-endian mode\n", |
| (cpuConfig & (1 << 12)) ? "little" : "big"); |
| |
| printk |
| ("%s: GT64240 found at ioaddr 0x%lx, irq %d.\n", |
| dev->name, ioaddr, irq); |
| |
| if (gt64240_debug && version_printed++ == 0) |
| printk("%s: %s", dev->name, version); |
| |
| /* private struct aligned and zeroed by init_etherdev */ |
| /* Fill in the 'dev' fields. */ |
| dev->base_addr = ioaddr; |
| dev->irq = irq; |
| memcpy(dev->dev_addr, gt64240_station_addr[port_num], |
| sizeof(dev->dev_addr)); |
| |
| printk("%s: HW Address ", dev->name); |
| dump_hw_addr(dev->dev_addr); |
| |
| gp = dev->priv; |
| |
| gp->msg_enable = (debug < 0 ? GT64240_MSG_ENABLE : debug); |
| gp->port_num = port_num; |
| gp->io_size = GT64240_ETH_IO_SIZE; |
| gp->port_offset = port_num * GT64240_ETH_IO_SIZE; |
| gp->phy_addr = gt64240_phy_addr[port_num]; |
| |
| pcibios_read_config_byte(0, 0, PCI_REVISION_ID, &chip_rev); |
| gp->chip_rev = chip_rev; |
| printk("%s: GT64240 chip revision=%d\n", dev->name, gp->chip_rev); |
| |
| printk("%s: GT64240 ethernet port %d\n", dev->name, gp->port_num); |
| |
| #ifdef GT64240_NAPI |
| printk("Rx NAPI supported \n"); |
| #endif |
| |
| /* MII Initialization */ |
| gp->mii_if.dev = dev; |
| gp->mii_if.phy_id = dev->base_addr; |
| gp->mii_if.mdio_read = read_MII; |
| gp->mii_if.mdio_write = write_MII; |
| gp->mii_if.advertising = read_MII(dev, MII_ADVERTISE); |
| |
| // Allocate Rx and Tx descriptor rings |
| if (gp->rx_ring == NULL) { |
| // All descriptors in ring must be 16-byte aligned |
| gp->rx_ring = dmaalloc(sizeof(gt64240_rd_t) * RX_RING_SIZE |
| + |
| sizeof(gt64240_td_t) * TX_RING_SIZE, |
| &gp->rx_ring_dma); |
| if (gp->rx_ring == NULL) { |
| retval = -ENOMEM; |
| goto free_region; |
| } |
| |
| gp->tx_ring = |
| (gt64240_td_t *) (gp->rx_ring + RX_RING_SIZE); |
| gp->tx_ring_dma = |
| gp->rx_ring_dma + sizeof(gt64240_rd_t) * RX_RING_SIZE; |
| } |
| // Allocate the Rx Data Buffers |
| if (gp->rx_buff == NULL) { |
| gp->rx_buff = |
| dmaalloc(PKT_BUF_SZ * RX_RING_SIZE, &gp->rx_buff_dma); |
| if (gp->rx_buff == NULL) { |
| dmafree(sizeof(gt64240_rd_t) * RX_RING_SIZE |
| + sizeof(gt64240_td_t) * TX_RING_SIZE, |
| gp->rx_ring); |
| retval = -ENOMEM; |
| goto free_region; |
| } |
| } |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_probe1, rx_ring=%p, tx_ring=%p\n", |
| dev->name, gp->rx_ring, gp->tx_ring); |
| |
| // Allocate Rx Hash Table |
| if (gp->hash_table == NULL) { |
| gp->hash_table = (char *) dmaalloc(RX_HASH_TABLE_SIZE, |
| &gp->hash_table_dma); |
| if (gp->hash_table == NULL) { |
| dmafree(sizeof(gt64240_rd_t) * RX_RING_SIZE |
| + sizeof(gt64240_td_t) * TX_RING_SIZE, |
| gp->rx_ring); |
| dmafree(PKT_BUF_SZ * RX_RING_SIZE, gp->rx_buff); |
| retval = -ENOMEM; |
| goto free_region; |
| } |
| } |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_probe1, hash=%p\n", |
| dev->name, gp->hash_table); |
| |
| spin_lock_init(&gp->lock); |
| |
| dev->open = gt64240_open; |
| dev->hard_start_xmit = gt64240_tx; |
| dev->stop = gt64240_close; |
| dev->get_stats = gt64240_get_stats; |
| dev->do_ioctl = gt64240_ioctl; |
| dev->set_multicast_list = gt64240_set_rx_mode; |
| dev->tx_timeout = gt64240_tx_timeout; |
| dev->watchdog_timeo = GT64240ETH_TX_TIMEOUT; |
| |
| #ifdef GT64240_NAPI |
| dev->poll = gt64240_poll; |
| dev->weight = 64; |
| #endif |
| dev->ethtool_ops = &gp_ethtool_ops; |
| |
| /* Fill in the fields of the device structure with ethernet values. */ |
| return 0; |
| |
| free_region: |
| release_region(ioaddr, gp->io_size); |
| unregister_netdev(dev); |
| if (dev->priv != NULL) |
| kfree(dev->priv); |
| kfree(dev); |
| printk("%s: gt64240_probe1 failed. Returns %d\n", |
| dev->name, retval); |
| return retval; |
| } |
| |
| |
| static void reset_tx(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int i; |
| |
| abort(dev, sdcmrAT); |
| |
| for (i = 0; i < TX_RING_SIZE; i++) { |
| if (gp->tx_skbuff[i]) { |
| if (in_interrupt()) |
| dev_kfree_skb_irq(gp->tx_skbuff[i]); |
| else |
| dev_kfree_skb(gp->tx_skbuff[i]); |
| gp->tx_skbuff[i] = NULL; |
| } |
| //- gp->tx_ring[i].cmdstat = 0; // CPU owns |
| gp->tx_ring[i].cmdstat = |
| (u32) (txGenCRC | txEI | txPad | txFirst | txLast); |
| gp->tx_ring[i].byte_cnt = 0; |
| gp->tx_ring[i].buff_ptr = 0; |
| gp->tx_ring[i].next = |
| gp->tx_ring_dma + sizeof(gt64240_td_t) * (i + 1); |
| if (gt64240_debug > 4) |
| dump_tx_desc(dev, i); |
| } |
| /* Wrap the ring. */ |
| gp->tx_ring[i - 1].next = gp->tx_ring_dma; |
| if (gt64240_debug > 4) |
| dump_tx_desc(dev, i - 1); |
| |
| // setup only the lowest priority TxCDP reg |
| GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0, |
| gp->tx_ring_dma); |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0, 0); /* ROLLINS */ |
| //- GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0,virt_to_phys(&gp->tx_ring[0])); /* ROLLINS */ |
| |
| GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR1, 0); |
| |
| // init Tx indeces and pkt counter |
| gp->tx_next_in = gp->tx_next_out = 0; |
| gp->tx_count = 0; |
| } |
| |
| static void reset_rx(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int i; |
| |
| abort(dev, sdcmrAR); |
| |
| for (i = 0; i < RX_RING_SIZE; i++) { |
| gp->rx_ring[i].next = |
| gp->rx_ring_dma + sizeof(gt64240_rd_t) * (i + 1); |
| gp->rx_ring[i].buff_ptr = gp->rx_buff_dma + i * PKT_BUF_SZ; |
| gp->rx_ring[i].buff_sz = PKT_BUF_SZ; |
| gp->rx_ring[i].byte_cnt = 0; /* just for debug printk's */ |
| // Give ownership to device, set first and last, enable interrupt |
| gp->rx_ring[i].cmdstat = |
| (uint32_t) (rxFirst | rxLast | rxOwn | rxEI); |
| if (gt64240_debug > 4) |
| dump_rx_desc(dev, i); |
| } |
| /* Wrap the ring. */ |
| gp->rx_ring[i - 1].next = gp->rx_ring_dma; |
| if (gt64240_debug > 4) |
| dump_rx_desc(dev, i - 1); |
| |
| // Setup only the lowest priority RxFDP and RxCDP regs |
| for (i = 0; i < 4; i++) { |
| if (i == 0) { |
| GT64240ETH_WRITE(gp, GT64240_ETH_1ST_RX_DESC_PTR0, |
| gp->rx_ring_dma); |
| GT64240ETH_WRITE(gp, GT64240_ETH_CURR_RX_DESC_PTR0, |
| gp->rx_ring_dma); |
| } else { |
| GT64240ETH_WRITE(gp, |
| GT64240_ETH_1ST_RX_DESC_PTR0 + |
| i * 4, 0); |
| GT64240ETH_WRITE(gp, |
| GT64240_ETH_CURR_RX_DESC_PTR0 + |
| i * 4, 0); |
| } |
| } |
| |
| // init Rx NextOut index |
| gp->rx_next_out = 0; |
| } |
| |
| |
| static int gt64240_init(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| |
| if (gt64240_debug > 3) { |
| printk("%s: gt64240_init: dev=%p\n", dev->name, dev); |
| printk("%s: gt64240_init: scs0_lo=%04x, scs0_hi=%04x\n", |
| dev->name, GT64240_READ(0x008), |
| GT64240_READ(0x010)); |
| printk("%s: gt64240_init: scs1_lo=%04x, scs1_hi=%04x\n", |
| dev->name, GT64240_READ(0x208), |
| GT64240_READ(0x210)); |
| printk("%s: gt64240_init: scs2_lo=%04x, scs2_hi=%04x\n", |
| dev->name, GT64240_READ(0x018), |
| GT64240_READ(0x020)); |
| printk("%s: gt64240_init: scs3_lo=%04x, scs3_hi=%04x\n", |
| dev->name, GT64240_READ(0x218), |
| GT64240_READ(0x220)); |
| } |
| // Stop and disable Port |
| hard_stop(dev); |
| |
| GT64240_WRITE(COMM_UNIT_INTERRUPT_MASK, 0x07070777); /*+prk21aug01 */ |
| if (gt64240_debug > 2) |
| printk |
| ("%s: gt64240_init: CIU Cause=%08x, Mask=%08x, EAddr=%08x\n", |
| dev->name, GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE), |
| GT64240_READ(COMM_UNIT_INTERRUPT_MASK), |
| GT64240_READ(COMM_UNIT_ERROR_ADDRESS)); |
| |
| // Set-up hash table |
| memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear it |
| gp->hash_mode = 0; |
| // Add a single entry to hash table - our ethernet address |
| gt64240_add_hash_entry(dev, dev->dev_addr); |
| // Set-up DMA ptr to hash table |
| GT64240ETH_WRITE(gp, GT64240_ETH_HASH_TBL_PTR, gp->hash_table_dma); |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Hash Tbl Ptr=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_HASH_TBL_PTR)); |
| |
| // Setup Tx |
| reset_tx(dev); |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Curr Tx Desc Ptr0=%x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_CURR_TX_DESC_PTR0)); |
| |
| // Setup Rx |
| reset_rx(dev); |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: 1st/Curr Rx Desc Ptr0=%x/%x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_1ST_RX_DESC_PTR0), |
| GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0)); |
| |
| if (gt64240_debug > 3) |
| dump_MII(dev); |
| write_MII(dev, 0, 0x8000); /* force a PHY reset -- self-clearing! */ |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: PhyAD=%x\n", dev->name, |
| GT64240_READ(GT64240_ETH_PHY_ADDR_REG)); |
| |
| // setup DMA |
| // We want the Rx/Tx DMA to write/read data to/from memory in |
| // Big Endian mode. Also set DMA Burst Size to 8 64Bit words. |
| #ifdef DESC_DATA_BE |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_CONFIG, |
| (0xf << sdcrRCBit) | sdcrRIFB | (3 << |
| sdcrBSZBit)); |
| #else |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_CONFIG, sdcrBLMR | sdcrBLMT | |
| //- (0xf<<sdcrRCBit) | sdcrRIFB | (3<<sdcrBSZBit)); |
| (0xf << sdcrRCBit) | sdcrRIFB | (2 << |
| sdcrBSZBit)); |
| #endif |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: SDMA Config=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_SDMA_CONFIG)); |
| |
| #if 0 |
| // start Rx DMA |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD); |
| #endif |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: SDMA Cmd =%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_SDMA_COMM)); |
| |
| #if 1 |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, PORT_CONFIG); |
| #endif |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Port Config=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG)); |
| |
| /* |
| * Disable all Type-of-Service queueing. All Rx packets will be |
| * treated normally and will be sent to the lowest priority |
| * queue. |
| * |
| * Disable flow-control for now. FIX! support flow control? |
| */ |
| |
| #if 1 |
| // clear all the MIB ctr regs |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG_EXT, |
| EXT_CONFIG_CLEAR); |
| read_mib_counters(gp); |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG_EXT, |
| EXT_CONFIG_CLEAR | pcxrMIBclrMode); |
| |
| #endif |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Port Config Ext=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG_EXT)); |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Port Command=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_COMMAND)); |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_COMMAND, 0x0); |
| |
| netif_start_queue(dev); |
| |
| /* enable the port */ |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, |
| (PORT_CONFIG | pcrEN)); |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_init: Port Config=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG)); |
| #if 1 |
| // start Rx DMA |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD); |
| #endif |
| |
| |
| // enable interrupts |
| enable_ether_irq(dev); |
| |
| //--- gp->last_psr |= psrLink; /* KLUDGE ALERT */ |
| |
| // we should now be receiving frames |
| return 0; |
| } |
| |
| |
| static int gt64240_open(struct net_device *dev) |
| { |
| int retval; |
| |
| MOD_INC_USE_COUNT; |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_open: dev=%p\n", dev->name, dev); |
| |
| if ((retval = request_irq(dev->irq, >64240_interrupt, |
| SA_SHIRQ, dev->name, dev))) { |
| printk("%s: unable to get IRQ %d\n", dev->name, dev->irq); |
| MOD_DEC_USE_COUNT; |
| return retval; |
| } |
| // Initialize and startup the GT-64240 ethernet port |
| if ((retval = gt64240_init(dev))) { |
| printk("%s: error in gt64240_open\n", dev->name); |
| free_irq(dev->irq, dev); |
| MOD_DEC_USE_COUNT; |
| return retval; |
| } |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_open: Initialization done.\n", |
| dev->name); |
| |
| return 0; |
| } |
| |
| static int gt64240_close(struct net_device *dev) |
| { |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_close: dev=%p\n", dev->name, dev); |
| |
| // stop the device |
| if (netif_device_present(dev)) { |
| netif_stop_queue(dev); |
| hard_stop(dev); |
| } |
| |
| free_irq(dev->irq, dev); |
| |
| MOD_DEC_USE_COUNT; |
| return 0; |
| } |
| |
| #ifdef GT64240_NAPI |
| /* |
| * Function will release Tx skbs which are now complete |
| */ |
| static void gt64240_tx_fill(struct net_device *netdev, u32 status) |
| { |
| struct gt64240_private *gp = |
| (struct gt64240_private *) netdev->priv; |
| int nextOut, cdp; |
| gt64240_td_t *td; |
| u32 cmdstat; |
| |
| cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_TX_DESC_PTR0) |
| - gp->tx_ring_dma) / sizeof(gt64240_td_t); |
| |
| for (nextOut = gp->tx_next_out; nextOut != cdp; |
| nextOut = (nextOut + 1) % TX_RING_SIZE) { |
| if (--gp->intr_work_done == 0) |
| break; |
| |
| td = &gp->tx_ring[nextOut]; |
| cmdstat = td->cmdstat; |
| |
| if (cmdstat & (u32) txOwn) |
| break; |
| |
| if (gp->tx_full) { |
| gp->tx_full = 0; |
| if (gp->last_psr & psrLink) { |
| netif_wake_queue(netdev); |
| } |
| } |
| // decrement tx ring buffer count |
| if (gp->tx_count) |
| gp->tx_count--; |
| |
| // free the skb |
| if (gp->tx_skbuff[nextOut]) { |
| dev_kfree_skb_irq(gp->tx_skbuff[nextOut]); |
| gp->tx_skbuff[nextOut] = NULL; |
| } |
| } |
| |
| gp->tx_next_out = nextOut; |
| |
| if ((status & icrTxEndLow) && gp->tx_count != 0) |
| // we must restart the DMA |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, |
| sdcmrERD | sdcmrTXDL); |
| } |
| |
| /* |
| * Main function for NAPI |
| */ |
| static int gt64240_poll(struct net_device *netdev, int *budget) |
| { |
| struct gt64240_private *gp = |
| (struct gt64240_private *) netdev->priv; |
| unsigned long flags; |
| int done = 1, orig_budget, work_done; |
| u32 status = GT64240ETH_READ(gp, GT64240_ETH_INT_CAUSE); |
| |
| spin_lock_irqsave(&gp->lock, flags); |
| gt64240_tx_fill(netdev, status); |
| |
| if (GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0) != |
| gp->rx_next_out) { |
| orig_budget = *budget; |
| if (orig_budget > netdev->quota) |
| orig_budget = netdev->quota; |
| |
| work_done = gt64240_rx(netdev, status, orig_budget); |
| *budget -= work_done; |
| netdev->quota -= work_done; |
| if (work_done >= orig_budget) |
| done = 0; |
| if (done) { |
| __netif_rx_complete(netdev); |
| enable_ether_irq(netdev); |
| } |
| } |
| |
| spin_unlock_irqrestore(&gp->lock, flags); |
| |
| return (done ? 0 : 1); |
| } |
| #endif |
| |
| static int gt64240_tx(struct sk_buff *skb, struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| unsigned long flags; |
| int nextIn; |
| |
| spin_lock_irqsave(&gp->lock, flags); |
| |
| nextIn = gp->tx_next_in; |
| |
| if (gt64240_debug > 3) { |
| printk("%s: gt64240_tx: nextIn=%d.\n", dev->name, nextIn); |
| } |
| |
| if (gp->tx_count >= TX_RING_SIZE) { |
| printk("%s: Tx Ring full, pkt dropped.\n", dev->name); |
| gp->stats.tx_dropped++; |
| spin_unlock_irqrestore(&gp->lock, flags); |
| return 1; |
| } |
| |
| if (!(gp->last_psr & psrLink)) { |
| printk("%s: gt64240_tx: Link down, pkt dropped.\n", |
| dev->name); |
| gp->stats.tx_dropped++; |
| spin_unlock_irqrestore(&gp->lock, flags); |
| //--- dump_MII(dev); /* KLUDGE ALERT !!! */ |
| return 1; |
| } |
| |
| if (gp->tx_ring[nextIn].cmdstat & txOwn) { |
| printk |
| ("%s: gt64240_tx: device owns descriptor, pkt dropped.\n", |
| dev->name); |
| gp->stats.tx_dropped++; |
| // stop the queue, so Tx timeout can fix it |
| netif_stop_queue(dev); |
| spin_unlock_irqrestore(&gp->lock, flags); |
| return 1; |
| } |
| // Prepare the Descriptor at tx_next_in |
| gp->tx_skbuff[nextIn] = skb; |
| gp->tx_ring[nextIn].byte_cnt = skb->len; |
| gp->tx_ring[nextIn].buff_ptr = virt_to_phys(skb->data); |
| |
| // make sure packet gets written back to memory |
| dma_cache_wback_inv((unsigned long) (skb->data), skb->len); |
| mb(); |
| |
| // Give ownership to device, set first and last desc, enable interrupt |
| // Setting of ownership bit must be *last*! |
| gp->tx_ring[nextIn].cmdstat = |
| txOwn | txGenCRC | txEI | txPad | txFirst | txLast; |
| |
| if (gt64240_debug > 5) { |
| dump_tx_desc(dev, nextIn); |
| dump_skb(dev, skb); |
| } |
| // increment tx_next_in with wrap |
| gp->tx_next_in = (nextIn + 1) % TX_RING_SIZE; |
| |
| //+prk20aug01: |
| if (0) { /* ROLLINS */ |
| GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0, |
| virt_to_phys(&gp->tx_ring[nextIn])); |
| } |
| |
| if (gt64240_debug > 3) { /*+prk17aug01 */ |
| printk |
| ("%s: gt64240_tx: TX_PTR0=0x%08x, EthPortStatus=0x%08x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_CURR_TX_DESC_PTR0), |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS)); |
| } |
| // If DMA is stopped, restart |
| if (!((GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS)) & psrTxLow)) { |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, |
| sdcmrERD | sdcmrTXDL); |
| } |
| |
| if (gt64240_debug > 3) { /*+prk17aug01 */ |
| printk |
| ("%s: gt64240_tx: TX_PTR0=0x%08x, EthPortStatus=0x%08x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_CURR_TX_DESC_PTR0), |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS)); |
| } |
| // increment count and stop queue if full |
| if (++gp->tx_count >= TX_RING_SIZE) { |
| gp->tx_full = 1; |
| netif_stop_queue(dev); |
| } |
| |
| dev->trans_start = jiffies; |
| spin_unlock_irqrestore(&gp->lock, flags); |
| |
| return 0; |
| } |
| |
| |
| static int |
| #ifdef GT64240_NAPI |
| gt64240_rx(struct net_device *dev, u32 status, int budget) |
| #else |
| gt64240_rx(struct net_device *dev, u32 status) |
| #endif |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| struct sk_buff *skb; |
| int pkt_len, nextOut, cdp; |
| gt64240_rd_t *rd; |
| u32 cmdstat; |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_rx: dev=%p, status=%x\n", |
| dev->name, dev, status); |
| |
| cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0) |
| - gp->rx_ring_dma) / sizeof(gt64240_rd_t); |
| |
| // Continue until we reach the current descriptor pointer |
| for (nextOut = gp->rx_next_out; nextOut != cdp; |
| nextOut = (nextOut + 1) % RX_RING_SIZE) { |
| |
| #ifdef GT64240_NAPI |
| if (budget <= 0) |
| break; |
| |
| budget--; |
| #endif |
| |
| if (--gp->intr_work_done == 0) |
| break; |
| |
| if (gt64240_debug > 4) |
| dump_rx_desc(dev, nextOut); |
| |
| rd = &gp->rx_ring[nextOut]; |
| cmdstat = rd->cmdstat; |
| |
| if (gt64240_debug > 3) |
| printk("%s: isr: Rx desc cmdstat=%x, nextOut=%d\n", |
| dev->name, cmdstat, nextOut); |
| |
| if (cmdstat & (u32) rxOwn) { |
| if (gt64240_debug > 2) |
| printk |
| ("%s: gt64240_rx: device owns descriptor!\n", |
| dev->name); |
| // DMA is not finished updating descriptor??? |
| // Leave and come back later to pick-up where we left off. |
| break; |
| } |
| // must be first and last (ie only) buffer of packet |
| if (!(cmdstat & (u32) rxFirst) |
| || !(cmdstat & (u32) rxLast)) { |
| printk |
| ("%s: gt64240_rx: desc not first and last!\n", |
| dev->name); |
| cmdstat |= (u32) rxOwn; |
| rd->cmdstat = cmdstat; |
| continue; |
| } |
| // Drop this received pkt if there were any errors |
| if ((cmdstat & (u32) rxErrorSummary) |
| || (status & icrRxError)) { |
| // update the detailed rx error counters that are not covered |
| // by the MIB counters. |
| if (cmdstat & (u32) rxOverrun) |
| gp->stats.rx_fifo_errors++; |
| cmdstat |= (u32) rxOwn; |
| rd->cmdstat = cmdstat; |
| continue; |
| } |
| |
| pkt_len = rd->byte_cnt; |
| |
| /* Create new skb. */ |
| // skb = dev_alloc_skb(pkt_len+2); |
| skb = dev_alloc_skb(1538); |
| if (skb == NULL) { |
| printk("%s: Memory squeeze, dropping packet.\n", |
| dev->name); |
| gp->stats.rx_dropped++; |
| cmdstat |= (u32) rxOwn; |
| rd->cmdstat = cmdstat; |
| continue; |
| } |
| skb->dev = dev; |
| skb_reserve(skb, 2); /* 16 byte IP header align */ |
| memcpy(skb_put(skb, pkt_len), |
| &gp->rx_buff[nextOut * PKT_BUF_SZ], pkt_len); |
| skb->protocol = eth_type_trans(skb, dev); |
| if (gt64240_debug > 4) /* will probably Oops! */ |
| dump_data(dev, &gp->rx_buff[nextOut * PKT_BUF_SZ], |
| pkt_len); |
| if (gt64240_debug > 4) |
| dump_skb(dev, skb); |
| |
| /* NIC performed some checksum computation */ |
| skb->ip_summed = CHECKSUM_UNNECESSARY; |
| #ifdef GT64240_NAPI |
| netif_receive_skb(skb); |
| #else |
| netif_rx(skb); /* pass the packet to upper layers */ |
| #endif |
| |
| // now we can release ownership of this desc back to device |
| cmdstat |= (u32) rxOwn; |
| rd->cmdstat = cmdstat; |
| |
| dev->last_rx = jiffies; |
| } |
| |
| if (gt64240_debug > 3 && nextOut == gp->rx_next_out) |
| printk("%s: gt64240_rx: RxCDP did not increment?\n", |
| dev->name); |
| |
| gp->rx_next_out = nextOut; |
| return 0; |
| } |
| |
| |
| static void gt64240_tx_complete(struct net_device *dev, u32 status) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| int nextOut, cdp; |
| gt64240_td_t *td; |
| u32 cmdstat; |
| |
| cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_TX_DESC_PTR0) |
| - gp->tx_ring_dma) / sizeof(gt64240_td_t); |
| |
| if (gt64240_debug > 3) { /*+prk17aug01 */ |
| nextOut = gp->tx_next_out; |
| printk |
| ("%s: tx_complete: TX_PTR0=0x%08x, cdp=%d. nextOut=%d.\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_CURR_TX_DESC_PTR0), |
| cdp, nextOut); |
| td = &gp->tx_ring[nextOut]; |
| } |
| |
| /*** NEED to check and CLEAR these errors every time thru here: ***/ |
| if (gt64240_debug > 2) { |
| if (GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE)) |
| printk |
| ("%s: gt64240_tx_complete: CIU Cause=%08x, Mask=%08x, EAddr=%08x\n", |
| dev->name, |
| GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE), |
| GT64240_READ(COMM_UNIT_INTERRUPT_MASK), |
| GT64240_READ(COMM_UNIT_ERROR_ADDRESS)); |
| GT64240_WRITE(COMM_UNIT_INTERRUPT_CAUSE, 0); |
| } |
| // Continue until we reach the current descriptor pointer |
| for (nextOut = gp->tx_next_out; nextOut != cdp; |
| nextOut = (nextOut + 1) % TX_RING_SIZE) { |
| |
| if (--gp->intr_work_done == 0) |
| break; |
| |
| td = &gp->tx_ring[nextOut]; |
| cmdstat = td->cmdstat; |
| |
| if (cmdstat & (u32) txOwn) { |
| // DMA is not finished writing descriptor??? |
| // Leave and come back later to pick-up where we left off. |
| break; |
| } |
| // increment Tx error stats |
| if (cmdstat & (u32) txErrorSummary) { |
| if (gt64240_debug > 2) |
| printk |
| ("%s: tx_complete: Tx error, cmdstat = %x\n", |
| dev->name, cmdstat); |
| gp->stats.tx_errors++; |
| if (cmdstat & (u32) txReTxLimit) |
| gp->stats.tx_aborted_errors++; |
| if (cmdstat & (u32) txUnderrun) |
| gp->stats.tx_fifo_errors++; |
| if (cmdstat & (u32) txLateCollision) |
| gp->stats.tx_window_errors++; |
| } |
| |
| if (cmdstat & (u32) txCollision) |
| gp->stats.collisions += |
| (unsigned long) ((cmdstat & txReTxCntMask) >> |
| txReTxCntBit); |
| |
| // Wake the queue if the ring was full |
| if (gp->tx_full) { |
| gp->tx_full = 0; |
| if (gp->last_psr & psrLink) { |
| netif_wake_queue(dev); |
| } |
| } |
| // decrement tx ring buffer count |
| if (gp->tx_count) |
| gp->tx_count--; |
| |
| // free the skb |
| if (gp->tx_skbuff[nextOut]) { |
| if (gt64240_debug > 3) |
| printk |
| ("%s: tx_complete: good Tx, skb=%p\n", |
| dev->name, gp->tx_skbuff[nextOut]); |
| dev_kfree_skb_irq(gp->tx_skbuff[nextOut]); |
| gp->tx_skbuff[nextOut] = NULL; |
| } else { |
| printk("%s: tx_complete: no skb!\n", dev->name); |
| } |
| } |
| |
| gp->tx_next_out = nextOut; |
| |
| if ((status & icrTxEndLow) && gp->tx_count != 0) { |
| // we must restart the DMA |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, |
| sdcmrERD | sdcmrTXDL); |
| } |
| } |
| |
| |
| static void gt64240_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
| { |
| struct net_device *dev = (struct net_device *) dev_id; |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| u32 status; |
| |
| if (dev == NULL) { |
| printk("%s: isr: null dev ptr\n", dev->name); |
| return; |
| } |
| |
| spin_lock(&gp->lock); |
| |
| if (gt64240_debug > 3) |
| printk("%s: isr: entry\n", dev->name); |
| |
| gp->intr_work_done = max_interrupt_work; |
| |
| while (gp->intr_work_done > 0) { |
| |
| status = GT64240ETH_READ(gp, GT64240_ETH_INT_CAUSE); |
| #ifdef GT64240_NAPI |
| /* dont ack Rx interrupts */ |
| if (!(status & icrRxBuffer)) |
| GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0); |
| #else |
| // ACK interrupts |
| GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0); |
| #endif |
| |
| if (gt64240_debug > 3) |
| printk("%s: isr: work=%d., icr=%x\n", dev->name, |
| gp->intr_work_done, status); |
| |
| if ((status & icrEtherIntSum) == 0) { |
| if (!(status & |
| (icrTxBufferLow | icrTxBufferHigh | |
| icrRxBuffer))) { |
| /* exit from the while() loop */ |
| break; |
| } |
| } |
| |
| if (status & icrMIIPhySTC) { |
| u32 psr = |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS); |
| if (gp->last_psr != psr) { |
| printk("%s: port status: 0x%08x\n", |
| dev->name, psr); |
| printk |
| ("%s: %s MBit/s, %s-duplex, flow-control %s, link is %s,\n", |
| dev->name, |
| psr & psrSpeed ? "100" : "10", |
| psr & psrDuplex ? "full" : "half", |
| psr & psrFctl ? "disabled" : |
| "enabled", |
| psr & psrLink ? "up" : "down"); |
| printk |
| ("%s: TxLowQ is %s, TxHighQ is %s, Transmitter is %s\n", |
| dev->name, |
| psr & psrTxLow ? "running" : |
| "stopped", |
| psr & psrTxHigh ? "running" : |
| "stopped", |
| psr & psrTxInProg ? "on" : "off"); |
| |
| if ((psr & psrLink) && !gp->tx_full && |
| netif_queue_stopped(dev)) { |
| printk |
| ("%s: isr: Link up, waking queue.\n", |
| dev->name); |
| netif_wake_queue(dev); |
| } else if (!(psr & psrLink) |
| && !netif_queue_stopped(dev)) { |
| printk |
| ("%s: isr: Link down, stopping queue.\n", |
| dev->name); |
| netif_stop_queue(dev); |
| } |
| |
| gp->last_psr = psr; |
| } |
| } |
| |
| if (status & (icrTxBufferLow | icrTxEndLow)) |
| gt64240_tx_complete(dev, status); |
| |
| if (status & icrRxBuffer) { |
| #ifdef GT64240_NAPI |
| if (netif_rx_schedule_prep(dev)) { |
| disable_ether_irq(dev); |
| __netif_rx_schedule(dev); |
| } |
| #else |
| gt64240_rx(dev, status); |
| #endif |
| } |
| // Now check TX errors (RX errors were handled in gt64240_rx) |
| if (status & icrTxErrorLow) { |
| printk("%s: isr: Tx resource error\n", dev->name); |
| } |
| |
| if (status & icrTxUdr) { |
| printk("%s: isr: Tx underrun error\n", dev->name); |
| } |
| } |
| |
| if (gp->intr_work_done == 0) { |
| // ACK any remaining pending interrupts |
| GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0); |
| if (gt64240_debug > 3) |
| printk("%s: isr: hit max work\n", dev->name); |
| } |
| |
| if (gt64240_debug > 3) |
| printk("%s: isr: exit, icr=%x\n", |
| dev->name, GT64240ETH_READ(gp, |
| GT64240_ETH_INT_CAUSE)); |
| |
| spin_unlock(&gp->lock); |
| } |
| |
| |
| static void gt64240_tx_timeout(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&gp->lock, flags); |
| |
| |
| if (!(gp->last_psr & psrLink)) { |
| spin_unlock_irqrestore(&gp->lock, flags); |
| } else { |
| printk("======------> gt64240_tx_timeout: %d jiffies \n", |
| GT64240ETH_TX_TIMEOUT); |
| |
| disable_ether_irq(dev); |
| spin_unlock_irqrestore(&gp->lock, flags); |
| reset_tx(dev); |
| enable_ether_irq(dev); |
| |
| netif_wake_queue(dev); |
| } |
| } |
| |
| |
| static void gt64240_set_rx_mode(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| unsigned long flags; |
| struct dev_mc_list *mcptr; |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_set_rx_mode: dev=%p, flags=%x\n", |
| dev->name, dev, dev->flags); |
| |
| // stop the Receiver DMA |
| abort(dev, sdcmrAR); |
| |
| spin_lock_irqsave(&gp->lock, flags); |
| |
| if (dev->flags & IFF_PROMISC) |
| GT64240ETH_SETBIT(gp, GT64240_ETH_PORT_CONFIG, pcrPM); |
| else |
| GT64240ETH_CLRBIT(gp, GT64240_ETH_PORT_CONFIG, pcrPM); |
| /* |
| GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, |
| (PORT_CONFIG | pcrPM | pcrEN)); |
| */ |
| |
| memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear hash table |
| // Add our ethernet address |
| gt64240_add_hash_entry(dev, dev->dev_addr); |
| if (dev->mc_count) { |
| for (mcptr = dev->mc_list; mcptr; mcptr = mcptr->next) { |
| if (gt64240_debug > 2) { |
| printk("%s: gt64240_set_rx_mode: addr=\n", |
| dev->name); |
| dump_hw_addr(mcptr->dmi_addr); |
| } |
| gt64240_add_hash_entry(dev, mcptr->dmi_addr); |
| } |
| } |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_set_rx: Port Config=%x\n", dev->name, |
| GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG)); |
| |
| // restart Rx DMA |
| GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD); |
| |
| spin_unlock_irqrestore(&gp->lock, flags); |
| } |
| |
| static struct net_device_stats *gt64240_get_stats(struct net_device *dev) |
| { |
| struct gt64240_private *gp = (struct gt64240_private *) dev->priv; |
| unsigned long flags; |
| |
| if (gt64240_debug > 3) |
| printk("%s: gt64240_get_stats: dev=%p\n", dev->name, dev); |
| |
| if (netif_device_present(dev)) { |
| spin_lock_irqsave(&gp->lock, flags); |
| update_stats(gp); |
| spin_unlock_irqrestore(&gp->lock, flags); |
| } |
| |
| return &gp->stats; |
| } |
| MODULE_AUTHOR("MontaVista Software, Inc."); |
| MODULE_DESCRIPTION("Ethernet driver for the MIPS GT96100 Advanced Communication Controller. Modified for the Gallileo/Marvell GT-64240 Communication Controller."); |
| MODULE_LICENSE("GPL"); |