[PATCH] Xilinx TEMAC driver
David H. Lynch Jr.
dhlii at dlasys.net
Tue Jul 24 19:14:45 EST 2007
Hopefully this is not too much of a mess.
This is a very preliminary patch to add support for the Xilinx TEMAC.
This is closer approximation to a normal linux driver.
There are no external dependencies aside from a properly setup
xparameters.h and
platform data structure for the TEMAC - i.e. no need for xilinx_edk,
xilinx_common, ...
The whole driver is in a single source file.
It autonegotiates, and it actually works in some Pico Firmware where
the MV/Xilinx driver does not.
Somewhere long ago this started out based on the Xilinx code from
the Trek Webserver sample.
As such it is also losely based on the xilinx_edk code.
Hopefully, I remembered to provide proper attribution. This is
preliminary so things like that will get fixed.
It includes support for the LocalLink TEMAC, though the LL_TEMAC
performance is poor, and I could not figure
out anyway to make it interrupt driven so it had a fairly high rate
of dropped packets.
It ONLY supports the FIFO based PLB TEMAC. If you want SGDMA add it
or use the Xilinx/MV driver.
There is alot of cruft in the driver that needs yanked, bits and
peices of #ifdefed out code from the xilinx EDK or ldd3
or whatever I thought I needed, and have not yet been willing to delete.
I am also using dman near the same identical source for a TEMAC
driver for GreenHills, and there are likely some
#ifdef's that only make sense in that context.
At somepoint I will try to clean all of the above crap out.
I also need to clean up my git tree so that I can produce a proper
patch.
Enough caveats - patch follows.
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7d57f4a..fe5aa83 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2332,6 +2337,59 @@ config ATL1
endif # NETDEV_1000
+config PICO_TEMAC
+ tristate "Pico Xilinx 10/100/1000 Mbit Local Link/PLB TEMAC support"
+ depends on XILINX_VIRTEX && !XILINX_OLD_TEMAC && !XILINX_TEMAC
+ select MII
+ select NET_POLL_CONTROLLER
+# select PHYLIB
+ help
+ This driver supports Tri-Mode, 10/100/1000 Mbit EMAC IP
+ from Xilinx EDK.
+
+config PICO_DEBUG_TEMAC
+ bool 'Pico TEMAC Debugging'
+ default y
+ depends on PICO_TEMAC && PICO_DEBUG
+
+
#
# 10 Gigabit Ethernet
#
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a77affa..2248dd4 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -227,3 +227,8 @@ obj-$(CONFIG_NETCONSOLE) += netconsole.o
obj-$(CONFIG_FS_ENET) += fs_enet/
obj-$(CONFIG_NETXEN_NIC) += netxen/
+obj-$(CONFIG_PICO_TEMAC) += temac.o
diff --git a/drivers/net/temac.c b/drivers/net/temac.c
new file mode 100644
index 0000000..01d30c4
--- /dev/null
+++ b/drivers/net/temac.c
@@ -0,0 +1,3742 @@
+/*
+
+ linux/drivers/net/temac.c
+
+ Driver for Xilinx hard temac ethernet NIC's
+
+ Author: David H. Lynch Jr. <dhlii at dlasys.net>
+ Copyright (C) 2005 DLA Systems
+ The author may be reached as dhlii at sdlasys.net, or C/O
+ DLA Systems
+ 354 Rudy Dam rd.
+ Lititz PA 17543
+
+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
+ things that need looked at:
+ process_transmits
+ temac_EtherRead
+ temac_hard_start_xmit
+ ehter_reset
+ temac_get_link_status
+
+$Id: temac.c,v 0.10 2005/12/14 10:03:27 dhlii Exp $
+
+*/
+#define DRV_NAME "temac"
+#define DRV_DESCRIPTION "Xilinx hard Tri-Mode Eth MAC driver"
+#define DRV_VERSION "0.10a"
+#define DRV_RELDATE "07/10/2006"
+
+#if defined(__KERNEL__)
+#define LINUX 1
+#endif
+static const char version[] = DRV_NAME ".c:v" DRV_VERSION " "
DRV_RELDATE " David H. Lynch Jr. (dhlii at dlasys.net)\n";
+
+#if defined(LINUX)
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+
+#include <linux/mii.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/xilinx_devices.h>
+#include <linux/ethtool.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#define FRAME_ALIGNMENT 8 /* Byte
alignment of ethernet frames */
+typedef char EthFrame[9000] __attribute__ ((aligned(FRAME_ALIGNMENT)));
+
+#define TRUE 1
+#define FALSE 0
+
+#define TEMAC_RX_TIMEOUT (jiffies + ((1 * HZ)/5))
+#define TEMAC_TX_TIMEOUT (jiffies + (2 * HZ))
+#define TEMAC_MII_TIMEOUT (jiffies + (2 * HZ)) /* timer
wakeup time : 2 second */
+
+/*
+Transmit timeout, default 5 seconds.
+ */
+static int
+watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+static struct platform_device *temac_device;
+
+/* for exclusion of all program flows (processes, ISRs and BHs) */
+DEFINE_SPINLOCK(XTE_spinlock);
+#define res_size(_r) (((_r)->end - (_r)->start) + 1)
+#define EnetDbPrint(dev,msg)
+#define Success 0
+#define Failure -1
+#define Error int
+
+
+#else
+
+#define EIO 500
+
+// additional MII defines
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+
+#define MII_CTRL1000 0x09 /* 1000BASE-T control */
+#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full
duplex */
+#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_ESTATUS 0x0f /* Extended Status */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+#define MAX_TEMAC_DEVS 2
+
+#ifndef NUM_TX_BDS
+# define NUM_TX_BDS 32
+#endif
+#ifndef NUM_RX_BDS
+# define NUM_RX_BDS 32
+#endif
+
+
+#include "miiphy.h"
+#include "enet_iodevice.h"
+#include "interrupt.h"
+#include "interruptcontroller.h"
+#include "cksum.h"
+#include <string.h>
+
+#define spin_lock(lock)
+#define spin_unlock(lock)
+#define spin_lock_irqsave(lock, flags)
+#define spin_unlock_irqrestore(lock, flags)
+int is_valid_ether_addr(const UINT1 *addr);
+// #define is_valid_ether_addr(addr) 0 // this is in Pico BSP now
+#define mfdcr(addr) -1
+#define mtdcr(addr,val)
+#define netif_wake_queue(lp) 0
+#define netif_stop_queue(lp) 0
+#define netif_rx(lp) 0
+#define printk_ratelimit() 0
+#define eth_type_trans(skb, lp) 0
+typedef unsigned long spinlock_t;
+typedef unsigned long irqreturn_t;
+#define IRQ_HANDLED EVENT_HANDLED
+
+
+
+#define MAX_CACHE_LINE_SIZE 64
+
+#define NUM_RX_EVENTS (NUM_RX_BDS)
+#define NUM_TX_EVENTS (NUM_TX_BDS * 2)
+
+#ifndef NUM_CLOSE_ACTIONS
+# define NUM_CLOSE_ACTIONS 32
+#endif
+
+#ifndef MAXMOVELEN
+# define MAXMOVELEN 12800
+#endif
+
+#define MINIMUM_PACKET_LENGTH 64
+
+typedef unsigned long u32;
+typedef unsigned int u16;
+typedef unsigned char u8;
+
+/*
+ * Network device statistics. Akin to the 2.0 ether stats but
+ * with byte counters.
+ */
+
+struct net_device_stats
+{
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long multicast; /* multicast packets received */
+ unsigned long collisions;
+
+ /* detailed rx_errors: */
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors; /* receiver ring buff overflow */
+ unsigned long rx_crc_errors; /* recved pkt with crc error */
+ unsigned long rx_frame_errors; /* recv'd frame alignment error */
+ unsigned long rx_fifo_errors; /* recv'r fifo overrun */
+ unsigned long rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ unsigned long tx_aborted_errors;
+ unsigned long tx_carrier_errors;
+ unsigned long tx_fifo_errors;
+ unsigned long tx_heartbeat_errors;
+ unsigned long tx_window_errors;
+
+ /* for cslip etc */
+ unsigned long rx_compressed;
+ unsigned long tx_compressed;
+};
+
+struct timer_list
+{
+ unsigned long rx_packets; /* total packets received */
+};
+
+#define SKB_RX (1 << 0)
+#define SKB_TX (1 << 1)
+#define SKB_INUSE (1 << 2)
+#define SKB_ALLOC (1 << 3)
+#define SKB_FULL (1 << 4)
+#define SKB_PAD (1 << 5)
+
+struct sk_buff {
+ UINT4 flags; // asorted flag bits
+ UINT4 fifo_data; // fifo address
+ UINT4 fifo_reg; // fifo address
+ UINT4 size;
+ UINT4 len; // Owner, Status,
Buffer Length
+ UINT4 user; // values from ghs
+ UINT4 user_data; // value from ghs
+ UINT4 protocol;
+ struct temac_local * dev;
+ unsigned char *data;
+ unsigned char *tail;
+} ;
+
+#define net_device temac_local
+#define jiffies 0
+#endif
+
+// board types
+#define TEMAC_LL 0
+#define TEMAC_PLB 1
+
+#define PHY_DCR 1
+
+#define ALIGNMENT_SEND 8
+#define ALIGNMENT_RECV 8
+
+#define MII_ANI 0x10
+#define PHY_NUM 0
+
+#define MII_SSR 0x11
+#define MII_SSR_LINK (1 << 10)
+#define MII_SSR_SPDMASK 0xC000
+#define MII_SSR_SPD1000 (1 << 15)
+#define MII_SSR_SPD100 (1 << 14)
+#define MII_SSR_SPD10 0
+#define MII_SSR_FD (1 << 13)
+
+#define MII_ISR 0x13
+
+// packet size info
+#define XTE_MTU 1500 /* max
MTU size of Ethernet frame */
+#define XTE_HDR_SIZE 14 /* size
of Ethernet header */
+#define XTE_TRL_SIZE 4 /* size
of Ethernet trailer (FCS) */
+#define XTE_MAX_FRAME_SIZE (XTE_MTU + XTE_HDR_SIZE +
XTE_TRL_SIZE)
+
+#define TIMEOUT_ERROR -1
+#define SRC_RDY_TIMEOUT_ERROR -2
+#define SOF_TIMEOUT_ERROR -3
+#define UNKNOWN_ERROR -4
+
+#define XST_FAILURE 1L
+#define XST_DEVICE_NOT_FOUND 2L
+#define XST_DEVICE_IS_STARTED 5L
+#define XST_DEVICE_IS_STOPPED 6L
+#define XST_FIFO_ERROR 7L /* an
error occurred during an operation with a FIFO such as an underrun or
overrun, this error requires the device to be reset */
+#define XST_NOT_POLLED 10L /* the
device is not configured for polled mode operation */
+#define XST_FIFO_NO_ROOM 11L /* a
FIFO did not have room to put the specified data into */
+#define XST_NO_DATA 13L /*
there was no data available */
+#define XST_NO_FEATURE 19L /*
device is not configured with the requested feature */
+#define XST_DATA_LOST 26L /*
driver defined error */
+#define XST_RECV_ERROR 27L /*
generic receive error */
+#define XST_SEND_ERROR 28L /*
generic transmit error */
+#define XST_PFIFO_ERROR 504L /*
generic packet FIFO error */
+#define XST_PFIFO_DEADLOCK 505L /*
packet FIFO is reporting * empty and full simultaneously */
+#define XST_IPIF_ERROR 541L /*
generic ipif error */
+
+
+
+/** Configuration options
+ *
+ * Device configuration options. See the temac_setoptions(),
+ * XTemac_ClearOptions() and XTemac_GetOptions() for information on how
to use
+ * options.
+ *
+ * The default state of the options are noted and are what the device
and driver
+ * will be set to after calling XTemac_Reset() or XTemac_Initialize().
+ *
+ */
+
+#define XTE_PROMISC_OPTION (1 << 0) /**<
Accept all incoming packets. This option defaults to disabled (cleared) */
+#define XTE_JUMBO_OPTION (1 << 1) /**<
Jumbo frame support for Tx & Rx. This option defaults to disabled
(cleared) */
+#define XTE_VLAN_OPTION (1 << 2) /**<
VLAN Rx & Tx frame support. This option defaults to disabled (cleared) */
+#define XTE_FLOW_CONTROL_OPTION (1 << 4) /**<
Enable recognition of flow control frames on Rx This option defaults to
enabled (set) */
+#define XTE_FCS_STRIP_OPTION (1 << 5) /**<
Strip FCS and PAD from incoming frames. Note: PAD from VLAN frames is
not stripped. This option defaults to disabled (set) */
+#define XTE_FCS_INSERT_OPTION (1 << 6) /**<
Generate FCS field and add PAD automatically for outgoing frames. This
option defaults to enabled (set) */
+#define XTE_LENTYPE_ERR_OPTION (1 << 7) /**<
Enable Length/Type error checking for incoming frames. When this option is
+
set, the MAC will filter frames that have a mismatched type/length field
+
and if XTE_REPORT_RXERR_OPTION is set, the user is notified when these
+
types of frames are encountered. When this option is cleared, the MAC will
+
allow these types of frames to be received.
+
This option defaults to enabled (set) */
+#define XTE_POLLED_OPTION (1 << 9) /**<
Polled mode communications.
+
Users may enter/exit polled mode
+
from any interrupt driven mode.
+
This option defaults to disabled (cleared) */
+
+#define XTE_REPORT_RXERR_OPTION (1 << 10) /**<
Enable reporting of dropped receive packets due to errors This option
defaults to enabled (set) */
+#define XTE_TRANSMITTER_ENABLE_OPTION (1 << 11) /**<
Enable the transmitter. This option defaults to enabled (set) */
+#define XTE_RECEIVER_ENABLE_OPTION (1 << 12) /**<
Enable the receiver This option defaults to enabled (set) */
+#define XTE_BROADCAST_OPTION (1 << 13) /**<
Allow reception of the broadcast address This option defaults to enabled
(set) */
+#define XTE_MULTICAST_CAM_OPTION (1 << 14) /**<
Allows reception of multicast addresses programmed into CAM This option
defaults to disabled (clear) */
+#define XTE_REPORT_TXSTATUS_OVERRUN_OPTION (1 << 15) /**<
Enable reporting the overrun of the Transmit status FIFO. This type of
+
error is latched by HW and can be cleared only by a reset. SGDMA systems,
+
this option should be enabled since the DMA engine is responsible for
+
keeping this from occurring. For FIFO direct systems, this error may be
+ a
nuisance because a SW system may be able to transmit frames faster
+
than the interrupt handler can handle retrieving statuses.
+
This option defaults to enabled (set) */
+#define XTE_ANEG_OPTION (1 << 16) /**<
Enable autonegotiation interrupt This option defaults to disabled (clear) */
+
+#define XTE_DEFAULT_OPTIONS \
+ (XTE_FLOW_CONTROL_OPTION | \
+ XTE_BROADCAST_OPTION | \
+ XTE_FCS_INSERT_OPTION | \
+ XTE_FCS_STRIP_OPTION | \
+ XTE_LENTYPE_ERR_OPTION | \
+ XTE_TRANSMITTER_ENABLE_OPTION | \
+ XTE_REPORT_RXERR_OPTION | \
+ XTE_REPORT_TXSTATUS_OVERRUN_OPTION | \
+ XTE_RECEIVER_ENABLE_OPTION) /**<
Default options set when device is initialized or reset */
+
+
+#define XTE_END_OF_PACKET 1 /**<
The data written is the last for the * current packet */
+#define XTE_PARTIAL_PACKET 0 /**<
There is more data to come for the * current packet */
+
+//------------------------------------------------
+
+#define PHY_TIMEOUT 10000
+
+/** Register offsets
+ *
+ * These constants define the offsets to each of the registers from the
+ * register base address, each of the constants are a number of bytes
+ */
+#define XPF_V200A_RESET_REG_OFFSET 0UL
/**< Reset register */
+#define XPF_V200A_COUNT_STATUS_REG_OFFSET 4UL
/**< Count/Status register */
+
+#define XPF_V200A_RESET_FIFO_MASK 0x0000000A /**
* This constant is used with the Reset Register */
+
+/** Occupancy/Vacancy Count Register constants
+ */
+/** Constant used with the Occupancy/Vacancy Count Register. This
+ * register also contains FIFO status
+ */
+#define XPF_V200A_COUNT_MASK 0x00FFFFFF
+
+
+/************************** Constant Definitions
*****************************/
+
+#define XTE_RESET_IPIF_DELAY_US 1 /**<
Number of Us to delay after IPIF reset */
+
+/* Register offset definitions. Unless otherwise noted, register access
is 32 bit. */
+/** IPIF interrupt and reset registers */
+#define XTE_DISR_OFFSET 0x00000000 /**<
Device interrupt status */
+#define XTE_DIPR_OFFSET 0x00000004 /**<
Device interrupt pending */
+/** Interrupt status bits for top level interrupts
+ * These bits are associated with the XTE_DISR_OFFSET, XTE_DIPR_OFFSET,
+ * and XTE_DIER_OFFSET registers.
+ */
+#define XTE_DXR_SEND_FIFO_MASK (1 << 6) /**<
Send FIFO channel */
+#define XTE_DXR_RECV_FIFO_MASK (1 << 5) /**<
Receive FIFO channel */
+#define XTE_DXR_CORE_MASK (1 << 2) /**<
Core */
+#define XTE_DXR_DPTO_MASK (1 << 1) /**<
Data phase timeout */
+#define XTE_DXR_TERR_MASK (1 << 0) /**<
Transaction error */
+#define XTE_DIER_OFFSET 0x00000008 /**<
Device interrupt enable */
+#define XTE_DGIE_OFFSET 0x0000001C /**<
Device global interrupt enable */
+#define XTE_DGIE_ENABLE_MASK (1 << 31) /**<
Write this value to DGIE to enable interrupts from this device */
+#define XTE_IPISR_OFFSET 0x00000020 /**< IP
interrupt status */
+#define XTE_IPIER_OFFSET 0x00000028 /**< IP
interrupt enable */
+#define XTE_DSR_OFFSET 0x00000040 /**<
Device software reset (write) */
+#define XTE_DSR_RESET_MASK 0x0000000A /**<
Write this value to DSR to reset entire core */
+// #define XTE_MIR_OFFSET 0x00000040 /**<
Identification (read) */
+
+/** Interrupt status bits for MAC interrupts
+ * These bits are associated with XTE_IPISR_OFFSET and XTE_IPIER_OFFSET
+ * registers.
+ *
+ */
+#define XTE_IPXR_XMIT_DONE_MASK (1 << 0) /**< Tx
complete MUST read TSR to clear */
+#define XTE_IPXR_RECV_DONE_MASK (1 << 1) /**< Rx
complete MUST read RSR to clear */
+#define XTE_IPXR_AUTO_NEG_MASK (1 << 2) /**<
Auto negotiation complete */
+#define XTE_IPXR_RECV_REJECT_MASK (1 << 3) /**< Rx
packet rejected */
+//#define XTE_IPXR_XMIT_SFIFO_EMPTY_MASK (1 << 4) /**< Tx
status fifo empty */
+//#define XTE_IPXR_RECV_LFIFO_EMPTY_MASK (1 << 5) /**< Rx
length fifo empty */
+#define XTE_IPXR_XMIT_LFIFO_FULL_MASK (1 << 6) /**< Tx
length fifo full */
+#define XTE_IPXR_RECV_LFIFO_OVER_MASK (1 << 7) /**< Rx
length fifo overrun Note that this signal is no longer asserted by HW */
+#define XTE_IPXR_RECV_LFIFO_UNDER_MASK (1 << 8) /**< Rx
length fifo underrun requires RESET to clear */
+#define XTE_IPXR_XMIT_SFIFO_OVER_MASK (1 << 9) /**< Tx
status fifo overrun requires RESET to clear */
+#define XTE_IPXR_XMIT_SFIFO_UNDER_MASK (1 << 10) /**< Tx
status fifo underrun requires RESET to clear */
+#define XTE_IPXR_XMIT_LFIFO_OVER_MASK (1 << 11) /**< Tx
length fifo overrun requires RESET to clear */
+#define XTE_IPXR_XMIT_LFIFO_UNDER_MASK (1 << 12) /**< Tx
length fifo underrun requires RESET to clear */
+#define XTE_IPXR_RECV_PFIFO_ABORT_MASK (1 << 13) /**< Rx
packet rejected due to full packet FIFO */
+#define XTE_IPXR_RECV_LFIFO_ABORT_MASK (1 << 14) /**< Rx
packet rejected due to full length FIFO */
+#define XTE_IPXR_MII_PEND_MASK (1 << 15) /**<
Mii operation now pending */
+#define XTE_IPXR_MII_DONE_MASK (1 << 16) /**<
Mii operation has completed */
+#define XTE_IPXR_XMIT_PFIFO_UNDER_MASK (1 << 17) /**< Tx
packet FIFO underrun requires RESET to clear */
+//#define XTE_IPXR_XMIT_DMA_MASK (1 << 19) /**< Rx
dma channel */
+//#define XTE_IPXR_RECV_DMA_MASK (1 << 20) /**< Tx
dma channel */
+#define XTE_IPXR_RECV_FIFO_LOCK_MASK (1 << 21) /**< Rx
FIFO deadlock requires RESET to clear */
+#define XTE_IPXR_XMIT_FIFO_LOCK_MASK (1 << 22) /**< Tx
FIFO deadlock requires RESET to clear */
+
+
+#define XTE_IPXR_RECV_DROPPED_MASK \
+ (XTE_IPXR_RECV_REJECT_MASK | \
+ XTE_IPXR_RECV_PFIFO_ABORT_MASK | \
+ XTE_IPXR_RECV_LFIFO_ABORT_MASK) /**<
IPXR bits that indicate a dropped receive frame */
+
+#define XTE_IPXR_RECV_ERROR_MASK \
+ (XTE_IPXR_RECV_DROPPED_MASK | \
+ XTE_IPXR_RECV_LFIFO_UNDER_MASK) /**<
IPXR bits that indicate receive errors */
+
+#define XTE_IPXR_FIFO_FATAL_ERROR_MASK \
+ (XTE_IPXR_RECV_FIFO_LOCK_MASK | \
+ XTE_IPXR_XMIT_FIFO_LOCK_MASK | \
+ XTE_IPXR_XMIT_PFIFO_UNDER_MASK | \
+ XTE_IPXR_XMIT_LFIFO_UNDER_MASK | \
+ XTE_IPXR_XMIT_LFIFO_OVER_MASK | \
+ XTE_IPXR_XMIT_SFIFO_UNDER_MASK | \
+ XTE_IPXR_XMIT_SFIFO_OVER_MASK | \
+ XTE_IPXR_RECV_LFIFO_UNDER_MASK) /**<
IPXR bits that indicate fatal FIFO errors. These bits can only be
cleared by a device reset */
+
+
+/** IPIF transmit and receive packet fifo base offsets
+ * Individual registers and bit definitions are defined in
+ * xpacket_fifo_l_v2_00_a.h. This register group is not
accessible if
+ * the device instance is configured for SGDMA.
+ */
+#define XTE_PFIFO_TXREG_OFFSET 0x00002000 /**<
Packet FIFO Tx channel */
+#define XTE_PFIFO_TXDATA_OFFSET 0x00002100 /**<
IPIF Tx packet fifo port */
+
+#define XTE_PFIFO_RXREG_OFFSET 0x00002010 /**<
Packet FIFO Rx channel */
+#define XTE_PFIFO_RXDATA_OFFSET 0x00002200 /**<
IPIF Rx packet fifo port */
+// #define XTE_IPIF_ISC_TXISR_OFFSET 0x00002318
+// #define XTE_IPIF_ISC_TXIER_OFFSET 0x0000231c
+// #define XTE_IPIF_ISC_RXISR_OFFSET 0x00002358
+// #define XTE_IPIF_ISC_RXIER_OFFSET 0x0000235c
+
+/** PLB_TEMAC registers. The TPLR, TSR, RPLR, and RSR are not accessible
+ * when a device instance is configured for SGDMA. LLPS is not
accessible
+ * when a device instance is configured for FIFO direct.
+ */
+#define XTE_CR_OFFSET 0x00001000 /**<
Control Register (CR) */
+#define XTE_CR_BCREJ_MASK (1 << 2) /**<
Disable broadcast address filtering */
+#define XTE_CR_MCREJ_MASK (1 << 1) /**<
Disable multicast address filtering */
+#define XTE_CR_HRST_MASK (1 << 0) /**<
Reset the hard TEMAC core */
+
+#define XTE_TPLR_OFFSET 0x00001004 /**< Tx
packet length (FIFO) */
+#define XTE_TSR_OFFSET 0x00001008 /**< Tx
status (FIFO) */
+#define XTE_TSR_TXED_MASK 0x80000000 /**<
Excess deferral error */
+#define XTE_TSR_PFIFOU_MASK 0x40000000 /**<
Packet FIFO underrun */
+#define XTE_TSR_TXLC_MASK 0x01000000 /**<
Late collision error */
+#define XTE_TSR_ERROR_MASK (XTE_TSR_TXED_MASK | XTE_TSR_PFIFOU_MASK |
XTE_TSR_TXLC_MASK) /**< TSR bits that indicate an error */
+#define XTE_RPLR_OFFSET 0x0000100C /**< Rx
packet length (FIFO) */
+#define XTE_RSR_OFFSET 0x00001010 /**<
Receive status */
+#define XTE_TPPR_OFFSET 0x00001014 /**< Tx
pause packet */
+// #define XTE_LLPS_OFFSET 0x00001018 /**<
LLINK PFIFO status */
+
+
+/** HARD_TEMAC Core Registers
+ * These are registers defined within the device's hard core located in the
+ * processor block. They are accessed with the host interface. These
registers
+ * are addressed offset by XTE_HOST_IPIF_OFFSET or by the DCR base address
+ * if so configured.
+ *
+ * Access to these registers should go through macros
XIo_In32(XTE_HOST_IPIF_OFFSET+)
+ * and XIo_Out32(XTE_HOST_IPIF_OFFSET+) to guarantee proper access.
+ */
+#define XTE_HOST_IPIF_OFFSET 0x00003000 /**<
Offset of host registers when memory mapped into IPIF */
+#define XTE_RXC0_OFFSET 0x00000200 /**< Rx
configuration word 0 */
+#define XTE_RXC1_OFFSET 0x00000240 /**< Rx
configuration word 1 */
+//#define XTE_RXC1_RXRST_MASK (1 << 31) /**<
Receiver reset */
+#define XTE_RXC1_RXJMBO_MASK (1 << 30) /**<
Jumbo frame enable */
+#define XTE_RXC1_RXFCS_MASK (1 << 29) /**<
FCS not stripped */
+#define XTE_RXC1_RXEN_MASK (1 << 28) /**<
Receiver enable */
+#define XTE_RXC1_RXVLAN_MASK (1 << 27) /**<
VLAN enable */
+#define XTE_RXC1_RXHD_MASK (1 << 26) /**<
Half duplex */
+#define XTE_RXC1_RXLT_MASK (1 << 25) /**<
Length/type check disable */
+//#define XTE_RXC1_ERXC1_MASK 0x0000FFFF /**<
Pause frame source address bits [47:32]. Bits [31:0] are stored in
register ERXC0 */
+
+#define XTE_TXC_OFFSET 0x00000280 /**< Tx
configuration */
+#define XTE_TXC_TXRST_MASK (1 << 31) /**<
Transmitter reset */
+#define XTE_TXC_TXJMBO_MASK (1 << 30) /**<
Jumbo frame enable */
+#define XTE_TXC_TXFCS_MASK (1 << 29) /**<
Generate FCS */
+#define XTE_TXC_TXEN_MASK (1 << 28) /**<
Transmitter enable */
+#define XTE_TXC_TXVLAN_MASK (1 << 27) /**<
VLAN enable */
+#define XTE_TXC_TXHD_MASK (1 << 26) /**<
Half duplex */
+#define XTE_TXC_TXIFG_MASK (1 << 25) /**<
IFG adjust enable */
+
+#define XTE_FCC_OFFSET 0x000002C0 /**<
Flow control configuration */
+#define XTE_FCC_RXFLO_MASK (1 << 29) /**< Rx
flow control enable */
+//#define XTE_FCC_TXFLO_MASK (1 << 30) /**< Tx
flow control enable */
+
+#define XTE_EMCFG_OFFSET 0x00000300 /**<
EMAC configuration */
+#define XTE_EMCFG_LINKSPD_MASK 0xC0000000 /**<
Link speed */
+//#define XTE_EMCFG_RGMII_MASK (1 << 29) /**<
RGMII mode enable */
+//#define XTE_EMCFG_SGMII_MASK (1 << 28) /**<
SGMII mode enable */
+//#define XTE_EMCFG_1000BASEX_MASK (1 << 27) /**<
1000BaseX mode enable */
+//#define XTE_EMCFG_HOSTEN_MASK (1 << 26) /**<
Host interface enable */
+//#define XTE_EMCFG_TX16BIT (1 << 25) /**< 16
bit Tx client enable */
+//#define XTE_EMCFG_RX16BIT (1 << 24) /**< 16
bit Rx client enable */
+#define XTE_EMCFG_LINKSPD_10 0x00000000 /**<
XTE_EMCFG_LINKSPD_MASK for 10 Mbit */
+#define XTE_EMCFG_LINKSPD_100 (1 << 30) /**<
XTE_EMCFG_LINKSPD_MASK for 100 Mbit */
+#define XTE_EMCFG_LINKSPD_1000 (1 << 31) /**<
XTE_EMCFG_LINKSPD_MASK for 1000 Mbit */
+
+// #define XTE_GMIC_OFFSET 0x00000320 /**<
RGMII/SGMII configuration */
+#define XTE_MC_OFFSET 0x00000340 /**<
Management configuration */
+#define XTE_MC_MDIO_MASK (1 << 6) /**<
MII management enable */
+#define XTE_MDIO_CLOCK_DIV_100MHz 0x28 /* 100
MHz host clock */
+#define XTE_MDIO_DIV_DFT 29 /*
Default MDIO clock divisor */
+// #define XTE_MC_CLK_DVD_MAX 0x3F /**<
Maximum MDIO divisor */
+
+#define XTE_UAW0_OFFSET 0x00000380 /**<
Unicast address word 0 */
+#define XTE_UAW1_OFFSET 0x00000384 /**<
Unicast address word 1 */
+#define XTE_UAW1_MASK 0x0000FFFF /**<
Station address bits [47:32] Station address bits [31:0] are stored in
register UAW0 */
+
+#define XTE_MAW0_OFFSET 0x00000388 /**<
Multicast address word 0 */
+#define XTE_MAW1_OFFSET 0x0000038C /**<
Multicast address word 1 */
+#define XTE_AFM_OFFSET 0x00000390 /**<
Promisciuous mode */
+#define XTE_AFM_EPPRM_MASK (1 << 31) /**<
Promiscuous mode enable */
+
+#define XGP_IRSTATUS 0x3A0 /*
Interrupt Request status */
+#define XGP_IRENABLE 0x3A4 /*
Interrupt Request enable */
+
+ /** MII Mamagement Control register (MGTCR) */
+#define XTE_MGTDR_OFFSET 0x000003B0 /**<
MII data */
+#define XGP_E0_MIIM_ADDR 0x000003B4 /**<
MII control */
+#define XTE_MGTCR_RWN_MASK (1 << 10) /**<
Read-not-write,0=read 1=write */
+#define XTE_MGTCR_PHYAD_MASK 0x000003E0 /**<
PHY address */
+#define XTE_MGTCR_REGAD_MASK 0x0000001F /**<
PHY register address */
+#define XTE_MGTCR_PHYAD_SHIFT_MASK 5 /**<
Shift bits for PHYAD */
+#define MGTCR_PHYAD(x) ((x & 0x1f) << 5) /**< PHY
address */
+#define MGTCR_REGAD(x) (x & 0x1f) /**< PHY
register address */
+
+
+#define PFIFO_64BIT_WIDTH_BYTES 8
+
+#define XGP_HIF_BASEADDR 0x10c
+#define XGP_HIF_DATA_REG_MSW_OFFSET 0x000
+#define XGP_HIF_DATA_REG_LSW_OFFSET 0x001
+#define XGP_HIF_CNTL_REG_OFFSET 0x002
+#define XGP_CON_REG_PERIPH_RESET (1 << 31)
+#define XGP_CON_REG_TEMAC_RESET (1 << 30)
+#define XGP_CON_REG_PHY_RESET (1 << 29)
+#define XGP_HIF_RDY_STATUS_OFFSET 0x003
+
+/** DCR Ready Status Register masks for EMAC0 */
+#define XGP_HIF_RDYSTAT_CONFIG0_READ_MASK (1 << 5)
+#define XGP_HIF_RDYSTAT_CONFIG0_WRITE_MASK (1 << 6)
+#define XGP_HIF_RDYSTAT_AF0_WRITE_MASK (1 << 4)
+#define XGP_HIF_RDYSTAT_AF0_READ_MASK (1 << 3)
+#define XGP_HIF_RDYSTAT_MIIM0_WRITE_MASK (1 << 2)
+#define XGP_HIF_RDYSTAT_MIIM0_READ_MASK (1 << 1)
+
+/** DCR control register CntlReg masks * */
+#define XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE (1 << 15) /*
Write enable */
+
+
+
+#define db_printf DEBUG_PRINTK
+/* use 0 for production, 1 for verification, >1 for debug */
+#if defined(CONFIG_PICO_DEBUG_TEMAC)
+#define DEBUG_FUNC if (lp->dbg)
{dbg_printk("\n%s:%s()",DRV_NAME, __FUNCTION__);}
+#define DEBUG_FUNC_EXIT if (lp->dbg) {dbg_printk("\n%s:%s()
exit",DRV_NAME,__FUNCTION__);}
+#define DEBUG_FUNC_EX(ret) if (lp->dbg)
{dbg_printk("\n%s:%s(%d)exit",DRV_NAME,__FUNCTION__,ret);}
+#define DEBUG_PRINTL(args...) if (lp->dbg) dbg_printk("\n" __FILE__
": " args)
+#define DEBUG_PRINTK(args...) if (lp->dbg) dbg_printk(args)
+#define DEBUG_PUTS(fmt...) if (lp->dbg) dbg_puts(fmt)
+void dbg_printk(unsigned char *fmt, ...);
+static unsigned int debug = 1;
+#else
+#define DEBUG_FUNC do { } while(0)
+#define DEBUG_FUNC_EXIT do { } while(0)
+#define DEBUG_PRINTL(args...) do { } while(0)
+#define DEBUG_PRINTK(args...) do { } while(0)
+#define DEBUG_PUTS(args...) do { } while(0)
+#define dump_skb(lp, skb) 0
+#define dump_skb_data(lp, skb, str) 0
+static unsigned int debug = 1;
+#endif
+
+#define mHoldS_SetEmpty(F) ((F)->ByteIndex = 0)
+#define mHoldR_SetEmpty(F) ((F)->ByteIndex = (F)->Width)
+/*******************************************************************************
+ * Primitive read from 64 bit FIFO. Use two 32-bit wide I/O accesses.
+ *
+ * @param F - Address to a temac_pktFifo structure
+ * @param DestPtr - Destination data address aligned on 4 byte boundary
+ *
+
******************************************************************************/
+#define mReadFifo64(F, DestPtr) \
+ (DestPtr)[0] = _ior(F->DataBaseAddress); \
+ (DestPtr)[1] = _ior(F->DataBaseAddress + 4);
+
+#define mPush64(F) mWriteFifo64(F, &F->Hold[0])
+#define mHold_GetIndex(F) ((F)->ByteIndex)
+#define mHoldS_IsEmpty(F) ((F)->ByteIndex == 0)
+#define mHoldR_IsEmpty(F) ((F)->ByteIndex >= (F)->Width)
+#define mHold_CopyOut(F, I, D) ((D) = (*(u8*)(((u8*)(&(F)->Hold[0])) +
(I))))
+#define mHold_SetIndex(F, D) ((F)->ByteIndex = (D))
+#define mHold_CopyIn(F, I, D) (*(u8*)(((u8*)(&(F)->Hold[0])) + (I)) = (D))
+#define mPop64(F) mReadFifo64(F, &F->Hold[0])
+#define mHold_SetIndex(F, D) ((F)->ByteIndex = (D))
+#define mHoldS_IsFull(F) ((F)->ByteIndex >= (F)->Width)
+#define mHold_Advance(F, D) ((F)->ByteIndex += (D))
+/*******************************************************************************
+ * Primitive write to 64 bit FIFO. Use two 32-bit wide I/O accesses.
+ *
+ * @param F - Address to a temac_pktFifo structure
+ * @param SrcPtr - Source data address aligned on 4 byte boundary
+ *
+
******************************************************************************/
+#define mWriteFifo64(F, SrcPtr) \
+ { \
+ register u32 Faddr = F->DataBaseAddress; \
+ _iow(Faddr, (SrcPtr)[0]); \
+ _iow(Faddr + 4, (SrcPtr)[1]); \
+ }
+
+
+
+/* Structure/enum declaration ------------------------------- */
+
+/* This type encapsulates a packet FIFO channel and support attributes to
+ * allow unaligned data transfers.
+ */
+struct temac_pktFifo {
+ u32 Hold[2]; /* Holding register */
+ unsigned ByteIndex; /* Holding register index */
+ unsigned Width; /* Width of packet FIFO's
keyhole data port in bytes */
+ u32 RegBaseAddress; /**< Base address of
registers */
+ u32 DataBaseAddress; /**< Base address of data
for FIFOs */
+} ;
+
+struct temac_local {
+#if defined(LINUX)
+ struct sk_buff *deferred_skb; /* buffer for one
skb in case no room is available for transmission */
+
+// void *RxFramePtr; /* Address of first
RxFrame */
+ unsigned MaxFrameSz; /* Size in bytes of
largest frame for Tx or Rx */
+// u32 RxPktFifoDepth; /**< Depth of
receive packet FIFO in bits */
+// u32 TxPktFifoDepth; /**< Depth of
transmit packet FIFO in bits */
+// u16 MacFifoDepth; /**< Depth of the
status/length FIFOs in entries */
+
+ struct resource *nic_addr_res; /* resources found */
+ struct resource *phy_addr_res;
+ struct resource *nic_addr_req; /* resources
requested */
+ struct resource *phy_addr_req;
+ void __iomem *nic_vaddr; /* Register I/O base
address */
+
+ struct mii_if_info mii_if;
+#else
+ EnetDevice enetDevice;
+ InterruptHandler handler;
+ struct sk_buff __skb[(NUM_TX_BDS+NUM_RX_BDS) +
(MAX_CACHE_LINE_SIZE/sizeof(struct sk_buff))];
+ char name[20];
+ u32 base_addr;
+ u8 dev_addr[6];
+
+ u32 disablecount;
+ u32 enablecount;
+ u32 tx_reset_pending;
+ u32 rx_reset_pending;
+ u32 reads_denied_during_reset;
+ u32 writes_denied_during_reset;
+
+ int devno;
+ Error (*GetLinkStatus)(struct temac_local *
lp, LinkSpeed *linkSpeed, LinkStatus *linkStatus, LinkDuplex *linkDuplex);
+
+ PHY mii_if;
+ u32 trans_start;
+ u32 last_rx;
+#endif
+ unsigned int mii:1; /* mii port
available */
+ u8 regshift;
+ u32 msg_enable; /* debug message
level */
+ struct net_device_stats stats; /* Statistics for
this device */
+ unsigned int phy_mode; /* dcr */
+ u16 phy_addr;
+ u32 phy_status;
+ unsigned int poll:1;
+ unsigned int dbg:1; /* debug ? */
+ unsigned int nic_type; /* plb/ll */
+ int LinkSpeed; /* Speed of link
10/100/1000 */
+ u32 options; /* Current options
word */
+// u32 TxPktFifoErrors; /**< Number of Tx
packet FIFO errors detected */
+// u32 TxStatusErrors;
+// u32 RxPktFifoErrors; /**< Number of Rx
packet FIFO errors detected */
+// u32 RxRejectErrors;
+// u32 FifoErrors; /**< Number of
length/status FIFO errors detected */
+// u32 IpifErrors; /**< Number of IPIF
transaction and data phase errors detected */
+// u32 Interrupts; /**< Number of
interrupts serviced */
+ spinlock_t lock;
+ u16 dcr_host;
+ struct timer_list rx_timer;
+ struct timer_list mii_timer;
+
+ /* Packet FIFO channels */
+ struct temac_pktFifo RecvFifo; /* Receive channel */
+ struct temac_pktFifo SendFifo; /* Transmit channel */
+
+} ;
+
+#if !defined(LINUX)
+static void EmacPhyInit(struct temac_local* lp);
+
+#define DEBUG_LOG
+
+extern void usDelay(u32 usec);
+
+static void
+mdelay(u32 delay) {
+ while(delay--) {
+ usDelay(1000);
+ }
+}
+
+/****************************************************************************/
+static struct sk_buff*
+get_free_skb(struct temac_local* lp, int num_skb) {
+ int ii;
+ // DEBUG_FUNC;
+ for(ii=0;ii < (NUM_TX_BDS+NUM_RX_BDS);ii++){
+ struct sk_buff *skb = &lp->__skb[ii];
+ // DEBUG_PRINTK("\n__skb[%d]
flags=%0x,%0x",ii,lp->__skb[ii].flags, skb->flags );
+ // if (lp->dbg) dump_skb(lp, skb);
+ if ((skb->flags & SKB_INUSE) == 0) {
+ skb->flags |= SKB_INUSE ;
+ // DEBUG_PRINTK(" found %d %0x %0x",ii, skb, &lp->__skb[ii]);
+ // this cast appears critical
+ return (struct sk_buff *) &lp->__skb[ii];
+ }
+ }
+ // DEBUG_PRINTK(" ~found\n");
+ return NULL;
+}
+
+static u32
+get_free_skb_count(struct temac_local* lp) {
+ int ii;
+ struct sk_buff *skb ;
+ u32 ret = 0;
+ // DEBUG_PRINTK("\nget_free_skb_count()=");
+ for(ii=0;ii < (NUM_TX_BDS+NUM_RX_BDS);ii++){
+ skb = &lp->__skb[ii];
+ if ((skb->flags & SKB_INUSE) == 0) ret++;
+
+ }
+ // DEBUG_PRINTK("%d",ret);
+ return ret;
+}
+
+static void
+skb_put(struct sk_buff *skb, u32 len) {
+ skb->len = len;
+ skb->tail = skb->data+len;
+}
+
+#define dev_kfree_skb_irq dev_kfree_skb
+static void
+dev_kfree_skb(struct sk_buff *skb) {
+ memset((void*)skb, 0, sizeof(struct sk_buff));
+}
+
+static struct sk_buff *
+dev_alloc_skb(u32 len) {
+ static struct sk_buff *skb;
+ struct temac_local* lp = &temac_dev_array[0];
+ int ii;
+ // DEBUG_PRINTK("\ndev_alloc_skb(len=%d)",len);
+ for(ii=0;ii < (NUM_TX_BDS+NUM_RX_BDS);ii++){
+ skb = &lp->__skb[ii];
+
+ // DEBUG_PRINTK("\nskb[%d] flags=%0x, len=%d", ii, skb->flags,
skb->size );
+ if (((skb->flags & (SKB_INUSE | SKB_RX | SKB_ALLOC)) ==
(SKB_INUSE | SKB_RX)) && (skb->size >= len) && (skb->len == 0)) {
+ skb->flags |= SKB_ALLOC;
+ // DEBUG_PRINTK(" found %d %0x %0x",ii, skb, &lp->__skb[ii]);
+ return skb;
+ }
+ }
+ return NULL;
+}
+static struct temac_local temac_dev_array[MAX_TEMAC_DEVS];
+static u8 num_devs = 0;
+
+static struct temac_local*
+temac_allocate(void) {
+ struct temac_local* lp;
+
+ if (num_devs >= MAX_TEMAC_DEVS) {
+ return NULL;
+ }
+
+ lp = &temac_dev_array[num_devs];
+ num_devs++;
+
+ return lp;
+}
+#endif
+
+static u32
+_ior(u32 offset) {
+ u32 value;
+ value = (*(volatile u32 *) (offset));
+ __asm__ __volatile__("eieio");
+ return value;
+}
+
+static void
+_iow(u32 offset, u32 value) {
+ (*(volatile u32 *) (offset) = value);
+ __asm__ __volatile__("eieio");
+}
+
+static u32
+ior(struct net_device *ndev, int offset) {
+ return _ior(ndev->base_addr + offset);
+}
+
+static u32
+ios(struct net_device *ndev) {
+ return ior(ndev, XTE_IPIER_OFFSET) & ior(ndev, XTE_IPISR_OFFSET);
+}
+
+static void
+iow(struct net_device *ndev, int offset, u32 value) {
+ _iow(ndev->base_addr + offset, value);
+}
+
+/***************************************************************************
+ * Reads an MII register from the MII PHY attached to the Xilinx Temac.
+ *
+ * Parameters:
+ * dev - the temac device.
+ * phy_addr - the address of the PHY [0..31]
+ * reg_num - the number of the register to read. 0-6 are defined by
+ * the MII spec, but most PHYs have more.
+ * reg_value - this is set to the specified register's value
+ *
+ * Returns:
+ * Success or Failure
+ */
+static Error
+g_mdio_read(struct net_device *ndev, int phy_id, int reg_num, u16 *
reg_val) {
+ struct temac_local *lp = ndev->priv;
+ u32 timeout, status;
+ unsigned long flags;
+ *reg_val = 0;
+ // unsigned long flags;
+
+ // DEBUG_PRINTK("\nmdio_read(%x,%x)= ", phy_id, reg_num);
+ timeout = PHY_TIMEOUT;
+ /* Start a read */
+
+ /* Make sure no other PHY operation is currently in progress */
+ if (ior(ndev, XTE_IPISR_OFFSET) & XTE_IPXR_MII_PEND_MASK)
+ return Failure;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr(lp->phy_addr + XGP_HIF_DATA_REG_LSW_OFFSET,
((phy_id << 5) | (reg_num)));
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET,
XGP_E0_MIIM_ADDR);
+ while (!(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET) & XGP_HIF_RDYSTAT_MIIM0_READ_MASK));
+ *reg_val = (u16) mfdcr(lp->phy_addr +
XGP_HIF_DATA_REG_LSW_OFFSET);
+ break;
+
+ default:
+ /* Construct Mgtcr mask for the operation */
+ /* Write and wait for completion */
+ iow(ndev, XTE_HOST_IPIF_OFFSET + XGP_E0_MIIM_ADDR,
(reg_num & XTE_MGTCR_REGAD_MASK) | ((phy_id <<
XTE_MGTCR_PHYAD_SHIFT_MASK) & XTE_MGTCR_PHYAD_MASK) | XTE_MGTCR_RWN_MASK);
+
+ while (!(status = ior(ndev, XTE_IPISR_OFFSET) &
XTE_IPXR_MII_DONE_MASK) && timeout--) ;
+ if (!(status & (XTE_IPXR_MII_DONE_MASK)))
+ return Failure;
+
+ *reg_val = ior(ndev, XTE_HOST_IPIF_OFFSET +
XTE_MGTDR_OFFSET); /* Read data */
+
+ /* Clear MII status bits */
+ iow(ndev, XTE_IPISR_OFFSET, ior(ndev, XTE_IPISR_OFFSET)
& (XTE_IPXR_MII_DONE_MASK | XTE_IPXR_MII_PEND_MASK));
+ break;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+// DEBUG_PRINTK("%x", *reg_val);
+ return Success;
+}
+
+static int
+mdio_read(struct net_device *ndev, int phy_id, int reg_num) {
+ u16 ret = 0;
+ g_mdio_read(ndev, phy_id, reg_num, &ret);
+ return ret;
+}
+
+/***************************************************************************
+ * Writes an MII register from the MII PHY attached to the Xilinx Temac.
+ *
+ * Parameters:
+ * dev - the temac device.
+ * phy_id - the address of the PHY [0..31]
+ * reg_num - the number of the register to read. 0-6 are defined by
+ * the MII spec, but most PHYs have more.
+ * reg_value - the value to set
+ *
+ * Returns:
+ * Success or Failure
+ */
+static void
+mdio_write(struct net_device *ndev, int phy_id, int reg_num, int reg_val) {
+ struct temac_local *lp = ndev->priv;
+ u32 timeout, status;
+ unsigned long flags;
+ int EmacNum = 0;
+ // DEBUG_FUNC;
+ // DEBUG_PRINTK("\nmdio_write(%x,%x, %x)= ", phy_id,
reg_num,reg_value);
+ timeout = PHY_TIMEOUT;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr(lp->phy_addr + XGP_HIF_DATA_REG_LSW_OFFSET,
(reg_val));
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET,
XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE | XTE_MGTDR_OFFSET);
+ mtdcr(lp->phy_addr + XGP_HIF_DATA_REG_LSW_OFFSET,
((phy_id << 5) | (reg_num)));
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET,
XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE | ((EmacNum) << 10) | XGP_E0_MIIM_ADDR);
+ while (!(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET) & XGP_HIF_RDYSTAT_MIIM0_WRITE_MASK));
+ break;
+
+ default:
+ /* Make sure no other PHY operation is currently in
progress */
+ if (ior(ndev, XTE_IPISR_OFFSET) & XTE_IPXR_MII_PEND_MASK)
+ return ;
+ /* Construct Mgtcr mask for the operation */
+ /* Write and wait for completion */
+ iow(ndev, XTE_HOST_IPIF_OFFSET + XTE_MGTDR_OFFSET,
reg_val & 0xffff);
+ iow(ndev, XTE_HOST_IPIF_OFFSET + XGP_E0_MIIM_ADDR,
(reg_num & XTE_MGTCR_REGAD_MASK) | ((phy_id <<
XTE_MGTCR_PHYAD_SHIFT_MASK) & XTE_MGTCR_PHYAD_MASK));
+
+ while (!(status = ior(ndev, XTE_IPISR_OFFSET) &
XTE_IPXR_MII_DONE_MASK) && timeout--) ;
+ if (!(status & (XTE_IPXR_MII_DONE_MASK)))
+ return ;
+
+ /* Clear MII status bits */
+ iow(ndev, XTE_IPISR_OFFSET, ior(ndev, XTE_IPISR_OFFSET)
& (XTE_IPXR_MII_DONE_MASK | XTE_IPXR_MII_PEND_MASK));
+ break;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ // return Success;
+}
+
+static u32
+emac_cfg_read(struct net_device *ndev, u16 phy_id, u16 reg_num) {
+ struct temac_local *lp = ndev->priv;
+ int EmacNum = 0;
+ u32 ret = 0;
+
+ if (lp->mii) {
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET, (EmacNum
<< 10) | (reg_num));
+ while (!(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET) & (XGP_HIF_RDYSTAT_CONFIG0_READ_MASK << (8 *
(EmacNum)))));
+ return (u32) mfdcr(lp->phy_addr +
XGP_HIF_DATA_REG_LSW_OFFSET);
+ default:
+ return ior(ndev, XTE_HOST_IPIF_OFFSET + reg_num);
+ }
+ }
+
+ return ret;
+}
+
+static void
+emac_cfg_write(struct net_device *ndev, u16 phy_id, u32 reg_num, u32 val) {
+ struct temac_local *lp = ndev->priv;
+ int EmacNum = 0;
+
+ if (lp->mii) {
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr(lp->phy_addr + XGP_HIF_DATA_REG_LSW_OFFSET, (val));
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET,
XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE | (EmacNum << 10) | (reg_num));
+ while (!(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET) & XGP_HIF_RDYSTAT_CONFIG0_WRITE_MASK));
+ break;
+ default:
+ iow(ndev, XTE_HOST_IPIF_OFFSET + reg_num, val);
+ break;
+ }
+ }
+}
+
+static u32
+emac_AF_read(struct net_device *ndev, int reg_num) {
+ struct temac_local *lp = ndev->priv;
+ int EmacNum = 0;
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr((lp->phy_addr) + XGP_HIF_CNTL_REG_OFFSET,
(EmacNum << 10) | (reg_num));
+ while ( !(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET ) & (XGP_HIF_RDYSTAT_AF0_READ_MASK <<
(8*(EmacNum)))));
+ return (u32) mfdcr((lp->phy_addr) +
XGP_HIF_DATA_REG_LSW_OFFSET) ;
+ default:
+ return ior(ndev, XTE_HOST_IPIF_OFFSET + reg_num);
+ }
+ return 0;
+}
+
+static void
+emac_AF_write(struct net_device *ndev, int reg_num, u32 val) {
+ struct temac_local *lp = ndev->priv;
+ int EmacNum = 0;
+
+ if (lp->mii) {
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ mtdcr(lp->phy_addr + XGP_HIF_DATA_REG_LSW_OFFSET, (val));
+ mtdcr(lp->phy_addr + XGP_HIF_CNTL_REG_OFFSET,
XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE | ((EmacNum) << 10) | (reg_num));
+ while (!(mfdcr(lp->phy_addr +
XGP_HIF_RDY_STATUS_OFFSET) & XGP_HIF_RDYSTAT_AF0_WRITE_MASK));
+ break;
+ default:
+ iow(ndev, XTE_HOST_IPIF_OFFSET + reg_num, val);
+ break;
+ }
+ }
+}
+#if defined(CONFIG_PICO_DEBUG_TEMAC)
+
+static void
+dump_skb(struct temac_local* lp, struct sk_buff *skb) {
+#if !defined(LINUX)
+ DEBUG_PRINTK("\nskb->flags\t%0x", skb->flags);
+ DEBUG_PRINTK("\nskb->fifo_data\t%0x", skb->fifo_data);
+ DEBUG_PRINTK("\nskb->fifo_reg\t%0x", skb->fifo_reg);
+ DEBUG_PRINTK("\nskb->size\t%d", skb->size);
+#endif
+ DEBUG_PRINTK("\nskb->len\t%0d", skb->len);
+#if !defined(LINUX)
+ DEBUG_PRINTK("\nskb->user\t%0x", skb->user);
+ DEBUG_PRINTK("\nskb->user_data\t%0x", skb->user_data);
+#endif
+ DEBUG_PRINTK("\nskb->dev\t%0x", skb->dev);
+ DEBUG_PRINTK("\nskb->data\t%0x", skb->data);
+ DEBUG_PRINTK("\nskb->tail\t%0x", skb->tail);
+}
+
+static void
+dump_skb_data(struct temac_local *lp, struct sk_buff *skb, char *str) {
+ int ii;
+ u8 *cp = skb->data ;
+
+ DEBUG_PRINTK("\n%s %d bytes", str, skb->len);
+ for (ii = 0; ii < skb->len; ii++) {
+ if (( ii % 16) == 0) DEBUG_PRINTK("\n");
+ if (( ii % 8) == 0) DEBUG_PRINTK(" ");
+ DEBUG_PRINTK("%02x ", cp[ii]);
+ }
+ DEBUG_PRINTK("\n");
+}
+
+static void
+disp_emac_cfg(struct net_device *ndev, char *rname, int regnum, int idx) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret;
+ int ii;
+
+ if ((idx % 4) == 0) DEBUG_PRINTK("\n\t");
+ ret = emac_cfg_read(ndev, PHY_NUM, regnum);
+ if (rname) {
+ DEBUG_PRINTK("%s:", rname);
+ for(ii=strlen(rname);ii<10;ii++) DEBUG_PRINTK(" ");
+ DEBUG_PRINTK("0x%08x ", ret);
+ } else
+ DEBUG_PRINTK("R%03d: 0x%08x ", regnum, ret);
+}
+
+static void
+disp_temac_cfg(struct net_device *ndev, char *rname, int addr, int idx) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret = 0;
+ int ii;
+
+ if ((idx % 4) == 0) DEBUG_PRINTK("\n\t");
+ switch (lp->phy_mode) {
+ case PHY_DCR:
+ break;
+ default:
+ ret = _ior(ndev->base_addr + addr);
+ break;
+ }
+ if (rname) {
+ DEBUG_PRINTK("%s:", rname);
+ for(ii=strlen(rname);ii<10;ii++) DEBUG_PRINTK(" ");
+ DEBUG_PRINTK("0x%08x ", ret);
+ } else
+ DEBUG_PRINTK("R%03d: 0x%08x ", addr, ret);
+}
+
+static void
+disp_mii(struct net_device *ndev, char *rname, int regnum) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret;
+ int ii;
+ if ((regnum % 4) == 0) DEBUG_PRINTK("\n\t");
+ ret = mdio_read(ndev, PHY_NUM, regnum);
+ if (rname) {
+ DEBUG_PRINTK("%s:", rname);
+ for(ii=strlen(rname);ii<10;ii++) DEBUG_PRINTK(" ");
+ DEBUG_PRINTK("0x%08x ", ret);
+ } else
+ DEBUG_PRINTK("R%02d: 0x%08x ", regnum, ret);
+}
+
+static void
+temac_dump(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret;
+ int ii, jj;
+
+#if defined(LINUX)
+ DEBUG_PRINTK("\nresources:\n\tirq=%d ", ndev->irq);
+ DEBUG_PRINTK("\n\tnic physical address =%x ",
lp->nic_addr_res->start);
+ DEBUG_PRINTK("size %x ", res_size(lp->nic_addr_res));
+ DEBUG_PRINTK("virtual address %lx", ndev->base_addr);
+ DEBUG_PRINTK("\n\t irq=%d mem=%lx phy=%x", ndev->irq,
ndev->base_addr, lp->phy_addr);
+#else
+ DEBUG_PRINTK("\nresources:\n\tirq=%d ", ndev->handler.vect);
+ DEBUG_PRINTK("virtual address %x ", ndev->base_addr);
+ DEBUG_PRINTK("\n\t irq=%d mem=%x phy=%x", ndev->handler.vect,
ndev->base_addr, ndev->phy_addr);
+#endif
+
+ DEBUG_PRINTK("\n\t");
+ ret = mdio_read(ndev, PHY_NUM, MII_SSR) ;
+ switch (ret & MII_SSR_SPDMASK) {
+ case MII_SSR_SPD1000:
+ DEBUG_PRINTK("1000");
+ break;
+ case MII_SSR_SPD100:
+ DEBUG_PRINTK("100");
+ break;
+ case MII_SSR_SPD10:
+ DEBUG_PRINTK("10");
+ break;
+ default:
+ DEBUG_PRINTK("Error : invalid PHY mode/speed");
+ };
+ DEBUG_PRINTK("BASE-T, ");
+ if ((ret & MII_SSR_FD) == MII_SSR_FD) {
+ DEBUG_PRINTK("FD");
+ } else {
+ DEBUG_PRINTK("HD");
+ }
+ if ((ret & MII_SSR_LINK) == 0) {
+ DEBUG_PRINTK(" Ethernet Link Down");
+ }
+ /* read mii phy registers */
+ DEBUG_PRINTK("\nReading PHY Regs through DCR:\n\t");
+ for (ii = 0; ii < 32; ii++) {
+ switch (ii) {
+ case MII_ANI:
+ disp_mii(ndev, "ANI", ii);
+ break;
+ case MII_SSR:
+ disp_mii(ndev, "SSR", ii);
+ break;
+ case MII_ISR:
+ disp_mii(ndev, "ISR", ii);
+ break;
+ case MII_BMCR:
+ disp_mii(ndev, "MCR", ii);
+ break;
+ case MII_BMSR:
+ disp_mii(ndev, "MSR", ii);
+ break;
+ case MII_PHYSID1:
+ disp_mii(ndev,"PHYSID1",ii);
+ break;
+ case MII_PHYSID2:
+ disp_mii(ndev,"PHYSID2",ii);
+ break;
+ case MII_ADVERTISE:
+ disp_mii(ndev,"ADV", ii);
+ break;
+ case MII_LPA:
+ disp_mii(ndev,"LPA", ii);
+ break;
+ case MII_EXPANSION:
+ disp_mii(ndev,"EXPANSION", ii);
+ break;
+ case MII_CTRL1000:
+ disp_mii(ndev,"CTRL1000", ii);
+ break;
+ case MII_STAT1000:
+ disp_mii(ndev,"STAT1000", ii);
+ break;
+ case MII_ESTATUS:
+ disp_mii(ndev,"ESTATUS", ii);
+ break;
+ case MII_DCOUNTER:
+ disp_mii(ndev,"DCOUNTER", ii);
+ break;
+ case MII_NWAYTEST:
+ disp_mii(ndev,"NWAYTEST", ii);
+ break;
+ case MII_RERRCOUNTER:
+ disp_mii(ndev,"RERRCOUNT", ii);
+ break;
+ case MII_SREVISION:
+ disp_mii(ndev,"SREVISION",ii);
+ break;
+ case MII_RESV1:
+ disp_mii(ndev,"RESV1",ii);
+ break;
+ case MII_LBRERROR:
+ disp_mii(ndev,"LBERROR",ii);
+ break;
+ case MII_PHYADDR:
+ disp_mii(ndev,"PHYADDR",ii);
+ break;
+ case MII_RESV2:
+ disp_mii(ndev,"RESV2",ii);
+ break;
+ case MII_TPISTATUS:
+ disp_mii(ndev,"TPISTATUS",ii);
+ break;
+ case MII_NCONFIG:
+ disp_mii(ndev,"NCONFIG",ii);
+ break;
+#if 0
+ case MII_FCSCOUNTER:
+ disp_mii(ndev,"FCSCOUNTER",ii);
+ break;
+#endif
+ default:
+ disp_mii(ndev,0, ii);
+ break;
+ }
+ }
+ /*
+ Print TEMAC Receiver and Transmitter configuration
+ */
+ DEBUG_PRINTK("\nReading Hard TEMAC Regs:\n");
+
+ for (ii = 0x200,jj = 0; ii <= 0x3a4; ii += 4) {
+ switch (ii) {
+ case XTE_RXC0_OFFSET:
+ disp_emac_cfg(ndev, "RxCW0", ii, jj++);
+ break;
+ case XTE_RXC1_OFFSET:
+ disp_emac_cfg(ndev, "RxCW1", ii, jj++);
+ break;
+ case XTE_TXC_OFFSET:
+ disp_emac_cfg(ndev, "TxCW", ii, jj++);
+ break;
+ case XTE_FCC_OFFSET:
+ disp_emac_cfg(ndev, "Flow", ii, jj++);
+ break;
+ case XTE_EMCFG_OFFSET:
+ disp_emac_cfg(ndev, "ModeCfg", ii, jj++);
+ break;
+ case XTE_MC_OFFSET:
+ disp_emac_cfg(ndev, "MgmtCfg", ii, jj++);
+ break;
+ case XTE_UAW0_OFFSET:
+ disp_emac_cfg(ndev, "MacAddr0", ii, jj++);
+ break;
+ case XTE_UAW1_OFFSET:
+ disp_emac_cfg(ndev, "MacAddr1", ii, jj++);
+ break;
+ case XTE_MAW0_OFFSET:
+ disp_emac_cfg(ndev, "AtAddr0", ii, jj++);
+ break;
+ case XTE_MAW1_OFFSET:
+ disp_emac_cfg(ndev, "AtAddr1", ii, jj++);
+ break;
+ case XTE_AFM_OFFSET:
+ disp_emac_cfg(ndev, "AtCAF", ii, jj++);
+ break;
+ case XGP_IRSTATUS:
+ disp_emac_cfg(ndev, "ISR", ii, jj++);
+ break;
+ case XGP_IRENABLE:
+ disp_emac_cfg(ndev, "IER", ii, jj++);
+ break;
+ default:
+ break;
+ }
+ }
+ DEBUG_PRINTK("\n");
+
+ if (lp->nic_type == TEMAC_PLB) {
+ DEBUG_PRINTK("\nReading PLB TEMAC Regs:\n");
+
+ for (ii = 0x0,jj = 4;ii <= 0x1020; ii += 4) {
+ switch (ii) {
+ case XTE_DISR_OFFSET:
+ disp_temac_cfg(ndev, "DISR", ii, jj++);
+ break;
+ case XTE_DIPR_OFFSET:
+ disp_temac_cfg(ndev, "DIPR", ii, jj++);
+ break;
+ case XTE_DIER_OFFSET:
+ disp_temac_cfg(ndev, "DIER", ii, jj++);
+ break;
+ case XTE_DGIE_OFFSET:
+ disp_temac_cfg(ndev, "DGIE", ii, jj++);
+ break;
+ case XTE_IPISR_OFFSET:
+ disp_temac_cfg(ndev, "IPISR", ii, jj++);
+ break;
+ case XTE_IPIER_OFFSET:
+ disp_temac_cfg(ndev, "IPIER", ii, jj++);
+ break;
+ case XTE_DSR_OFFSET:
+ disp_temac_cfg(ndev, "MIR", ii, jj++);
+ break;
+ case XTE_TPLR_OFFSET:
+ // disp_temac_cfg(ndev, "TPLR", ii, jj++); // do
not try to display this register - BAD things will happen
+ break;
+ case XTE_CR_OFFSET:
+ disp_temac_cfg(ndev, "CR", ii, jj++);
+ break;
+ case XTE_TSR_OFFSET:
+ // disp_temac_cfg(ndev, "TSR", ii, jj++);
+ break;
+ case XTE_RPLR_OFFSET:
+ // disp_temac_cfg(ndev, "RPLR", ii, jj++);
+ break;
+ case XTE_RSR_OFFSET:
+ disp_temac_cfg(ndev, "RSR", ii, jj++);
+ break;
+ case XTE_TPPR_OFFSET:
+ disp_temac_cfg(ndev, "TPPR", ii, jj++);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ DEBUG_PRINTK("\nDisplaying Options:\n");
+
+ for (ii = 0, jj = 0;ii < 32; ii++) {
+ if ( lp->options & ( 1 << ii)) {
+ jj++;
+ if ((jj % 4) == 0) DEBUG_PRINTK("\n\t");
+ switch ( 1 << ii) {
+ case XTE_PROMISC_OPTION:
+ DEBUG_PRINTK("PROMISC ");
+ break;
+ case XTE_JUMBO_OPTION:
+ DEBUG_PRINTK("JUMBO ");
+ break;
+ case XTE_VLAN_OPTION:
+ DEBUG_PRINTK("VLAN ");
+ break;
+ case XTE_FLOW_CONTROL_OPTION:
+ DEBUG_PRINTK("FLOW_CONTROL ");
+ break;
+ case XTE_FCS_STRIP_OPTION:
+ DEBUG_PRINTK("FCS_STRIP ");
+ break;
+ case XTE_FCS_INSERT_OPTION:
+ DEBUG_PRINTK("FCS_INSERT ");
+ break;
+ case XTE_LENTYPE_ERR_OPTION:
+ DEBUG_PRINTK("LENTYPE ERR ");
+ break;
+ case XTE_POLLED_OPTION:
+ DEBUG_PRINTK("POLLED ");
+ break;
+ case XTE_REPORT_RXERR_OPTION:
+ DEBUG_PRINTK("REPORT_RXERR ");
+ break;
+ case XTE_TRANSMITTER_ENABLE_OPTION:
+ DEBUG_PRINTK("TRANSMITTER_ENABLE ");
+ break;
+ case XTE_RECEIVER_ENABLE_OPTION:
+ DEBUG_PRINTK("RECEIVER_ENABLE ");
+ break;
+ case XTE_BROADCAST_OPTION:
+ DEBUG_PRINTK("BROADCAST ");
+ break;
+ case XTE_MULTICAST_CAM_OPTION:
+ DEBUG_PRINTK("MULTICAST_CAM ");
+ break;
+ case XTE_REPORT_TXSTATUS_OVERRUN_OPTION:
+ DEBUG_PRINTK("REPORT_TXSTATUS_OVERRUN ");
+ break;
+ case XTE_ANEG_OPTION:
+ DEBUG_PRINTK("ANEG ");
+ break;
+ default:
+ DEBUG_PRINTK("UNK ");
+ break;
+ }
+ }
+ }
+ DEBUG_PRINTK("\n");
+}
+#endif
+
+/*
+Changes the mac address if the controller is not running.
+
+static int (*set_mac_address)(struct net_device *dev, void *addr);
+Function that can be implemented if the interface supports the ability
to change its
+hardware address. Many interfaces don't support this ability at all.
Others use the
+default eth_mac_addr implementation (from drivers/net/net_init.c).
eth_mac_addr
+only copies the new address into dev->dev_addr, and it does so only if
the interface
+is not running. Drivers that use eth_mac_addr should set the hardware MAC
+address from dev->dev_addr in their open method.
+
+*/
+static u32
+temac_set_mac_address(struct net_device *ndev, void *address) {
+ struct temac_local *lp = ndev->priv;
+#if defined(LINUX)
+ u8 addr[] = { 0x0, 0x50, 0xc2, 0x44, 0x2f, 0xff };
+ int ii;
+
+ // DEBUG_FUNC;
+ if (address)
+ memcpy(ndev->dev_addr, address, ETH_ALEN);
+
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ printk(KERN_ERR "%s: Invalid ethernet MAC address. Please set
using ifconfig\n", ndev->name);
+ for (ii=0; ii<6; ii++)
+ ndev->dev_addr[ii] = addr[ii];
+ }
+#endif
+ /* set up unicast MAC address filter set its mac address */
+ emac_AF_write(ndev, XTE_UAW0_OFFSET, ((ndev->dev_addr[3] << 24) |
(ndev->dev_addr[2] << 16) | (ndev->dev_addr[1] << 8) |
(ndev->dev_addr[0])));
+ /* There are reserved bits in EUAW1 so don't affect them Set MAC
bits [47:32] in EUAW1 */
+ emac_AF_write(ndev, XTE_UAW1_OFFSET, (ndev->dev_addr[4] &
0x000000FF) | (ndev->dev_addr[5] << 8) | (emac_AF_read(ndev,
XTE_UAW1_OFFSET) & ~XTE_UAW1_MASK));
+
+ /* enable address filter */
+ emac_AF_write(ndev, XTE_AFM_OFFSET, 0);
+ return Success;
+}
+
+/*
+OPTIONAL
+static void (*set_multicast_list)(struct net_device *dev);
+Method called when the multicast list for the device changes and when the
+flags change. See the section Multicast for further details and a sample
+implementation.
+*/
+static void
+temac_set_multicast_list(struct net_device *ndev) {
+}
+
+static u32 temac_setoptions(struct net_device *ndev, u32 Options) ;
+/*
+Initilize temac board
+ */
+static void
+temac_device_reset(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret;
+ u32 Reg;
+ int ii;
+
+ // DEBUG_FUNC;
+ /* Perform a software reset */
+ /* Reset the device */
+ switch (lp->nic_type) {
+ case TEMAC_PLB:
+ iow(ndev, XTE_DSR_OFFSET,
XTE_DSR_RESET_MASK); // reset everything
+#if 0
+ iow(ndev, XTE_CR_OFFSET, XTE_CR_HRST_MASK);
// Reset HARD TEMAC
+ emac_cfg_write(lp, PHY_NUM, XTE_RXC1_OFFSET,
XTE_RXC1_RXRST_MASK);
+ while (emac_cfg_read(lp, PHY_NUM, XTE_RXC1_OFFSET) &
XTE_RXC1_RXRST_MASK); // Wait for the receiver to finish reset
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET,
XTE_TXC_TXRST_MASK);
+ while (emac_cfg_read(lp, PHY_NUM, XTE_TXC_OFFSET) &
XTE_TXC_TXRST_MASK); // Wait for the receiver to finish reset
+#endif
+
+ /* Initialize the packet FIFOs */
+ lp->RecvFifo.RegBaseAddress = (u32) (ndev->base_addr +
XTE_PFIFO_RXREG_OFFSET);
+ lp->RecvFifo.DataBaseAddress = (u32) (ndev->base_addr +
XTE_PFIFO_RXDATA_OFFSET);
+ _iow(lp->RecvFifo.RegBaseAddress +
XPF_V200A_RESET_REG_OFFSET, XPF_V200A_RESET_FIFO_MASK);
+ lp->SendFifo.RegBaseAddress = (u32) (ndev->base_addr +
XTE_PFIFO_TXREG_OFFSET);
+ lp->SendFifo.DataBaseAddress = (u32) (ndev->base_addr +
XTE_PFIFO_TXDATA_OFFSET);
+ _iow(lp->SendFifo.RegBaseAddress +
XPF_V200A_RESET_REG_OFFSET, XPF_V200A_RESET_FIFO_MASK);
+
+ /* Choose an access algorithm.
+ * Note: 64-bit wide FIFO is the only width supported at
this time
+ */
+ lp->RecvFifo.Width = PFIFO_64BIT_WIDTH_BYTES;
+ lp->SendFifo.Width = PFIFO_64BIT_WIDTH_BYTES;
+
+ /* Initialize the holds */
+ mHoldS_SetEmpty(&lp->SendFifo);
+ mHoldR_SetEmpty(&lp->RecvFifo);
+
+ /* Reset the hardware and set default options */
+
+ /* Stop the device and reset HW */
+ iow(ndev, XTE_DGIE_OFFSET, 0); /* Disable interrupts */
+
+ /* Disable the receiver */
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET)& ~XTE_RXC1_RXEN_MASK);
+
+ /* Stopping the receiver in mid-packet causes a dropped
packet indication
+ * from HW. Clear it.
+ */
+ if (ior(ndev, XTE_IPISR_OFFSET) & XTE_IPXR_RECV_REJECT_MASK)
+ iow(ndev, XTE_IPISR_OFFSET, XTE_IPXR_RECV_REJECT_MASK);
+
+ /* Reset IPIF */
+ iow(ndev, XTE_DSR_OFFSET, XTE_DSR_RESET_MASK);
+ udelay(XTE_RESET_IPIF_DELAY_US);
+
+ /* Default IPIF interrupt block enable mask */
+ iow(ndev, XTE_DIER_OFFSET, XTE_DXR_CORE_MASK |
XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK | XTE_DXR_RECV_FIFO_MASK |
XTE_DXR_SEND_FIFO_MASK);
+#if 0
+ // Interframe gap: The HW has an enable bit to change the
IFG through the
+ // XTE_TPPR_OFFSET register. Rather than make the user set
this bit then
+ // change the register, simplifiy the process by always
setting the
+ // enable bit. All the user needs to do is use
XTemac_SetIfg() thereafter.
+ // The default IFG is 96 bit times. Whatever is in the
register adds to that.
+ // By default leave this register at 0 so we have 96 bit times.
+ // Now set IFG adjust enable
+ iow(ndev, XTE_TPPR_OFFSET, 0);
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(lp, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXIFG_MASK);
+ emac_cfg_write(lp, PHY_NUM, XTE_MC_OFFSET,
XTE_XTE_MDIO_DIV_DFT | XTE_MC_MDIO_MASK); // Set default MDIO divisor
+
+ // Reset the FIFOs - check this out for TEMAC
+ iow(ndev,
(XTE_PFIFO_TXREG_OFFSET+XPF_V200A_RESET_REG_OFFSET),
XPF_V200A_RESET_FIFO_MASK);
+ iow(ndev,
(XTE_PFIFO_RXREG_OFFSET+XPF_V200A_RESET_REG_OFFSET),
XPF_V200A_RESET_FIFO_MASK);
+#endif
+ break;
+#if defined(CONFIG_PICO_LL_TEMAC)
+ case TEMAC_LL:
+ /* Hold the PHY in reset */
+ mtdcr(lp->dcr_host, XGP_CON_REG_PHY_RESET);
+ mdelay(40);
+ /* reset the peripheral and the emac hold the temac in
reset since this bit is not self-clearing */
+ mtdcr(lp->dcr_host, XGP_CON_REG_TEMAC_RESET);
+ mdelay(40);
+
+ /*
+ reset the peripheral. This is self-clearing. This will
also kick
+ the temac and PHY out of reset.
+ Note that this may enable the Tx/Rx of the
+ temac if tied-off that way in hardware, but that should
be OK.
+ */
+ mtdcr(lp->dcr_host, XGP_CON_REG_PERIPH_RESET);
+ mdelay(40);
+
+ mtdcr(lp->dcr_host, 0);
+ mdelay(40);
+ break;
+#endif
+ }
+ /* Sync default options with HW but leave receiver and transmitter
disabled. */
+ temac_setoptions(ndev, lp->options &
~(XTE_TRANSMITTER_ENABLE_OPTION | XTE_RECEIVER_ENABLE_OPTION));
+
+ /* Set default MDIO divisor */
+ /* Set up MII management registers to write to PHY */
+ emac_cfg_write(ndev, PHY_NUM, XTE_MC_OFFSET, XTE_MC_MDIO_MASK |
XTE_MDIO_DIV_DFT);
+
+ /*
+ Set A-N Advertisement Regs for Full Duplex modes ONLY
+ address 4 = Autonegotiate Advertise Register
+ Disable 1000 Mbps for negotiation if not built for GEth
+ */
+ mdio_write(ndev, PHY_NUM, MII_ADVERTISE, mdio_read(ndev, PHY_NUM,
MII_ADVERTISE) | ADVERTISE_10FULL | ADVERTISE_100FULL | ADVERTISE_CSMA);
+ mdio_write(ndev, PHY_NUM, MII_CTRL1000, ADVERTISE_1000FULL);
+
+ /*
+ Soft reset the PHY
+ address 0 = Basic Mode Control Register
+ */
+ mdio_write(ndev, PHY_NUM, MII_BMCR, mdio_read(ndev, PHY_NUM,
MII_BMCR) | BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Wait for a PHY Link (auto-negotiation to complete)... */
+ // if (lp->dbg) printk("\nWaiting for ethernet link..");
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ ii = 64;
+ while (((ret & BMSR_LSTATUS) != BMSR_LSTATUS) && ii--) {
+ DEBUG_PRINTK(".");
+ mdelay(500);
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ }
+ ret = mdio_read(ndev, PHY_NUM, MII_SSR) ;
+
+ Reg = emac_cfg_read(ndev, PHY_NUM, XTE_EMCFG_OFFSET) &
~XTE_EMCFG_LINKSPD_MASK;
+ if (ret & MII_SSR_LINK) {
+ switch (ret & MII_SSR_SPDMASK) {
+ case MII_SSR_SPD1000: /*
1000Base-T */
+ lp->LinkSpeed = 1000;
+ emac_cfg_write(ndev, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
(u32)XTE_EMCFG_LINKSPD_1000);
+ break;
+ case MII_SSR_SPD100: /* 100Base-T */
+ lp->LinkSpeed = 100;
+ emac_cfg_write(ndev, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_100);
+ break;
+ case MII_SSR_SPD10: /* 10Base-T */
+ lp->LinkSpeed = 10;
+ // emac_cfg_write(ndev, 0, XTE_EMCFG_OFFSET,
XTE_EMCFG_LINKSPD_10);
+ break;
+ };
+ if ((ret & MII_SSR_FD) == 0x0){
+ /* set up Tx/Rx config reg for half duplex */
+ ret = emac_cfg_read(ndev, 0, XTE_TXC_OFFSET);
+ emac_cfg_write(ndev, 0, XTE_TXC_OFFSET, ret |
XTE_TXC_TXHD_MASK);
+ ret = emac_cfg_read(ndev, 0, XTE_RXC1_OFFSET);
+ emac_cfg_write(ndev, 0, XTE_RXC1_OFFSET, ret |
XTE_RXC1_RXHD_MASK);
+ }
+ // DEBUG_PRINTK("\nEthernet connected at %dMbps Full-Duplex",
lp->LinkSpeed);
+ } else {
+ if ((ret & MII_SSR_LINK) == 0x0)
+ DEBUG_PRINTK("\nEthernet Link Down");
+ }
+ temac_set_mac_address(ndev,0);
+ /* Set address filter table */
+ temac_set_multicast_list(ndev);
+ if (lp->nic_type == TEMAC_PLB) {
+ lp->options &= ~XTE_FCS_INSERT_OPTION ;
+ // XTE_JUMBO_OPTION | XTE_TRANSMITTER_ENABLE_OPTION |
XTE_RECEIVER_ENABLE_OPTION | XTE_FLOW_CONTROL_OPTION |
XTE_BROADCAST_OPTION | XTE_FCS_STRIP_OPTION | XTE_LENTYPE_ERR_OPTION |
XTE_REPORT_RXERR_OPTION | XTE_REPORT_TXSTATUS_OVERRUN_OPTION;
+ }
+ if (lp->poll) lp->options |= XTE_POLLED_OPTION ;
+ if (temac_setoptions(ndev, lp->options))
+ DEBUG_PRINTK("Error setting TEMAC options, code %d\r\n", ret);
+ if (lp->nic_type == TEMAC_PLB) {
+ /* Allow interrupts (if not in polled mode) and exit */
+ // DEBUG_PRINTK("temac->options %0x\n", lp->options);
+ if ((lp->options & XTE_POLLED_OPTION) == 0) {
+ iow(ndev, XTE_DGIE_OFFSET, (u32) XTE_DGIE_ENABLE_MASK);
+ // DEBUG_PRINTK("enable interrupts\n");
+ }
+ }
+#if !defined(LINUX)
+ phyInit(&lp->mii_if, (void *) lp, g_mdio_read, mdio_write);
+
+ result = phySearch(&lp->mii_if);
+
+ if (result == Success) {
+ LinkSpeed linkSpeed;
+ LinkStatus linkStatus;
+ LinkDuplex linkDuplex;
+
+ lp->mii_if.GetLinkStatus(&lp->mii_if, &linkSpeed, &linkStatus,
&linkDuplex);
+ if (linkDuplex == LinkFullDuplex) {
+ emac_cfg_write(lp, PHY_NUM, XTE_RXC1_OFFSET,
(emac_cfg_read(lp, PHY_NUM, XTE_RXC1_OFFSET) & ~XTE_RXC1_RXHD_MASK));
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET,
(emac_cfg_read(lp, PHY_NUM, XTE_TXC_OFFSET) & ~XTE_TXC_TXHD_MASK));
+ DEBUG_PRINTK("\ntemac_phy_init(FD)");
+ } else {
+ emac_cfg_write(lp, PHY_NUM, XTE_RXC1_OFFSET,
(emac_cfg_read(lp, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXHD_MASK));
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET,
(emac_cfg_read(lp, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXHD_MASK));
+ DEBUG_PRINTK("\ntemac_phy_init(HD)");
+ }
+ Reg = emac_cfg_read(lp, PHY_NUM, XTE_EMCFG_OFFSET) &
~XTE_EMCFG_LINKSPD_MASK;
+ switch (linkSpeed) {
+ case Link1000Mbps:
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
(u32) XTE_EMCFG_LINKSPD_1000);
+ DEBUG_PRINTK("\ntemac_phy_init(1000)");
+ break;
+ case Link100Mbps:
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_100);
+ DEBUG_PRINTK("\ntemac_phy_init(100)");
+ break;
+ case Link10Mbps:
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_10);
+ DEBUG_PRINTK("\ntemac_phy_init(10)");
+ break;
+ case LinkSpeedAuto:
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_100);
+ DEBUG_PRINTK("\ntemac_phy_init(auto)");
+ default:
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_100);
+ DEBUG_PRINTK("\ntemac_phy_init(??)
linkSpeed=%d",linkSpeed);
+ break;
+ }
+ }
+#endif
+ /*
+ Print PHY regs so that we can see if they are configured correctly
+ */
+ // if(lp->dbg) temac_dump(ndev);
+// Turns on packet generator and stops (Test Only)
+#if(0)
+ mdio_write(ndev, 0, XGP_PCS_EXTADDR_REG, 0x12);
+ Data = mdio_read(ndev, 0, XGP_PCS_EXTADDR_REG);
+ mdio_write(ndev, 0, XGP_PCS_EXTFUNC_REG, Data | 0x38);
+ while (1);
+#endif
+
+ /* Init Driver variable */
+ ndev->trans_start = 0;
+ spin_lock_init(&lp->lock);
+}
+
+/*****************************************************************************/
+/**
+ * Set options for the driver/device. The driver should be stopped with
+ * XTemac_Stop() before changing options.
+ *
+ * @param InstancePtr is a pointer to the instance to be worked on.
+ * @param Options are the options to set. Multiple options can be set
by OR'ing
+ * XTE_*_OPTIONS constants together. Options not specified are not
+ * affected.
+ *
+ * @return
+ * - 0 if the options were set successfully
+ * - XST_DEVICE_IS_STARTED if the device has not yet been stopped
+ * - XST_NO_FEATURE if setting an option requires HW support not present
+ *
+ * @note
+ * See xtemac.h for a description of the available options.
+ *
+
******************************************************************************/
+static u32
+temac_setoptions(struct net_device *ndev, u32 Options) {
+ struct temac_local *lp = ndev->priv;
+
+ /* Turn on jumbo packet support for both Rx and Tx */
+ if (Options & XTE_JUMBO_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXJMBO_MASK);
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXJMBO_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) & ~XTE_TXC_TXJMBO_MASK);
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) & ~XTE_RXC1_RXJMBO_MASK);
+ }
+
+ /* Turn on VLAN packet support for both Rx and Tx */
+ if (Options & XTE_VLAN_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXVLAN_MASK);
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXVLAN_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) & ~XTE_TXC_TXVLAN_MASK);
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) & ~XTE_RXC1_RXVLAN_MASK);
+ }
+
+ /* Turn on FCS stripping on receive packets */
+ if (Options & XTE_FCS_STRIP_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXFCS_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) & ~ XTE_RXC1_RXFCS_MASK);
+ }
+
+ /* Turn on FCS insertion on transmit packets */
+ if (Options & XTE_FCS_INSERT_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXFCS_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) & ~XTE_TXC_TXFCS_MASK);
+ }
+
+ /* Turn on length/type field checking on receive packets */
+ if (Options & XTE_LENTYPE_ERR_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXLT_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) & ~XTE_RXC1_RXLT_MASK);
+ }
+
+
+ /* Rest of options twiddle bits of other registers. Handle them one at
+ * a time
+ */
+
+ /* Turn on flow control */
+ if (Options & XTE_FLOW_CONTROL_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_FCC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_FCC_OFFSET) | XTE_FCC_RXFLO_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_FCC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_FCC_OFFSET) & ~XTE_FCC_RXFLO_MASK);
+ }
+
+ /* Turn on promiscuous frame filtering (all frames are received ) */
+ if (Options & XTE_PROMISC_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_AFM_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_AFM_OFFSET) | XTE_AFM_EPPRM_MASK);
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_AFM_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_AFM_OFFSET) & ~XTE_AFM_EPPRM_MASK);
+ }
+
+ /* Allow broadcast address filtering */
+ if (Options & XTE_BROADCAST_OPTION) {
+ iow(ndev, XTE_CR_OFFSET, ior(ndev, XTE_CR_OFFSET) &
~XTE_CR_BCREJ_MASK);
+ } else {
+ iow(ndev, XTE_CR_OFFSET, ior(ndev, XTE_CR_OFFSET) |
XTE_CR_BCREJ_MASK);
+ }
+
+ /* Allow multicast address filtering */
+ if (Options & XTE_MULTICAST_CAM_OPTION) {
+ iow(ndev, XTE_CR_OFFSET, ior(ndev, XTE_CR_OFFSET) &
~XTE_CR_MCREJ_MASK);
+ } else {
+ iow(ndev, XTE_CR_OFFSET, ior(ndev, XTE_CR_OFFSET) |
XTE_CR_MCREJ_MASK);
+ }
+
+ /* Enable interrupts related to rejection of bad frames */
+ if (Options & XTE_REPORT_RXERR_OPTION) {
+ /* Clear out any previous error conditions that may have existed
+ * prior to enabling the reporting of these types of errors
+ */
+ iow(ndev, XTE_IPISR_OFFSET, ior(ndev, XTE_IPISR_OFFSET) &
XTE_IPXR_RECV_DROPPED_MASK );
+
+ /* Whether these are enabled here are based on the last call to
+ * XTemac_IntrFifoEnable/Disable() and
XTemac_IntrSgDmaEnable/Disable()
+ * for the receive channel.
+ *
+ * If receive interrupts are enabled, then enable these
interrupts. This
+ * way, when XTemac_Start() is called, these interrupt enables
take
+ * effect right away.
+ *
+ * If receive interrupts are disabled, then don't do anything
here. The
+ * XTemac_IntrFifoEnable() and XTemac_IntrSgDmaEnable()
functions when
+ * called will check this option and enable these interrupts if
needed.
+ */
+ // if (lp->Flags & (XTE_FLAGS_RECV_FIFO_INT_ENABLE)) {
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) |
XTE_IPXR_RECV_DROPPED_MASK );
+ // }
+ } else {
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) &
~XTE_IPXR_RECV_DROPPED_MASK);
+ }
+
+
+ /* Enable interrrupt related to assertion of auto-negotiate HW
interrupt */
+ if (Options & XTE_ANEG_OPTION) {
+ /* Clear out any previous interupt condition that may have existed
+ * prior to enabling the reporting of auto negotiation
+ */
+ iow(ndev, XTE_IPISR_OFFSET, ior(ndev, XTE_IPISR_OFFSET) &
XTE_IPXR_AUTO_NEG_MASK);
+
+ /* Make this interupt source enabled when XTemac_Start() is
called */
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) &
XTE_IPXR_AUTO_NEG_MASK);
+ } else {
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) &
~XTE_IPXR_AUTO_NEG_MASK);
+ }
+
+
+ /* Enable transmitter if not already enabled */
+ if (Options & XTE_TRANSMITTER_ENABLE_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) | XTE_TXC_TXEN_MASK);
+ if (!(lp->options & XTE_POLLED_OPTION)) {
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) |
XTE_IPXR_XMIT_DONE_MASK);
+ if (lp->options & XTE_REPORT_TXSTATUS_OVERRUN_OPTION)
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET)
| XTE_IPXR_XMIT_SFIFO_OVER_MASK);
+ }
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_TXC_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_TXC_OFFSET) & ~XTE_TXC_TXEN_MASK);
+ }
+
+
+ /* Enable receiver? */
+ if (Options & XTE_RECEIVER_ENABLE_OPTION) {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) | XTE_RXC1_RXEN_MASK);
+ if (!(lp->options & XTE_POLLED_OPTION)) {
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET) |
XTE_IPXR_RECV_DONE_MASK);
+ if (lp->options & XTE_REPORT_TXSTATUS_OVERRUN_OPTION)
+ iow(ndev, XTE_IPIER_OFFSET, ior(ndev, XTE_IPIER_OFFSET)
| XTE_IPXR_RECV_DROPPED_MASK);
+ }
+ } else {
+ emac_cfg_write(ndev, PHY_NUM, XTE_RXC1_OFFSET,
emac_cfg_read(ndev, PHY_NUM, XTE_RXC1_OFFSET) & ~XTE_RXC1_RXEN_MASK);
+ }
+
+ /* The remaining options not handled here are managed elsewhere in the
+ * driver. No register modifications are needed at this time.
Reflecting the
+ * option in lp->Options is good enough for now.
+ */
+
+ /* Set options word to its new value */
+ lp->options |= Options;
+
+ return (0);
+}
+
+#if defined(CONFIG_PICO_LL_TEMAC)
+#define LL_SOF_MASK ((1 << 8))
+#define LL_EOF_MASK ((1 << 7))
+#define LL_SOP_MASK ((1 << 6))
+#define LL_EOP_MASK ((1 << 5))
+#define LL_RX_SRC_RDY_MASK ((1 << 4))
+#define LL_REM_MASK (0x0000000F)
+
+#define LL_TX_DATA 0x00
+#define LL_TX_CTRL 0x08
+#define LL_TX_RDY 0x10
+#define LL_RX_DATA 0x20
+#define LL_RX_CTRL 0x28
+#define LL_RX_RDY 0x30
+
+static int
+ll_recvFrame(struct net_device *ndev, struct sk_buff *skb, u8 *bd) {
+ struct temac_local *lp = ndev->priv;
+ u32 cw;
+ u32 dw;
+ u32 src_rdy_counter = 0;
+ u32 sof_counter = 0;
+ enum { S_IDLE=1, S_HEADER, S_PAYLOAD, S_FOOTER } state = S_IDLE;
+ const u32 src_rdy_counter_max = 10;
+ const u32 sof_counter_max = 10;
+ u8 *b ;
+
+ // DEBUG_FUNC;
+ // skb->data SHOULD == skb-->tail;
+ b = skb->tail ;
+ skb->len = 0;
+ while (1) {
+
+ /* read the control word to see if the source ready bit is
active (low) */
+ while (((cw = ior(ndev,LL_RX_CTRL)) & LL_RX_SRC_RDY_MASK) ==
LL_RX_SRC_RDY_MASK) {
+
+ /* only hang around for some amount of time waiting for
data to be present */
+ if (++src_rdy_counter == src_rdy_counter_max) {
+ skb->len = 0;
+ return SRC_RDY_TIMEOUT_ERROR;
+ }
+ }
+
+ /* reset watchdog counter */
+ src_rdy_counter = 0;
+ /*
+ IDLE STATE
+ */
+ if (state == S_IDLE) {
+
+ /*
+ haven't reached the SOF yet
+ only hang around for so long awaiting an SOF before returning
+ */
+ if ((cw & LL_SOF_MASK)) {
+ if (++sof_counter == sof_counter_max) {
+ skb->len = 0;
+ return SOF_TIMEOUT_ERROR;
+ }
+ }
+
+ else {
+
+ /*
+ reset watchdog counter
+ */
+ sof_counter = 0;
+ /*
+ ensure we record the start of a frame
+ */
+ state = S_HEADER;
+ }
+
+ /* ignore the LocalLink Header Words */
+ dw = ior(ndev,LL_RX_DATA);
+ }
+ /*
+ HEADER STATE
+ */
+ else if (state == S_HEADER) {
+ dw = ior(ndev, LL_RX_DATA);
+ if (!(cw & LL_SOP_MASK)) {
+ state = S_PAYLOAD;
+ *((u32 *) b) = dw;
+ b += 4;
+ skb->len += 4;
+ skb->tail += 4;
+ }
+ }
+ /* PAYLOAD STATE */
+ else if (state == S_PAYLOAD) {
+ *((u32 *) b) = ior(ndev,LL_RX_DATA);
+ b += 4;
+ skb->len += 4;
+ skb->tail += 4;
+ if (!(cw & LL_EOP_MASK))
+ state = S_FOOTER;
+ }
+ /* FOOTER STATE */
+ else if (state == S_FOOTER) {
+ *((u32 *) bd) = ior(ndev,LL_RX_DATA);
+ bd += 4;
+ if (!(cw & LL_EOF_MASK)) {
+
+ /* we've got the last data word */
+ cw = ~cw & LL_REM_MASK;
+ /* update the payload count according to the (masked)
rem value */
+ while (cw) {
+ if (cw & 1)
+ skb->len++;
+ cw >>= 1;
+ }
+
+ /* return success */
+ return 0;
+ }
+ }
+ }
+}
+
+static u8
+recvBd[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static void
+ll_temac_interrupt(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ /* read the control word to see if the source ready bit is active
(low) */
+ while ((ior(ndev, LL_RX_CTRL) & LL_RX_SRC_RDY_MASK) !=
LL_RX_SRC_RDY_MASK) {
+ u32 ret ;
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(XTE_MAX_FRAME_SIZE);
+ if (!skb) {
+ if (printk_ratelimit())
+ DEBUG_PRINTK("temac: low on memory - packet dropped\n");
+ lp->stats.rx_dropped++;
+ spin_unlock(&lp->lock);
+ return ;
+ // return IRQ_HANDLED;
+ }
+ if ((ret = ll_recvFrame(ndev, skb, recvBd))) {
+ DEBUG_PRINTK("\nwhoops=%x",ret);
+ } else {
+ // if (lp->dbg) dump_skb_data(skb, "Recv");
+ /* Pass to upper layer */
+ skb->dev = ndev;
+ skb->protocol = eth_type_trans(skb, ndev);
+ lp->stats.rx_bytes += skb->len;
+ lp->stats.rx_packets++;
+ netif_rx(skb);
+ }
+ }
+}
+#endif
+
+static void
+FifoRecvHandler(struct net_device *ndev);
+
+static void
+plb_temac_interrupt(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ u32 disr ;
+ u32 ipisr ;
+
+ // DEBUG_FUNC;
+ /* Get top level interrupt status. The status is self clearing when
the interrupt source is cleared */
+ disr = ior(ndev, XTE_DISR_OFFSET);
+#if 0
+ DEBUG_PRINTK("\nDISR %08x",disr);
+ if (disr & (XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK)) {
+ lp->IpifErrors++;
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() TERR");
+ // ErrorHandler(XST_IPIF_ERROR, disr, 0);
+ }
+ /* Receive packet FIFO is deadlocked */
+ if (disr & XTE_DXR_RECV_FIFO_MASK) {
+ lp->RxPktFifoErrors++;
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() RX DEADLOCK");
+ }
+ /* Transmit packet FIFO is deadlocked */
+ if (disr & XTE_DXR_SEND_FIFO_MASK) {
+ lp->TxPktFifoErrors++;
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() TX DEADLOCK");
+ }
+#endif
+ /* Handle core interrupts */
+ if (disr & XTE_DXR_CORE_MASK) {
+ /* Calculate which enabled interrupts have been asserted */
+ ipisr = ios(ndev);
+ if (!ipisr) {
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() !ipisr");
+ return ;
+ }
+
+ // DEBUG_PRINTK("\nIPISR %0x", ior(ndev, XTE_IPISR_OFFSET));
+ // DEBUG_PRINTK("\nIPIER %0x", ior(ndev, XTE_IPIER_OFFSET));
+
+ /* Check for fatal status/length FIFO errors. These errors
can't be * cleared */
+ if (ipisr & XTE_IPXR_FIFO_FATAL_ERROR_MASK) {
+#if 0
+ lp->FifoErrors++;
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() Fatal FIFO ERR");
+
+#if !defined(LINUX)
+ if (ipisr & XTE_IPXR_RECV_LFIFO_OVER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Rx Length FIFO Overrun");
+ DEBUG_PRINTK("\nRx Length FIFO Overrun");
+ }
+
+ if (ipisr & XTE_IPXR_RECV_LFIFO_UNDER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Rx Length FIFO Underrun");
+ DEBUG_PRINTK("\nRx Length FIFO Underrun");
+ }
+#endif
+ if (ipisr & XTE_IPXR_XMIT_SFIFO_OVER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Tx Status FIFO Overrun");
+ DEBUG_PRINTK("\nTx Status FIFO Overrun");
+ }
+
+ if (ipisr & XTE_IPXR_XMIT_SFIFO_UNDER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Tx Status FIFO Underrun");
+ DEBUG_PRINTK("\nTx Status FIFO Underrun");
+ }
+
+ if (ipisr & XTE_IPXR_XMIT_LFIFO_OVER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Tx Length FIFO Overrun");
+ DEBUG_PRINTK("\nTx Length FIFO Overrun");
+ }
+
+ if (ipisr & XTE_IPXR_XMIT_LFIFO_UNDER_MASK) { /* requires
reset */
+ EnetDbPrint(&lp->enetDevice, "Tx Length FIFO Underrun");
+ DEBUG_PRINTK("\nTx Length FIFO Underrun");
+ }
+#endif
+ // ErrorHandler(XST_FIFO_ERROR, CorePending &
XTE_IPXR_FIFO_FATAL_ERROR_MASK, 0);
+ temac_device_reset(ndev);
+ }
+ /* A receive packet has arrived. Call the receive handler.
+ *
+ * Acking this interrupt is not done here. The handler has a
choice:
+ * 1) Call XTemac_FifoRecv() which will ack this interrupt
source, or
+ * 2) Call XTemac_IntrFifoDisable() and defer XTEmac_FifoRecv()
to a
+ * later time. Failure to do one of these actions will leave this
+ * interupt still pending resulting in an exception loop.
+ */
+ if (ipisr & XTE_IPXR_RECV_DONE_MASK) {
+ // DEBUG_PRINTK("\ntemac_interrupt_fifo() FifoRecv()");
+ FifoRecvHandler(ndev);
+ }
+
+ /* A transmit has completed. Pull off all statuses that are
available.
+ * For each status that contains a non-fatal error, the error
handler
+ * is invoked. For fatal errors, the error handler is invoked
once and
+ * assumes the callback will reset the device.
+ *
+ * Unless there was a fatal error, then call the send handler since
+ * resources in the packet FIFO, transmit length FIFO, and transmit
+ * status FIFO have been freed up. This gives the handler a chance
+ * to enqueue new frame(s).
+ */
+ if (ipisr & XTE_IPXR_XMIT_DONE_MASK) {
+
+ // DEBUG_PRINTK("\nplb_temac_interrupt()
XTE_IPXR_XMIT_DONE_MASK");
+ // DEBUG_PRINTK("\ntemac_interrupt_fifo() XMIT DONE");
+ /* While XMIT_DONE persists */
+ do {
+ u32 Reg;
+ /* Get TSR, try to clear XMIT_DONE */
+ Reg = ior(ndev, XTE_TSR_OFFSET);
+ iow(ndev, XTE_IPISR_OFFSET, XTE_IPXR_XMIT_DONE_MASK);
+
+ /* Does TSR indicate error? */
+ if (Reg & XTE_TSR_ERROR_MASK) {
+ // lp->TxStatusErrors++;
+ DEBUG_PRINTK("\ntemac_interrupt_fifo() Send Error");
+ // ErrorHandler(XST_SEND_ERROR, Reg, 0);
+
+ /* Fatal errors end processing immediately */
+ if (Reg & XTE_TSR_PFIFOU_MASK) {
+ return;
+ }
+ }
+
+ /* Read IPISR and test XMIT_DONE again */
+ ipisr = ior(ndev, XTE_IPISR_OFFSET);
+ } while (ipisr & XTE_IPXR_XMIT_DONE_MASK);
+ // lp->stats.tx_packets++;
+ }
+
+ /* Check for dropped receive frame. Ack the interupt then call the
+ * error handler
+ */
+ if (ipisr & XTE_IPXR_RECV_DROPPED_MASK) {
+ iow(ndev, XTE_IPISR_OFFSET, ipisr &
XTE_IPXR_RECV_DROPPED_MASK);
+ // lp->RxRejectErrors++;
+ // DEBUG_PRINTK("\nplb_temac_interrupt_fifo() RX DROPPED");
+ }
+ }
+ // DEBUG_FUNC_EXIT;
+}
+/****************************************************************************
+ * Device Operational Functions *
+ * *
+ * These functions are used to operate the device at runtime *
+
****************************************************************************/
+
+static irqreturn_t
+temac_interrupt(int irq, void *ndev_id, struct pt_regs *regs) {
+ struct net_device *ndev = ndev_id;
+ struct temac_local *lp = ndev->priv;
+ // DEBUG_FUNC;
+ if (!ndev) {
+ if (lp->dbg) printk ("temac_interrupt() without DEVICE arg\n");
+ return IRQ_HANDLED;
+ }
+#if 0
+ switch (irq) {
+ case -1:
+ if (lp->dbg) printk ("temac_interrupt() NET_POLL\n");
+ break;
+ case -2:
+ // if (lp->dbg) printk ("temac_interrupt()
temac_rx_timeout()\n");
+ break;
+ case 0:
+ /* Call it. */
+ // if (lp->dbg) printk("temac_interrupt() INT\n");
+ // lp->Interrupts++; /* Log
interrupt */
+ break;
+ default:
+ /* Call it. */
+ if (lp->dbg) printk("temac_interrupt() IRQ=%d\n",irq);
+ break;
+ }
+#endif
+
+ /* A real interrupt coming */
+ spin_lock(&lp->lock);
+#if defined(CONFIG_PICO_LL_TEMAC)
+ switch (lp->nic_type) {
+ case TEMAC_PLB:
+ plb_temac_interrupt(ndev);
+ break;
+
+ case TEMAC_LL:
+ ll_temac_interrupt(ndev);
+ break;
+ }
+#else
+ plb_temac_interrupt(ndev);
+#endif
+
+#if 0
+ /* read from PHY status register */
+ ret = mdio_read(ndev, PHY_NUM, MII_FCSCOUNTER);
+ if (lp->phy_status != ret) {
+ lp->phy_status = ret;
+ if (lp->dbg) DEBUG_PRINTK("\nPHY interrupt status register
%08x", ret);
+ if ((ret & 0x00000c00) == (0x00000c00)) {
+ if (lp->dbg) DEBUG_PRINTK("\nautoneg complete , link up");
+ } else if (ret & 0x00000400) {
+ if (lp->dbg) DEBUG_PRINTK("\nlink down");
+ }
+ }
+#endif
+
+#if defined(LINUX)
+ #warning "need to tell linux transmission complete"
+#else
+ EnetNotifyTask(&lp->enetDevice); // must be called on
transmission or reception complete - should only be called once/interrupt
+#endif
+ spin_unlock(&lp->lock);
+ /* Ack the interrupts we saw and processed */
+ // iow(ndev, XTE_IPISR_OFFSET, ints);
+ // iow(ndev, XTE_DISR_OFFSET, dev_ints);
+ // DEBUG_FUNC_EXIT;
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_PICO_LL_TEMAC)
+static u32
+rem_table[] = {
+ 0x0000000F, 0x00000008, 0x0000000C, 0x0000000E,
+};
+
+static int
+ll_sendFrame(struct net_device *ndev, struct sk_buff *skb, u8 *bd) {
+ int i;
+ u32 cw;
+ u32 *bp = (u32 *) skb->data;
+ u32 *bdesc = (u32 *) bd;
+
+ // DEBUG_FUNC;
+ /*
+ Send Buffer Descriptor as LocalLink Header
+ */
+
+ iow(ndev, LL_TX_DATA, bdesc[0]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & (LL_SOF_MASK | LL_REM_MASK)));
+ iow(ndev, LL_TX_DATA, bdesc[1]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[2]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[3]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[4]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[5]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[6]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+ iow(ndev, LL_TX_DATA, bdesc[7]);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_REM_MASK));
+
+ /*
+ Send Ethernet Frame in LocalLink payload
+ */
+ for (i = 0; i < skb->len / 4; i++) {
+
+ /* Assume caches are off */
+ iow(ndev, LL_TX_DATA, bp[i]);
+ cw = 0;
+ /* Detect End of Packet */
+ if (i == (skb->len / 4 - 1)) {
+ if (skb->len % 4) {
+ cw |= LL_REM_MASK;
+ } else {
+ cw |= LL_EOP_MASK | LL_REM_MASK;
+ }
+ }
+ /* Detect Start of Packet */
+ if (i == 0)
+ cw |= LL_SOP_MASK | LL_REM_MASK;
+ if (i > 0 && i < (skb->len / 4 - 1))
+ cw |= LL_REM_MASK;
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & cw));
+ }
+
+ /* If skb->len is not a multiple of 4, then send last 1, 2, or 3
bytes... */
+ if (skb->len % 4) {
+ iow(ndev, LL_TX_DATA, bp[i]);
+ cw = LL_EOP_MASK | rem_table[skb->len % 4];
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & cw));
+ }
+ /*
+ Send Dummy LocalLink Footer
+ */
+ iow(ndev, LL_TX_DATA, 0);
+ iow(ndev, LL_TX_CTRL, ~(0xffffffff & LL_EOF_MASK));
+ return 0;
+}
+#endif
+
+u8 bDescriptor[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static u32 Read_64(struct temac_local *lp, void *BufPtr, u32 ByteCount,
int Eop);
+static u32 Write_64(struct temac_local *lp, void *BufPtr, u32 ByteCount);
+
+static int
+plb_sendFrame(struct net_device *ndev, struct sk_buff *skb, u8 * bd) {
+ struct temac_local *lp = ndev->priv;
+ u32 Reg;
+
+ // DEBUG_FUNC;
+ /* Load the FIFO */
+ /* Transfer the data using the best/fastest method */
+ Write_64(lp, skb->data, skb->len);
+
+ /* Make sure the packet FIFO didn't report an error */
+ Reg = ior(ndev, XTE_DISR_OFFSET);
+ if (Reg & XTE_DXR_SEND_FIFO_MASK) {
+ // DEBUG_PRINTK("\nplb_sendFrame() Error: XTE_DXR_SEND_FIFO_MASK");
+ return -EIO;
+ }
+
+ /* Verify no IPIF errors */
+ if (Reg & (XTE_DXR_DPTO_MASK | XTE_DXR_TERR_MASK)) {
+ /* Only bump stats in polled mode. For interrupt driven mode,
this stat
+ * is bumped in temac_interrupt_fifo()
+ */
+ // DEBUG_PRINTK("\nplb_sendFrame() Error: XTE_DXR_TERR_MASK");
+ return -EIO;
+ }
+
+ /* Initiate transmit */
+ /* See if transmit length FIFO is full. If it is, try to clear the
+ * status. If it the status remains, then return an error
+ */
+ Reg = ior(ndev, XTE_IPISR_OFFSET);
+ if (Reg & XTE_IPXR_XMIT_LFIFO_FULL_MASK) {
+ iow(ndev, XTE_IPISR_OFFSET, XTE_IPXR_XMIT_LFIFO_FULL_MASK);
+
+ Reg = ior(ndev, XTE_IPISR_OFFSET);
+ if (Reg & XTE_IPXR_XMIT_LFIFO_FULL_MASK) {
+ // lp->FifoErrors++;
+ // DEBUG_PRINTK("\nplb_sendFrame() Error:
XTE_IPIXR_XMIT_LFIFO_FULL_MASK");
+ return -EIO;
+ }
+ }
+
+ // DEBUG_PRINTK("\nplb_sendFrame() XTE_TPLR_OFFSET -start xmit");
+ /* Start transmit */
+ iow(ndev, XTE_TPLR_OFFSET, skb->len);
+
+ // DEBUG_FUNC_EXIT;
+ return 0;
+}
+
+#define XTE_RX_SINK_BUFFER_SIZE 1024
+static void
+FifoRecvHandler(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ struct sk_buff *skb;
+ u32 len;
+ u32 ret = 0;
+ static u32 rx_buffer_sink[XTE_RX_SINK_BUFFER_SIZE / sizeof(u32)];
+
+ // DEBUG_FUNC;
+ spin_lock(&XTE_spinlock);
+ // while ((ret = XTemac_FifoQueryRecvStatus(ndev)) == XST_NO_DATA
&& NumTries--); // not in interrupt version
+
+ /* If the receive length FIFO is empty, then there's no packet
waiting */
+ if (!((ret = ior(ndev, XTE_IPISR_OFFSET)) & XTE_IPXR_RECV_DONE_MASK)) {
+ // DEBUG_PRINTK("\n%s: XTemac could not read received packet
length, error=%d.\n", ndev->name, ret);
+ lp->stats.rx_errors++;
+ // reset(ndev, __LINE__);
+ spin_unlock(&XTE_spinlock);
+ return;
+ }
+ /* Get the length */
+ len = ior(ndev, XTE_RPLR_OFFSET);
+ // DEBUG_PRINTK("\nlen=%d.\n", len);
+
+ /* The IPXR_RECV_DONE_MASK status bit is tied to the RSR register.
To clear
+ * this condition, read from the RSR (which has no information)
then write
+ * to the IPISR register to ack the status.
+ */
+ ret = ior(ndev, XTE_RSR_OFFSET);
+ iow(ndev, XTE_IPISR_OFFSET, XTE_IPXR_RECV_DONE_MASK);
+
+ if (!(skb = dev_alloc_skb(len + ALIGNMENT_RECV))) {
+ /* Couldn't get memory. */
+ lp->stats.rx_dropped++;
+ // DEBUG_PRINTK("\n%s: XTemac could not allocate receive
buffer.", ndev->name);
+
+ /* consume data in Xilinx TEMAC RX data fifo so it is sync with
RX length fifo */
+ for (; len > XTE_RX_SINK_BUFFER_SIZE; len -=
XTE_RX_SINK_BUFFER_SIZE) {
+ Read_64(lp, rx_buffer_sink, XTE_RX_SINK_BUFFER_SIZE,
XTE_PARTIAL_PACKET);
+ }
+ Read_64(lp, rx_buffer_sink, len, XTE_END_OF_PACKET);
+
+ spin_unlock(&XTE_spinlock);
+ return;
+ }
+ /* Read the packet data */
+ if ((ret = Read_64(lp, skb->data, len, XTE_END_OF_PACKET))) {
+ lp->stats.rx_errors++;
+ dev_kfree_skb_irq(skb);
+ // DEBUG_PRINTK("\n%s: XTemac could not receive buffer,
error=%d.", ndev->name, ret);
+ // reset(lp, __LINE__);
+ spin_unlock(&XTE_spinlock);
+ return;
+ }
+ spin_unlock(&XTE_spinlock);
+
+ skb_put(skb, len); /* Tell the skb how much data we got. */
+ // if (lp->dbg) dump_skb(lp, skb);
+ // if (lp->dbg) dump_skb_data(lp, skb, "recv");
+ skb->dev = ndev; /* Fill out required meta-data. */
+ lp->stats.rx_bytes += skb->len;
+
+#if !defined(LINUX)
+/*
+This function must be called when a packet is successfully received.
+user_data should be the value that was passed in as an argument to
EtherRead().
+pkt and len specify the packet that was received.
+ returns true/false. if false the buffer should be reused
+*/
+
+ packetUsed = EnetPacketReceived(&lp->enetDevice, (Address)
skb->data, skb->len, skb->user_data);
+ if (packetUsed) {
+ dev_kfree_skb_irq(skb);
+ } else {
+ // mark SKB available
+ skb->flags &= ~SKB_ALLOC ; // not allocated
+ skb->len = 0;
+ skb->tail = skb->data;
+ }
+
+#endif
+ skb->protocol = eth_type_trans(skb, ndev);
+ // DEBUG_PRINTK("\nskb->protocol=%x", skb->protocol);
+ /* skb->ip_summed = CHECKSUM_NONE; */
+ ret = netif_rx(skb); /* Send the packet
upstream. */
+ ndev->last_rx = jiffies;
+ lp->stats.rx_packets++;
+ // DEBUG_FUNC_EXIT;
+}
+
+/*******************************************************************************
+* Read into the 64 bit holding buffer from the receive packet FIFO.
+* Each time the holding buffer becomes full, then it is flushed to the
+* provided buffer.
+*
+* @param F is a pointer to the packet FIFO instance to be worked on.
+* @param BufPtr is the destination buffer address on any alignment
+* @param ByteCount is the number of bytes to transfer
+*
+*******************************************************************************/
+static void
+Read64_Unaligned(struct temac_local *lp, void *BufPtr, u32 ByteCount) {
+ struct temac_pktFifo * F = &lp->RecvFifo;
+ u8 *DestPtr = (u8 *) BufPtr;
+ unsigned FifoTransfersLeft;
+ unsigned PartialBytes;
+ unsigned BytesLeft;
+ int i;
+
+ // DEBUG_FUNC;
+ /* Stage 1: The hold may have some residual bytes that must be flushed
+ * to the buffer before anything is read from the FIFO
+ */
+
+ /* Calculate the number of bytes to flush to the buffer from the hold.
+ * If the number of bytes to flush is greater than the "Bytes"
requested,
+ * then adjust accordingly.
+ */
+ i = mHold_GetIndex(F);
+ PartialBytes = PFIFO_64BIT_WIDTH_BYTES - i;
+
+ if (PartialBytes > ByteCount) {
+ PartialBytes = ByteCount;
+ }
+
+ /* Calculate the number of bytes remaining after flushing to the
buffer */
+ BytesLeft = ByteCount - PartialBytes;
+
+ /* Move the hold's index forward */
+ mHold_Advance(F, PartialBytes);
+
+ /* Copy bytes */
+ while (PartialBytes--) {
+ mHold_CopyOut(F, i, *DestPtr);
+ i++;
+ DestPtr++;
+ }
+
+ /* No more data to process */
+ if (!BytesLeft) {
+ return;
+ }
+
+ /* Stage 2: The hold is empty now, if any more bytes are left to
process, then
+ * it will begin with nothing in the hold. Use the hold as a
temporary storage
+ * area to contain the data.
+ *
+ * The hold is filled with FIFO data, then that data is written to
the buffer.
+ * Do this FifoTransfersLeft times
+ */
+
+ /* Calculate the number of times a push will need to occur */
+ FifoTransfersLeft = BytesLeft / PFIFO_64BIT_WIDTH_BYTES;
+
+ /* Calculate the number of partial bytes left after this stage */
+ PartialBytes = BytesLeft - (FifoTransfersLeft *
PFIFO_64BIT_WIDTH_BYTES);
+
+ /* Write to the hold and push data to the FIFO */
+ while (FifoTransfersLeft--) {
+ /* Load the hold with the next data set from the FIFO */
+ mPop64(F);
+
+ /* Write hold to buffer */
+ for (i = 0; i < PFIFO_64BIT_WIDTH_BYTES; i++) {
+ mHold_CopyOut(F, i, *DestPtr);
+ DestPtr++;
+ }
+ }
+
+ /* No more data to process
+ * After processing full FIFO chunks of data, the hold is empty at this
+ * point
+ */
+ if (!PartialBytes) {
+ return;
+ }
+
+ /* Stage 3: All that is left is to fill the hold one more time with
FIFO
+ * data, then write the remaining requested bytes to the buffer
+ */
+
+ /* Get FIFO data */
+ mPop64(F);
+
+ /* Copy bytes from the hold to the buffer */
+ for (i = 0; i < PartialBytes; i++) {
+ mHold_CopyOut(F, i, *DestPtr);
+ DestPtr++;
+ }
+
+ /* Set the hold's index to its final correct value */
+ mHold_SetIndex(F, PartialBytes);
+ // DEBUG_FUNC_EXIT;
+}
+
+/*******************************************************************************
+* Read directly from the 64 bit wide receive FIFO into an aligned
destination
+* buffer. Leftover bytes are written to the holding buffer.
+*
+* @param F is a pointer to the packet FIFO instance to be worked on.
+* @param BufPtr is the destination buffer address on 32-bit alignment
+* @param ByteCount is the number of bytes to transfer
+*
+*******************************************************************************/
+static void
+Read64_Aligned(struct temac_local *lp, u32 * BufPtr, u32 ByteCount) {
+ struct temac_pktFifo * F = &lp->RecvFifo;
+ unsigned FifoTransfersLeft = ByteCount / PFIFO_64BIT_WIDTH_BYTES;
+ unsigned PartialBytes = ByteCount & (PFIFO_64BIT_WIDTH_BYTES - 1);
+
+ // DEBUG_FUNC;
+ /* Direct transfer */
+ while (FifoTransfersLeft--) {
+ mReadFifo64(F, BufPtr);
+ BufPtr += 2;
+ }
+
+ /* Leftover bytes are left in the holding area */
+ if (PartialBytes) {
+ Read64_Unaligned(lp, BufPtr, PartialBytes);
+ }
+ // DEBUG_FUNC_EXIT;
+}
+
+/*******************************************************************************
+* Write to the 64 bit holding buffer. Each time it becomes full, then it is
+* pushed to the transmit FIFO.
+*
+* @param F is a pointer to the packet FIFO instance to be worked on.
+* @param BufPtr is the source buffer address on any alignment
+* @param ByteCount is the number of bytes to transfer
+*
+*******************************************************************************/
+static void
+Write64_Unaligned(struct temac_local *lp, void *BufPtr, u32 ByteCount) {
+ struct temac_pktFifo * F = &lp->SendFifo;
+ u8 *SrcPtr = (u8 *) BufPtr;
+ unsigned FifoTransfersLeft;
+ unsigned PartialBytes;
+ unsigned BytesLeft;
+ int i;
+
+ // DEBUG_FUNC;
+ /* Stage 1: The hold may be partially full. Write enough bytes to it to
+ * cause a push to the FIFO
+ */
+
+ /* Calculate the number of bytes needed to trigger a push, if not
enough
+ * bytes have been specified to cause a push, then adjust accordingly
+ */
+ i = mHold_GetIndex(F);
+ PartialBytes = PFIFO_64BIT_WIDTH_BYTES - i;
+ if (PartialBytes > ByteCount) {
+ PartialBytes = ByteCount;
+ }
+
+ /* Calculate the number of bytes remaining after the first push */
+ BytesLeft = ByteCount - PartialBytes;
+
+ /* Write to the hold and advance its index */
+ mHold_Advance(F, PartialBytes);
+
+ while (PartialBytes--) {
+ mHold_CopyIn(F, i, *SrcPtr);
+ SrcPtr++;
+ i++;
+ }
+
+ /* Push to fifo if needed */
+ if (mHoldS_IsFull(F)) {
+ mPush64(F);
+ mHoldS_SetEmpty(F);
+ }
+
+ /* No more data to process */
+ if (!BytesLeft) {
+ return;
+ }
+
+ /* Stage 2: The hold is empty now, if any more bytes are left to
process, then
+ * it will begin with nothing in the hold. Use the hold as a
temporary storage
+ * area to contain the data.
+ *
+ * The hold is filled then pushed out to the FIFOs a number of
times based on
+ * how many bytes are left to process.
+ */
+
+ /* Calculate the number of times a push will need to occur */
+ FifoTransfersLeft = BytesLeft / PFIFO_64BIT_WIDTH_BYTES;
+
+ /* Calculate the number of partial bytes left after this stage */
+ PartialBytes = BytesLeft - (FifoTransfersLeft *
PFIFO_64BIT_WIDTH_BYTES);
+
+ /* Write to the hold and push data to the FIFO */
+ while (FifoTransfersLeft--) {
+ for (i = 0; i < PFIFO_64BIT_WIDTH_BYTES; i++) {
+ mHold_CopyIn(F, i, *SrcPtr);
+ SrcPtr++;
+ }
+ mPush64(F);
+ }
+
+ /* No more data to process
+ * HoldIndex was left at 0 by stage 1, at this point, that is
+ * still the correct value.
+ */
+ if (!PartialBytes) {
+ return;
+ }
+
+ /* Stage 3: All that is left is to fill the hold with the remaining
data
+ * to be processed. There will be no push to the FIFO because there
is not
+ * enough data left to cause one.
+ */
+
+ /* Write to the hold and push data to the FIFO */
+ for (i = 0; i < PartialBytes; i++) {
+ mHold_CopyIn(F, i, *SrcPtr);
+ SrcPtr++;
+ }
+
+ /* Set the hold's index to its final correct value */
+ mHold_SetIndex(F, PartialBytes);
+ // DEBUG_FUNC_EXIT;
+}
+
+/*******************************************************************************
+* Write directly to the 64 bit wide transmit FIFO from an aligned source
+* buffer. Leftover bytes are written to the holding buffer.
+*
+* @param F is a pointer to the packet FIFO instance to be worked on.
+* @param BufPtr is the source buffer address on 32-bit alignment
+* @param ByteCount is the number of bytes to transfer
+*
+*******************************************************************************/
+static void
+Write64_Aligned(struct temac_local *lp, u32 * BufPtr, u32 ByteCount) {
+ struct temac_pktFifo * F = &lp->SendFifo;
+ unsigned FifoTransfersLeft = ByteCount / PFIFO_64BIT_WIDTH_BYTES;
+ unsigned PartialBytes = ByteCount & (PFIFO_64BIT_WIDTH_BYTES - 1);
+
+ // DEBUG_FUNC;
+ /* Direct transfer */
+ while (FifoTransfersLeft--) {
+ _iow(F->DataBaseAddress + 0, (BufPtr)[0]);
+ _iow(F->DataBaseAddress + 4, (BufPtr)[1]);
+ BufPtr += 2;
+ }
+
+ /* Leftover bytes are left in the holding area */
+ if (PartialBytes) {
+ Write64_Unaligned(lp, BufPtr, PartialBytes);
+ }
+ // DEBUG_FUNC_EXIT;
+}
+
+/*******************************************************************************
+* Algorithm to read from a 64 bit wide receive packet FIFO with through
the
+* holding buffer.
+*
+* @param F is a pointer to a Temac FIFO instance to worked on.
+* @param BufPtr is the destination address on any alignment
+* @param ByteCount is the number of bytes to transfer
+*
+* @return 0 if transfer completed or XST_NO_DATA if the amount of
+* data being buffered by the driver plus the amount of data in the
+* packet FIFO is not enough to satisfy the number of bytes
requested
+* by the ByteCount parameter.
+*******************************************************************************/
+static u32
+Read_64(struct temac_local *lp, void *BufPtr, u32 ByteCount, int Eop) {
+ struct temac_pktFifo * F = &lp->RecvFifo;
+ unsigned BufAlignment = (unsigned) BufPtr & 3;
+ unsigned PartialBytes;
+ unsigned MaxBytes;
+ int HoldAlignment = mHold_GetIndex(F);
+
+ // DEBUG_FUNC;
+ /* Determine how many bytes can be read from the packet FIFO */
+ MaxBytes = _ior(lp->RecvFifo.RegBaseAddress +
XPF_V200A_COUNT_STATUS_REG_OFFSET) & XPF_V200A_COUNT_MASK;
+ MaxBytes *= PFIFO_64BIT_WIDTH_BYTES;
+
+ /* Case 1: Buffer aligned on 4-byte boundary and Hold is empty
+ *
+ * 1. Read all bytes using the fastest transfer method
+ */
+ if ((BufAlignment == 0) && (mHoldR_IsEmpty(F))) {
+ /* Enough data in fifo? */
+ if (ByteCount > MaxBytes) {
+ return (XST_NO_DATA);
+ }
+
+ Read64_Aligned(lp, (u32 *) BufPtr, ByteCount);
+ }
+
+ /* Case 2: Buffer and Hold are byte aligned with each other
+ *
+ * 1. Transfer enough bytes from the Hold to the buffer to trigger a
+ * read from the FIFO.
+ *
+ * 2. The state of the buffer and Hold are now as described by
Case 1 so
+ * read remaining bytes using the fastest transfer method
+ */
+ else if (BufAlignment == (HoldAlignment % PFIFO_64BIT_WIDTH_BYTES)) {
+ PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;
+
+ if (ByteCount < PartialBytes) {
+ PartialBytes = ByteCount;
+ }
+
+ /* Enough data in fifo? Must account for the number of bytes
the driver
+ * is currently buffering
+ */
+ if (ByteCount > (MaxBytes + PartialBytes)) {
+ return (XST_NO_DATA);
+ }
+
+ Read64_Unaligned(lp, BufPtr, PartialBytes);
+ Read64_Aligned(lp, (u32 *) ((u32) BufPtr + PartialBytes),
ByteCount - PartialBytes);
+ }
+
+ /* Case 3: No alignment to take advantage of
+ *
+ * 1. Read FIFOs using the slower method.
+ */
+ else {
+ /* Enough data in fifo? Must account for the number of bytes
the driver
+ * is currently buffering
+ */
+ PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;
+ if (ByteCount > (MaxBytes + PartialBytes)) {
+ return (XST_NO_DATA);
+ }
+
+ Read64_Unaligned(lp, BufPtr, ByteCount);
+ }
+
+ /* If this marks the end of packet, then dump any remaining data in the
+ * hold. The dumped data in this context is meaningless.
+ */
+ if (Eop == XTE_END_OF_PACKET) {
+ mHoldR_SetEmpty(F);
+ }
+ // DEBUG_FUNC_EXIT;
+ return (0);
+}
+/*******************************************************************************
+* Algorithm to write to a 64 bit wide transmit packet FIFO through the
holding
+* buffer.
+*
+* @param FPtr is a pointer to a Temac FIFO instance to worked on.
+* @param BufPtr is the source buffer address on any alignment
+* @param ByteCount is the number of bytes to transfer
+* @param Eop specifies whether the last byte written is the last byte
of the
+* packet.
+*
+* @return 0
+*******************************************************************************/
+static u32
+Write_64(struct temac_local *lp, void *BufPtr, u32 ByteCount) {
+ struct temac_pktFifo * F = &lp->SendFifo;
+ unsigned BufAlignment = (unsigned) BufPtr & 3;
+ unsigned PartialBytes;
+ int HoldAlignment = mHold_GetIndex(F);
+
+ // DEBUG_FUNC;
+ /* Case 1: Buffer aligned on 4-byte boundary and Hold is empty
+ *
+ * 1. Write all bytes using the fastest transfer method
+ */
+ if ((BufAlignment == 0) && (mHoldS_IsEmpty(F))) {
+ Write64_Aligned(lp, (u32 *) BufPtr, ByteCount);
+ }
+
+ /* Case 2: Buffer and Hold are byte aligned with each other
+ *
+ * 1. Transfer enough bytes from the buffer to the Hold to
trigger a flush
+ * to the FIFO.
+ *
+ * 2. The state of the buffer and Hold are as described by Case 1 so
+ * write remaining bytes using the fastest transfer method
+ */
+ else if (BufAlignment == (HoldAlignment % PFIFO_64BIT_WIDTH_BYTES)) {
+ PartialBytes = PFIFO_64BIT_WIDTH_BYTES - HoldAlignment;
+
+ if (ByteCount < PartialBytes) {
+ PartialBytes = ByteCount;
+ }
+
+ Write64_Unaligned(lp, BufPtr, PartialBytes);
+ Write64_Aligned(lp, (u32 *) ((u32) BufPtr + PartialBytes),
ByteCount - PartialBytes);
+ }
+
+ /* Case 3: No alignment to take advantage of
+ *
+ * 1. Read FIFOs using the slower method.
+ */
+ else {
+ Write64_Unaligned(lp, BufPtr, ByteCount);
+ }
+
+ /* If TxBytes is non-zero then the caller wants to transmit data
from the
+ * FIFO
+ */
+
+ /* Push the hold to the FIFO if data is present */
+ if (!mHoldS_IsEmpty(F)) {
+ mPush64(F);
+ mHoldS_SetEmpty(F);
+ }
+
+ // DEBUG_FUNC_EXIT;
+ return (0);
+}
+#if 0
+
+static u32
+Read64(struct temac_local *lp, void *BufPtr, u32 ByteCount, int Eop) {
+ u8 *rbuf = BufPtr ;
+ u32 ret ;
+ u32 len ;
+ int ii ;
+
+ // DEBUG_FUNC;
+ while (ior(ndev, XTE_IPISR_OFFSET) & XTE_IPXR_RECV_DONE_MASK) {
+ /* Get the length */
+ len = ior(ndev, XTE_RPLR_OFFSET);
+ DEBUG_PRINTK("\nRead64()=%0x",len);
+
+ /* The IPXR_RECV_DONE_MASK status bit is tied to the RSR
register. To clear
+ * this condition, read from the RSR (which has no information)
then write
+ * to the IPISR register to ack the status.
+ */
+ ret = ior(ndev, XTE_RSR_OFFSET);
+ iow(ndev, XTE_IPISR_OFFSET, XTE_IPXR_RECV_DONE_MASK);
+ ret = Read_64(lp, BufPtr, ByteCount, Eop) ;
+ if (lp->dbg) {
+ for(ii=0;ii<len;ii++){
+ if ((ii%16)==0) DEBUG_PRINTK("\n%08x ", ii);
+ DEBUG_PRINTK("%02x ", rbuf[ii]);
+ }
+ DEBUG_PRINTK("\n");
+ }
+ }
+ DEBUG_FUNC_EXIT;
+ return ret;
+}
+
+static void
+sendTest(struct temac_local *lp) {
+ unsigned char sbuf[] = { 0xff,0xff,0xff,0xff,0xff,0xff,
+ 0x08,0x00,0x3e,0x26,0x15,0x59,
+ 0x00, 0x42,
+ 0xfe, 0xff,
+
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f
+ };
+ unsigned char rbuf[512] ;
+ int ii;
+ u32 Status;
+ int len = sizeof(sbuf);
+
+ DEBUG_FUNC;
+#if 0
+ iow(ndev,XTE_DSR_OFFSET, XTE_DSR_RESET_MASK); //
Reset PLB TEMAC
+ iow(ndev, XTE_CR_OFFSET, XTE_CR_HRST_MASK); // Reset
HARD TEMAC
+ emac_cfg_write(lp, PHY_NUM, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK);
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET, XTE_TXC_TXRST_MASK);
+ emac_cfg_write(lp, PHY_NUM, XTE_MC_OFFSET, XTE_XTE_MDIO_DIV_DFT |
XTE_MC_MDIO_MASK); // Set default MDIO divisor
+
+ emac_cfg_write(lp, PHY_NUM, XTE_RXC1_OFFSET,
XTE_RXC1_RXEN_MASK); // Enable Rx, VLAN, Jumbo, Strip FCS
+ emac_cfg_write(lp, PHY_NUM, XTE_TXC_OFFSET,
XTE_TXC_TXEN_MASK+XTE_TXC_TXIFG_MASK); // Enable Tx, VLAN, Jumbo,
No client supplied FCS
+ emac_cfg_write(lp, PHY_NUM, XTE_EMCFG_OFFSET,
XTE_EMCFG_LINKSPD_10+XTE_EMCFG_HOSTEN_MASK); // Set 1G Host Interface
+ emac_cfg_write(lp, PHY_NUM, XTE_UAW0_OFFSET, ((buf[6]<<8) | (buf[7])));
+ emac_cfg_write(lp, PHY_NUM, XTE_UAW1_OFFSET, ((buf[8]<<24) |
(buf[9]<<16) | (buf[10]<<8) | (buf[11])));
+#endif
+ iow(ndev, XTE_DGIE_OFFSET,0); // disable interrupts
+ emac_cfg_write(lp, PHY_NUM, XTE_AFM_OFFSET, (u32)
XTE_AFM_EPPRM_MASK); // temporarily enable Promiscuous
+ // for(ii=0;ii<len;ii++)
+ // sbuf[ii] = 0xff;
+
+ Write_64(lp, sbuf, len);
+ Status = Read64(lp, rbuf, len, XTE_END_OF_PACKET);
+ DEBUG_PRINTK("\ndone\n");
+ iow(ndev, XTE_DGIE_OFFSET, 0xffffffff);
+}
+#endif
+/************************** Variable Definitions
*****************************/
+
+/*
+ * array of masks associated with the bit position, improves performance
+ * in the ISR and acknowledge functions, this table is shared between all
+ * instances of the driver, this table is not statically initialized
because
+ * the size of the table is based upon the maximum used interrupt id
+ */
+
+
+/*
+Hardware start transmission.
+Send a packet to media from the upper layer.
+Method that initiates the transmission of a packet.
+The full packet (protocol headers and all) is contained in a socket
buffer (sk_buff) structure.
+Socket buffers are introduced later in this chapter.
+skb->data points to the packet
+skb->len is its length
+ */
+static int
+temac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ u32 ret = 0;
+ unsigned long flags = 0;
+
+ // DEBUG_FUNC;
+ netif_stop_queue(ndev);
+ spin_lock_irqsave(&XTE_spinlock, flags);
+ ndev->trans_start = jiffies;
+
+#if defined(CONFIG_PICO_LL_TEMAC)
+ switch (lp->nic_type) {
+ case TEMAC_PLB:
+ ret = plb_sendFrame(ndev,skb, bDescriptor);
+ break;
+ case TEMAC_LL:
+ ret = ll_sendFrame(ndev,skb, bDescriptor);
+ break;
+ }
+#else
+ // DEBUG_PRINTK("\n>plb_sendFrame()");
+ ret = plb_sendFrame(ndev,skb, bDescriptor);
+#endif
+ // DEBUG_PRINTK("\n<plb_sendFrame()");
+ if (ret) {
+ // reset(dev, __LINE__);
+ lp->stats.tx_errors++;
+ spin_unlock_irqrestore(&XTE_spinlock, flags);
+ // DEBUG_PRINTK("\n%d=temac_hard_start_transmit() error", ret);
+ // DEBUG_FUNC_EX(ret);
+ return -EIO;
+ }
+ lp->stats.tx_bytes += skb->len;
+ lp->stats.tx_packets++;
+
+ dev_kfree_skb(skb); /* free this SKB */ // this crashes driver
eventually
+
+ // dump_skb_data(lp,skb, "Send");
+ netif_wake_queue(ndev);
+ spin_unlock_irqrestore(&XTE_spinlock, flags);
+ // DEBUG_PRINTK("\n%d=temac_hard_start_transmit() exit",ret);
+ // DEBUG_FUNC_EX(0);
+ return 0;
+}
+
+static void
+temac_shutdown(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+
+ // DEBUG_FUNC;
+ /* RESET device */
+ mdio_write(ndev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
+ /* Power-Down PHY */
+ /* Disable all interrupt */
+ /* Disable RX */
+}
+
+/*
+Stop the interface.
+Stops the interface. The interface is stopped when it is brought down.
+This function should reverse operations performed at open time.
+*/
+static int
+temac_stop(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+
+ // DEBUG_FUNC;
+ spin_lock_irqsave(&XTE_spinlock, flags);
+
+ del_timer(&lp->rx_timer);
+ del_timer(&lp->mii_timer);
+
+ /* Stop Send queue */
+ netif_stop_queue(ndev);
+ /* Now we could stop the ndevice */
+ netif_carrier_off(ndev);
+ /*
+ * If not in polled mode, free the interrupt. Currently, there
+ * isn't any code to set polled mode, so this check is probably
+ * superfluous.
+ */
+ free_irq(ndev->irq, ndev);
+ temac_shutdown(ndev);
+ return 0;
+}
+
+/* temac_release_board release a board, and any mapped resources */
+static void
+temac_release_board(struct platform_device *pdev, struct net_device
*ndev) {
+ struct temac_local *lp = ndev->priv;
+
+ // DEBUG_FUNC;
+ /* unmap our resources */
+ iounmap((void *) ndev->base_addr);
+
+ /* release the resources */
+
+ if (lp->phy_addr_req != NULL) {
+ release_resource(lp->phy_addr_req);
+ kfree(lp->phy_addr_req);
+ }
+
+ if (lp->nic_addr_res != NULL) {
+ release_resource(lp->nic_addr_res);
+ kfree(lp->nic_addr_req);
+ }
+}
+
+/*
+Whenever an application needs to get statistics for the interface, this
method is
+called. This happens, for example, when ifconfig or netstat -i is run.
A sample
+implementation for snull is introduced in the section Statistical
Information.
+ */
+static struct net_device_stats
+*temac_get_stats(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+
+ // DEBUG_FUNC;
+ return &lp->stats;
+}
+
+/*
+ * This function is used to handle ports that do not have an interrupt.
+ */
+static void
+temac_rx_timeout(unsigned long data) {
+ struct net_device *ndev = (struct net_device *) data;
+ struct temac_local *lp = ndev->priv;
+
+ // DEBUG_FUNC;
+ disable_irq(ndev->irq);
+ temac_interrupt(-2, ndev, NULL);
+ enable_irq(ndev->irq);
+
+ mod_timer(&lp->rx_timer, TEMAC_RX_TIMEOUT);
+}
+/*
+ A periodic timer routine
+ Dynamic media sense, allocated Rx buffer...
+ */
+static void
+temac_mii_timeout(unsigned long data) {
+ struct net_device *ndev = (struct net_device *) data;
+ struct temac_local *lp = ndev->priv;
+
+ // DEBUG_FUNC;
+ mii_check_media(&lp->mii_if, netif_msg_link(lp), 0);
+ /* Set timer again */
+ mod_timer(&lp->mii_timer, TEMAC_MII_TIMEOUT);
+}
+
+static int
+temac_open(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+
+ // DEBUG_FUNC;
+
+ /*
+ * Just to be safe, stop TX queue and the ndevice first. If the
ndevice is
+ * already stopped, an error will be returned. In this case, we don't
+ * really care,.
+ */
+ netif_stop_queue(ndev);
+ spin_lock_irqsave(&XTE_spinlock, flags);
+
+ if (ndev->mtu > XTE_MTU)
+ ndev->mtu = XTE_MTU;
+
+ /*
+ Enable interrupts if not in polled mode
+ */
+ if (ndev->irq < 0) {
+ lp->poll = 1;
+ } else if (request_irq(ndev->irq, &temac_interrupt, 0, ndev->name,
ndev)) {
+ printk(KERN_ERR "%s: XTemac could not allocate interrupt
%d. reverting to polled IO\n", ndev->name, ndev->irq);
+ lp->poll = 1;
+ }
+
+ /* Initialize TEMAC board */
+ temac_device_reset(ndev);
+
+ /* set and active a timer process */
+ lp->mii_timer.data = (unsigned long) ndev;
+ lp->mii_timer.function = &temac_mii_timeout;
+ init_timer(&lp->mii_timer);
+ mod_timer(&lp->mii_timer, TEMAC_MII_TIMEOUT);
+
+ lp->rx_timer.data = (unsigned long) ndev;
+ lp->rx_timer.function = &temac_rx_timeout;
+ init_timer(&lp->rx_timer);
+ mod_timer(&lp->rx_timer, TEMAC_RX_TIMEOUT);
+
+ mii_check_media(&lp->mii_if, netif_msg_link(lp), 1);
+ /* We're ready to go. */
+ netif_start_queue(ndev);
+ spin_unlock_irqrestore(&XTE_spinlock, flags);
+
+ return 0;
+}
+static int
+temac_do_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) {
+ struct temac_local *lp = ndev->priv;
+ int rc;
+ unsigned long flags;
+
+ /* SIOC[GS]MIIxxx ioctls */
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ rc = generic_mii_ioctl(&lp->mii_if, if_mii(rq), cmd, NULL);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ rc = -EOPNOTSUPP;
+ }
+
+ return rc;
+}
+
+/*
+Our watchdog timed out. Called by the networking layer
+Method called by the networking code when a packet transmission fails
to complete within a reasonable period, on the assumption that an
interrupt has been
+missed or the interface has locked up. It should handle the problem and
resume packet transmission.
+*/
+static void
+temac_tx_timeout(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+
+ // DEBUG_FUNC;
+ spin_lock_irqsave(&lp->lock, flags);
+
+ netif_stop_queue(ndev);
+ printk(KERN_ERR "%s: XTemac exceeded transmit timeout of %lu ms.
Resetting emac.\n", ndev->name, TEMAC_TX_TIMEOUT * 1000UL / HZ);
+ lp->stats.tx_errors++;
+ temac_device_reset(ndev);
+ ndev->trans_start = jiffies;
+ netif_wake_queue(ndev); /* We can accept TX
packets again */
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/*
+OPTIONAL
+void (*poll_controller)(struct net_device *dev);
+Function that asks the driver to check for events on the interface in
situations
+where interrupts are disabled. It is used for specific in-kernel
networking tasks,
+such as remote consoles and kernel debugging over the network.
+
+*/
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void
+temac_poll_controller(struct net_device *ndev) {
+ // DEBUG_FUNC;
+ disable_irq(ndev->irq);
+ temac_interrupt(-1, ndev, NULL);
+ enable_irq(ndev->irq);
+}
+#endif
+/*
+OPTIONAL
+
+int (*change_mtu)(struct net_device *dev, int new_mtu);
+Function that takes action if there is a change in the maximum transfer
unit (MTU)
+for the interface. If the driver needs to do anything particular when
the MTU is
+changed by the user, it should declare its own function; otherwise, the
default does
+the right thing. snull has a template for the function if you are
interested.
+*/
+int temac_change_mtu(struct net_device *dev, int new_mtu) {
+#if 0
+ int head_size = XTE_HDR_SIZE;
+ struct temac_local *lp = (struct temac_local *)dev->priv;
+ int max_frame = new_mtu + head_size + XTE_TRL_SIZE;
+ int min_frame = 1 + head_size + XTE_TRL_SIZE;
+
+ // DEBUG_FUNC;
+ if ((max_frame < min_frame) || (max_frame > lp->max_frame_size))
+ return -EINVAL;
+
+ dev->mtu = new_mtu; /* change mtu in net_device structure */
+#endif
+ return 0;
+}
+
+static int
+temac_ethtool_get_settings(struct net_device *ndev, struct ethtool_cmd
*cmd) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ mii_ethtool_gset(&lp->mii_if, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ r = 0;
+ }
+ return r;
+}
+
+static int
+temac_ethtool_set_settings(struct net_device *ndev, struct ethtool_cmd
*cmd) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ r = mii_ethtool_sset(&lp->mii_if, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return r;
+}
+
+static void
+temac_ethtool_get_drvinfo(struct net_device *ndev, struct
ethtool_drvinfo *info) {
+
+ memset(info, 0, sizeof(struct ethtool_drvinfo));
+ strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
+ strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
+ /* Also tell how much memory is neinfoinfo for dumping register
values */
+ info->regdump_len = 0;
+}
+
+static u32
+temac_ethtool_get_link(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ r = mii_link_ok(&lp->mii_if);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return r;
+}
+
+static u32
+temac_ethtool_get_msglevel(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ return lp->msg_enable;
+}
+
+static void
+temac_ethtool_set_msglevel(struct net_device *ndev, u32 value) {
+ struct temac_local *lp = ndev->priv;
+ lp->msg_enable = value;
+}
+
+static int
+temac_ethtool_nway_reset(struct net_device *ndev) {
+ struct temac_local *lp = ndev->priv;
+ unsigned long flags;
+ int r = -EOPNOTSUPP;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+ r = mii_nway_restart(&lp->mii_if);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return r;
+}
+
+static struct ethtool_ops temac_ethtool_ops = {
+ .get_settings = temac_ethtool_get_settings,
+ .set_settings = temac_ethtool_set_settings,
+ .get_drvinfo = temac_ethtool_get_drvinfo,
+ .get_msglevel = temac_ethtool_get_msglevel,
+ .set_msglevel = temac_ethtool_set_msglevel,
+ .nway_reset = temac_ethtool_nway_reset,
+ .get_link = temac_ethtool_get_link,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .get_tso = ethtool_op_get_tso,
+ .get_perm_addr = ethtool_op_get_perm_addr,
+};
+/*
+Search TEMAC board, allocate space and register it
+ */
+static int
+temac_device_probe(struct platform_device *pdev) {
+ struct net_device *ndev;
+ struct xtemac_platform_data *pdata = pdev->dev.platform_data;
+ struct temac_local *lp; /* Point a board information structure */
+ int i;
+ u32 ret = 0;
+ int nic_size;
+ // DEBUG_FUNC;
+ printk(version);
+ /* Init network device */
+ ndev = alloc_etherdev(sizeof(struct temac_local));
+ if (!ndev) {
+ printk("%s: could not allocate device.\n", DRV_NAME);
+ return -ENOMEM;
+ }
+
+ SET_MODULE_OWNER(ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ /* setup board info structure */
+ lp = (struct temac_local *) ndev->priv;
+ // lp->einfo = einfo;
+ /* Clear memory */
+ memset(lp, 0, sizeof(*lp));
+ spin_lock_init(&lp->lock);
+/* get device configuration from platform Device */
+ ndev->irq = platform_get_irq(pdev, 0);
+ lp->nic_type = TEMAC_PLB;
+ lp->dbg = 1;
+ lp->options = XTE_DEFAULT_OPTIONS;
+
+ if (pdev->num_resources < 2) {
+ printk("%s: insufficient resources %d.\n", DRV_NAME,
pdev->num_resources);
+ ret = -ENODEV;
+ goto out;
+ }
+ lp->nic_type = pdata->nic_type;
+ if ((pdata->rx_pkt_fifo_depth >= 131072) &&
(pdata->tx_pkt_fifo_depth >= 131072)) {
+ lp->MaxFrameSz = sizeof(EthFrame);
+ } else {
+ lp->MaxFrameSz = 1536; /* Sized to fit within cache lines */
+ }
+// lp->MacFifoDepth = pdata->mac_fifo_depth;
+ lp->dcr_host = pdata->dcr_host;
+#if defined(CONFIG_PICO_LL_TEMAC)
+ switch (lp->nic_type) {
+ case TEMAC_PLB:
+ // ndev->base_addr = XPAR_PLB_TEMAC_0_BASEADDR;
+ lp->phy_mode = 0;
+ break;
+ case TEMAC_LL:
+ // ndev->base_addr = XPAR_PLB_LL_IF_0_BASEADDR ;
+ lp->phy_addr =
XGP_HIF_BASEADDR; /* set default
address of PHY */
+ lp->phy_mode = PHY_DCR;
+ break;
+ }
+#else
+ lp->phy_mode = 0;
+#endif
+ lp->nic_addr_res = platform_get_resource(pdev, IORESOURCE_MEM,
0); /* address of NIC */
+ if (lp->nic_addr_res == NULL) {
+ printk(KERN_ERR DRV_NAME ":insufficient resources\n");
+ ret = -ENOENT;
+ goto out;
+ }
+ nic_size = res_size(lp->nic_addr_res);
+ lp->nic_addr_req = request_mem_region(lp->nic_addr_res->start,
nic_size, pdev->name);
+ if (lp->nic_addr_req == NULL) {
+ printk(KERN_ERR DRV_NAME ":cannot claim address reg area\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ ndev->base_addr = (u32) ioremap(lp->nic_addr_res->start, nic_size);
+ if (ndev->base_addr == 0) {
+ printk(KERN_ERR "failed to ioremap address reg\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* fill in parameters for net-dev structure */
+
+/* done getting platform_device parameters start initializine
device */
+ /* Set all handlers to stub values, let user configure this data */
+ lp->mii = 1; /* really important can't read/write
anyting until set */
+ lp->regshift = 3;
+
+ lp->LinkSpeed = 1000; // Tell
driver that the PHY is 10/100/1000 capable
+ lp->mii_if.full_duplex = 1;
+ lp->mii_if.phy_id_mask = 0x1f;
+ lp->mii_if.reg_num_mask = 0x1f;
+ lp->mii = 1;
+ lp->msg_enable = debug;
+ lp->mii_if.dev = ndev;
+ lp->mii_if.mdio_read = mdio_read;
+ lp->mii_if.mdio_write = mdio_write;
+ /* Set the mii phy_id so that we can query the link state */
+ // if (lp->mii)
+ // lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) &
0x1f;
+ // memcpy(ndev->dev_addr, "\0\1\2345", ETH_ALEN);
+
+ temac_set_mac_address(ndev,pdata->mac_addr);
+ /* from this point we assume that we have found a TEMAC */
+ /* driver system function */
+ ether_setup(ndev);
+ /* The TEMAC-specific entries in the device structure. */
+ ndev->open = &temac_open;
+ ndev->stop = &temac_stop;
+ ndev->get_stats = &temac_get_stats;
+ ndev->do_ioctl = &temac_do_ioctl;
+ ndev->tx_timeout = &temac_tx_timeout;
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ ndev->hard_start_xmit = &temac_hard_start_xmit;
+ ndev->set_multicast_list = &temac_set_multicast_list;
+ ndev->ethtool_ops = &temac_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ ndev->poll_controller = temac_poll_controller;
+#endif
+ ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
+ ndev->change_mtu = temac_change_mtu;
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+ if (ret == 0) {
+ printk("%s: %s_temac at %lx,%x IRQ %d MAC: ", ndev->name,
(lp->nic_type == TEMAC_PLB) ? "plb" : "ll", ndev->base_addr,
lp->phy_addr, ndev->irq);
+ for (i = 0; i < 5; i++)
+ printk("%02x:", ndev->dev_addr[i]);
+ printk("%02x", ndev->dev_addr[5]);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ printk(" NET_POLL");
+#endif
+ /* print h/w id */
+ if (lp->nic_type == TEMAC_PLB) {
+ ret = ior(ndev,XTE_DSR_OFFSET);
+ printk(KERN_INFO " id %d.%d%c, block id %d, type %d\n",
((ret >> 28) & 0xf), ((ret >> 21) & 0x7f), (((ret >> 16) & 0x1f) + 'a'),
((ret >> 16) & 0xff), ((ret >> 0) & 0xff));
+ }
+ }
+ return 0;
+// release:
+ out:
+ printk("%s: not found (%d).\n", DRV_NAME, ret);
+ temac_release_board(pdev, ndev);
+ kfree(ndev);
+ return ret;
+}
+
+
+static int
+temac_device_suspend(struct platform_device *pdev, pm_message_t state) {
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ // DEBUG_FUNC;
+ if (ndev) {
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ temac_shutdown(ndev);
+ }
+ }
+ return 0;
+}
+
+static int
+temac_device_resume(struct platform_device *pdev) {
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ // DEBUG_FUNC;
+ if (ndev) {
+
+ if (netif_running(ndev)) {
+ temac_device_reset(ndev);
+ netif_device_attach(ndev);
+ }
+ }
+ return 0;
+}
+
+static int __devexit
+temac_device_remove(struct platform_device *pdev) {
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ // DEBUG_FUNC;
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(ndev);
+ temac_release_board(pdev, ndev);
+ free_netdev(ndev); /* free device structure */
+ // DEBUG_FUNC_EXIT;
+ return 0;
+}
+
+static struct platform_driver temac_driver = {
+ .probe = temac_device_probe,
+ .remove = temac_device_remove,
+ .suspend = temac_device_suspend,
+ .resume = temac_device_resume,.driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init
+temac_init_module(void) {
+ int err;
+ if ((err = platform_driver_register(&temac_driver))) { /* search
board and register */
+ printk(KERN_ERR "Driver registration failed\n");
+ return err;
+ }
+#if 0
+ temac_device = platform_device_alloc(DRV_NAME, 0);
+ if (!temac_device) {
+ goto out_unregister;
+ }
+
+ if (platform_device_add(temac_device)) {
+ platform_device_put(temac_device);
+ temac_device = NULL;
+ }
+ return 0;
+out_unregister:
+ platform_driver_unregister(&temac_driver);
+ return -ENOMEM;
+#else
+ return 0;
+
+#endif
+}
+
+static void __exit
+temac_cleanup_module(void) {
+ platform_driver_unregister(&temac_driver);
+ if (temac_device) {
+ platform_device_unregister(temac_device);
+ temac_device = NULL;
+ }
+}
+
+module_init(temac_init_module);
+module_exit(temac_cleanup_module);
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "temac debug level (1-4)");
+//module_param(speed, int, 0);
+//static unsigned int speed = CONFIG_XILINX_OLD_TEMAC_SPEED;
+//MODULE_PARM_DESC(speed, "temac speed 10,100,1000");
+MODULE_AUTHOR("David H. Lynch Jr. <dhlii at dlasys.net>");
+MODULE_LICENSE("GPL");
+
+/*
+
+OPTIONAL functions
+
+Function (called before hard_start_xmit) that builds the hardware
header from
+the source and destination hardware addresses that were previously
retrieved; its
+job is to organize the information passed to it as arguments into an
appropriate,
+device-specific hardware header. eth_header is the default function for
Ethernet-
+like interfaces, and ether_setup assigns this field accordingly.
+int (*hard_header) (struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len);
+
+Function used to rebuild the hardware header after ARP resolution
completes
+but before a packet is transmitted. The default function used by
Ethernet devices
+uses the ARP support code to fill the packet with missing information.
+int (*rebuild_header)(struct sk_buff *skb);
+
+Changes the interface configuration. This method is the entry point for
configuring the driver. The I/O address for the device and its interrupt
number can be
+changed at runtime using set_config. This capability can be used by the
system
+administrator if the interface cannot be probed for. Drivers for modern
hardware normally do not need to implement this method.
+int (*set_config)(struct net_device *dev, struct ifmap *map);
+
+Method provided by NAPI-compliant drivers to operate the interface in a
polled
+mode, with interrupts disabled. NAPI (and the weight field) are covered
in the
+section Receive Interrupt Mitigation.
+int (*poll)(struct net_device *dev; int *quota) L
+
+header_cache is called to fill in the hh_cache structure with the
results of an ARP
+query. Almost all Ethernet-like drivers can use the default
eth_header_cache
+implementation.
+int (*header_cache) (struct neighbour *neigh, struct hh_cache *hh);
+
+Method that updates the destination address in the hh_cache structure in
+response to a change. Ethernet devices use eth_header_cache_update.
+int (*header_cache_update) (struct hh_cache *hh, struct net_device
*dev, unsigned char *haddr);
+
+The hard_header_parse method extracts the source address from the
packet contained in skb,
+copying it into the buffer at haddr. The return value from the function
is the length of that address. Ethernet devices normally use
eth_header_parse.
+int (*hard_header_parse) (struct sk_buff *skb, unsigned char *haddr);
+
+Utility Fields
+
+The remaining struct net_device data fields are used by the interface
to hold useful
+status information. Some of the fields are used by ifconfig and netstat
to provide the
+user with information about the current configuration. Therefore, an
interface
+should assign values to these fields:
+
+unsigned long trans_start;
+unsigned long last_rx;
+
+Fields that hold a jiffies value. The driver is responsible for
updating these values when transmission begins and when a packet is
received, respectively. The
+trans_start value is used by the networking subsystem to detect
transmitter
+lockups. last_rx is currently unused, but the driver should maintain
this field
+anyway to be prepared for future use.
+
+int watchdog_timeo;
+
+The minimum time (in jiffies) that should pass before the networking layer
+decides that a transmission timeout has occurred and calls the drivers
tx_timeout function.
+
+static void *priv;
+The equivalent of filp->private_data. In modern drivers, this field is
set by
+alloc_netdev and should not be accessed directly; use netdev_priv instead.
+
+struct dev_mc_list *mc_list;
+
+int mc_count;
+Fields that handle multicast transmission. mc_count is the count of
items in mc_list.
+See the section Multicast for further details.
+
+spinlock_t xmit_lock;
+
+int xmit_lock_owner;
+The xmit_lock is used to avoid multiple simultaneous calls to the drivers
+hard_start_xmit function. xmit_lock_owner is the number of the CPU that
has
+obtained xmit_lock. The driver should make no changes to these fields.
+*/
+#if 0
+static u32
+emac_AFM_read(struct net_device *ndev, int reg_num, int MultAddrReg,
u32 *mult_addr_msw, u32 *mult_addr_lsw) {
+ struct temac_local *lp = ndev->priv;
+ int EmacNum = 0;
+ mtdcr((lp->phy_addr) + XGP_HIF_DATA_REG_LSW_OFFSET, MultAddrReg );
+ mtdcr((lp->phy_addr) + XGP_HIF_CNTL_REG_OFFSET,
XGP_HIF_CNTL_REG_OFFSET_DCR_WRITE | (EmacNum << 10) | reg_num);
+ while ( !(mfdcr(lp->phy_addr + XGP_HIF_RDY_STATUS_OFFSET ) &
(XGP_HIF_RDYSTAT_AF0_READ_MASK << (8*EmacNum))));
+ *mult_addr_lsw = mfdcr((lp->phy_addr) + XGP_HIF_DATA_REG_LSW_OFFSET);
+ *mult_addr_msw = mfdcr((lp->phy_addr) + XGP_HIF_DATA_REG_MSW_OFFSET);
+}
+
+static void
+emac_AFM_write(struct net_device *ndev, int reg_num, u32 mult_addr_msw,
u32 mult_addr_lsw) {
+ emac_AF_write(ndev, XTE_MAW0_OFFSET, (((mult_addr_msw << 24) &
0xff000000) | ((mult_addr_msw << 8) & 0x00ff0000) | ((mult_addr_msw >>
8) & 0x0000ff00) | ((mult_addr_msw >> 24) & 0x000000ff)));
+ emac_AF_write(ndev, XTE_MAW1_OFFSET, (reg_num << 16) |
((mult_addr_lsw<<8) & 0xff00) | ((mult_addr_lsw >>8 ) & 0x00ff)) ;
+
+}
+#endif
+/*
+ vim: tabstop=4 shiftwidth=4 softtabstop=4 nolist expandtab
+*/
diff --git a/include/linux/xilinx_devices.h b/include/linux/xilinx_devices.h
new file mode 100755
index 0000000..747453a
--- /dev/null
+++ b/include/linux/xilinx_devices.h
@@ -0,0 +1,104 @@
+/*
+ * include/linux/xilinx_devices.h
+ *
+ * Definitions for any platform device related flags or structures for
+ * Xilinx EDK IPs
+ *
+ * Author: MontaVista Software, Inc.
+ * source at mvista.com
+ *
+ * 2002-2005 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#ifdef __KERNEL__
+#ifndef _XILINX_DEVICE_H_
+#define _XILINX_DEVICE_H_
+
+#include <linux/types.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+#include <linux/device.h>
+#else
+#include <linux/platform_device.h>
+#endif
+
+/*- 10/100 Mb Ethernet Controller IP (XEMAC) -*/
+
+struct xemac_platform_data {
+ u32 device_flags;
+ u32 dma_mode;
+ u32 has_mii;
+ u32 has_err_cnt;
+ u32 has_cam;
+ u32 has_jumbo;
+ u32 tx_dre;
+ u32 rx_dre;
+ u32 tx_hw_csum;
+ u32 rx_hw_csum;
+ u8 mac_addr[6];
+};
+
+/* Flags related to XEMAC device features */
+#define XEMAC_HAS_ERR_COUNT 0x00000001
+#define XEMAC_HAS_MII 0x00000002
+#define XEMAC_HAS_CAM 0x00000004
+#define XEMAC_HAS_JUMBO 0x00000008
+
+/* Possible DMA modes supported by XEMAC */
+#define XEMAC_DMA_NONE 1
+#define XEMAC_DMA_SIMPLE 2 /* simple 2 channel DMA */
+#define XEMAC_DMA_SGDMA 3 /* scatter gather DMA */
+
+/*- 10/100/1000 Mb Ethernet Controller IP (XTEMAC) -*/
+
+struct xtemac_platform_data {
+#ifdef XPAR_TEMAC_0_INCLUDE_RX_CSUM
+ u8 tx_dre;
+ u8 rx_dre;
+ u8 tx_csum;
+ u8 rx_csum;
+ u8 phy_type;
+#endif
+ u8 dma_mode;
+ u32 rx_pkt_fifo_depth;
+ u32 tx_pkt_fifo_depth;
+ u16 mac_fifo_depth;
+ u8 dcr_host;
+ u8 dre;
+
+ u8 mac_addr[6];
+
+#if defined(CONFIG_XILINX_OLD_TEMAC)
+ u16 speed;
+#endif
+#if defined(CONFIG_PICO_TEMAC) || defined(CONFIG_XILINX_OLD_TEMAC)
+ u8 phy_type;
+ u8 nic_type;
+#endif
+};
+
+/* Possible DMA modes supported by XTEMAC */
+#define XTEMAC_DMA_NONE 1
+#define XTEMAC_DMA_SIMPLE 2 /* simple 2 channel DMA */
+#define XTEMAC_DMA_SGDMA 3 /* scatter gather DMA */
+
+/*- SPI -*/
+
+struct xspi_platform_data {
+ u32 device_flags;
+ u8 num_slave_bits;
+};
+
+/* Flags related to XSPI device features */
+#define XSPI_HAS_FIFOS 0x00000001
+#define XSPI_SLAVE_ONLY 0x00000002
+
+/*- GPIO -*/
+
+/* Flags related to XGPIO device features */
+#define XGPIO_IS_DUAL 0x00000001
+
+#endif /* _XILINX_DEVICE_H_ */
+#endif /* __KERNEL__ */
--
Dave Lynch DLA Systems
Software Development: Embedded Linux
717.627.3770 dhlii at dlasys.net http://www.dlasys.net
fax: 1.253.369.9244 Cell: 1.717.587.7774
Over 25 years' experience in platforms, languages, and technologies too numerous to list.
"Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction."
Albert Einstein
More information about the Linuxppc-embedded
mailing list