[linux-yocto] [PATCH 42/70] drivers/net: Added support for acp network driver
Paul Butler
butler.paul at gmail.com
Mon Jun 10 18:46:05 PDT 2013
From: Jiang Lu <lu.jiang at windriver.com>
Added support for acp network driver.
Signed-off-by: Paul Butler <paul.butler at windriver.com>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/lsi/Kconfig | 32 +
drivers/net/ethernet/lsi/Makefile | 3 +
drivers/net/ethernet/lsi/lsi_acp_mdio.c | 239 +++
drivers/net/ethernet/lsi/lsi_acp_net.c | 2808 +++++++++++++++++++++++++++++++
6 files changed, 3084 insertions(+)
create mode 100644 drivers/net/ethernet/lsi/Kconfig
create mode 100644 drivers/net/ethernet/lsi/Makefile
create mode 100644 drivers/net/ethernet/lsi/lsi_acp_mdio.c
create mode 100644 drivers/net/ethernet/lsi/lsi_acp_net.c
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c63a64c..22109fb 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -176,5 +176,6 @@ source "drivers/net/ethernet/tundra/Kconfig"
source "drivers/net/ethernet/via/Kconfig"
source "drivers/net/ethernet/xilinx/Kconfig"
source "drivers/net/ethernet/xircom/Kconfig"
+source "drivers/net/ethernet/lsi/Kconfig"
endif # ETHERNET
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 9676a51..10af94d 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/
obj-$(CONFIG_NET_VENDOR_VIA) += via/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
+obj-$(CONFIG_LSI_NET) += lsi/
diff --git a/drivers/net/ethernet/lsi/Kconfig b/drivers/net/ethernet/lsi/Kconfig
new file mode 100644
index 0000000..8c9dc08
--- /dev/null
+++ b/drivers/net/ethernet/lsi/Kconfig
@@ -0,0 +1,32 @@
+config LSI_NET
+ bool "Network interface for LSI's APP and AXXIA"
+ help
+ Network interface driver for LSI's APP and AXXIA series boards.
+
+config LSI_NET_NUM_RX_DESC
+ int "LSI Axxia Network Driver: Number of receive descriptors"
+ depends on LSI_NET
+ default "4"
+ help
+ The number of receive descriptors to allocate.
+
+config LSI_NET_RX_BUF_SZ
+ int "NIC driver: Size of the receive buffer"
+ depends on LSI_NET
+ default "32768"
+ help
+ The size of the receive buffer.
+
+config LSI_NET_NUM_TX_DESC
+ int "NIC driver: Number of transmit descriptors"
+ depends on LSI_NET
+ default "4"
+ help
+ The number of transmit descriptors to allocate.
+
+config LSI_NET_TX_BUF_SZ
+ int "NIC driver: Size of the transmit buffer"
+ depends on LSI_NET
+ default "32768"
+ help
+ The size of the transmit buffer.
diff --git a/drivers/net/ethernet/lsi/Makefile b/drivers/net/ethernet/lsi/Makefile
new file mode 100644
index 0000000..9618c59
--- /dev/null
+++ b/drivers/net/ethernet/lsi/Makefile
@@ -0,0 +1,3 @@
+# Makefile for the LSI FEMAC network interface.
+
+obj-$(CONFIG_LSI_NET) += lsi_acp_mdio.o lsi_acp_net.o
diff --git a/drivers/net/ethernet/lsi/lsi_acp_mdio.c b/drivers/net/ethernet/lsi/lsi_acp_mdio.c
new file mode 100644
index 0000000..eaa35fa
--- /dev/null
+++ b/drivers/net/ethernet/lsi/lsi_acp_mdio.c
@@ -0,0 +1,239 @@
+/*
+ * drivers/net/ethernet/lsi/lsi_acp_mdio.c
+ *
+ * Copyright (C) 2010 LSI
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+
+#include <asm/lsi/acp_ncr.h>
+
+/*
+ ==============================================================================
+ ==============================================================================
+ MDIO Access
+ ==============================================================================
+ ==============================================================================
+*/
+
+#ifndef CONFIG_ACPISS
+
+#define BZ33327_WA
+
+static unsigned long mdio_base;
+static DEFINE_SPINLOCK(mdio_lock);
+
+#define MDIO_CONTROL_RD_DATA ((void *)(mdio_base + 0x0))
+#define MDIO_STATUS_RD_DATA ((void *)(mdio_base + 0x4))
+#define MDIO_CLK_OFFSET ((void *)(mdio_base + 0x8))
+#define MDIO_CLK_PERIOD ((void *)(mdio_base + 0xc))
+
+/*
+ ------------------------------------------------------------------------------
+ acp_mdio_read
+*/
+
+int
+acp_mdio_read(unsigned long address, unsigned long offset,
+ unsigned short *value)
+{
+ unsigned long command = 0;
+ unsigned long status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdio_lock, flags);
+#if defined(BZ33327_WA)
+ /* Set the mdio_busy (status) bit. */
+ status = in_le32(MDIO_STATUS_RD_DATA);
+ status |= 0x40000000;
+ out_le32(MDIO_STATUS_RD_DATA, status);
+#endif /* BZ33327_WA */
+
+ /* Write the command. */
+ command |= 0x10000000; /* op_code: read */
+ command |= (address & 0x1f) << 16; /* port_addr (target device) */
+ command |= (offset & 0x1f) << 21;/* device_addr (target register) */
+ out_le32(MDIO_CONTROL_RD_DATA, command);
+
+#if defined(BZ33327_WA)
+ /* Wait for the mdio_busy (status) bit to clear. */
+ do {
+ status = in_le32(MDIO_STATUS_RD_DATA);
+ } while (0 != (status & 0x40000000));
+
+ /* Wait for the mdio_busy (control) bit to clear. */
+ do {
+ command = in_le32(MDIO_CONTROL_RD_DATA);
+ } while (0 != (command & 0x80000000));
+
+ *value = (unsigned short)(command & 0xffff);
+#endif /* BZ33327_WA */
+ spin_unlock_irqrestore(&mdio_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(acp_mdio_read);
+
+/*
+ ------------------------------------------------------------------------------
+ acp_mdio_write
+*/
+
+int
+acp_mdio_write(unsigned long address, unsigned long offset,
+ unsigned short value)
+{
+ unsigned long command = 0;
+ unsigned long status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdio_lock, flags);
+
+ /* Wait for mdio_busy (control) to be clear. */
+ do {
+ command = in_le32(MDIO_CONTROL_RD_DATA);
+ } while (0 != (command & 0x80000000));
+
+#if defined(BZ33327_WA)
+ /* Set the mdio_busy (status) bit. */
+ status = in_le32(MDIO_STATUS_RD_DATA);
+ status |= 0x40000000;
+ out_le32(MDIO_STATUS_RD_DATA, status);
+#endif /* BZ33327_WA */
+
+ /* Write the command. */
+ command = 0x08000000; /* op_code: write */
+ command |= (address & 0x1f) << 16; /* port_addr (target device) */
+ command |= (offset & 0x1f) << 21;/* device_addr (target register) */
+ command |= (value & 0xffff); /* value */
+ out_le32(MDIO_CONTROL_RD_DATA, command);
+
+#if defined(BZ33327_WA)
+ /* Wait for the mdio_busy (status) bit to clear. */
+ do {
+ status = in_le32(MDIO_STATUS_RD_DATA);
+ } while (0 != (status & 0x40000000));
+#endif /* BZ33327_WA */
+
+ /* Wait for the mdio_busy (control) bit to clear. */
+ do {
+ command = in_le32(MDIO_CONTROL_RD_DATA);
+ } while (0 != (command & 0x80000000));
+
+ spin_unlock_irqrestore(&mdio_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(acp_mdio_write);
+
+/*
+ ------------------------------------------------------------------------------
+ acp_mdio_initialize
+*/
+
+static int
+acp_mdio_initialize(void)
+{
+ if (is_asic()) {
+ out_le32(MDIO_CLK_OFFSET, 0x10);
+ out_le32(MDIO_CLK_PERIOD, 0x2c);
+ } else {
+ out_le32(MDIO_CLK_OFFSET, 0x05);
+ out_le32(MDIO_CLK_PERIOD, 0x0c);
+ }
+
+ return 0;
+}
+
+#endif /* ! CONFIG_ACPISS */
+
+/*
+ ==============================================================================
+ ==============================================================================
+ Interrupts
+ ==============================================================================
+ ==============================================================================
+*/
+
+/*
+ ------------------------------------------------------------------------------
+ acp_wrappers_init
+*/
+
+int __init
+acp_wrappers_init(void)
+{
+ int rc = -1;
+ struct device_node *np = NULL;
+ const u32 *field;
+ u64 mdio_phys_address;
+ u32 mdio_size;
+
+ printk(KERN_INFO "Initializing ACP Wrappers.\n");
+
+#ifndef CONFIG_ACPISS
+
+ np = of_find_node_by_type(np, "network");
+
+ while (np && !of_device_is_compatible(np, "acp-femac"))
+ np = of_find_node_by_type(np, "network");
+
+ if (np) {
+ field = of_get_property(np, "enabled", NULL);
+
+ if (!field || (field && (0 == *field))) {
+ printk(KERN_WARNING "Networking is Not Enabled.\n");
+ goto acp_wrappers_init_done;
+ }
+
+ field = of_get_property(np, "mdio-reg", NULL);
+
+ if (!field) {
+ printk(KERN_ERR
+ "Couldn't get \"mdio-reg\" property.\n");
+ } else {
+ mdio_phys_address = of_translate_address(np, field);
+ mdio_size = field[1];
+ rc = 0;
+ }
+ }
+
+ if (0 != rc) {
+ mdio_phys_address = 0x002000409000ULL;
+ mdio_size = 0x1000;
+ printk(KERN_WARNING
+ "** MDIO Address Not Specified in Device Tree.\n");
+ }
+
+ mdio_base = (unsigned long)ioremap(mdio_phys_address, mdio_size);
+ rc = acp_mdio_initialize();
+#endif
+
+ if (0 != rc)
+ printk(KERN_ERR "MDIO Initiailzation Failed!\n");
+
+acp_wrappers_init_done:
+ return 0;
+}
+module_init(acp_wrappers_init);
+
+MODULE_AUTHOR("LSI Corporation");
+MODULE_DESCRIPTION("Timing Test");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/lsi/lsi_acp_net.c b/drivers/net/ethernet/lsi/lsi_acp_net.c
new file mode 100644
index 0000000..bc5ee3b
--- /dev/null
+++ b/drivers/net/ethernet/lsi/lsi_acp_net.c
@@ -0,0 +1,2808 @@
+/*
+ * drivers/net/ethernet/lsi/lsi_acp_net.c
+ *
+ * Copyright (C) 2009 LSI
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+
+#include <asm/lsi/acp_ncr.h>
+
+extern int acp_mdio_read(unsigned long,
+ unsigned long,
+ unsigned short *);
+extern int acp_mdio_write(unsigned long,
+ unsigned long,
+ unsigned short);
+extern int ubootenv_get(const char *, char *);
+
+/* Base Addresses of the RX, TX, and DMA Registers. */
+static void *rx_base;
+static void *tx_base;
+static void *dma_base;
+
+/*
+ ======================================================================
+ ======================================================================
+ Optimizations
+ ======================================================================
+ ======================================================================
+*/
+
+#define DISABLE_TX_INTERRUPTS
+/*#define PRELOAD_RX_BUFFERS*/
+
+#undef DMA_CACHABLE
+/*#define DMA_CACHABLE*/
+
+/*
+ =============================================================================
+ NAPI Support (new and newer)...
+ =============================================================================
+*/
+
+#define LSINET_NAPI
+#define LSINET_NAPI_WEIGHT 64
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+ PHY
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+#undef PHYLESS
+
+#ifndef PHYLESS
+
+/* -- control -- */
+
+#define PHY_CONTROL 0x00
+
+union phy_control {
+ unsigned short raw;
+
+ struct {
+ unsigned short soft_reset:1;
+ unsigned short loop_back:1;
+ unsigned short force100:1; /* speedBit0 */
+ unsigned short autoneg_enable:1;
+ unsigned short power_down:1;
+ unsigned short isolate:1;
+ unsigned short restart_autoneg:1;
+ unsigned short full_duplex:1; /* duplex */
+ unsigned short collision_test:1;
+ unsigned short unused:7;
+ } __packed bits;
+} __packed;
+
+/* -- status -- */
+
+#define PHY_STATUS 0x01
+
+union phy_status {
+ unsigned short raw;
+
+ struct {
+ unsigned short t4_capable:1;
+ unsigned short tx_fdx_capable:1;
+ unsigned short tx_capable:1;
+ unsigned short bt_fdx_capable:1;
+ unsigned short tenbt_capable:1;
+ unsigned short unused:4;
+ unsigned short mf_pream_suppress:1;
+ unsigned short autoneg_comp:1; /* autoNegDone */
+ unsigned short remote_fault:1; /* remoutFault */
+ unsigned short autoneg_capable:1;
+ unsigned short link_status:1; /* linkStatus */
+ unsigned short jabber_detect:1;
+ unsigned short extd_reg_capable:1;
+ } __packed bits;
+} __packed;
+
+/* -- id_high -- */
+
+#define PHY_ID_HIGH 0x02
+
+union phy_id_high {
+ unsigned short raw;
+
+ struct {
+ unsigned short id:16;
+ } __packed bits;
+} __packed;
+
+/* -- id_low -- */
+
+#define PHY_ID_LOW 0x03
+
+union phy_id_low {
+ unsigned short raw;
+
+ struct {
+ unsigned short id:6;
+ unsigned short model:6;
+ unsigned short revision:4;
+ } __packed bits;
+} __packed;
+
+/* -- autoneg_advertise -- */
+
+#define PHY_AUTONEG_ADVERTISE 0x04
+
+/* -- link_partner_ability -- */
+
+#define PHY_LINK_PARTNER_ABILITY 0x05
+
+/* -- -- */
+
+#define MICREL_PHY_AUXILIARY_CONTROL_STATUS 0x1f
+
+union micrel_phy_auxiliary_control_status {
+ unsigned short raw;
+
+ struct {
+ unsigned short unused:3;
+ unsigned short energy:1;
+ unsigned short force_link:1;
+ unsigned short power_saving:1;
+ unsigned short interrupt_level:1;
+ unsigned short jabber_enable:1;
+ unsigned short autoneg_indication:1;
+ unsigned short enable_pause:1;
+ unsigned short isolate:1;
+ unsigned short op_mode_indication:3;
+ unsigned short unused2:2;
+ } bits;
+};
+
+/*
+ Auto-negotiation Advertisement Values.
+*/
+
+#define PHY_AUTONEG_ADVERTISE_100FULL 0x101
+#define PHY_AUTONEG_ADVERTISE_100 0x081
+#define PHY_AUTONEG_ADVERTISE_10FULL 0x041
+#define PHY_AUTONEG_ADVERTISE_10 0x021
+
+#define UNKNOWN_PHY 0x0
+#define BCM5221_PHY 0x1
+#define MICREL_PHY 0x2
+
+static int phy_read(int, int, unsigned short *);
+static int phy_write(int, int, unsigned short);
+static int phy_speed(int);
+static int phy_duplex(int);
+static int phy_renegotiate(int);
+static int phy_enable(struct net_device *);
+
+#endif /* PHYLESS */
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ NIC Interface
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+static int enable_rx_tx(struct net_device *);
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Device Data Structures
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+
+struct appnic_dma_descriptor {
+
+ /* Word 0 */
+ unsigned long unused:24;
+ /* big endian to little endian */
+ unsigned long byte_swapping_on:1;
+ unsigned long error:1;
+ unsigned long interrupt_on_completion:1;
+ unsigned long end_of_packet:1;
+ unsigned long start_of_packet:1;
+ unsigned long write:1;
+ /* 00=Fill|01=Block|10=Scatter */
+ unsigned long transfer_type:2;
+
+ /* Word 1 */
+ unsigned long pdu_length:16;
+ unsigned long data_transfer_length:16;
+
+ /* Word 2 */
+ unsigned long target_memory_address;
+
+ /* Word 3 */
+ unsigned long host_data_memory_pointer;
+
+} __packed;
+
+union appnic_queue_pointer {
+
+ unsigned long raw;
+
+ struct {
+ unsigned long unused:11;
+ unsigned long generation_bit:1;
+ unsigned long offset:20;
+ } __packed bits;
+
+} __packed;
+
+/*
+ =============================================================================
+ The Device Struction
+ =============================================================================
+*/
+
+struct appnic_device {
+
+ /* net_device */
+ struct net_device *device;
+
+ /* Addresses, Interrupt, and PHY stuff. */
+ unsigned long rx_base;
+ unsigned long tx_base;
+ unsigned long dma_base;
+ unsigned long interrupt;
+ unsigned long mdio_clock;
+ unsigned long phy_address;
+ unsigned long ad_value;
+ unsigned char mac_addr[6];
+
+#ifdef LSINET_NAPI
+ /* napi */
+ struct napi_struct napi;
+#endif /* LSINET_ENABLE_NAPI */
+
+ /* statistics */
+ struct net_device_stats stats;
+
+ /*
+ * DMA-able memory.
+ */
+
+ int dma_alloc_size;
+ void *dma_alloc;
+ dma_addr_t dma_alloc_dma;
+ int dma_alloc_offset;
+
+ /* tail pointers */
+ volatile union appnic_queue_pointer *rx_tail;
+ dma_addr_t rx_tail_dma;
+ volatile union appnic_queue_pointer *tx_tail;
+ dma_addr_t tx_tail_dma;
+
+ /* descriptors */
+ struct appnic_dma_descriptor *rx_desc;
+ dma_addr_t rx_desc_dma;
+ unsigned rx_num_desc;
+ struct appnic_dma_descriptor *tx_desc;
+ dma_addr_t tx_desc_dma;
+ unsigned tx_num_desc;
+
+ /* buffers */
+ unsigned rx_buf_sz;
+ unsigned rx_buf_per_desc;
+ void *rx_buf;
+ dma_addr_t rx_buf_dma;
+ unsigned tx_buf_sz;
+ unsigned tx_buf_per_desc;
+ void *tx_buf;
+ dma_addr_t tx_buf_dma;
+
+ /*
+ * The local pointers
+ */
+
+ union appnic_queue_pointer rx_tail_copy;
+ union appnic_queue_pointer rx_head;
+
+ union appnic_queue_pointer tx_tail_copy;
+ union appnic_queue_pointer tx_head;
+
+ /*
+ * Polling Mode?
+ */
+
+ int polling;
+
+ /*
+ * Spin Lock
+ */
+
+ spinlock_t lock;
+ spinlock_t extra_lock;
+};
+
+#define DESCRIPTOR_GRANULARITY 64
+#define BUFFER_ALIGNMENT 64
+
+#define ALIGN64B(address) \
+ ((((unsigned long) (address) + (64UL - 1UL)) & ~(64UL - 1UL)))
+
+#define ALIGN64B_OFFSET(address) \
+ (ALIGN64B(address) - (unsigned long) (address))
+
+#define APPNIC_NAME "appnic"
+
+/*
+ * Overview
+ * --------
+ *
+ * Register offset decoding is as follows:
+ *
+ * Bit(s) Description
+ *
+ * 16:15 define the Channel. There is only one; therefore, 00.
+ * 14:12 define the MAC within the channel. Only one so 000.
+ * 11:10 define the register "space" as follows:
+ * 00 = fast ethernet MACmw.l 06000000 ffffffff 3200000
+ * 10 = global
+ * 11 = interrupt
+ * 9: 2 register
+ * 1: 0 always 00, 32 bit registers only.
+ *
+ * Receive registers start at the base address. Transmit registers start
+ * at 0x20000 above the base address. DMA start at a completely different
+ * base address (in this case 0x8000000 above the base).
+ *
+*/
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Registers.
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+/* SMII Status ------------------------------------------------------ */
+
+#define APPNIC_RX_SMII_STATUS (rx_base + 0x10)
+#define APPNIC_RX_SMII_STATUS_SPEED 0x01
+#define APPNIC_RX_SMII_STATUS_DUPLEX 0x02
+#define APPNIC_RX_SMII_STATUS_LINK 0x04
+#define APPNIC_RX_SMII_STATUS_JABBER 0x08
+#define APPNIC_RX_SMII_STATUS_FCD 0x10 /* False Carrier Detect */
+
+#define SMII_SPEED_100(smii_status) \
+ (0 != (smii_status & APPNIC_RX_SMII_STATUS_SPEED))
+#define SMII_DUPLEX(smii_status) \
+ (0 != (smii_status & APPNIC_RX_SMII_STATUS_DUPLEX))
+#define SMII_LINK(smii_status) \
+ (0 != (smii_status & APPNIC_RX_SMII_STATUS_LINK))
+#define SMII_JABBER(smii_status) \
+ (0 != (smii_status & APPNIC_RX_SMII_STATUS_JABBER))
+
+/* Receive Configuration -------------------------------------------- */
+
+#define APPNIC_RX_CONF (rx_base + 0x004c)
+#define APPNIC_RX_CONF_ENABLE 0x0001
+/* Pass Any Packet */
+#define APPNIC_RX_CONF_PAP 0x0002
+#define APPNIC_RX_CONF_JUMBO9K 0x0008
+#define APPNIC_RX_CONF_STRIPCRC 0x0010
+/* Accept All MAC Types */
+#define APPNIC_RX_CONF_AMT 0x0020
+/* Accept Flow Control */
+#define APPNIC_RX_CONF_AFC 0x0040
+/* Enable VLAN */
+#define APPNIC_RX_CONF_VLAN 0x0200
+/* RX MAC Speed, 1=100MBS */
+#define APPNIC_RX_CONF_SPEED 0x0800
+/* 1=Duplex Mode */
+#define APPNIC_RX_CONF_DUPLEX 0x1000
+/* 1=Enable */
+#define APPNIC_RX_CONF_LINK 0x2000
+/*
+ * Determines the action taken when the FE MAC
+ * receives an FC packet in FD mode.
+ */
+#define APPNIC_RX_CONF_RXFCE 0x4000
+/*
+ * Controls the insertion of FC packets
+ * by the MAC transmitter.
+ */
+#define APPNIC_RX_CONF_TXFCE 0x8000
+
+/* Receive Stat Overflow -------------------------------------------- */
+
+#define APPNIC_RX_STAT_OVERFLOW (rx_base + 0x278)
+
+/* Receive Stat Undersize ------------------------------------------- */
+
+#define APPNIC_RX_STAT_UNDERSIZE (rx_base + 0x280)
+
+/* Receive Stat Oversize -------------------------------------------- */
+
+#define APPNIC_RX_STAT_OVERSIZE (rx_base + 0x2b8)
+
+/* Receive Stat Multicast ------------------------------------------- */
+
+#define APPNIC_RX_STAT_MULTICAST (rx_base + 0x2d0)
+
+/* Receive Stat Packet OK ------------------------------------------- */
+
+#define APPNIC_RX_STAT_PACKET_OK (rx_base + 0x2c0)
+
+/* Receive Stat CRC Error ------------------------------------------- */
+
+#define APPNIC_RX_STAT_CRC_ERROR (rx_base + 0x2c8)
+
+/* Receive Stat Align Error ----------------------------------------- */
+
+#define APPNIC_RX_STAT_ALIGN_ERROR (rx_base + 0x2e8)
+
+/* Receive Ethernet Mode -------------------------------------------- */
+
+#define APPNIC_RX_MODE (rx_base + 0x0800)
+#define APPNIC_RX_MODE_ETHERNET_MODE_ENABLE 0x00001
+
+/* Receive Soft Reset ----------------------------------------------- */
+
+#define APPNIC_RX_SOFT_RESET (rx_base + 0x0808)
+#define APPNIC_RX_SOFT_RESET_MAC_0 0x00001
+
+/* Receive Internal Interrupt Control ------------------------------- */
+
+#define APPNIC_RX_INTERNAL_INTERRUPT_CONTROL (rx_base + 0xc00)
+#define APPNIC_RX_INTERNAL_INTERRUPT_CONTROL_MAC_0 0x1
+
+/* Receive External Interrupt Control ------------------------------- */
+
+#define APPNIC_RX_EXTERNAL_INTERRUPT_CONTROL (rx_base + 0xc04)
+#define APPNIC_RX_EXTERNAL_INTERRUPT_CONTROL_MAC_0_HIGH_LOW 0x10
+#define APPNIC_RX_EXTERNAL_INTERRUPT_CONTROL_MAC_0 0x1
+
+/* Receive Interrupt Status ----------------------------------------- */
+
+#define APPNIC_RX_INTERRUPT_STATUS (rx_base + 0xc20)
+#define APPNIC_RX_INTERRUPT_EXTERNAL_STATUS_MAC_0 0x10
+#define APPNIC_RX_INTERRUPT_INTERNAL_STATUS_MAC_0 0x1
+
+/* Transmit Watermark ----------------------------------------------- */
+
+#define APPNIC_TX_WATERMARK (tx_base + 0x18)
+#define APPNIC_TX_WATERMARK_TXCONFIG_DTPA_ASSERT 0x8000
+#define APPNIC_TX_WATERMARK_TXCONFIG_DTPA_DISABLE 0x4000
+#define APPNIC_TX_WATERMARK_TXCONFIG_DTPA_WATER_MARK_HIGH 0x3f00
+#define APPNIC_TX_WATERMARK_TXCONFIG_DTPA_WATER_MARK_LOW 0x3f
+
+/* Swap Source Address Registers ------------------------------------ */
+
+#define APPNIC_SWAP_SOURCE_ADDRESS_2 (tx_base + 0x20)
+#define APPNIC_SWAP_SOURCE_ADDRESS_1 (tx_base + 0x24)
+#define APPNIC_SWAP_SOURCE_ADDRESS_0 (tx_base + 0x28)
+
+/* Transmit Extended Configuration ---------------------------------- */
+
+#define APPNIC_TX_EXTENDED_CONF (tx_base + 0x30)
+#define APPNIC_TX_EXTENDED_CONF_TRANSMIT_COLLISION_WATERMARK_LEVEL 0xf000
+#define APPNIC_TX_EXTENDED_CONF_EXCESSIVE_DEFFERED_PACKET_DROP 0x200
+#define APPNIC_TX_EXTENDED_CONF_JUMBO9K 0x100
+#define APPNIC_TX_EXTENDED_CONF_LATE_COLLISION_WINDOW_COUNT 0xff
+
+/* Transmit Half Duplex Configuration ------------------------------- */
+
+#define APPNIC_TX_HALF_DUPLEX_CONF (tx_base + 0x34)
+#define APPNIC_TX_HALF_DUPLEX_CONF_RANDOM_SEED_VALUE 0xff
+
+/* Transmit Configuration ------------------------------------------- */
+
+#define APPNIC_TX_CONF (tx_base + 0x0050)
+#define APPNIC_TX_CONF_ENABLE_SWAP_SA 0x8000
+#define APPNIC_TX_CONF_LINK 0x2000
+#define APPNIC_TX_CONF_DUPLEX 0x1000
+#define APPNIC_TX_CONF_SPEED 0x0800
+#define APPNIC_TX_CONF_XBK_RST_RX_NTX 0x0600
+#define APPNIC_TX_CONF_IFG 0x01f0
+#define APPNIC_TX_CONF_APP_CRC_ENABLE 0x0004
+#define APPNIC_TX_CONF_PAD_ENABLE 0x0002
+#define APPNIC_TX_CONF_ENABLE 0x0001
+
+#define TX_CONF_SET_IFG(tx_configuration, ifg) \
+ do { \
+ (tx_configuration) &= ~APPNIC_TX_CONF_IFG; \
+ (tx_configuration) |= ((ifg & 0x1f) << 4); \
+ } while (0);
+
+/* Transmit Time Value Configuration -------------------------------- */
+
+#define APPNIC_TX_TIME_VALUE_CONF (tx_base + 0x5c)
+#define APPNIC_TX_TIME_VALUE_CONF_PAUSE_VALUE 0xffff
+
+/* Transmit Stat Underrun ------------------------------------------- */
+
+#define APPNIC_TX_STAT_UNDERRUN (tx_base + 0x300)
+
+/* Transmit Stat Packet OK ------------------------------------------ */
+
+#define APPNIC_TX_STAT_PACKET_OK (tx_base + 0x318)
+
+/* Transmit Stat Undersize ------------------------------------------ */
+
+#define APPNIC_TX_STAT_UNDERSIZE (tx_base + 0x350)
+
+/* Transmit Status Late Collision ----------------------------------- */
+
+#define APPNIC_TX_STATUS_LATE_COLLISION (tx_base + 0x368)
+
+/* Transmit Status Excessive Collision ------------------------------ */
+
+#define APPNIC_TX_STATUS_EXCESSIVE_COLLISION (tx_base + 0x370)
+
+/* Transmit Stat Collision Above Watermark -------------------------- */
+
+#define APPNIC_TX_STAT_COLLISION_ABOVE_WATERMARK (tx_base + 0x380)
+
+/* Transmit Mode ---------------------------------------------------- */
+
+#define APPNIC_TX_MODE (tx_base + 0x800)
+#define APPNIC_TX_MODE_ETHERNET_MODE_ENABLE 0x1
+
+/* Transmit Soft Reset ---------------------------------------------- */
+
+#define APPNIC_TX_SOFT_RESET (tx_base + 0x808)
+#define APPNIC_TX_SOFT_RESET_MAC_0 0x1
+
+/* Transmit Interrupt Control --------------------------------------- */
+
+#define APPNIC_TX_INTERRUPT_CONTROL (tx_base + 0xc00)
+#define APPNIC_TX_INTERRUPT_CONTROL_MAC_0 0x1
+
+/* Transmit Interrupt Status ---------------------------------------- */
+
+#define APPNIC_TX_INTERRUPT_STATUS (tx_base + 0xc20)
+#define APPNIC_TX_INTERRUPT_STATUS_MAC_0 0x1
+
+/* */
+
+#define APPNIC_DMA_PCI_CONTROL (dma_base + 0x00)
+
+/* */
+
+#define APPNIC_DMA_CONTROL (dma_base + 0x08)
+
+/* DMA Interrupt Status --------------------------------------------- */
+
+#define APPNIC_DMA_INTERRUPT_STATUS (dma_base + 0x18)
+#define APPNIC_DMA_INTERRUPT_STATUS_RX 0x2
+#define APPNIC_DMA_INTERRUPT_STATUS_TX 0x1
+#define RX_INTERRUPT(dma_interrupt_status) \
+ (0 != (dma_interrupt_status & APPNIC_DMA_INTERRUPT_STATUS_RX))
+#define TX_INTERRUPT(dma_interrupt_status) \
+ (0 != (dma_interrupt_status & APPNIC_DMA_INTERRUPT_STATUS_TX))
+
+/* DMA Interrupt Enable --------------------------------------------- */
+
+#define APPNIC_DMA_INTERRUPT_ENABLE (dma_base + 0x1c)
+#define APPNIC_DMA_INTERRUPT_ENABLE_RECEIVE 0x2
+#define APPNIC_DMA_INTERRUPT_ENABLE_TRANSMIT 0x1
+
+/* DMA Receive Queue Base Address ----------------------------------- */
+
+#define APPNIC_DMA_RX_QUEUE_BASE_ADDRESS (dma_base + 0x30)
+
+/* DMA Receive Queue Size ------------------------------------------- */
+
+#define APPNIC_DMA_RX_QUEUE_SIZE (dma_base + 0x34)
+
+/* DMA Transmit Queue Base Address ---------------------------------- */
+
+#define APPNIC_DMA_TX_QUEUE_BASE_ADDRESS (dma_base + 0x38)
+
+/* DMA Transmit Queue Size ------------------------------------------ */
+
+#define APPNIC_DMA_TX_QUEUE_SIZE (dma_base + 0x3c)
+
+/* DMA Recevie Tail Pointer Address --------------------------------- */
+
+#define APPNIC_DMA_RX_TAIL_POINTER_ADDRESS (dma_base + 0x48)
+
+/* DMA Transmit Tail Pointer Address -------------------------------- */
+
+#define APPNIC_DMA_TX_TAIL_POINTER_ADDRESS (dma_base + 0x4c)
+
+/* DMA Receive Head Pointer ----------------------------------------- */
+
+#define APPNIC_DMA_RX_HEAD_POINTER (dma_base + 0x50)
+#define APPNIC_DMA_RX_HEAD_POINTER_GB 0x100000
+#define APPNIC_DMA_RX_HEAD_POINTER_POINTER 0x0fffff
+
+/* DMA Receive Tail Pointer Local Copy ------------------------------ */
+
+#define APPNIC_DMA_RX_TAIL_POINTER_LOCAL_COPY (dma_base + 0x54)
+#define APPNIC_DMA_RX_TAIL_POINTER_LOCAL_COPY_GB 0x100000
+#define APPNIC_DMA_RX_TAIL_POINTER_LOCAL_COPY_POINTER 0x0fffff
+
+/* DMA Transmit Head Pointer ---------------------------------------- */
+
+#define APPNIC_DMA_TX_HEAD_POINTER (dma_base + 0x58)
+#define APPNIC_DMA_TX_HEAD_POINTER_GB 0x100000
+#define APPNIC_DMA_TX_HEAD_POINTER_POINTER 0x0fffff
+
+/* DMA Transmit Tail Pointer Local Copy ----------------------------- */
+
+#define APPNIC_DMA_TX_TAIL_POINTER_LOCAL_COPY (dma_base + 0x5c)
+#define APPNIC_DMA_TX_TAIL_POINTER_LOCAL_COPY_GB 0x100000
+#define APPNIC_DMA_TX_TAIL_POINTER_LOCAL_COPY_POINTER 0x0fffff
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Access
+
+ N.B. On the APP, DMA transfers from the NIC MUST USE THE MEMORY
+ ALIAS AT 0x60000000!
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+#define readio(address) in_le32((u32 *) (address))
+
+#define writeio(value, address) out_le32((u32 *) (address), (value))
+
+static inline void
+readdescriptor(unsigned long address, struct appnic_dma_descriptor *descriptor)
+{
+ unsigned long *from = (unsigned long *) address;
+ unsigned long *to = (unsigned long *) descriptor;
+
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ return;
+}
+
+static inline void
+writedescriptor(unsigned long address,
+ const struct appnic_dma_descriptor *descriptor)
+{
+ unsigned long *to = (unsigned long *) address;
+ unsigned long *from = (unsigned long *) descriptor;
+
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ *to++ = swab32(*from++);
+ return;
+}
+
+static inline union appnic_queue_pointer
+swab_queue_pointer(const union appnic_queue_pointer *old_queue)
+{
+ union appnic_queue_pointer new_queue;
+ new_queue.raw = swab32(old_queue->raw);
+ return new_queue;
+}
+
+#define SWAB_QUEUE_POINTER(pointer) \
+swab_queue_pointer((const union appnic_queue_pointer *) (pointer))
+
+#define read_mac(address) readio((address))
+#define write_mac(value, address) writeio((value), (address))
+
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Module Information
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+MODULE_AUTHOR("John Jacques");
+MODULE_DESCRIPTION("Agere APP3xx ethernet driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ----- Note On Buffer Space -----
+ *
+ * Minimum number of descriptors is 64 for the receiver and 64 for the
+ * transmitter; therefore, 2048 bytes (16 bytes each).
+ * This driver uses the following parameters,
+ * all of which may be set on the command line if this drivers is used
+ * as a module.
+ *
+ * - rx_num_desc : Number of receive descriptors. This must be a multiple
+ * of 64.
+ * - tx_num_desc : Number of transmit descriptors. This must be a multiple
+ * of 64.
+ *
+ * The scheme used will be as follows:
+ *
+ * - num_[rt]x_desc will be adjusted to be a multiple of 64 (if necessary).
+ * - An skb (with the data area 64 byte aligned) will be allocated for each rx
+ * descriptor.
+ */
+
+/*
+ * Receiver
+ */
+
+int rx_num_desc = (CONFIG_LSI_NET_NUM_RX_DESC * DESCRIPTOR_GRANULARITY);
+module_param(rx_num_desc, int, 0);
+MODULE_PARM_DESC(rx_num_desc, "appnic : Number of receive descriptors");
+
+int rx_buf_sz = CONFIG_LSI_NET_RX_BUF_SZ;
+module_param(rx_buf_sz, int, 0);
+MODULE_PARM_DESC(rx_buf_sz, "appnic : Receive buffer size");
+
+/*
+ * Transmitter
+ */
+
+int tx_num_desc = (CONFIG_LSI_NET_NUM_TX_DESC * DESCRIPTOR_GRANULARITY);
+module_param(tx_num_desc, int, 0);
+MODULE_PARM_DESC(tx_num_desc, "appnic : Number of receive descriptors");
+
+int tx_buf_sz = CONFIG_LSI_NET_TX_BUF_SZ;
+module_param(tx_buf_sz, int, 0);
+MODULE_PARM_DESC(tx_buf_sz, "Appnic : Receive buffer size");
+
+/*
+ * Only 1 device is possible...
+ */
+
+struct net_device *this_net_device;
+
+static unsigned long dropped_by_stack;
+static unsigned long out_of_tx_descriptors;
+static unsigned long transmit_interrupts;
+#ifdef LSINET_NAPI
+static unsigned long receive_interrupts;
+#endif
+
+#define APPNIC_TIMER_PERIOD 5
+#ifndef PHYLESS
+static void appnic_timer_handler(unsigned long);
+static struct timer_list appnic_timer;
+#endif /* PHYLESS */
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Utility Functions
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+/*
+ ----------------------------------------------------------------------
+ clear_statistics
+*/
+
+static void clear_statistics(struct appnic_device *device)
+{
+ int waste;
+
+ /*
+ * Clear memory.
+ */
+
+ memset((void *) &(device->stats), 0,
+ sizeof(struct net_device_stats));
+
+ /*
+ * Clear counters.
+ */
+
+ waste = read_mac(APPNIC_RX_STAT_PACKET_OK); /* rx_packets */
+ waste = read_mac(APPNIC_TX_STAT_PACKET_OK); /* tx_packets */
+
+ /* rx_bytes kept by driver. */
+ /* tx_bytes kept by driver. */
+ /* rx_errors will be the sum of the rx errors available. */
+ /* tx_errors will be the sum of the tx errors available. */
+ /* rx_dropped (unable to allocate skb) will be maintained by driver */
+ /* tx_dropped (unable to allocate skb) will be maintained by driver */
+
+ /* multicast */
+
+ waste = read_mac(APPNIC_RX_STAT_MULTICAST);
+
+ /* collisions will be the sum of the three following. */
+
+ waste = read_mac(APPNIC_TX_STATUS_LATE_COLLISION);
+ waste = read_mac(APPNIC_TX_STATUS_EXCESSIVE_COLLISION);
+ waste = read_mac(APPNIC_TX_STAT_COLLISION_ABOVE_WATERMARK);
+
+ /* rx_length_errors will be the sum of the two following. */
+
+ waste = read_mac(APPNIC_RX_STAT_UNDERSIZE);
+ waste = read_mac(APPNIC_RX_STAT_OVERSIZE);
+
+ /* rx_over_errors (out of descriptors?) maintained by the driver. */
+ /* rx_crc_errors */
+
+ waste = read_mac(APPNIC_RX_STAT_CRC_ERROR);
+
+ /* rx_frame_errors */
+
+ waste = read_mac(APPNIC_RX_STAT_ALIGN_ERROR);
+
+ /* rx_fifo_errors */
+
+ waste = read_mac(APPNIC_RX_STAT_OVERFLOW);
+
+ /* rx_missed will not be maintained. */
+ /* tx_aborted_errors will be maintained by the driver. */
+ /* tx_carrier_errors will not be maintained. */
+ /* tx_fifo_errors */
+
+ waste = read_mac(APPNIC_TX_STAT_UNDERRUN);
+
+ /* tx_heartbeat_errors */
+ /* tx_window_errors */
+
+ /* rx_compressed will not be maintained. */
+ /* tx_compressed will not be maintained. */
+
+ /*
+ * That's all.
+ */
+
+ return;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * get_hw_statistics
+ *
+ * -- NOTES --
+ *
+ * 1) The hardware clears the statistics registers after a read.
+ */
+
+static void get_hw_statistics(struct appnic_device *device)
+{
+ unsigned long flags;
+
+ /* tx_packets */
+
+ device->stats.tx_packets += read_mac(APPNIC_TX_STAT_PACKET_OK);
+
+ /* multicast */
+
+ device->stats.multicast += read_mac(APPNIC_RX_STAT_MULTICAST);
+
+ /* collision */
+
+ device->stats.collisions += read_mac(APPNIC_TX_STATUS_LATE_COLLISION);
+ device->stats.collisions +=
+ read_mac(APPNIC_TX_STATUS_EXCESSIVE_COLLISION);
+ device->stats.collisions +=
+ read_mac(APPNIC_TX_STAT_COLLISION_ABOVE_WATERMARK);
+
+ /* rx_length_errors */
+
+ device->stats.rx_length_errors += read_mac(APPNIC_RX_STAT_UNDERSIZE);
+ device->stats.rx_length_errors += read_mac(APPNIC_RX_STAT_OVERSIZE);
+
+ /* tx_fifo_errors */
+
+ device->stats.tx_fifo_errors += read_mac(APPNIC_TX_STAT_UNDERRUN);
+
+ /*
+ * Lock this section out so the statistics maintained by the driver
+ * don't get clobbered.
+ */
+
+
+ spin_lock_irqsave(&device->lock, flags);
+
+ device->stats.rx_errors +=
+ (device->stats.rx_length_errors +
+ device->stats.rx_crc_errors +
+ device->stats.rx_frame_errors +
+ device->stats.rx_fifo_errors +
+ device->stats.rx_dropped +
+ device->stats.rx_over_errors);
+
+ device->stats.rx_dropped = 0;
+ device->stats.rx_over_errors = 0;
+
+ device->stats.tx_errors +=
+ (device->stats.tx_fifo_errors +
+ device->stats.tx_aborted_errors);
+ device->stats.tx_aborted_errors = 0;
+
+ spin_unlock_irqrestore(&device->lock, flags);
+
+ /*
+ * That's all.
+ */
+
+ return;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * queue_initialized
+ *
+ * Returns the number of descriptors that are ready to receive packets
+ * or are waiting to transmit packets. (from tail to head).
+ */
+
+static int queue_initialized(union appnic_queue_pointer head,
+ union appnic_queue_pointer tail,
+ int size)
+{
+ int initialized;
+
+ /* Calculate the number of descriptors currently initialized. */
+
+ if (head.bits.generation_bit == tail.bits.generation_bit) {
+ /* same generation */
+ initialized = (head.bits.offset - tail.bits.offset);
+ } else {
+ /* different generation */
+ initialized = head.bits.offset +
+ (size * sizeof(struct appnic_dma_descriptor) -
+ tail.bits.offset);
+ }
+
+ /* number of descriptors is offset / sizeof(a descriptor) */
+ initialized /= sizeof(struct appnic_dma_descriptor);
+
+ return initialized;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * queue_uninitialzed
+ *
+ * Returns the number of unused/uninitialized descriptors. (from head to tail).
+*/
+
+static int queue_uninitialized(union appnic_queue_pointer head,
+ union appnic_queue_pointer tail,
+ int size)
+{
+ int allocated;
+
+ /* Calculate the number of descriptors currently unused/uninitialized */
+
+ if (head.bits.generation_bit == tail.bits.generation_bit)
+ /* Same generation. */
+ allocated = ((size * sizeof(struct appnic_dma_descriptor)) -
+ head.bits.offset) + tail.bits.offset;
+ else
+ /* Different generation. */
+ allocated = tail.bits.offset - head.bits.offset;
+
+ /* number of descriptors is offset / sizeof(a descriptor). */
+ allocated /= sizeof(struct appnic_dma_descriptor);
+
+ /* That's all. */
+ return allocated;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * queue_increment
+ */
+
+static void queue_increment(union appnic_queue_pointer *queue,
+ int number_of_descriptors)
+{
+ queue->bits.offset += sizeof(struct appnic_dma_descriptor);
+
+ if ((number_of_descriptors * sizeof(struct appnic_dma_descriptor)) ==
+ queue->bits.offset) {
+
+ queue->bits.offset = 0;
+ queue->bits.generation_bit =
+ (0 == queue->bits.generation_bit) ? 1 : 0;
+ }
+
+ return;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * queue_decrement
+ */
+
+static void queue_decrement(union appnic_queue_pointer *queue,
+ int number_of_descriptors)
+{
+ if (0 == queue->bits.offset) {
+ queue->bits.offset =
+ ((number_of_descriptors - 1) *
+ sizeof(struct appnic_dma_descriptor));
+ queue->bits.generation_bit =
+ (0 == queue->bits.generation_bit) ? 1 : 0;
+ } else {
+ queue->bits.offset -= sizeof(struct appnic_dma_descriptor);
+ }
+
+ return;
+}
+
+
+#ifndef PHYLESS
+
+static void appnic_timer_handler(unsigned long __opaque)
+{
+ struct net_device *device = (struct net_device *) __opaque;
+
+ enable_rx_tx(device);
+ appnic_timer.expires = jiffies + (APPNIC_TIMER_PERIOD * HZ);
+ add_timer(&appnic_timer);
+}
+
+#endif
+
+/*
+ * ----------------------------------------------------------------------
+ * enable_rx_tx
+ *
+ * -- NOTES --
+ *
+ * 1) Does not change the default values in the extended and
+ * half-duplex configuration registers.
+ */
+
+static int enable_rx_tx(struct net_device *device)
+{
+ int return_code = 1;
+ int carrier_state = 0;
+ unsigned long rx_configuration;
+ unsigned long tx_configuration = 0;
+ union phy_status phy_status;
+ struct appnic_device *dev = netdev_priv(device);
+
+ rx_configuration =
+ (APPNIC_RX_CONF_STRIPCRC |
+ APPNIC_RX_CONF_RXFCE |
+ APPNIC_RX_CONF_TXFCE);
+ tx_configuration =
+ (APPNIC_TX_CONF_ENABLE_SWAP_SA |
+ APPNIC_TX_CONF_APP_CRC_ENABLE |
+ APPNIC_TX_CONF_PAD_ENABLE);
+
+ TX_CONF_SET_IFG(tx_configuration, 0xf);
+
+ /*
+ * Setup the receive and transmit configuration registers (using smii
+ * status to set speed/duplex and check the link status).
+ */
+
+ if ((0 == phy_read(dev->phy_address, PHY_STATUS, &phy_status.raw)) &&
+ (0 == phy_read(dev->phy_address, PHY_STATUS, &phy_status.raw))) {
+
+ if (1 == phy_status.bits.autoneg_comp) {
+ if (1 == phy_status.bits.link_status) {
+ if (1 == phy_speed(dev->phy_address)) {
+ rx_configuration |=
+ APPNIC_RX_CONF_SPEED;
+ tx_configuration |=
+ APPNIC_TX_CONF_SPEED;
+ }
+
+ if (1 == phy_duplex(dev->phy_address)) {
+ rx_configuration |=
+ APPNIC_RX_CONF_DUPLEX;
+ tx_configuration |=
+ APPNIC_TX_CONF_DUPLEX;
+ }
+
+ rx_configuration |=
+ (APPNIC_RX_CONF_ENABLE |
+ APPNIC_RX_CONF_LINK);
+ tx_configuration |=
+ (APPNIC_TX_CONF_LINK |
+ APPNIC_TX_CONF_ENABLE);
+ return_code = 0;
+ carrier_state = 1;
+ } else {
+ netif_carrier_off(device);
+ }
+ } else {
+ netif_carrier_off(device);
+ }
+ } else {
+ pr_err("acp-femac: phy_read() failed!\n");
+ }
+
+ if (rx_configuration != read_mac(APPNIC_RX_CONF))
+ write_mac(rx_configuration, APPNIC_RX_CONF);
+
+ if (tx_configuration != read_mac(APPNIC_TX_CONF))
+ write_mac(tx_configuration, APPNIC_TX_CONF);
+
+ if (0 != carrier_state)
+ netif_carrier_on(device);
+ else
+ netif_carrier_off(device);
+
+ return return_code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * disable_rx_tx
+ */
+
+static void disable_rx_tx(void)
+{
+ unsigned long tx_configuration;
+ unsigned long rx_configuration;
+
+ pr_info("acp-femac: Disabling the interface.\n");
+
+ rx_configuration = read_mac(APPNIC_RX_CONF);
+ rx_configuration &= ~APPNIC_RX_CONF_ENABLE;
+ write_mac(rx_configuration, APPNIC_RX_CONF);
+
+ tx_configuration = read_mac(APPNIC_TX_CONF);
+ tx_configuration &= ~APPNIC_TX_CONF_ENABLE;
+
+ write_mac(tx_configuration, APPNIC_TX_CONF);
+
+ /* That's all. */
+ return;
+}
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+ PHY interface (BCM5221)
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+#ifndef PHYLESS
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_read
+ *
+ * Returns -1 if unsuccessful, the (short) value otherwise.
+ */
+
+static int
+phy_read(int phy, int reg, unsigned short *value)
+{
+ return acp_mdio_read(phy, reg, value);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_write
+ */
+
+static int
+phy_write(int phy, int reg, unsigned short value)
+{
+ return acp_mdio_write(phy, reg, value);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_speed
+ *
+ * Returns the speed (1=100, 0=10) or an error (-1).
+ */
+
+static int
+phy_speed(int phy)
+{
+ union micrel_phy_auxiliary_control_status aux;
+
+ phy_read(phy, MICREL_PHY_AUXILIARY_CONTROL_STATUS, &aux.raw);
+
+ switch ((aux.bits.op_mode_indication & 3)) {
+ case 2:
+ return 1;
+ break;
+ case 1:
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_duplex
+ *
+ * Returns duplex status (1=full duplex, 0=half duplex) or an error (-1).
+ */
+
+static int
+phy_duplex(int phy)
+{
+ union micrel_phy_auxiliary_control_status aux;
+
+ phy_read(phy, MICREL_PHY_AUXILIARY_CONTROL_STATUS, &aux.raw);
+
+ return (aux.bits.op_mode_indication >> 2) & 0x1;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_renegotiate
+ */
+
+static int
+phy_renegotiate(int phy)
+{
+ union phy_control control;
+ union phy_status status;
+ int autoneg_retries = 2;
+ int autoneg_complete_retries = 8;
+
+ pr_info("acp-femac: Initiating Auto Negotiation ");
+
+ /* Takes about 2 seconds for autoneg to complete ... */
+ do {
+ phy_read(phy, PHY_CONTROL, &control.raw);
+ control.bits.restart_autoneg = 1;
+ phy_write(phy, PHY_CONTROL, control.raw);
+
+ do {
+ pr_cont(".");
+ mdelay(500);
+ phy_read(phy, PHY_STATUS, &status.raw);
+ } while ((0 < --autoneg_complete_retries) &&
+ (0 == status.bits.autoneg_comp));
+
+ if (0 != status.bits.autoneg_comp)
+ break;
+
+ pr_cont(".");
+
+ } while (0 < --autoneg_retries);
+
+ if (0 == status.bits.autoneg_comp) {
+ pr_info("acp-femac: Auto Negotiation Failed\n");
+ return -1;
+ }
+
+ pr_info("acp-femac: Auto Negotiation Succeeded\n");
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * phy_enable
+ */
+
+static int phy_enable(struct net_device *device)
+{
+ struct appnic_device *dev = netdev_priv(device);
+
+ if (phy_write(dev->phy_address, PHY_AUTONEG_ADVERTISE,
+ (unsigned short)dev->ad_value) != 0)
+ return -1;
+
+ phy_renegotiate(dev->phy_address);
+
+ return 0;
+}
+
+#endif /* PHYLESS */
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Linux Network Driver Interface
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+/*
+ * ----------------------------------------------------------------------
+ * handle_transmit_interrupt
+ */
+
+static void handle_transmit_interrupt(struct net_device *device)
+{
+ struct appnic_device *dev = netdev_priv(device);
+
+ /*
+ * The hardware's tail pointer should be one descriptor (or more)
+ * ahead of software's copy.
+ */
+
+ while (0 < queue_initialized(SWAB_QUEUE_POINTER(dev->tx_tail),
+ dev->tx_tail_copy, dev->tx_num_desc)) {
+ queue_increment(&dev->tx_tail_copy, dev->tx_num_desc);
+ }
+
+ return;
+}
+
+/* static DECLARE_MUTEX(rpm); */
+
+/*
+ * ----------------------------------------------------------------------
+ * lsinet_rx_packet
+ */
+
+static void
+lsinet_rx_packet(struct net_device *device)
+{
+ struct appnic_device *adapter = netdev_priv(device);
+ struct appnic_dma_descriptor descriptor;
+ struct sk_buff *sk_buff;
+ unsigned bytes_copied = 0;
+ unsigned error_num = 0;
+ int return_code;
+ unsigned long ok_stat, overflow_stat, crc_stat, align_stat;
+
+ /*
+ * TEMP HACK:
+ * should use down_interruptible
+ */
+ spin_lock(&adapter->extra_lock);
+ readdescriptor(((unsigned long) adapter->rx_desc +
+ adapter->rx_tail_copy.bits.offset), &descriptor);
+
+ sk_buff = dev_alloc_skb(1600);
+
+ if ((struct sk_buff *) 0 == sk_buff) {
+ pr_err("acp-femac: dev_alloc_skb() failed! Dropping packet.\n");
+ spin_unlock(&adapter->extra_lock);
+ return;
+ }
+
+ ok_stat = read_mac(APPNIC_RX_STAT_PACKET_OK);
+ overflow_stat = read_mac(APPNIC_RX_STAT_OVERFLOW);
+ crc_stat = read_mac(APPNIC_RX_STAT_CRC_ERROR);
+ align_stat = read_mac(APPNIC_RX_STAT_ALIGN_ERROR);
+
+ /*
+ * Copy the received packet into the skb.
+ */
+
+ while (0 < queue_initialized(SWAB_QUEUE_POINTER(adapter->rx_tail),
+ adapter->rx_tail_copy, adapter->rx_num_desc)) {
+
+#ifdef PRELOAD_RX_BUFFERS
+ {
+ unsigned char *buffer;
+ buffer = skb_put(sk_buff, descriptor.pdu_length);
+ memcmp(buffer, buffer, descriptor.pdu_length);
+ memcpy((void *) buffer,
+ (void *) (descriptor.host_data_memory_pointer +
+ adapter->dma_alloc_offset),
+ descriptor.pdu_length);
+ }
+#else /* PRELOAD_RX_BUFFERS */
+ memcpy((void *) skb_put(sk_buff,
+ descriptor.pdu_length),
+ (void *) (descriptor.host_data_memory_pointer +
+ adapter->dma_alloc_offset),
+ descriptor.pdu_length);
+#endif /* PRELOAD_RX_BUFFERS */
+ bytes_copied += descriptor.pdu_length;
+ descriptor.data_transfer_length = adapter->rx_buf_per_desc;
+ writedescriptor(((unsigned long) adapter->rx_desc +
+ adapter->rx_tail_copy.bits.offset),
+ &descriptor);
+ if (0 != descriptor.error)
+ error_num = 1;
+ queue_increment(&adapter->rx_tail_copy,
+ adapter->rx_num_desc);
+ if (0 != descriptor.end_of_packet)
+ break;
+ readdescriptor(((unsigned long) adapter->rx_desc +
+ adapter->rx_tail_copy.bits.offset),
+ &descriptor);
+ }
+
+ if (0 == descriptor.end_of_packet) {
+ pr_err("acp-femac: No end of packet! %lu/%lu/%lu/%lu\n",
+ ok_stat, overflow_stat, crc_stat, align_stat);
+ BUG();
+ dev_kfree_skb(sk_buff);
+
+ } else {
+ if (0 == error_num) {
+ struct ethhdr *ethhdr =
+ (struct ethhdr *) sk_buff->data;
+ unsigned char broadcast[] = { 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff };
+ unsigned char multicast[] = { 0x01, 0x00 };
+
+ if ((0 == memcmp((const void *) &(ethhdr->h_dest[0]),
+ (const void *) &(device->dev_addr[0]),
+ sizeof(ethhdr->h_dest))) ||
+ (0 == memcmp((const void *)
+ &(ethhdr->h_dest[0]),
+ (const void *) &(broadcast[0]),
+ sizeof(ethhdr->h_dest))) ||
+ (0 == memcmp((const void *)
+ &(ethhdr->h_dest[0]),
+ (const void *) &(multicast[0]),
+ sizeof(multicast)))) {
+
+ adapter->stats.rx_bytes += bytes_copied;
+ ++adapter->stats.rx_packets;
+ sk_buff->dev = device;
+ sk_buff->protocol = eth_type_trans(sk_buff,
+ device);
+#ifdef LSINET_NAPI
+ return_code = netif_receive_skb(sk_buff);
+#else
+ return_code = netif_rx(sk_buff);
+#endif
+
+ if (NET_RX_DROP == return_code)
+ ++dropped_by_stack;
+ } else {
+ dev_kfree_skb(sk_buff);
+ }
+ } else {
+ dev_kfree_skb(sk_buff);
+
+ if (0 != overflow_stat)
+ ++adapter->stats.rx_fifo_errors;
+ else if (0 != crc_stat)
+ ++adapter->stats.rx_crc_errors;
+ else if (0 != align_stat)
+ ++adapter->stats.rx_frame_errors;
+ }
+ }
+
+ /* TEMP */
+ spin_unlock(&adapter->extra_lock);
+
+ /* That's all. */
+ return;
+}
+
+/*
+ * ============================================================================
+ * lsinet_rx_packets
+ */
+
+static int
+lsinet_rx_packets(struct net_device *device, int max)
+{
+ struct appnic_device *adapter = netdev_priv(device);
+ union appnic_queue_pointer queue;
+ int updated_head_pointer = 0;
+ int packets = 0;
+
+ queue.raw = adapter->rx_tail_copy.raw;
+
+ /* Receive Packets */
+
+ while (0 < queue_initialized(SWAB_QUEUE_POINTER(adapter->rx_tail),
+ queue, adapter->rx_num_desc)) {
+ struct appnic_dma_descriptor descriptor;
+
+ readdescriptor(((unsigned long) adapter->rx_desc +
+ queue.bits.offset),
+ &descriptor);
+
+ if (0 != descriptor.end_of_packet) {
+ lsinet_rx_packet(device);
+ ++packets;
+ queue.raw = adapter->rx_tail_copy.raw;
+
+ if ((-1 != max) && (packets == max))
+ break;
+ } else {
+ queue_increment(&queue, adapter->rx_num_desc);
+ }
+ }
+
+ /* Update the Head Pointer */
+
+ while (1 < queue_uninitialized(adapter->rx_head,
+ adapter->rx_tail_copy,
+ adapter->rx_num_desc)) {
+
+ struct appnic_dma_descriptor descriptor;
+
+ readdescriptor(((unsigned long) adapter->rx_desc +
+ adapter->rx_head.bits.offset), &descriptor);
+ descriptor.data_transfer_length = adapter->rx_buf_per_desc;
+ descriptor.write = 1;
+ descriptor.pdu_length = 0;
+ descriptor.start_of_packet = 0;
+ descriptor.end_of_packet = 0;
+ descriptor.interrupt_on_completion = 1;
+ writedescriptor(((unsigned long) adapter->rx_desc +
+ adapter->rx_head.bits.offset),
+ &descriptor);
+ queue_increment(&adapter->rx_head, adapter->rx_num_desc);
+ updated_head_pointer = 1;
+ }
+
+ if (0 != updated_head_pointer)
+ write_mac(adapter->rx_head.raw, APPNIC_DMA_RX_HEAD_POINTER);
+
+ return packets;
+}
+
+#ifdef LSINET_NAPI
+
+/*
+ * ============================================================================
+ * lsinet_poll
+ */
+
+static int
+lsinet_poll(struct napi_struct *napi, int budget)
+{
+ struct appnic_device *adapter =
+ container_of(napi, struct appnic_device, napi);
+ struct net_device *device = adapter->device;
+ union appnic_queue_pointer queue;
+ int status;
+
+ int cur_budget = budget;
+ int done;
+ unsigned long dma_interrupt_status;
+
+ queue.raw = adapter->rx_tail_copy.raw;
+
+ done = 1;
+
+ do {
+
+ /* Acknowledge the RX interrupt. */
+ write_mac(~APPNIC_DMA_INTERRUPT_ENABLE_RECEIVE,
+ APPNIC_DMA_INTERRUPT_STATUS);
+
+ cur_budget -= lsinet_rx_packets(device, cur_budget);
+ if (0 == cur_budget)
+ break;
+
+ dma_interrupt_status = read_mac(APPNIC_DMA_INTERRUPT_STATUS);
+
+ } while ((RX_INTERRUPT(dma_interrupt_status)) && cur_budget);
+
+ if (done) {
+ napi_complete(napi);
+ /* re-enable receive interrupts */
+ write_mac((APPNIC_DMA_INTERRUPT_ENABLE_RECEIVE |
+ APPNIC_DMA_INTERRUPT_ENABLE_TRANSMIT),
+ APPNIC_DMA_INTERRUPT_ENABLE);
+ }
+
+ status = (done ? 0 : 1);
+ return status;
+}
+
+#else /* Not using NAPI, define the Rx interrupt handler instead */
+
+/*
+ * ----------------------------------------------------------------------
+ * handle_receive_interrupt
+ */
+
+static void handle_receive_interrupt(struct net_device *device)
+{
+ struct appnic_device *dev = netdev_priv(device);
+ union appnic_queue_pointer queue;
+
+ queue.raw = dev_->rx_tail_copy.raw;
+ lsinet_rx_packets(device, -1);
+
+ return;
+}
+#endif /* else not NAPI */
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_isr
+ */
+
+static irqreturn_t appnic_isr(int irq, void *device_id)
+{
+ struct net_device *device = (struct net_device *) device_id;
+ struct appnic_device *dev = netdev_priv(device);
+ unsigned long dma_interrupt_status;
+ unsigned long flags;
+
+ /* Acquire the lock */
+ spin_lock_irqsave(&dev->lock, flags);
+
+
+ /* Get the status. */
+ dma_interrupt_status = read_mac(APPNIC_DMA_INTERRUPT_STATUS);
+
+#ifdef LSINET_NAPI
+ /* NAPI - don't ack RX interrupt */
+ write_mac(APPNIC_DMA_INTERRUPT_ENABLE_RECEIVE,
+ APPNIC_DMA_INTERRUPT_STATUS);
+#else
+ write_mac(0, APPNIC_DMA_INTERRUPT_STATUS);
+#endif
+
+ /* Handle interrupts */
+
+ if (TX_INTERRUPT(dma_interrupt_status)) {
+ /* transmition complete */
+ ++transmit_interrupts;
+ handle_transmit_interrupt(device);
+ }
+
+ if (RX_INTERRUPT(dma_interrupt_status)) {
+#ifdef LSINET_NAPI
+ ++receive_interrupts;
+ if (napi_schedule_prep(&dev->napi)) {
+
+ /*
+ * Disable RX interrupts and tell the
+ * system we've got work
+ */
+
+ write_mac(APPNIC_DMA_INTERRUPT_ENABLE_TRANSMIT,
+ APPNIC_DMA_INTERRUPT_ENABLE);
+ __napi_schedule(&dev->napi);
+ } else {
+ pr_err("acp-femac: NAPI bug! Int while in poll\n");
+ write_mac(APPNIC_DMA_INTERRUPT_ENABLE_TRANSMIT,
+ APPNIC_DMA_INTERRUPT_ENABLE);
+ }
+#else
+ /* Receive complete */
+ handle_receive_interrupt(device);
+#endif
+ }
+
+ /* release the lock */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_open
+ *
+ * Opens the interface. The interface is opened whenever ifconfig
+ * activates it. The open method should register any system resource
+ * it needs (I/O ports, IRQ, DMA, etc.) turn on the hardware, and
+ * increment the module usage count.
+ */
+
+int appnic_open(struct net_device *device)
+{
+ int return_code = 0;
+
+ /* Enable the receiver and transmitter. */
+ if (0 != enable_rx_tx(device)) {
+ pr_err("acp-femac: Unable to enable the appnic interface.\n");
+ disable_rx_tx();
+ return -EBUSY;
+ }
+
+#ifdef LSINET_NAPI
+ {
+ struct appnic_device *adapter = netdev_priv(device);
+ napi_enable(&adapter->napi);
+ }
+#endif /* LSINET_NAPI */
+
+ /* Install the interrupt handlers. */
+ return_code = request_irq(device->irq, appnic_isr, IRQF_DISABLED,
+ APPNIC_NAME, device);
+ if (0 != return_code) {
+ pr_err("acp-femac: request_irq() failed, returned 0x%x/%d\n",
+ return_code, return_code);
+ return return_code;
+ }
+
+ /* enable interrupts */
+ write_mac((APPNIC_DMA_INTERRUPT_ENABLE_RECEIVE |
+ APPNIC_DMA_INTERRUPT_ENABLE_TRANSMIT),
+ APPNIC_DMA_INTERRUPT_ENABLE);
+
+ {
+#ifndef PHYLESS
+ struct appnic_device *dev = netdev_priv(device);
+#endif
+
+#ifndef PHYLESS
+
+ init_timer(&appnic_timer);
+ appnic_timer.expires = jiffies + (APPNIC_TIMER_PERIOD * HZ);
+ appnic_timer.data = (unsigned long) device;
+ appnic_timer.function = appnic_timer_handler;
+ add_timer(&appnic_timer);
+ dev->polling = 1;
+
+#endif /* PHYLESS */
+
+ }
+
+ /* let the OS know we are ready to send packets */
+ netif_start_queue(device);
+
+ /* that's all */
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_stop
+ *
+ * Stops the interface. The interface is stopped when it is brought
+ * down; operations performed at open time should be reversed.
+ */
+
+int appnic_stop(struct net_device *device)
+{
+ int return_code = 0;
+
+ pr_info("acp-femac: Stopping the interface.\n");
+
+ /*
+ * Indicate to the OS that no more packets should be sent.
+ */
+
+ netif_stop_queue(device);
+
+ /*
+ * Stop the receiver and transmitter.
+ */
+
+ disable_rx_tx();
+
+ /* Disable NAPI. */
+#ifdef LSINET_NAPI
+ {
+ struct appnic_device *adapter = netdev_priv(device);
+ napi_disable(&adapter->napi);
+ }
+#endif
+
+ /*
+ * Free the interrupts.
+ */
+
+ free_irq(device->irq, device);
+
+#ifndef PHYLESS
+
+ del_timer(&appnic_timer);
+
+#endif /* PHYLESS */
+
+ /*
+ * That's all.
+ */
+
+ return return_code;
+
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_hard_start_xmit
+ *
+ * The method initiates the transmission of a packet. The full packet
+ * (protocol headers and all) is contained in a socket buffer (sk_buff)
+ * structure.
+ *
+ * ----- NOTES -----
+ *
+ * 1) This will not get called again by the kernel until it returns.
+ */
+
+int
+appnic_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *device)
+{
+ struct appnic_device *adapter = netdev_priv(device);
+ int length;
+ int buf_per_desc;
+
+ length = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
+ buf_per_desc = adapter->tx_buf_sz / adapter->tx_num_desc;
+
+ /*
+ * If enough transmit descriptors are available, copy and transmit.
+ */
+
+ while (((length / buf_per_desc) + 1) >=
+ queue_uninitialized(adapter->tx_head,
+ SWAB_QUEUE_POINTER(adapter->tx_tail),
+ adapter->tx_num_desc)) {
+ handle_transmit_interrupt(device);
+ }
+
+ if (((length / buf_per_desc) + 1) <
+ queue_uninitialized(adapter->tx_head,
+ SWAB_QUEUE_POINTER(adapter->tx_tail),
+ adapter->tx_num_desc)) {
+ int bytes_copied = 0;
+ struct appnic_dma_descriptor descriptor;
+
+ readdescriptor(((unsigned long) adapter->tx_desc +
+ adapter->tx_head.bits.offset), &descriptor);
+ descriptor.start_of_packet = 1;
+
+ while (bytes_copied < length) {
+ descriptor.write = 1;
+ descriptor.pdu_length = length;
+
+ if ((length - bytes_copied) > buf_per_desc) {
+ memcpy((void *)
+ (descriptor.host_data_memory_pointer +
+ adapter->dma_alloc_offset),
+ (void *) ((unsigned long) skb->data +
+ bytes_copied),
+ buf_per_desc);
+ descriptor.data_transfer_length = buf_per_desc;
+ descriptor.end_of_packet = 0;
+ descriptor.interrupt_on_completion = 0;
+ bytes_copied += buf_per_desc;
+ } else {
+ memcpy((void *)
+ (descriptor.host_data_memory_pointer +
+ adapter->dma_alloc_offset),
+ (void *) ((unsigned long) skb->data +
+ bytes_copied),
+ (length - bytes_copied));
+ descriptor.data_transfer_length =
+ (length - bytes_copied);
+ descriptor.end_of_packet = 1;
+#ifdef DISABLE_TX_INTERRUPTS
+ descriptor.interrupt_on_completion = 0;
+#else /* DISABLE_TX_INTERRUPTS */
+ descriptor.interrupt_on_completion = 1;
+#endif /* DISABLE_TX_INTERRUPTS */
+ bytes_copied = length;
+ }
+
+ adapter->stats.tx_bytes += bytes_copied;
+ writedescriptor(((unsigned long) adapter->tx_desc +
+ adapter->tx_head.bits.offset), &descriptor);
+ queue_increment(&adapter->tx_head,
+ adapter->tx_num_desc);
+ readdescriptor(((unsigned long) adapter->tx_desc +
+ adapter->tx_head.bits.offset), &descriptor);
+ descriptor.start_of_packet = 0;
+ }
+
+ write_mac(adapter->tx_head.raw, APPNIC_DMA_TX_HEAD_POINTER);
+ device->trans_start = jiffies;
+ } else {
+ ++out_of_tx_descriptors;
+ pr_err("acp-femac: No transmit descriptors available!\n");
+ }
+
+ /* Free the socket buffer */
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_net_device_stats
+ *
+ * Whenever an application needs to get statistics for the interface,
+ * this method is called. This happens, for example, when ifconfig or
+ * nstat -i is run.
+ */
+
+struct net_device_stats *appnic_get_stats(struct net_device *device)
+{
+ struct appnic_device *dev = netdev_priv(device);
+
+ /*
+ * Update the statistics structure.
+ */
+
+ get_hw_statistics(dev);
+
+ /*
+ * That's all.
+ */
+
+ return &dev->stats;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_set_mac_address
+ */
+
+static int
+appnic_set_mac_address(struct net_device *device, void *data)
+{
+ struct sockaddr *address = data;
+ unsigned long swap_source_address;
+
+ if (netif_running(device))
+ return -EBUSY;
+
+ memcpy(device->dev_addr, address->sa_data, 6);
+ memcpy(device->perm_addr, address->sa_data, 6);
+
+ swap_source_address = ((address->sa_data[4]) << 8) |
+ address->sa_data[5];
+ write_mac(swap_source_address, APPNIC_SWAP_SOURCE_ADDRESS_2);
+ swap_source_address = ((address->sa_data[2]) << 8) |
+ address->sa_data[3];
+ write_mac(swap_source_address, APPNIC_SWAP_SOURCE_ADDRESS_1);
+ swap_source_address = ((address->sa_data[0]) << 8) |
+ address->sa_data[1];
+ write_mac(swap_source_address, APPNIC_SWAP_SOURCE_ADDRESS_0);
+ memcpy(device->dev_addr, address->sa_data, device->addr_len);
+
+ return 0;
+}
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ ETHTOOL Operations
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_get_settings
+ */
+
+static int
+appnic_get_settings(struct net_device *device, struct ethtool_cmd *command)
+{
+ struct appnic_device *dev = netdev_priv(device);
+ unsigned short ad_value;
+ int speed;
+ int duplex;
+
+ memset(command, 0, sizeof(struct ethtool_cmd));
+
+ /* What the hardware supports. */
+
+ command->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full);
+
+#ifndef PHYLESS
+
+ /* What is currently advertised. */
+
+ if (0 != phy_read(dev->phy_address,
+ PHY_AUTONEG_ADVERTISE,
+ &ad_value)) {
+ pr_err("acp-femac: PHY read failed!\n");
+ return -EIO;
+ }
+
+ switch (ad_value) {
+ case 0x1e1:
+ command->advertising = (ADVERTISED_100baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_10baseT_Half);
+ break;
+
+ case 0xe1:
+ command->advertising = (ADVERTISED_100baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_10baseT_Half);
+ break;
+
+ case 0x61:
+ command->advertising = (ADVERTISED_10baseT_Full |
+ ADVERTISED_10baseT_Half);
+ break;
+
+ case 0x41:
+ default:
+ command->advertising = (ADVERTISED_10baseT_Half);
+ break;
+ }
+
+ /* The current speed. */
+
+ speed = phy_speed(dev->phy_address);
+ if (-1 == speed) {
+ pr_err("acp-femac: PHY read failed!\n");
+ return -EIO;
+ } else if (1 == speed) {
+ command->speed = SPEED_100;
+ } else {
+ command->speed = SPEED_10;
+ }
+
+ /* Is the current link duplex? */
+
+
+ duplex = phy_duplex(dev->phy_address);
+ if (-1 == duplex) {
+ pr_err("acp-femac: PHY read failed!\n");
+ return -EIO;
+ } else if (1 == duplex) {
+ command->duplex = DUPLEX_FULL;
+ } else {
+ command->duplex = DUPLEX_HALF;
+ }
+
+#endif /* PHYLESS */
+
+ /* Is autoneg enabled? */
+ command->autoneg = AUTONEG_ENABLE;
+
+ /* Return success. */
+ return 0;
+}
+
+/*
+ * Fill in the struture...
+ */
+
+static const struct ethtool_ops appnic_ethtool_ops = {
+ .get_settings = appnic_get_settings
+};
+
+
+/*
+ ======================================================================
+ ======================================================================
+ ======================================================================
+
+ Linux Module Interface.
+
+ ======================================================================
+ ======================================================================
+ ======================================================================
+*/
+
+static const struct net_device_ops appnic_netdev_ops = {
+ .ndo_open = appnic_open,
+ .ndo_stop = appnic_stop,
+ .ndo_get_stats = appnic_get_stats,
+ .ndo_set_mac_address = appnic_set_mac_address,
+ .ndo_start_xmit = appnic_hard_start_xmit,
+};
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_init
+ */
+
+int
+appnic_init(struct net_device *device)
+{
+ struct appnic_device *adapter = netdev_priv(device);
+ void *dma_offset;
+ int index;
+ unsigned long buf;
+ struct appnic_dma_descriptor descriptor;
+
+ /*
+ * Reset the MAC
+ */
+
+ write_mac(0x80000000, APPNIC_DMA_PCI_CONTROL);
+
+ /*
+ * Allocate memory and initialize the descriptors
+ */
+
+
+ /*
+ * fixup num_[rt]x_desc
+ */
+
+ if (0 != (rx_num_desc % DESCRIPTOR_GRANULARITY)) {
+ pr_warn("acp-femac: rx_num_desc was not a multiple of %d.\n",
+ DESCRIPTOR_GRANULARITY);
+ rx_num_desc += DESCRIPTOR_GRANULARITY -
+ (rx_num_desc % DESCRIPTOR_GRANULARITY);
+ }
+
+ adapter->rx_num_desc = rx_num_desc;
+
+ if (0 != (tx_num_desc % DESCRIPTOR_GRANULARITY)) {
+ pr_warn("acp-femac: tx_num_desc was not a multiple of %d.\n",
+ DESCRIPTOR_GRANULARITY);
+ tx_num_desc += DESCRIPTOR_GRANULARITY -
+ (tx_num_desc % DESCRIPTOR_GRANULARITY);
+ }
+
+ adapter->tx_num_desc = tx_num_desc;
+
+ /*
+ * up [rt]x_buf_sz. Must be some multiple of 64 bytes
+ * per descriptor.
+ */
+
+ if (0 != (rx_buf_sz % (BUFFER_ALIGNMENT * rx_num_desc))) {
+ pr_warn("acp-femac: rx_buf_sz was not a multiple of %d.\n",
+ (BUFFER_ALIGNMENT * rx_num_desc));
+ rx_buf_sz += (BUFFER_ALIGNMENT * rx_num_desc) -
+ (rx_buf_sz % (BUFFER_ALIGNMENT * rx_num_desc));
+ }
+
+ adapter->rx_buf_sz = rx_buf_sz;
+
+ if (0 != (tx_buf_sz % (BUFFER_ALIGNMENT * tx_num_desc))) {
+ pr_warn("acp-femac: tx_buf_sz was not a multiple of %d.\n",
+ (BUFFER_ALIGNMENT * tx_num_desc));
+ tx_buf_sz += (BUFFER_ALIGNMENT * tx_num_desc) -
+ (tx_buf_sz % (BUFFER_ALIGNMENT * tx_num_desc));
+ }
+
+ adapter->tx_buf_sz = tx_buf_sz;
+
+ /*
+ * Allocate dma-able memory
+ */
+
+ adapter->dma_alloc_size =
+ /* The tail pointers (rx and tx) */
+ (sizeof(union appnic_queue_pointer) * 2) +
+ /* The RX descriptor ring (and padding to allow
+ * 64 byte alignment) */
+ (sizeof(struct appnic_dma_descriptor) * adapter->rx_num_desc) +
+ (DESCRIPTOR_GRANULARITY) +
+ /* The TX descriptor ring (and padding...) */
+ (sizeof(struct appnic_dma_descriptor) * adapter->tx_num_desc) +
+ (DESCRIPTOR_GRANULARITY) +
+ /* The RX buffer (and padding...) */
+ (adapter->rx_buf_sz) + (BUFFER_ALIGNMENT) +
+ /* The TX buffer (and padding...) */
+ (adapter->tx_buf_sz) + (BUFFER_ALIGNMENT);
+
+#ifdef DMA_CACHABLE
+
+ adapter->dma_alloc = kmalloc(adapter->dma_alloc_size, GFP_KERNEL);
+
+ if ((void *)0 == adapter->dma_alloc) {
+ pr_err("Could not allocate %d bytes of DMA-able memory!\n",
+ adapter->dma_alloc_size);
+ kfree(adapter);
+ return -ENOMEM;
+ }
+
+ adapter->dma_alloc_dma = virt_to_phys(adapter->dma_alloc);
+
+#else
+ /*
+ * This needs to be set to something sane for
+ * dma_alloc_coherent()
+ */
+
+ device->dev.archdata.dma_ops = &dma_direct_ops;
+
+ adapter->dma_alloc = (void *) dma_alloc_coherent(&device->dev,
+ adapter->dma_alloc_size,
+ &adapter->dma_alloc_dma,
+ GFP_KERNEL);
+
+ if ((void *) 0 == adapter->dma_alloc) {
+ pr_err("Could not allocate %d bytes of DMA-able memory!\n",
+ adapter->dma_alloc_size);
+ kfree(adapter);
+ return -ENOMEM;
+ }
+#endif
+
+ adapter->dma_alloc_offset = (int)adapter->dma_alloc -
+ (int)adapter->dma_alloc_dma;
+ dma_offset = adapter->dma_alloc;
+
+ /*
+ * Initialize the tail pointers
+ */
+
+ adapter->rx_tail = (union appnic_queue_pointer *)dma_offset;
+ adapter->rx_tail_dma = (int)adapter->rx_tail -
+ (int)adapter->dma_alloc_offset;
+ dma_offset += sizeof(union appnic_queue_pointer);
+ memset((void *) adapter->rx_tail, 0,
+ sizeof(union appnic_queue_pointer));
+
+ adapter->tx_tail = (union appnic_queue_pointer *)dma_offset;
+ adapter->tx_tail_dma = (int)adapter->tx_tail -
+ (int)adapter->dma_alloc_offset;
+ dma_offset += sizeof(union appnic_queue_pointer);
+ memset((void *)adapter->tx_tail, 0,
+ sizeof(union appnic_queue_pointer));
+
+ /*
+ * Initialize the descriptor pointers
+ */
+
+ adapter->rx_desc = (struct appnic_dma_descriptor *)ALIGN64B(dma_offset);
+ adapter->rx_desc_dma = (int)adapter->rx_desc -
+ (int)adapter->dma_alloc_offset;
+ dma_offset += (sizeof(struct appnic_dma_descriptor) *
+ adapter->rx_num_desc) + (DESCRIPTOR_GRANULARITY);
+ memset((void *)adapter->rx_desc, 0,
+ (sizeof(struct appnic_dma_descriptor) * adapter->rx_num_desc));
+
+ adapter->tx_desc = (struct appnic_dma_descriptor *)ALIGN64B(dma_offset);
+ adapter->tx_desc_dma = (int)adapter->tx_desc -
+ (int)adapter->dma_alloc_offset;
+ dma_offset += (sizeof(struct appnic_dma_descriptor) *
+ adapter->tx_num_desc) + (DESCRIPTOR_GRANULARITY);
+ memset((void *)adapter->tx_desc, 0,
+ (sizeof(struct appnic_dma_descriptor) * adapter->tx_num_desc));
+
+ /*
+ * Initialize the buffer pointers
+ */
+
+ adapter->rx_buf = (void *)ALIGN64B(dma_offset);
+ adapter->rx_buf_dma = (int)adapter->rx_buf -
+ (int)adapter->dma_alloc_offset;
+ adapter->rx_buf_per_desc = adapter->rx_buf_sz / adapter->rx_num_desc;
+
+ dma_offset += (adapter->rx_buf_sz) + (BUFFER_ALIGNMENT);
+
+ adapter->tx_buf = (void *)ALIGN64B(dma_offset);
+ adapter->tx_buf_dma = (int)adapter->tx_buf -
+ (int)adapter->dma_alloc_offset;
+ adapter->tx_buf_per_desc =
+ adapter->tx_buf_sz / adapter->tx_num_desc;
+ dma_offset += (adapter->tx_buf_sz) + (BUFFER_ALIGNMENT);
+
+ /*
+ * Initialize the descriptors
+ */
+
+ buf = (unsigned long) adapter->rx_buf_dma;
+ for (index = 0; index < adapter->rx_num_desc; ++index) {
+ memset((void *) &descriptor, 0,
+ sizeof(struct appnic_dma_descriptor));
+ descriptor.write = 1;
+ descriptor.interrupt_on_completion = 1;
+ descriptor.host_data_memory_pointer = buf;
+ descriptor.data_transfer_length = adapter->rx_buf_per_desc;
+
+ writedescriptor(((unsigned long)adapter->rx_desc + (index *
+ sizeof(struct appnic_dma_descriptor))),
+ &descriptor);
+
+ buf += adapter->rx_buf_per_desc;
+ }
+
+ buf = (unsigned long)adapter->tx_buf_dma;
+
+ for (index = 0; index < adapter->tx_num_desc; ++index) {
+ memset((void *) &descriptor, 0,
+ sizeof(struct appnic_dma_descriptor));
+ descriptor.write = 1;
+ descriptor.interrupt_on_completion = 1;
+ descriptor.host_data_memory_pointer = buf;
+
+ writedescriptor(((unsigned long)adapter->tx_desc + (index *
+ sizeof(struct appnic_dma_descriptor))),
+ &descriptor);
+
+ buf += adapter->tx_buf_per_desc;
+ }
+
+ /*
+ * Initialize the spinlocks.
+ */
+
+ spin_lock_init(&adapter->lock);
+ spin_lock_init(&adapter->extra_lock);
+
+ /*
+ * Take MAC out of reset
+ */
+
+ write_mac(0x0, APPNIC_RX_SOFT_RESET);
+ write_mac(0x1, APPNIC_RX_MODE);
+ write_mac(0x0, APPNIC_TX_SOFT_RESET);
+ write_mac(0x1, APPNIC_TX_MODE);
+ if (is_asic())
+ write_mac(0x300a, APPNIC_TX_WATERMARK);
+ else
+ write_mac(0xc00096, APPNIC_TX_WATERMARK);
+ write_mac(0x1, APPNIC_TX_HALF_DUPLEX_CONF);
+ write_mac(0xffff, APPNIC_TX_TIME_VALUE_CONF);
+ write_mac(0x1, APPNIC_TX_INTERRUPT_CONTROL);
+ write_mac(0x5275, APPNIC_TX_EXTENDED_CONF);
+ write_mac(0x1, APPNIC_RX_INTERNAL_INTERRUPT_CONTROL);
+ write_mac(0x1, APPNIC_RX_EXTERNAL_INTERRUPT_CONTROL);
+ write_mac(0x40010000, APPNIC_DMA_PCI_CONTROL);
+ write_mac(0x30000, APPNIC_DMA_CONTROL);
+ writeio(0x280044, dma_base + 0x60);
+ writeio(0xc0, dma_base + 0x64);
+
+ /*
+ * Set the MAC address
+ */
+
+ {
+ struct sockaddr address;
+
+ memcpy(&(address.sa_data[0]), device->dev_addr, 6);
+ appnic_set_mac_address(device, &address);
+ }
+
+ /*
+ * Initialize the queue pointers.
+ */
+
+ /*
+ * Receiver
+ */
+
+ memset((void *) &adapter->rx_tail_copy,
+ 0, sizeof(union appnic_queue_pointer));
+ memset((void *) &adapter->rx_head,
+ 0, sizeof(union appnic_queue_pointer));
+
+ write_mac(adapter->rx_desc_dma, APPNIC_DMA_RX_QUEUE_BASE_ADDRESS);
+ write_mac((adapter->rx_num_desc *
+ sizeof(struct appnic_dma_descriptor)) / 1024,
+ APPNIC_DMA_RX_QUEUE_SIZE);
+
+ /*
+ * Indicate that all of the receive descriptors
+ * are ready
+ */
+
+ adapter->rx_head.bits.offset = (adapter->rx_num_desc - 1) *
+ sizeof(struct appnic_dma_descriptor);
+ write_mac(adapter->rx_tail_dma, APPNIC_DMA_RX_TAIL_POINTER_ADDRESS);
+
+ /*
+ * N.B.
+ *
+ * The boot loader may have used the NIC. If so, the
+ * tail pointer must be read and the head pointer (and
+ * local copy of the tail) based on it.
+ */
+
+ adapter->rx_tail->raw =
+ read_mac(APPNIC_DMA_RX_TAIL_POINTER_LOCAL_COPY);
+ adapter->rx_tail_copy.raw = adapter->rx_tail->raw;
+ adapter->rx_head.raw = adapter->rx_tail->raw;
+ queue_decrement(&adapter->rx_head, adapter->rx_num_desc);
+ adapter->rx_head.bits.generation_bit =
+ (0 == adapter->rx_head.bits.generation_bit) ? 1 : 0;
+ write_mac(adapter->rx_head.raw, APPNIC_DMA_RX_HEAD_POINTER);
+
+ /*
+ * Transmitter
+ */
+
+ memset((void *) &adapter->tx_tail_copy,
+ 0, sizeof(union appnic_queue_pointer));
+ memset((void *) &adapter->tx_head,
+ 0, sizeof(union appnic_queue_pointer));
+
+ write_mac(adapter->tx_desc_dma, APPNIC_DMA_TX_QUEUE_BASE_ADDRESS);
+ write_mac((adapter->tx_num_desc *
+ sizeof(struct appnic_dma_descriptor)) / 1024,
+ APPNIC_DMA_TX_QUEUE_SIZE);
+ write_mac(adapter->tx_tail_dma, APPNIC_DMA_TX_TAIL_POINTER_ADDRESS);
+
+ /*
+ * N.B.
+ *
+ * The boot loader may have used the NIC. If so, the
+ * tail pointer must be read and the head pointer (and
+ * local copy of the tail) based on it.
+ */
+
+ adapter->tx_tail->raw =
+ read_mac(APPNIC_DMA_TX_TAIL_POINTER_LOCAL_COPY);
+ adapter->tx_tail_copy.raw = adapter->tx_tail->raw;
+ adapter->tx_head.raw = adapter->tx_tail->raw;
+ write_mac(adapter->tx_head.raw, APPNIC_DMA_TX_HEAD_POINTER);
+
+ /* Clear statistics */
+ clear_statistics(adapter);
+
+ /* Initialize the PHY */
+#ifndef PHYLESS
+ if (0 != phy_enable(device))
+ pr_warn("acp-femac: Failed to initialize the PHY!\n");
+#endif /* PHYLESS */
+
+ /* Fill in the net_device structure */
+
+ ether_setup(device);
+ device->irq = irq_create_mapping(NULL, 33);
+ if (NO_IRQ == device) {
+ pr_err("acp-femac: irq_create_mapping() failed\n");
+ return -EBUSY;
+ }
+
+ if (0 != irq_set_irq_type(device->irq, IRQ_TYPE_LEVEL_HIGH)) {
+ pr_err("acp-femac: set_irq_type() failed\n");
+ return -EBUSY;
+ }
+
+ device->netdev_ops = &appnic_netdev_ops;
+
+ SET_ETHTOOL_OPS(device, &appnic_ethtool_ops);
+#ifdef LSINET_NAPI
+ memset((void *) &adapter->napi, 0, sizeof(struct napi_struct));
+ netif_napi_add(device, &adapter->napi,
+ lsinet_poll, LSINET_NAPI_WEIGHT);
+ adapter->device = device;
+#endif
+
+ /* That's all */
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * appnic_read_proc
+ */
+
+static int
+appnic_read_proc(char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int length;
+
+ length = sprintf(page, "-- appnic.c -- Profiling is disabled\n");
+
+ /* That's all */
+ return length;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * lsinet_init
+ */
+
+int
+lsinet_init(void)
+{
+ int rc = 0;
+ struct net_device *device;
+ struct device_node *np = NULL;
+ const u32 *field;
+ struct appnic_device *appnic_device;
+ unsigned char uboot_env_string[40];
+
+ /* Allocate space for the device. */
+
+ device = alloc_etherdev(sizeof(struct appnic_device));
+ if ((struct net_device *)0 == device) {
+ pr_err("acp-femac: Couldn't allocate net device.\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ this_net_device = device; /* For /proc/reads. */
+ appnic_device = netdev_priv(device);
+
+ /*
+ * Get the physical addresses, interrupt number, etc. from the
+ * device tree. If no entry exists (older boot loader...) just
+ * use the pre-devicetree method.
+ */
+
+ np = of_find_node_by_type(np, "network");
+
+ while (np && !of_device_is_compatible(np, "acp-femac"))
+ np = of_find_node_by_type(np, "network");
+
+ if (np) {
+ u64 value64;
+ u32 value32;
+ int length;
+
+ field = of_get_property(np, "enabled", NULL);
+
+ if (!field || (field && (0 == *field))) {
+ pr_err("acp-femac: Not Enabled\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ field = of_get_property(np, "reg", NULL);
+
+ if (!field) {
+ pr_err("acp-femac: Couldn't get \"reg\" property.\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ value64 = of_translate_address(np, field);
+ value32 = field[1];
+ field += 2;
+ rx_base = ioremap(value64, value32);
+ appnic_device->rx_base = (unsigned long)rx_base;
+ value64 = of_translate_address(np, field);
+ value32 = field[1];
+ field += 2;
+ tx_base = ioremap(value64, value32);
+ appnic_device->tx_base = (unsigned long)tx_base;
+ value64 = of_translate_address(np, field);
+ value32 = field[1];
+ field += 2;
+ dma_base = ioremap(value64, value32);
+ appnic_device->dma_base = (unsigned long)dma_base;
+
+ field = of_get_property(np, "interrupts", NULL);
+
+ if (!field)
+ goto device_tree_failed;
+ else
+ appnic_device->interrupt = field[0];
+
+ field = of_get_property(np, "mdio-clock", NULL);
+
+ if (!field)
+ goto device_tree_failed;
+ else
+ appnic_device->mdio_clock = field[0];
+
+ field = of_get_property(np, "phy-address", NULL);
+
+ if (!field)
+ goto device_tree_failed;
+ else
+ appnic_device->phy_address = field[0];
+
+ field = of_get_property(np, "ad-value", NULL);
+
+ if (!field)
+ goto device_tree_failed;
+ else
+ appnic_device->ad_value = field[0];
+
+ field = of_get_property(np, "mac-address", &length);
+
+ if (!field || 6 != length) {
+ goto device_tree_failed;
+ } else {
+ int i;
+ u8 *value;
+
+ value = (u8 *)field;
+
+ for (i = 0; i < 6; ++i)
+ appnic_device->mac_addr[i] = value[i];
+ }
+
+ memcpy(device->dev_addr, &appnic_device->mac_addr[0], 6);
+ memcpy(device->perm_addr, &appnic_device->mac_addr[0], 6);
+
+ goto device_tree_succeeded;
+
+device_tree_failed:
+ pr_err("acp-femac: Reading Device Tree Failed\n");
+ iounmap(rx_base);
+ iounmap(tx_base);
+ iounmap(dma_base);
+ rc = -EINVAL;
+ goto out;
+
+ } else {
+ /*
+ * Attempt to get device settings from the DTB failed, so
+ * try to grab the ethernet MAC from the u-boot environment
+ * and use hard-coded values for device base addresses.
+ */
+ unsigned char ethaddr_string[20];
+
+ if (0 != ubootenv_get("ethaddr", ethaddr_string)) {
+ pr_err("acp-femac: Could not read ethernet address!\n");
+ return -EBUSY;
+ }
+
+ {
+ char *string;
+ int i = 0;
+ u8 mac_address[6];
+
+ string = ethaddr_string;
+
+ while ((0 != string) && (6 > i)) {
+ char *value;
+ unsigned long res;
+ value = strsep(&string, ":");
+ if (kstrtoul(value, 16, &res))
+ return -EBUSY;
+ mac_address[i++] = (u8)res;
+ }
+
+ memcpy(device->dev_addr, mac_address, 6);
+ memcpy(device->perm_addr, mac_address, 6);
+ device->addr_len = 6;
+
+ pr_info(
+ "acp-femac: Using Static Addresses and Interrupts.\n");
+ rx_base = ioremap(0x002000480000ULL, 0x1000);
+ appnic_device->rx_base =
+ (unsigned long)ioremap(0x002000480000ULL, 0x1000);
+ tx_base = ioremap(0x002000481000ULL, 0x1000);
+ appnic_device->tx_base =
+ (unsigned long)ioremap(0x002000481000ULL, 0x1000);
+ dma_base = ioremap(0x002000482000ULL, 0x1000);
+ appnic_device->dma_base =
+ (unsigned long)ioremap(0x002000482000ULL, 0x1000);
+ appnic_device->interrupt = 33;
+ }
+ }
+
+device_tree_succeeded:
+
+ /* Override phy_address with u-boot environment variable if set. */
+ if (0 == ubootenv_get("phy_address", uboot_env_string)) {
+ /*
+ * User may have entered phy_address value in u-boot as
+ * either a decimal or hex value. Try reading both ways.
+ */
+ unsigned long res;
+ if (kstrtoul(uboot_env_string, 10, &res))
+ return -EBUSY;
+ appnic_device->phy_address = res;
+ if (appnic_device->phy_address == 0) {
+ unsigned long res;
+ ubootenv_get("phy_address", uboot_env_string);
+ if (kstrtoul(uboot_env_string, 16, &res))
+ return -EBUSY;
+ appnic_device->phy_address = res;
+ }
+ }
+
+ /* Override ad_value with u-boot environment variable if set. */
+ if (0 == ubootenv_get("ad_value", uboot_env_string)) {
+ /*
+ * Assume ad_value is always entered as a hex value,
+ * since u-boot defaults this value as hex.
+ */
+ unsigned long res;
+ if (kstrtoul(uboot_env_string, 16, &res))
+ return -EBUSY;
+ appnic_device->ad_value = res;
+ }
+
+ /* ad_value should never be 0. Use default if so ... */
+ if (appnic_device->ad_value == 0) {
+ appnic_device->ad_value = (PHY_AUTONEG_ADVERTISE_100FULL |
+ PHY_AUTONEG_ADVERTISE_100 |
+ PHY_AUTONEG_ADVERTISE_10FULL |
+ PHY_AUTONEG_ADVERTISE_10);
+ pr_err("acp-femac: Set ad_value to default of 0x%lx\n",
+ appnic_device->ad_value);
+ }
+
+ pr_info("acp-femac: ad_value = 0x%lx, phy_address = 0x%lx\n",
+ appnic_device->ad_value, appnic_device->phy_address);
+
+ /* Initialize the device. */
+
+ rc = appnic_init(device);
+ if (0 != rc) {
+ pr_err("acp-femac: appnic_init() failed: %d\n", rc);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ strcpy(this_net_device->name, "eth%d");
+
+ /* Register the device. */
+ rc = register_netdev(this_net_device);
+ if (0 != rc) {
+ pr_err("acp-femac: register_netdev() failed: %d\n", rc);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /* Create the /proc entry. */
+ create_proc_read_entry("driver/appnic", 0, NULL,
+ appnic_read_proc, NULL);
+
+out:
+ return rc;
+}
+
+module_init(lsinet_init);
+
+/*
+ * ----------------------------------------------------------------------
+ * lsinet_exit
+ */
+
+void __exit
+lsinet_exit(void)
+{
+ remove_proc_entry("driver/appnic", NULL);
+ unregister_netdev(this_net_device);
+
+ return;
+}
+
+module_exit(lsinet_exit);
--
1.8.3
More information about the linux-yocto
mailing list