aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/sb1250-mac.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c2920
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)