diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/sb1250-mac.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/sb1250-mac.c')
-rw-r--r-- | drivers/net/sb1250-mac.c | 2920 |
1 files changed, 2920 insertions, 0 deletions
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c new file mode 100644 index 00000000000..fd2e7c37490 --- /dev/null +++ b/drivers/net/sb1250-mac.c @@ -0,0 +1,2920 @@ +/* + * Copyright (C) 2001,2002,2003 Broadcom Corporation + * + * 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. + * + * 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 driver is designed for the Broadcom SiByte SOC built-in + * Ethernet controllers. Written by Mitch Lichtenberg at Broadcom Corp. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/config.h> +#include <linux/bitops.h> +#include <asm/processor.h> /* Processor type for cache alignment. */ +#include <asm/io.h> +#include <asm/cache.h> + +/* This is only here until the firmware is ready. In that case, + the firmware leaves the ethernet address in the register for us. */ +#ifdef CONFIG_SIBYTE_STANDALONE +#define SBMAC_ETH0_HWADDR "40:00:00:00:01:00" +#define SBMAC_ETH1_HWADDR "40:00:00:00:01:01" +#define SBMAC_ETH2_HWADDR "40:00:00:00:01:02" +#endif + + +/* These identify the driver base version and may not be removed. */ +#if 0 +static char version1[] __devinitdata = +"sb1250-mac.c:1.00 1/11/2001 Written by Mitch Lichtenberg\n"; +#endif + + +/* Operational parameters that usually are not changed. */ + +#define CONFIG_SBMAC_COALESCE + +#define MAX_UNITS 3 /* More are supported, limit only on options */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + + +MODULE_AUTHOR("Mitch Lichtenberg (Broadcom Corp.)"); +MODULE_DESCRIPTION("Broadcom SiByte SOC GB Ethernet driver"); + +/* A few user-configurable values which may be modified when a driver + module is loaded. */ + +/* 1 normal messages, 0 quiet .. 7 verbose. */ +static int debug = 1; +module_param(debug, int, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug messages"); + +/* mii status msgs */ +static int noisy_mii = 1; +module_param(noisy_mii, int, S_IRUGO); +MODULE_PARM_DESC(noisy_mii, "MII status messages"); + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#ifdef MODULE +static int options[MAX_UNITS] = {-1, -1, -1}; +module_param_array(options, int, NULL, S_IRUGO); +MODULE_PARM_DESC(options, "1-" __MODULE_STRING(MAX_UNITS)); + +static int full_duplex[MAX_UNITS] = {-1, -1, -1}; +module_param_array(full_duplex, int, NULL, S_IRUGO); +MODULE_PARM_DESC(full_duplex, "1-" __MODULE_STRING(MAX_UNITS)); +#endif + +#ifdef CONFIG_SBMAC_COALESCE +static int int_pktcnt = 0; +module_param(int_pktcnt, int, S_IRUGO); +MODULE_PARM_DESC(int_pktcnt, "Packet count"); + +static int int_timeout = 0; +module_param(int_timeout, int, S_IRUGO); +MODULE_PARM_DESC(int_timeout, "Timeout value"); +#endif + +#include <asm/sibyte/sb1250.h> +#include <asm/sibyte/sb1250_defs.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_mac.h> +#include <asm/sibyte/sb1250_dma.h> +#include <asm/sibyte/sb1250_int.h> +#include <asm/sibyte/sb1250_scd.h> + + +/********************************************************************** + * Simple types + ********************************************************************* */ + + +typedef unsigned long sbmac_port_t; + +typedef enum { sbmac_speed_auto, sbmac_speed_10, + sbmac_speed_100, sbmac_speed_1000 } sbmac_speed_t; + +typedef enum { sbmac_duplex_auto, sbmac_duplex_half, + sbmac_duplex_full } sbmac_duplex_t; + +typedef enum { sbmac_fc_auto, sbmac_fc_disabled, sbmac_fc_frame, + sbmac_fc_collision, sbmac_fc_carrier } sbmac_fc_t; + +typedef enum { sbmac_state_uninit, sbmac_state_off, sbmac_state_on, + sbmac_state_broken } sbmac_state_t; + + +/********************************************************************** + * Macros + ********************************************************************* */ + + +#define SBDMA_NEXTBUF(d,f) ((((d)->f+1) == (d)->sbdma_dscrtable_end) ? \ + (d)->sbdma_dscrtable : (d)->f+1) + + +#define NUMCACHEBLKS(x) (((x)+SMP_CACHE_BYTES-1)/SMP_CACHE_BYTES) + +#define SBMAC_READCSR(t) __raw_readq((unsigned long)t) +#define SBMAC_WRITECSR(t,v) __raw_writeq(v, (unsigned long)t) + + +#define SBMAC_MAX_TXDESCR 32 +#define SBMAC_MAX_RXDESCR 32 + +#define ETHER_ALIGN 2 +#define ETHER_ADDR_LEN 6 +#define ENET_PACKET_SIZE 1518 +/*#define ENET_PACKET_SIZE 9216 */ + +/********************************************************************** + * DMA Descriptor structure + ********************************************************************* */ + +typedef struct sbdmadscr_s { + uint64_t dscr_a; + uint64_t dscr_b; +} sbdmadscr_t; + +typedef unsigned long paddr_t; + +/********************************************************************** + * DMA Controller structure + ********************************************************************* */ + +typedef struct sbmacdma_s { + + /* + * This stuff is used to identify the channel and the registers + * associated with it. + */ + + struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */ + int sbdma_channel; /* channel number */ + int sbdma_txdir; /* direction (1=transmit) */ + int sbdma_maxdescr; /* total # of descriptors in ring */ +#ifdef CONFIG_SBMAC_COALESCE + int sbdma_int_pktcnt; /* # descriptors rx/tx before interrupt*/ + int sbdma_int_timeout; /* # usec rx/tx interrupt */ +#endif + + sbmac_port_t sbdma_config0; /* DMA config register 0 */ + sbmac_port_t sbdma_config1; /* DMA config register 1 */ + sbmac_port_t sbdma_dscrbase; /* Descriptor base address */ + sbmac_port_t sbdma_dscrcnt; /* Descriptor count register */ + sbmac_port_t sbdma_curdscr; /* current descriptor address */ + + /* + * This stuff is for maintenance of the ring + */ + + sbdmadscr_t *sbdma_dscrtable; /* base of descriptor table */ + sbdmadscr_t *sbdma_dscrtable_end; /* end of descriptor table */ + + struct sk_buff **sbdma_ctxtable; /* context table, one per descr */ + + paddr_t sbdma_dscrtable_phys; /* and also the phys addr */ + sbdmadscr_t *sbdma_addptr; /* next dscr for sw to add */ + sbdmadscr_t *sbdma_remptr; /* next dscr for sw to remove */ +} sbmacdma_t; + + +/********************************************************************** + * Ethernet softc structure + ********************************************************************* */ + +struct sbmac_softc { + + /* + * Linux-specific things + */ + + struct net_device *sbm_dev; /* pointer to linux device */ + spinlock_t sbm_lock; /* spin lock */ + struct timer_list sbm_timer; /* for monitoring MII */ + struct net_device_stats sbm_stats; + int sbm_devflags; /* current device flags */ + + int sbm_phy_oldbmsr; + int sbm_phy_oldanlpar; + int sbm_phy_oldk1stsr; + int sbm_phy_oldlinkstat; + int sbm_buffersize; + + unsigned char sbm_phys[2]; + + /* + * Controller-specific things + */ + + unsigned long sbm_base; /* MAC's base address */ + sbmac_state_t sbm_state; /* current state */ + + sbmac_port_t sbm_macenable; /* MAC Enable Register */ + sbmac_port_t sbm_maccfg; /* MAC Configuration Register */ + sbmac_port_t sbm_fifocfg; /* FIFO configuration register */ + sbmac_port_t sbm_framecfg; /* Frame configuration register */ + sbmac_port_t sbm_rxfilter; /* receive filter register */ + sbmac_port_t sbm_isr; /* Interrupt status register */ + sbmac_port_t sbm_imr; /* Interrupt mask register */ + sbmac_port_t sbm_mdio; /* MDIO register */ + + sbmac_speed_t sbm_speed; /* current speed */ + sbmac_duplex_t sbm_duplex; /* current duplex */ + sbmac_fc_t sbm_fc; /* current flow control setting */ + + unsigned char sbm_hwaddr[ETHER_ADDR_LEN]; + + sbmacdma_t sbm_txdma; /* for now, only use channel 0 */ + sbmacdma_t sbm_rxdma; + int rx_hw_checksum; + int sbe_idx; +}; + + +/********************************************************************** + * Externs + ********************************************************************* */ + +/********************************************************************** + * Prototypes + ********************************************************************* */ + +static void sbdma_initctx(sbmacdma_t *d, + struct sbmac_softc *s, + int chan, + int txrx, + int maxdescr); +static void sbdma_channel_start(sbmacdma_t *d, int rxtx); +static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *m); +static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m); +static void sbdma_emptyring(sbmacdma_t *d); +static void sbdma_fillring(sbmacdma_t *d); +static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d); +static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d); +static int sbmac_initctx(struct sbmac_softc *s); +static void sbmac_channel_start(struct sbmac_softc *s); +static void sbmac_channel_stop(struct sbmac_softc *s); +static sbmac_state_t sbmac_set_channel_state(struct sbmac_softc *,sbmac_state_t); +static void sbmac_promiscuous_mode(struct sbmac_softc *sc,int onoff); +static uint64_t sbmac_addr2reg(unsigned char *ptr); +static irqreturn_t sbmac_intr(int irq,void *dev_instance,struct pt_regs *rgs); +static int sbmac_start_tx(struct sk_buff *skb, struct net_device *dev); +static void sbmac_setmulti(struct sbmac_softc *sc); +static int sbmac_init(struct net_device *dev, int idx); +static int sbmac_set_speed(struct sbmac_softc *s,sbmac_speed_t speed); +static int sbmac_set_duplex(struct sbmac_softc *s,sbmac_duplex_t duplex,sbmac_fc_t fc); + +static int sbmac_open(struct net_device *dev); +static void sbmac_timer(unsigned long data); +static void sbmac_tx_timeout (struct net_device *dev); +static struct net_device_stats *sbmac_get_stats(struct net_device *dev); +static void sbmac_set_rx_mode(struct net_device *dev); +static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int sbmac_close(struct net_device *dev); +static int sbmac_mii_poll(struct sbmac_softc *s,int noisy); + +static void sbmac_mii_sync(struct sbmac_softc *s); +static void sbmac_mii_senddata(struct sbmac_softc *s,unsigned int data, int bitcnt); +static unsigned int sbmac_mii_read(struct sbmac_softc *s,int phyaddr,int regidx); +static void sbmac_mii_write(struct sbmac_softc *s,int phyaddr,int regidx, + unsigned int regval); + + +/********************************************************************** + * Globals + ********************************************************************* */ + +static uint64_t sbmac_orig_hwaddr[MAX_UNITS]; + + +/********************************************************************** + * MDIO constants + ********************************************************************* */ + +#define MII_COMMAND_START 0x01 +#define MII_COMMAND_READ 0x02 +#define MII_COMMAND_WRITE 0x01 +#define MII_COMMAND_ACK 0x02 + +#define BMCR_RESET 0x8000 +#define BMCR_LOOPBACK 0x4000 +#define BMCR_SPEED0 0x2000 +#define BMCR_ANENABLE 0x1000 +#define BMCR_POWERDOWN 0x0800 +#define BMCR_ISOLATE 0x0400 +#define BMCR_RESTARTAN 0x0200 +#define BMCR_DUPLEX 0x0100 +#define BMCR_COLTEST 0x0080 +#define BMCR_SPEED1 0x0040 +#define BMCR_SPEED1000 BMCR_SPEED1 +#define BMCR_SPEED100 BMCR_SPEED0 +#define BMCR_SPEED10 0 + +#define BMSR_100BT4 0x8000 +#define BMSR_100BT_FDX 0x4000 +#define BMSR_100BT_HDX 0x2000 +#define BMSR_10BT_FDX 0x1000 +#define BMSR_10BT_HDX 0x0800 +#define BMSR_100BT2_FDX 0x0400 +#define BMSR_100BT2_HDX 0x0200 +#define BMSR_1000BT_XSR 0x0100 +#define BMSR_PRESUP 0x0040 +#define BMSR_ANCOMPLT 0x0020 +#define BMSR_REMFAULT 0x0010 +#define BMSR_AUTONEG 0x0008 +#define BMSR_LINKSTAT 0x0004 +#define BMSR_JABDETECT 0x0002 +#define BMSR_EXTCAPAB 0x0001 + +#define PHYIDR1 0x2000 +#define PHYIDR2 0x5C60 + +#define ANAR_NP 0x8000 +#define ANAR_RF 0x2000 +#define ANAR_ASYPAUSE 0x0800 +#define ANAR_PAUSE 0x0400 +#define ANAR_T4 0x0200 +#define ANAR_TXFD 0x0100 +#define ANAR_TXHD 0x0080 +#define ANAR_10FD 0x0040 +#define ANAR_10HD 0x0020 +#define ANAR_PSB 0x0001 + +#define ANLPAR_NP 0x8000 +#define ANLPAR_ACK 0x4000 +#define ANLPAR_RF 0x2000 +#define ANLPAR_ASYPAUSE 0x0800 +#define ANLPAR_PAUSE 0x0400 +#define ANLPAR_T4 0x0200 +#define ANLPAR_TXFD 0x0100 +#define ANLPAR_TXHD 0x0080 +#define ANLPAR_10FD 0x0040 +#define ANLPAR_10HD 0x0020 +#define ANLPAR_PSB 0x0001 /* 802.3 */ + +#define ANER_PDF 0x0010 +#define ANER_LPNPABLE 0x0008 +#define ANER_NPABLE 0x0004 +#define ANER_PAGERX 0x0002 +#define ANER_LPANABLE 0x0001 + +#define ANNPTR_NP 0x8000 +#define ANNPTR_MP 0x2000 +#define ANNPTR_ACK2 0x1000 +#define ANNPTR_TOGTX 0x0800 +#define ANNPTR_CODE 0x0008 + +#define ANNPRR_NP 0x8000 +#define ANNPRR_MP 0x2000 +#define ANNPRR_ACK3 0x1000 +#define ANNPRR_TOGTX 0x0800 +#define ANNPRR_CODE 0x0008 + +#define K1TCR_TESTMODE 0x0000 +#define K1TCR_MSMCE 0x1000 +#define K1TCR_MSCV 0x0800 +#define K1TCR_RPTR 0x0400 +#define K1TCR_1000BT_FDX 0x200 +#define K1TCR_1000BT_HDX 0x100 + +#define K1STSR_MSMCFLT 0x8000 +#define K1STSR_MSCFGRES 0x4000 +#define K1STSR_LRSTAT 0x2000 +#define K1STSR_RRSTAT 0x1000 +#define K1STSR_LP1KFD 0x0800 +#define K1STSR_LP1KHD 0x0400 +#define K1STSR_LPASMDIR 0x0200 + +#define K1SCR_1KX_FDX 0x8000 +#define K1SCR_1KX_HDX 0x4000 +#define K1SCR_1KT_FDX 0x2000 +#define K1SCR_1KT_HDX 0x1000 + +#define STRAP_PHY1 0x0800 +#define STRAP_NCMODE 0x0400 +#define STRAP_MANMSCFG 0x0200 +#define STRAP_ANENABLE 0x0100 +#define STRAP_MSVAL 0x0080 +#define STRAP_1KHDXADV 0x0010 +#define STRAP_1KFDXADV 0x0008 +#define STRAP_100ADV 0x0004 +#define STRAP_SPEEDSEL 0x0000 +#define STRAP_SPEED100 0x0001 + +#define PHYSUP_SPEED1000 0x10 +#define PHYSUP_SPEED100 0x08 +#define PHYSUP_SPEED10 0x00 +#define PHYSUP_LINKUP 0x04 +#define PHYSUP_FDX 0x02 + +#define MII_BMCR 0x00 /* Basic mode control register (rw) */ +#define MII_BMSR 0x01 /* Basic mode status register (ro) */ +#define MII_K1STSR 0x0A /* 1K Status Register (ro) */ +#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */ + + +#define M_MAC_MDIO_DIR_OUTPUT 0 /* for clarity */ + +#define ENABLE 1 +#define DISABLE 0 + +/********************************************************************** + * SBMAC_MII_SYNC(s) + * + * Synchronize with the MII - send a pattern of bits to the MII + * that will guarantee that it is ready to accept a command. + * + * Input parameters: + * s - sbmac structure + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbmac_mii_sync(struct sbmac_softc *s) +{ + int cnt; + uint64_t bits; + int mac_mdio_genc; + + mac_mdio_genc = SBMAC_READCSR(s->sbm_mdio) & M_MAC_GENC; + + bits = M_MAC_MDIO_DIR_OUTPUT | M_MAC_MDIO_OUT; + + SBMAC_WRITECSR(s->sbm_mdio,bits | mac_mdio_genc); + + for (cnt = 0; cnt < 32; cnt++) { + SBMAC_WRITECSR(s->sbm_mdio,bits | M_MAC_MDC | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio,bits | mac_mdio_genc); + } +} + +/********************************************************************** + * SBMAC_MII_SENDDATA(s,data,bitcnt) + * + * Send some bits to the MII. The bits to be sent are right- + * justified in the 'data' parameter. + * + * Input parameters: + * s - sbmac structure + * data - data to send + * bitcnt - number of bits to send + ********************************************************************* */ + +static void sbmac_mii_senddata(struct sbmac_softc *s,unsigned int data, int bitcnt) +{ + int i; + uint64_t bits; + unsigned int curmask; + int mac_mdio_genc; + + mac_mdio_genc = SBMAC_READCSR(s->sbm_mdio) & M_MAC_GENC; + + bits = M_MAC_MDIO_DIR_OUTPUT; + SBMAC_WRITECSR(s->sbm_mdio,bits | mac_mdio_genc); + + curmask = 1 << (bitcnt - 1); + + for (i = 0; i < bitcnt; i++) { + if (data & curmask) + bits |= M_MAC_MDIO_OUT; + else bits &= ~M_MAC_MDIO_OUT; + SBMAC_WRITECSR(s->sbm_mdio,bits | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio,bits | M_MAC_MDC | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio,bits | mac_mdio_genc); + curmask >>= 1; + } +} + + + +/********************************************************************** + * SBMAC_MII_READ(s,phyaddr,regidx) + * + * Read a PHY register. + * + * Input parameters: + * s - sbmac structure + * phyaddr - PHY's address + * regidx = index of register to read + * + * Return value: + * value read, or 0 if an error occurred. + ********************************************************************* */ + +static unsigned int sbmac_mii_read(struct sbmac_softc *s,int phyaddr,int regidx) +{ + int idx; + int error; + int regval; + int mac_mdio_genc; + + /* + * Synchronize ourselves so that the PHY knows the next + * thing coming down is a command + */ + + sbmac_mii_sync(s); + + /* + * Send the data to the PHY. The sequence is + * a "start" command (2 bits) + * a "read" command (2 bits) + * the PHY addr (5 bits) + * the register index (5 bits) + */ + + sbmac_mii_senddata(s,MII_COMMAND_START, 2); + sbmac_mii_senddata(s,MII_COMMAND_READ, 2); + sbmac_mii_senddata(s,phyaddr, 5); + sbmac_mii_senddata(s,regidx, 5); + + mac_mdio_genc = SBMAC_READCSR(s->sbm_mdio) & M_MAC_GENC; + + /* + * Switch the port around without a clock transition. + */ + SBMAC_WRITECSR(s->sbm_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); + + /* + * Send out a clock pulse to signal we want the status + */ + + SBMAC_WRITECSR(s->sbm_mdio, + M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); + + /* + * If an error occurred, the PHY will signal '1' back + */ + error = SBMAC_READCSR(s->sbm_mdio) & M_MAC_MDIO_IN; + + /* + * Issue an 'idle' clock pulse, but keep the direction + * the same. + */ + SBMAC_WRITECSR(s->sbm_mdio, + M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); + + regval = 0; + + for (idx = 0; idx < 16; idx++) { + regval <<= 1; + + if (error == 0) { + if (SBMAC_READCSR(s->sbm_mdio) & M_MAC_MDIO_IN) + regval |= 1; + } + + SBMAC_WRITECSR(s->sbm_mdio, + M_MAC_MDIO_DIR_INPUT|M_MAC_MDC | mac_mdio_genc); + SBMAC_WRITECSR(s->sbm_mdio, + M_MAC_MDIO_DIR_INPUT | mac_mdio_genc); + } + + /* Switch back to output */ + SBMAC_WRITECSR(s->sbm_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc); + + if (error == 0) + return regval; + return 0; +} + + +/********************************************************************** + * SBMAC_MII_WRITE(s,phyaddr,regidx,regval) + * + * Write a value to a PHY register. + * + * Input parameters: + * s - sbmac structure + * phyaddr - PHY to use + * regidx - register within the PHY + * regval - data to write to register + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbmac_mii_write(struct sbmac_softc *s,int phyaddr,int regidx, + unsigned int regval) +{ + int mac_mdio_genc; + + sbmac_mii_sync(s); + + sbmac_mii_senddata(s,MII_COMMAND_START,2); + sbmac_mii_senddata(s,MII_COMMAND_WRITE,2); + sbmac_mii_senddata(s,phyaddr, 5); + sbmac_mii_senddata(s,regidx, 5); + sbmac_mii_senddata(s,MII_COMMAND_ACK,2); + sbmac_mii_senddata(s,regval,16); + + mac_mdio_genc = SBMAC_READCSR(s->sbm_mdio) & M_MAC_GENC; + + SBMAC_WRITECSR(s->sbm_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc); +} + + + +/********************************************************************** + * SBDMA_INITCTX(d,s,chan,txrx,maxdescr) + * + * Initialize a DMA channel context. Since there are potentially + * eight DMA channels per MAC, it's nice to do this in a standard + * way. + * + * Input parameters: + * d - sbmacdma_t structure (DMA channel context) + * s - sbmac_softc structure (pointer to a MAC) + * chan - channel number (0..1 right now) + * txrx - Identifies DMA_TX or DMA_RX for channel direction + * maxdescr - number of descriptors + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbdma_initctx(sbmacdma_t *d, + struct sbmac_softc *s, + int chan, + int txrx, + int maxdescr) +{ + /* + * Save away interesting stuff in the structure + */ + + d->sbdma_eth = s; + d->sbdma_channel = chan; + d->sbdma_txdir = txrx; + +#if 0 + /* RMON clearing */ + s->sbe_idx =(s->sbm_base - A_MAC_BASE_0)/MAC_SPACING; +#endif + + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_BYTES)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_COLLISIONS)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_LATE_COL)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_EX_COL)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_FCS_ERROR)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_ABORT)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_BAD)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_GOOD)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_RUNT)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_OVERSIZE)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BYTES)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_MCAST)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BCAST)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BAD)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_GOOD)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_RUNT)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_OVERSIZE)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_FCS_ERROR)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_LENGTH_ERROR)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_CODE_ERROR)), 0); + SBMAC_WRITECSR(IOADDR( + A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_ALIGN_ERROR)), 0); + + /* + * initialize register pointers + */ + + d->sbdma_config0 = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG0); + d->sbdma_config1 = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG1); + d->sbdma_dscrbase = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_BASE); + d->sbdma_dscrcnt = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT); + d->sbdma_curdscr = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR); + + /* + * Allocate memory for the ring + */ + + d->sbdma_maxdescr = maxdescr; + + d->sbdma_dscrtable = (sbdmadscr_t *) + kmalloc(d->sbdma_maxdescr*sizeof(sbdmadscr_t), GFP_KERNEL); + + memset(d->sbdma_dscrtable,0,d->sbdma_maxdescr*sizeof(sbdmadscr_t)); + + d->sbdma_dscrtable_end = d->sbdma_dscrtable + d->sbdma_maxdescr; + + d->sbdma_dscrtable_phys = virt_to_phys(d->sbdma_dscrtable); + + /* + * And context table + */ + + d->sbdma_ctxtable = (struct sk_buff **) + kmalloc(d->sbdma_maxdescr*sizeof(struct sk_buff *), GFP_KERNEL); + + memset(d->sbdma_ctxtable,0,d->sbdma_maxdescr*sizeof(struct sk_buff *)); + +#ifdef CONFIG_SBMAC_COALESCE + /* + * Setup Rx/Tx DMA coalescing defaults + */ + + if ( int_pktcnt ) { + d->sbdma_int_pktcnt = int_pktcnt; + } else { + d->sbdma_int_pktcnt = 1; + } + + if ( int_timeout ) { + d->sbdma_int_timeout = int_timeout; + } else { + d->sbdma_int_timeout = 0; + } +#endif + +} + +/********************************************************************** + * SBDMA_CHANNEL_START(d) + * + * Initialize the hardware registers for a DMA channel. + * + * Input parameters: + * d - DMA channel to init (context must be previously init'd + * rxtx - DMA_RX or DMA_TX depending on what type of channel + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbdma_channel_start(sbmacdma_t *d, int rxtx ) +{ + /* + * Turn on the DMA channel + */ + +#ifdef CONFIG_SBMAC_COALESCE + SBMAC_WRITECSR(d->sbdma_config1, + V_DMA_INT_TIMEOUT(d->sbdma_int_timeout) | + 0); + SBMAC_WRITECSR(d->sbdma_config0, + M_DMA_EOP_INT_EN | + V_DMA_RINGSZ(d->sbdma_maxdescr) | + V_DMA_INT_PKTCNT(d->sbdma_int_pktcnt) | + 0); +#else + SBMAC_WRITECSR(d->sbdma_config1,0); + SBMAC_WRITECSR(d->sbdma_config0, + V_DMA_RINGSZ(d->sbdma_maxdescr) | + 0); +#endif + + SBMAC_WRITECSR(d->sbdma_dscrbase,d->sbdma_dscrtable_phys); + + /* + * Initialize ring pointers + */ + + d->sbdma_addptr = d->sbdma_dscrtable; + d->sbdma_remptr = d->sbdma_dscrtable; +} + +/********************************************************************** + * SBDMA_CHANNEL_STOP(d) + * + * Initialize the hardware registers for a DMA channel. + * + * Input parameters: + * d - DMA channel to init (context must be previously init'd + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbdma_channel_stop(sbmacdma_t *d) +{ + /* + * Turn off the DMA channel + */ + + SBMAC_WRITECSR(d->sbdma_config1,0); + + SBMAC_WRITECSR(d->sbdma_dscrbase,0); + + SBMAC_WRITECSR(d->sbdma_config0,0); + + /* + * Zero ring pointers + */ + + d->sbdma_addptr = 0; + d->sbdma_remptr = 0; +} + +static void sbdma_align_skb(struct sk_buff *skb,int power2,int offset) +{ + unsigned long addr; + unsigned long newaddr; + + addr = (unsigned long) skb->data; + + newaddr = (addr + power2 - 1) & ~(power2 - 1); + + skb_reserve(skb,newaddr-addr+offset); +} + + +/********************************************************************** + * SBDMA_ADD_RCVBUFFER(d,sb) + * + * Add a buffer to the specified DMA channel. For receive channels, + * this queues a buffer for inbound packets. + * + * Input parameters: + * d - DMA channel descriptor + * sb - sk_buff to add, or NULL if we should allocate one + * + * Return value: + * 0 if buffer could not be added (ring is full) + * 1 if buffer added successfully + ********************************************************************* */ + + +static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *sb) +{ + sbdmadscr_t *dsc; + sbdmadscr_t *nextdsc; + struct sk_buff *sb_new = NULL; + int pktsize = ENET_PACKET_SIZE; + + /* get pointer to our current place in the ring */ + + dsc = d->sbdma_addptr; + nextdsc = SBDMA_NEXTBUF(d,sbdma_addptr); + + /* + * figure out if the ring is full - if the next descriptor + * is the same as the one that we're going to remove from + * the ring, the ring is full + */ + + if (nextdsc == d->sbdma_remptr) { + return -ENOSPC; + } + + /* + * Allocate a sk_buff if we don't already have one. + * If we do have an sk_buff, reset it so that it's empty. + * + * Note: sk_buffs don't seem to be guaranteed to have any sort + * of alignment when they are allocated. Therefore, allocate enough + * extra space to make sure that: + * + * 1. the data does not start in the middle of a cache line. + * 2. The data does not end in the middle of a cache line + * 3. The buffer can be aligned such that the IP addresses are + * naturally aligned. + * + * Remember, the SOCs MAC writes whole cache lines at a time, + * without reading the old contents first. So, if the sk_buff's + * data portion starts in the middle of a cache line, the SOC + * DMA will trash the beginning (and ending) portions. + */ + + if (sb == NULL) { + sb_new = dev_alloc_skb(ENET_PACKET_SIZE + SMP_CACHE_BYTES * 2 + ETHER_ALIGN); + if (sb_new == NULL) { + printk(KERN_INFO "%s: sk_buff allocation failed\n", + d->sbdma_eth->sbm_dev->name); + return -ENOBUFS; + } + + sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN); + + /* mark skbuff owned by our device */ + sb_new->dev = d->sbdma_eth->sbm_dev; + } + else { + sb_new = sb; + /* + * nothing special to reinit buffer, it's already aligned + * and sb->data already points to a good place. + */ + } + + /* + * fill in the descriptor + */ + +#ifdef CONFIG_SBMAC_COALESCE + /* + * Do not interrupt per DMA transfer. + */ + dsc->dscr_a = virt_to_phys(sb_new->tail) | + V_DMA_DSCRA_A_SIZE(NUMCACHEBLKS(pktsize+ETHER_ALIGN)) | + 0; +#else + dsc->dscr_a = virt_to_phys(sb_new->tail) | + V_DMA_DSCRA_A_SIZE(NUMCACHEBLKS(pktsize+ETHER_ALIGN)) | + M_DMA_DSCRA_INTERRUPT; +#endif + + /* receiving: no options */ + dsc->dscr_b = 0; + + /* + * fill in the context + */ + + d->sbdma_ctxtable[dsc-d->sbdma_dscrtable] = sb_new; + + /* + * point at next packet + */ + + d->sbdma_addptr = nextdsc; + + /* + * Give the buffer to the DMA engine. + */ + + SBMAC_WRITECSR(d->sbdma_dscrcnt,1); + + return 0; /* we did it */ +} + +/********************************************************************** + * SBDMA_ADD_TXBUFFER(d,sb) + * + * Add a transmit buffer to the specified DMA channel, causing a + * transmit to start. + * + * Input parameters: + * d - DMA channel descriptor + * sb - sk_buff to add + * + * Return value: + * 0 transmit queued successfully + * otherwise error code + ********************************************************************* */ + + +static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *sb) +{ + sbdmadscr_t *dsc; + sbdmadscr_t *nextdsc; + uint64_t phys; + uint64_t ncb; + int length; + + /* get pointer to our current place in the ring */ + + dsc = d->sbdma_addptr; + nextdsc = SBDMA_NEXTBUF(d,sbdma_addptr); + + /* + * figure out if the ring is full - if the next descriptor + * is the same as the one that we're going to remove from + * the ring, the ring is full + */ + + if (nextdsc == d->sbdma_remptr) { + return -ENOSPC; + } + + /* + * Under Linux, it's not necessary to copy/coalesce buffers + * like it is on NetBSD. We think they're all contiguous, + * but that may not be true for GBE. + */ + + length = sb->len; + + /* + * fill in the descriptor. Note that the number of cache + * blocks in the descriptor is the number of blocks + * *spanned*, so we need to add in the offset (if any) + * while doing the calculation. + */ + + phys = virt_to_phys(sb->data); + ncb = NUMCACHEBLKS(length+(phys & (SMP_CACHE_BYTES - 1))); + + dsc->dscr_a = phys | + V_DMA_DSCRA_A_SIZE(ncb) | +#ifndef CONFIG_SBMAC_COALESCE + M_DMA_DSCRA_INTERRUPT | +#endif + M_DMA_ETHTX_SOP; + + /* transmitting: set outbound options and length */ + + dsc->dscr_b = V_DMA_DSCRB_OPTIONS(K_DMA_ETHTX_APPENDCRC_APPENDPAD) | + V_DMA_DSCRB_PKT_SIZE(length); + + /* + * fill in the context + */ + + d->sbdma_ctxtable[dsc-d->sbdma_dscrtable] = sb; + + /* + * point at next packet + */ + + d->sbdma_addptr = nextdsc; + + /* + * Give the buffer to the DMA engine. + */ + + SBMAC_WRITECSR(d->sbdma_dscrcnt,1); + + return 0; /* we did it */ +} + + + + +/********************************************************************** + * SBDMA_EMPTYRING(d) + * + * Free all allocated sk_buffs on the specified DMA channel; + * + * Input parameters: + * d - DMA channel + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbdma_emptyring(sbmacdma_t *d) +{ + int idx; + struct sk_buff *sb; + + for (idx = 0; idx < d->sbdma_maxdescr; idx++) { + sb = d->sbdma_ctxtable[idx]; + if (sb) { + dev_kfree_skb(sb); + d->sbdma_ctxtable[idx] = NULL; + } + } +} + + +/********************************************************************** + * SBDMA_FILLRING(d) + * + * Fill the specified DMA channel (must be receive channel) + * with sk_buffs + * + * Input parameters: + * d - DMA channel + * + * Return value: + * nothing + ********************************************************************* */ + +static void sbdma_fillring(sbmacdma_t *d) |